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,7 +39,9 @@ public class ClassScope extends Scope { ...@@ -39,7 +39,9 @@ public class ClassScope extends Scope {
public void printTo(IndentPrintWriter pw) { public void printTo(IndentPrintWriter pw) {
TreeSet<Symbol> ss = new TreeSet<Symbol>(Symbol.LOCATION_COMPARATOR); TreeSet<Symbol> ss = new TreeSet<Symbol>(Symbol.LOCATION_COMPARATOR);
for (Symbol symbol : symbols.values()) { for (Symbol symbol : symbols.values()) {
ss.add(symbol); if (!symbol.isFunctionTable()) {
ss.add(symbol);
}
} }
pw.println("CLASS SCOPE OF '" + owner.getName() + "':"); pw.println("CLASS SCOPE OF '" + owner.getName() + "':");
pw.incIndent(); pw.incIndent();
......
package decaf.symbol; package decaf.symbol;
import java.util.Iterator;
import java.util.List;
import java.util.zip.CRC32;
import decaf.Driver; import decaf.Driver;
import decaf.Location; import decaf.Location;
import decaf.tree.Tree.Block; import decaf.tree.Tree.Block;
...@@ -107,6 +111,14 @@ public class Function extends Symbol { ...@@ -107,6 +111,14 @@ public class Function extends Symbol {
+ name + " : " + type; + 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 @Override
public boolean isClass() { public boolean isClass() {
return false; return false;
...@@ -117,4 +129,26 @@ public class Function extends Symbol { ...@@ -117,4 +129,26 @@ public class Function extends Symbol {
return false; 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 { ...@@ -63,6 +63,10 @@ public abstract class Symbol {
return type; return type;
} }
public void setName(String name) {
this.name = name;
}
public String getName() { public String getName() {
return name; return name;
} }
......
package decaf.typecheck; package decaf.typecheck;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import decaf.Driver; import decaf.Driver;
import decaf.tree.Tree; import decaf.tree.Tree;
import decaf.error.AmbiguousNewDeclarationError;
import decaf.error.BadArrElementError; import decaf.error.BadArrElementError;
import decaf.error.BadInheritanceError; import decaf.error.BadInheritanceError;
import decaf.error.BadOverrideError; import decaf.error.BadOverrideError;
import decaf.error.BadVarTypeError; import decaf.error.BadVarTypeError;
import decaf.error.ClassNotFoundError; import decaf.error.ClassNotFoundError;
import decaf.error.ConflictMainError;
import decaf.error.DecafError; import decaf.error.DecafError;
import decaf.error.DeclConflictError; import decaf.error.DeclConflictError;
import decaf.error.NoMainClassError; import decaf.error.NoMainClassError;
import decaf.error.OverridingVarError; import decaf.error.OverridingVarError;
import decaf.error.RedefinitionError;
import decaf.error.VarOverrideMethodError;
import decaf.scope.ClassScope; import decaf.scope.ClassScope;
import decaf.scope.GlobalScope; import decaf.scope.GlobalScope;
import decaf.scope.LocalScope; import decaf.scope.LocalScope;
import decaf.scope.ScopeStack; import decaf.scope.ScopeStack;
import decaf.symbol.Class; import decaf.symbol.Class;
import decaf.symbol.Function; import decaf.symbol.Function;
import decaf.symbol.FunctionTable;
import decaf.symbol.Symbol; import decaf.symbol.Symbol;
import decaf.symbol.Variable; import decaf.symbol.Variable;
import decaf.type.BaseType; import decaf.type.BaseType;
...@@ -137,21 +144,51 @@ public class BuildSym extends Tree.Visitor { ...@@ -137,21 +144,51 @@ public class BuildSym extends Tree.Visitor {
funcDef.returnType.accept(this); funcDef.returnType.accept(this);
Function f = new Function(funcDef.statik, funcDef.name, Function f = new Function(funcDef.statik, funcDef.name,
funcDef.returnType.type, funcDef.body, funcDef.getLocation()); 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; 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()); table.open(f.getAssociatedScope());
for (Tree.VarDef d : funcDef.formals) { for (Tree.VarDef d : funcDef.formals) {
d.accept(this); d.accept(this);
f.appendParam(d.symbol); f.appendParam(d.symbol);
f_signature.appendParam(d.symbol);
} }
funcDef.body.accept(this); funcDef.body.accept(this);
table.close(); 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 // visiting types
...@@ -262,23 +299,52 @@ public class BuildSym extends Tree.Visitor { ...@@ -262,23 +299,52 @@ public class BuildSym extends Tree.Visitor {
Symbol suspect = iter.next(); Symbol suspect = iter.next();
Symbol sym = table.lookup(suspect.getName(), true); Symbol sym = table.lookup(suspect.getName(), true);
if (sym != null && !sym.isClass()) { if (sym != null && !sym.isClass()) {
if ((suspect.isVariable() && sym.isFunction()) if (suspect.isFunctionTable() && sym.isVariable()) {
|| (suspect.isFunction() && sym.isVariable())) { List<Function> functions = ((FunctionTable) suspect).getFunctions();
issueError(new DeclConflictError(suspect.getLocation(), Iterator<Function> iter2 = functions.iterator();
suspect.getName(), sym.getLocation())); while (iter2.hasNext()) {
Function func = iter2.next();
issueError(new DeclConflictError(func.getLocation(),
func.getName(), sym.getLocation()));
}
iter.remove();
} else if (suspect.isVariable() && sym.isFunctionTable()) {
issueError(new VarOverrideMethodError(suspect.getName(), ((FunctionTable) sym).getFunctions(), suspect.getLocation()));
iter.remove(); iter.remove();
} else if (suspect.isFunction()) { } else if (suspect.isFunctionTable()) {
if (((Function) suspect).isStatik() List<Function> functions = ((FunctionTable) suspect).getFunctions();
|| ((Function) sym).isStatik()) { List<Function> parentFunctions = ((FunctionTable) sym).getFunctions();
issueError(new DeclConflictError(suspect.getLocation(), Iterator<Function> iter2 = functions.iterator();
suspect.getName(), sym.getLocation())); while (iter2.hasNext()) {
iter.remove(); Function func = iter2.next();
} else if (!suspect.getType().compatible(sym.getType())) { Iterator<Function> iter3 = parentFunctions.iterator();
issueError(new BadOverrideError(suspect.getLocation(), while (iter3.hasNext()) {
suspect.getName(), Function parentFunc = iter3.next();
((ClassScope) sym.getScope()).getOwner() if (func.conflictArgList(parentFunc)) {
.getName())); if (func.isStatik()
iter.remove(); || 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()) { } else if (suspect.isVariable()) {
issueError(new OverridingVarError(suspect.getLocation(), issueError(new OverridingVarError(suspect.getLocation(),
...@@ -296,14 +362,29 @@ public class BuildSym extends Tree.Visitor { ...@@ -296,14 +362,29 @@ public class BuildSym extends Tree.Visitor {
return false; return false;
} }
table.open(c.getAssociatedScope()); table.open(c.getAssociatedScope());
Symbol main = table.lookup(Driver.getDriver().getOption() Symbol mainSym = table.lookup(Driver.getDriver().getOption()
.getMainFuncName(), false); .getMainFuncName(), false);
if (main == null || !main.isFunction()) { if (mainSym == null || !mainSym.isFunctionTable()) {
return false; return false;
} }
((Function) main).setMain(true); FunctionTable mainTable = (FunctionTable) mainSym;
FuncType type = (FuncType) main.getType(); List<Function> functions = mainTable.getFunctions();
return type.getReturnType().equal(BaseType.VOID) 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(); && type.numOfParams() == 0 && ((Function) main).isStatik();
}
} }
} }
...@@ -39,9 +39,11 @@ import decaf.scope.ScopeStack; ...@@ -39,9 +39,11 @@ import decaf.scope.ScopeStack;
import decaf.scope.Scope.Kind; import decaf.scope.Scope.Kind;
import decaf.symbol.Class; import decaf.symbol.Class;
import decaf.symbol.Function; import decaf.symbol.Function;
import decaf.symbol.FunctionTable;
import decaf.symbol.Symbol; import decaf.symbol.Symbol;
import decaf.symbol.Variable; import decaf.symbol.Variable;
import decaf.type.*; import decaf.type.*;
import decaf.utils.FuncUtils;
public class TypeCheck extends Tree.Visitor { public class TypeCheck extends Tree.Visitor {
...@@ -135,32 +137,50 @@ public class TypeCheck extends Tree.Visitor { ...@@ -135,32 +137,50 @@ public class TypeCheck extends Tree.Visitor {
} }
} }
private void checkCallExpr(Tree.CallExpr callExpr, Symbol f) { @Override
Type receiverType = callExpr.receiver == null ? ((ClassScope) table public void visitCallExpr(Tree.CallExpr callExpr) {
.lookForScope(Scope.Kind.CLASS)).getOwner().getType() if (callExpr.receiver != null) {
: callExpr.receiver.type; callExpr.receiver.usedForRef = true;
if (f == null) { callExpr.receiver.accept(this);
issueError(new FieldNotFoundError(callExpr.getLocation(), if (callExpr.receiver.type.equal(BaseType.ERROR)) {
callExpr.method, receiverType.toString())); callExpr.type = BaseType.ERROR;
callExpr.type = BaseType.ERROR; return;
} else if (!f.isFunction()) { }
issueError(new NotClassMethodError(callExpr.getLocation(), if (callExpr.method.equals("length")) {
callExpr.method, receiverType.toString())); if (callExpr.receiver.type.isArrayType()) {
callExpr.type = BaseType.ERROR; if (callExpr.actuals.size() > 0) {
} else { issueError(new BadLengthArgError(callExpr.getLocation(),
Function func = (Function) f; callExpr.actuals.size()));
callExpr.symbol = func; }
callExpr.type = func.getReturnType(); callExpr.type = BaseType.INT;
if (callExpr.receiver == null && currentFunction.isStatik() callExpr.isArrayLength = true;
&& !func.isStatik()) { return;
issueError(new RefNonStaticError(callExpr.getLocation(), } else if (!callExpr.receiver.type.isClassType()) {
currentFunction.getName(), func.getName())); issueError(new BadLengthError(callExpr.getLocation()));
callExpr.type = BaseType.ERROR;
return;
}
} }
if (!func.isStatik() && callExpr.receiver != null
&& callExpr.receiver.isClass) { if (!callExpr.receiver.type.isClassType()) {
issueError(new NotClassFieldError(callExpr.getLocation(), issueError(new NotClassFieldError(callExpr.getLocation(),
callExpr.method, callExpr.receiver.type.toString())); callExpr.method, callExpr.receiver.type.toString()));
callExpr.type = BaseType.ERROR;
return;
} }
}
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()) { if (func.isStatik()) {
callExpr.receiver = null; callExpr.receiver = null;
} else { } else {
...@@ -169,74 +189,7 @@ public class TypeCheck extends Tree.Visitor { ...@@ -169,74 +189,7 @@ public class TypeCheck extends Tree.Visitor {
callExpr.receiver.accept(this); 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;
}
callExpr.receiver.usedForRef = true;
callExpr.receiver.accept(this);
if (callExpr.receiver.type.equal(BaseType.ERROR)) {
callExpr.type = BaseType.ERROR;
return;
} }
if (callExpr.method.equals("length")) {
if (callExpr.receiver.type.isArrayType()) {
if (callExpr.actuals.size() > 0) {
issueError(new BadLengthArgError(callExpr.getLocation(),
callExpr.actuals.size()));
}
callExpr.type = BaseType.INT;
callExpr.isArrayLength = true;
return;
} else if (!callExpr.receiver.type.isClassType()) {
issueError(new BadLengthError(callExpr.getLocation()));
callExpr.type = BaseType.ERROR;
return;
}
}
if (!callExpr.receiver.type.isClassType()) {
issueError(new NotClassFieldError(callExpr.getLocation(),
callExpr.method, callExpr.receiver.type.toString()));
callExpr.type = BaseType.ERROR;
return;
}
ClassScope cs = ((ClassType) callExpr.receiver.type)
.getClassScope();
checkCallExpr(callExpr, cs.lookupVisible(callExpr.method));
} }
@Override @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