Commit d5dd7e00 by Tianqi Yang

feat(overload): build the symbol table and check types for overload

Add FuncUtils: - getCallFunction: Get a suitable function symbol for a specific call or return an error Return value is of type getCallFunctionReturn - getArgListString: Get the string of a function with function name and parameter list Modify the process of building the symbol table: - visitMethodDef: Modify the function table with this name instead of inserting a single function Add the signature of this function into the table - checkOverride: Check overrides of overloaded functions - isMainClass: Issue errors if main function is overloaded Modify type checks: - call FuncUtils.getCallFunction to get the symbol of this function call Modify symbol Function: - toSignature: Return the signature of this function - conflictArgList: Check whether this function is conflicted with another
parent 9b68a48c
......@@ -39,8 +39,10 @@ public class ClassScope extends Scope {
public void printTo(IndentPrintWriter pw) {
TreeSet<Symbol> ss = new TreeSet<Symbol>(Symbol.LOCATION_COMPARATOR);
for (Symbol symbol : symbols.values()) {
if (!symbol.isFunctionTable()) {
ss.add(symbol);
}
}
pw.println("CLASS SCOPE OF '" + owner.getName() + "':");
pw.incIndent();
for (Symbol symbol : ss) {
......
package decaf.symbol;
import java.util.Iterator;
import java.util.List;
import java.util.zip.CRC32;
import decaf.Driver;
import decaf.Location;
import decaf.tree.Tree.Block;
......@@ -107,6 +111,14 @@ public class Function extends Symbol {
+ name + " : " + type;
}
public String toSignature() {
String s = toString();
CRC32 crc32 = new CRC32 ();
crc32.update(s.getBytes());
s = "FUNCTION_" + s.replaceAll("[^_0-9A-Za-z]", "_") + "_" + crc32.getValue();
return s;
}
@Override
public boolean isClass() {
return false;
......@@ -117,4 +129,26 @@ public class Function extends Symbol {
return false;
}
public boolean conflictArgList(Function f) {
List<Type> arg = getType().getArgList();
List<Type> farg = f.getType().getArgList();
if ((arg.size() - (isStatik() ? 0 : 1)) != farg.size() - (f.isStatik() ? 0 : 1)) {
return false;
}
Iterator<Type> iter = arg.iterator();
Iterator<Type> fiter = farg.iterator();
if (!isStatik()) {
iter.next();
}
if (!f.isStatik()) {
fiter.next();
}
for ( ; iter.hasNext(); ) {
if (!iter.next().equals(fiter.next())) {
return false;
}
}
return true;
}
}
......@@ -63,6 +63,10 @@ public abstract class Symbol {
return type;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
......
package decaf.typecheck;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import decaf.Driver;
import decaf.tree.Tree;
import decaf.error.AmbiguousNewDeclarationError;
import decaf.error.BadArrElementError;
import decaf.error.BadInheritanceError;
import decaf.error.BadOverrideError;
import decaf.error.BadVarTypeError;
import decaf.error.ClassNotFoundError;
import decaf.error.ConflictMainError;
import decaf.error.DecafError;
import decaf.error.DeclConflictError;
import decaf.error.NoMainClassError;
import decaf.error.OverridingVarError;
import decaf.error.RedefinitionError;
import decaf.error.VarOverrideMethodError;
import decaf.scope.ClassScope;
import decaf.scope.GlobalScope;
import decaf.scope.LocalScope;
import decaf.scope.ScopeStack;
import decaf.symbol.Class;
import decaf.symbol.Function;
import decaf.symbol.FunctionTable;
import decaf.symbol.Symbol;
import decaf.symbol.Variable;
import decaf.type.BaseType;
......@@ -137,21 +144,51 @@ public class BuildSym extends Tree.Visitor {
funcDef.returnType.accept(this);
Function f = new Function(funcDef.statik, funcDef.name,
funcDef.returnType.type, funcDef.body, funcDef.getLocation());
f.setScope(table.getCurrentScope());
Function f_signature = new Function(funcDef.statik, funcDef.name,
funcDef.returnType.type, funcDef.body, funcDef.getLocation());
funcDef.symbol = f;
Symbol sym = table.lookup(funcDef.name, false);
if (sym != null) {
issueError(new DeclConflictError(funcDef.getLocation(),
funcDef.name, sym.getLocation()));
} else {
table.declare(f);
}
table.open(f.getAssociatedScope());
for (Tree.VarDef d : funcDef.formals) {
d.accept(this);
f.appendParam(d.symbol);
f_signature.appendParam(d.symbol);
}
funcDef.body.accept(this);
table.close();
Symbol sym = table.lookup(funcDef.name, false);
if (sym != null) {
if (sym.isFunctionTable()) {
List<Function> functions = ((FunctionTable) sym).getFunctions();
Function conflict = null;
for (Function func : functions) {
if (f.conflictArgList(func)) {
conflict = func;
break;
}
}
if (conflict != null) {
if (f.getReturnType().equals(conflict.getReturnType())) {
issueError(new RedefinitionError(f, conflict, f.getLocation()));
} else {
issueError(new AmbiguousNewDeclarationError(f, conflict, f.getLocation()));
}
} else {
((FunctionTable) sym).appendFunction(f);
f_signature.setName(f.toSignature());
table.declare(f_signature);
}
} else {
issueError(new DeclConflictError(funcDef.getLocation(),
funcDef.name, sym.getLocation()));
}
} else{
FunctionTable funcTable = new FunctionTable(funcDef.name);
funcTable.appendFunction(f);
table.declare(funcTable);
f_signature.setName(f.toSignature());
table.declare(f_signature);
}
}
// visiting types
......@@ -262,23 +299,52 @@ public class BuildSym extends Tree.Visitor {
Symbol suspect = iter.next();
Symbol sym = table.lookup(suspect.getName(), true);
if (sym != null && !sym.isClass()) {
if ((suspect.isVariable() && sym.isFunction())
|| (suspect.isFunction() && sym.isVariable())) {
issueError(new DeclConflictError(suspect.getLocation(),
suspect.getName(), sym.getLocation()));
iter.remove();
} else if (suspect.isFunction()) {
if (((Function) suspect).isStatik()
|| ((Function) sym).isStatik()) {
issueError(new DeclConflictError(suspect.getLocation(),
suspect.getName(), sym.getLocation()));
if (suspect.isFunctionTable() && sym.isVariable()) {
List<Function> functions = ((FunctionTable) suspect).getFunctions();
Iterator<Function> iter2 = functions.iterator();
while (iter2.hasNext()) {
Function func = iter2.next();
issueError(new DeclConflictError(func.getLocation(),
func.getName(), sym.getLocation()));
}
iter.remove();
} else if (!suspect.getType().compatible(sym.getType())) {
issueError(new BadOverrideError(suspect.getLocation(),
suspect.getName(),
((ClassScope) sym.getScope()).getOwner()
.getName()));
} else if (suspect.isVariable() && sym.isFunctionTable()) {
issueError(new VarOverrideMethodError(suspect.getName(), ((FunctionTable) sym).getFunctions(), suspect.getLocation()));
iter.remove();
} else if (suspect.isFunctionTable()) {
List<Function> functions = ((FunctionTable) suspect).getFunctions();
List<Function> parentFunctions = ((FunctionTable) sym).getFunctions();
Iterator<Function> iter2 = functions.iterator();
while (iter2.hasNext()) {
Function func = iter2.next();
Iterator<Function> iter3 = parentFunctions.iterator();
while (iter3.hasNext()) {
Function parentFunc = iter3.next();
if (func.conflictArgList(parentFunc)) {
if (func.isStatik()
|| parentFunc.isStatik()) {
issueError(new DeclConflictError(func.getLocation(),
func.getName(), parentFunc.getLocation()));
iter2.remove();
}
}
}
}
iter2 = parentFunctions.iterator();
while (iter2.hasNext()) {
Function parentFunc = iter2.next();
Iterator<Function> iter3 = functions.iterator();
boolean overrided = false;
while (iter3.hasNext()) {
Function func = iter3.next();
if (func.conflictArgList(parentFunc)) {
overrided = true;
break;
}
}
if (!overrided) {
functions.add(parentFunc);
}
}
} else if (suspect.isVariable()) {
issueError(new OverridingVarError(suspect.getLocation(),
......@@ -296,14 +362,29 @@ public class BuildSym extends Tree.Visitor {
return false;
}
table.open(c.getAssociatedScope());
Symbol main = table.lookup(Driver.getDriver().getOption()
Symbol mainSym = table.lookup(Driver.getDriver().getOption()
.getMainFuncName(), false);
if (main == null || !main.isFunction()) {
if (mainSym == null || !mainSym.isFunctionTable()) {
return false;
}
((Function) main).setMain(true);
FunctionTable mainTable = (FunctionTable) mainSym;
List<Function> functions = mainTable.getFunctions();
if (functions.size() > 1) {
Iterator<Function> iter = functions.iterator();
List<Function> prevList = new LinkedList<>();
prevList.add(iter.next());
for ( ; iter.hasNext(); ) {
Function func = iter.next();
issueError(new ConflictMainError(func, prevList, func.getLocation()));
prevList.add(func);
}
return true;
} else {
Function main = mainTable.getFunctions().get(0);
main.setMain(true);
FuncType type = (FuncType) main.getType();
return type.getReturnType().equal(BaseType.VOID)
&& type.numOfParams() == 0 && ((Function) main).isStatik();
}
}
}
......@@ -39,9 +39,11 @@ import decaf.scope.ScopeStack;
import decaf.scope.Scope.Kind;
import decaf.symbol.Class;
import decaf.symbol.Function;
import decaf.symbol.FunctionTable;
import decaf.symbol.Symbol;
import decaf.symbol.Variable;
import decaf.type.*;
import decaf.utils.FuncUtils;
public class TypeCheck extends Tree.Visitor {
......@@ -135,76 +137,9 @@ public class TypeCheck extends Tree.Visitor {
}
}
private void checkCallExpr(Tree.CallExpr callExpr, Symbol f) {
Type receiverType = callExpr.receiver == null ? ((ClassScope) table
.lookForScope(Scope.Kind.CLASS)).getOwner().getType()
: callExpr.receiver.type;
if (f == null) {
issueError(new FieldNotFoundError(callExpr.getLocation(),
callExpr.method, receiverType.toString()));
callExpr.type = BaseType.ERROR;
} else if (!f.isFunction()) {
issueError(new NotClassMethodError(callExpr.getLocation(),
callExpr.method, receiverType.toString()));
callExpr.type = BaseType.ERROR;
} else {
Function func = (Function) f;
callExpr.symbol = func;
callExpr.type = func.getReturnType();
if (callExpr.receiver == null && currentFunction.isStatik()
&& !func.isStatik()) {
issueError(new RefNonStaticError(callExpr.getLocation(),
currentFunction.getName(), func.getName()));
}
if (!func.isStatik() && callExpr.receiver != null
&& callExpr.receiver.isClass) {
issueError(new NotClassFieldError(callExpr.getLocation(),
callExpr.method, callExpr.receiver.type.toString()));
}
if (func.isStatik()) {
callExpr.receiver = null;
} else {
if (callExpr.receiver == null && !currentFunction.isStatik()) {
callExpr.receiver = new Tree.ThisExpr(callExpr.getLocation());
callExpr.receiver.accept(this);
}
}
for (Tree.Expr e : callExpr.actuals) {
e.accept(this);
}
List<Type> argList = func.getType().getArgList();
int argCount = func.isStatik() ? callExpr.actuals.size()
: callExpr.actuals.size() + 1;
if (argList.size() != argCount) {
issueError(new BadArgCountError(callExpr.getLocation(),
callExpr.method, func.isStatik() ? argList.size()
: argList.size() - 1, callExpr.actuals.size()));
} else {
Iterator<Type> iter1 = argList.iterator();
if (!func.isStatik()) {
iter1.next();
}
Iterator<Tree.Expr> iter2 = callExpr.actuals.iterator();
for (int i = 1; iter1.hasNext(); i++) {
Type t1 = iter1.next();
Tree.Expr e = iter2.next();
Type t2 = e.type;
if (!t2.equal(BaseType.ERROR) && !t2.compatible(t1)) {
issueError(new BadArgTypeError(e.getLocation(), i,
t2.toString(), t1.toString()));
}
}
}
}
}
@Override
public void visitCallExpr(Tree.CallExpr callExpr) {
if (callExpr.receiver == null) {
ClassScope cs = (ClassScope) table.lookForScope(Kind.CLASS);
checkCallExpr(callExpr, cs.lookupVisible(callExpr.method));
return;
}
if (callExpr.receiver != null) {
callExpr.receiver.usedForRef = true;
callExpr.receiver.accept(this);
if (callExpr.receiver.type.equal(BaseType.ERROR)) {
......@@ -233,10 +168,28 @@ public class TypeCheck extends Tree.Visitor {
callExpr.type = BaseType.ERROR;
return;
}
}
ClassScope cs = ((ClassType) callExpr.receiver.type)
.getClassScope();
checkCallExpr(callExpr, cs.lookupVisible(callExpr.method));
for (Tree.Expr e : callExpr.actuals) {
e.accept(this);
}
FuncUtils.getCallFunctionReturn returnVal = FuncUtils.getCallFunction(callExpr.receiver, callExpr.method, callExpr.actuals, table, currentFunction, callExpr.getLocation());
if (!returnVal.ok) {
issueError(returnVal.error);
callExpr.type = BaseType.ERROR;
} else {
Function func = returnVal.symbol;
callExpr.symbol = func;
callExpr.type = func.getReturnType();
if (func.isStatik()) {
callExpr.receiver = null;
} else {
if (callExpr.receiver == null && !currentFunction.isStatik()) {
callExpr.receiver = new Tree.ThisExpr(callExpr.getLocation());
callExpr.receiver.accept(this);
}
}
}
}
@Override
......
package decaf.utils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import decaf.symbol.Function;
import decaf.tree.Tree;
import decaf.Location;
import decaf.error.*;
import decaf.scope.*;
import decaf.scope.Scope.Kind;
import decaf.symbol.*;
import decaf.type.*;
public final class FuncUtils
{
/**
* @return -1: argCountError, 0 : OK, positive: error at loc i
*/
public static int checkTypeListCompatible (boolean aIsStatik, boolean bIsStatik, List<Type> a, List<Type> b) {
int sizeA = a.size ();
int sizeB = b.size ();
if (!aIsStatik) {
--sizeA;
}
if (!bIsStatik) {
--sizeB;
}
if (sizeA != sizeB) {
return -1;
}
Iterator<Type> iter1 = a.iterator();
Iterator<Type> iter2 = b.iterator();
if (!aIsStatik) {
iter1.next();
}
if (!bIsStatik) {
iter2.next();
}
for (int i = 1; iter1.hasNext(); i++) {
Type t1 = iter1.next();
Type t2 = iter2.next();
if (!t1.compatible(t2)) {
return i;
}
}
return 0;
}
private static DecafError checkCallFunction (Tree.Expr receiver, List<Tree.Expr> argList, Function func, String name, Function currentFunction, Location loc) {
if (receiver == null && currentFunction.isStatik()
&& !func.isStatik()) {
return new RefNonStaticError(loc,
currentFunction.getName(), func.getName());
}
if (!func.isStatik() && receiver != null
&& receiver.isClass) {
return new NotClassFieldError(loc,
name, receiver.type.toString());
}
List<Type> typeList = new ArrayList <> ();
for (Tree.Expr e : argList) {
typeList.add(e.type);
}
List<Type> funcArgList = func.getType().getArgList();
int returnVal = checkTypeListCompatible(true, func.isStatik(), typeList, funcArgList);
if (returnVal == -1 ) {
return new BadArgCountError(loc, name, func.isStatik() ? funcArgList.size() : funcArgList.size() - 1, argList.size());
} else if (returnVal > 0 ) {
return new BadArgTypeError(argList.get(returnVal - 1).getLocation(), returnVal, typeList.get(returnVal - 1).toString(), funcArgList.get(func.isStatik() ? returnVal - 1 : returnVal).toString());
}
return null;
}
private static boolean checkFunctionCompatible (Function a, Function b) {
int returnVal = checkTypeListCompatible(a.isStatik(), b.isStatik(), a.getType().getArgList(), b.getType().getArgList());
return returnVal == 0;
}
public static class getCallFunctionReturn
{
public getCallFunctionReturn (Function func)
{
ok = true;
symbol = func;
}
public getCallFunctionReturn (DecafError decafError)
{
ok = false;
error = decafError;
}
public boolean ok;
public Function symbol;
public DecafError error;
}
public static getCallFunctionReturn getCallFunction (
Tree.Expr receiver, String name, List<Tree.Expr> argList,
ScopeStack table, Function currentFunction, Location loc) {
ClassScope cs;
if (receiver == null) {
cs = (ClassScope) table.lookForScope(Kind.CLASS);
} else {
cs = ((ClassType) receiver.type).getClassScope();
}
Symbol sym = cs.lookupVisible(name);
Type receiverType = receiver == null ? ((ClassScope) table
.lookForScope(Scope.Kind.CLASS)).getOwner().getType()
: receiver.type;
if (sym == null) {
return new getCallFunctionReturn (new FieldNotFoundError(loc,
name, receiverType.toString()));
} else if (!sym.isFunctionTable()) {
return new getCallFunctionReturn (new NotClassMethodError(loc,
name, receiverType.toString()));
} else{
List<Type> typeList = new ArrayList <> ();
for (Tree.Expr e : argList) {
typeList.add(e.type);
}
FunctionTable functable = (FunctionTable) sym;
List<DecafError> errorList = new ArrayList <> ();
List<Function> functions = functable.getFunctions();
for (Function func : functions) {
DecafError error = checkCallFunction(receiver, argList, func, name, currentFunction, loc);
errorList.add(error);
}
Function minFunction = null;
Iterator<DecafError> errorIter = errorList.iterator();
for (Function func : functions) {
DecafError error = errorIter.next();
if (error == null) {
if (minFunction == null || checkFunctionCompatible(func, minFunction)) {
minFunction = func;
}
}
}
if (minFunction == null) {
if (functions.size() == 1) {
return new getCallFunctionReturn (errorList.get(0));
} else {
return new getCallFunctionReturn (new NoMatchMethodError(name, typeList, functions, errorList, loc));
}
} else {
errorIter = errorList.iterator();
boolean ok = true;
for (Function func : functions) {
DecafError error = errorIter.next();
if (error == null) {
if (!checkFunctionCompatible(minFunction, func)) {
ok = false;
break;
}
}
}
if (ok) {
return new getCallFunctionReturn (minFunction);
} else {
List<Function> candidates = new ArrayList<>();
errorIter = errorList.iterator();
for (Function func : functions) {
if (errorIter.next() == null) {
boolean comp = false;
for (Function func2 : functions) {
if (!func2.equals(func) && checkFunctionCompatible(func2, func)) {
comp = true;
break;
}
}
if (!comp) {
candidates.add(func);
}
}
}
return new getCallFunctionReturn (new MethodCallAmbiguousError(name, typeList, candidates, loc));
}
}
}
}
public static String getArgListString (String name, List<Type> argList, boolean isStatik) {
String res = name + "(";
Iterator<Type> iter = argList.iterator();
if (!isStatik) iter.next();
boolean firstArg = true;
while (iter.hasNext()) {
if (firstArg) {
firstArg = false;
} else {
res += ", ";
}
res += iter.next();
}
res += ")";
return res;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment