From 0d19d221dc01822c3b6815a06a7c6b208bfaa965 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 31 Dec 2019 23:44:19 +0100 Subject: [PATCH 01/50] first steps towards supporting type classes --- .../AttrPossibleFunctionSignatures.java | 47 +++++++++++++- .../attributes/names/FuncLink.java | 5 ++ .../attributes/prettyPrint/PrettyPrinter.java | 7 +++ .../interpreter/EvaluateExpr.java | 57 ++++++++++++----- .../interpreter/ILInterpreter.java | 10 ++- .../interpreter/LocalState.java | 21 +++++++ .../intermediatelang/interpreter/State.java | 5 +- .../imtranslation/ExprTranslation.java | 21 +++++-- .../translation/imtranslation/Flatten.java | 5 +- .../imtranslation/ImTranslator.java | 23 ++++++- .../wurstscript/types/TypeClassInstance.java | 45 ++++++++++++++ .../wurstscript/types/VariableBinding.java | 2 +- .../types/WurstTypeBoundTypeParam.java | 47 ++++++++++---- .../wurstscript/types/WurstTypeTypeParam.java | 32 ++++++++++ .../validation/WurstValidator.java | 14 +++++ .../tests/GenericsWithTypeclassesTests.java | 62 +++++++++++++++++++ 16 files changed, 354 insertions(+), 49 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index f653eb4da..02278b3d1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -7,7 +7,11 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; +import de.peeeq.wurstscript.utils.Pair; +import fj.data.Option; +import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -47,8 +51,13 @@ private static ImmutableCollection findBestSignature(StmtCall List argTypes = AttrFuncDef.argumentTypes(fc); for (FunctionSignature sig : res) { FunctionSignature sig2 = sig.matchAgainstArgs(argTypes, fc); - if (sig2 != null) { - resultBuilder2.add(sig2); + Pair> typeClassMatched = findTypeClasses(sig2, fc); + if (typeClassMatched.getB().isEmpty()) { + resultBuilder2.add(typeClassMatched.getA()); + } else { + for (CompileError err : typeClassMatched.getB()) { + fc.getErrorHandler().sendError(err); + } } } ImmutableCollection res2 = resultBuilder2.build(); @@ -76,6 +85,40 @@ private static ImmutableCollection findBestSignature(StmtCall } } + private static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { + List errors = new ArrayList<>(); + VariableBinding mapping = sig.getMapping(); + for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { + Option matchedTypeOpt = mapping.get(tp); + List constraints = new ArrayList<>(); + if (tp.getTypeParamConstraints() instanceof TypeExprList) { + for (TypeExpr c : ((TypeExprList) tp.getTypeParamConstraints())) { + WurstType ct = c.attrTyp(); + if (ct instanceof WurstTypeInterface) { + constraints.add((WurstTypeInterface) ct); + } + } + } + if (matchedTypeOpt.isNone()) { + if (!constraints.isEmpty()) { + errors.add(new CompileError(fc.attrSource(), "Type parameter " + tp.getName() + " is not bound, so type constraints cannot be solved.")); + } + continue; + } + WurstTypeBoundTypeParam matchedType = matchedTypeOpt.some(); + for (WurstTypeInterface constraint : constraints) { + VariableBinding mapping2 = matchedType.matchAgainstSupertype(constraint, fc, mapping, VariablePosition.RIGHT); + if (mapping2 == null) { + errors.add(new CompileError(fc.attrSource(), "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint + ".")); + } else { + mapping = mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(constraint))); + } + } + } + sig = sig.setTypeArgs(fc, mapping); + return Pair.create(sig, errors); + } + public static ImmutableCollection calculate(ExprNewObject fc) { TypeDef typeDef = fc.attrTypeDef(); if (!(typeDef instanceof ClassDef)) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java index 49496a550..c801fee87 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java @@ -6,6 +6,7 @@ import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.VariableBinding; import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeTypeParam; import de.peeeq.wurstscript.types.WurstTypeVararg; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -288,4 +289,8 @@ public VariableBinding getVariableBinding() { public boolean hasIfNotDefinedAnnotation() { return def.attrHasAnnotation("ifNotDefined"); } + + public FuncLink withReceiverType(WurstType newReceiverType) { + return new FuncLink(getVisibility(), def, typeParams, newReceiverType, def, parameterNames, parameterTypes, returnType, mapping); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index 93bb07a9d..03fb9bb01 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -752,4 +752,11 @@ public static void prettyPrint(SomeSuperConstructorCall c, Spacer spacer, String public static void prettyPrint(NoTypeParamConstraints noTypeParamConstraints, Spacer spacer, StringBuilder sb, int indent) { // nothing } + + public static String print(Element element) { + Spacer spacer = new DefaultSpacer(); + StringBuilder sb = new StringBuilder(); + element.prettyPrint(spacer, sb, 0); + return sb.toString(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 26396c102..891840b5b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -9,9 +9,11 @@ import de.peeeq.wurstscript.intermediatelang.*; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.TypesHelper; +import fj.data.Either; import org.eclipse.jdt.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -29,21 +31,21 @@ public static ILconst eval(ImFuncRef e, ProgramState globalState, LocalState loc public static @Nullable ILconst eval(ImFunctionCall e, ProgramState globalState, LocalState localState) { ImFunction f = e.getFunc(); ImExprs arguments = e.getArguments(); - return evaluateFunc(globalState, localState, f, arguments, e); + return evaluateFunc(globalState, localState, f, e.getTypeArguments(), arguments, e); } public static @Nullable ILconst evaluateFunc(ProgramState globalState, - LocalState localState, ImFunction f, List args2, Element trace) { + LocalState localState, ImFunction f, List typeArguments, List args2, Element trace) { ILconst[] args = new ILconst[args2.size()]; for (int i = 0; i < args2.size(); i++) { args[i] = args2.get(i).evaluate(globalState, localState); } - return evaluateFunc(globalState, f, trace, args); + return evaluateFunc(globalState, f, trace, typeArguments, args); } @Nullable - private static ILconst evaluateFunc(ProgramState globalState, ImFunction f, Element trace, ILconst[] args) { - LocalState r = ILInterpreter.runFunc(globalState, f, trace, args); + private static ILconst evaluateFunc(ProgramState globalState, ImFunction f, Element trace, List typeArguments, ILconst[] args) { + LocalState r = ILInterpreter.runFunc(globalState, f, trace, typeArguments, args); return r.getReturnVal(); } @@ -185,26 +187,30 @@ public static ILconst eval(ImVarArrayAccess e, ProgramState globalState, LocalSt List args = mc.getArguments(); + ImMethod mostPrecise = findMostPreciseMethod(mc.attrTrace(), globalState, receiver, mc.getMethod()); - ImMethod mostPrecise = mc.getMethod(); + // execute most precise method + ILconst[] eargs = new ILconst[args.size() + 1]; + eargs[0] = receiver; + for (int i = 0; i < args.size(); i++) { + eargs[i + 1] = args.get(i).evaluate(globalState, localState); + } + return evaluateFunc(globalState, mostPrecise.getImplementation(), mc, mc.getTypeArguments(), eargs); + } + private static ImMethod findMostPreciseMethod(de.peeeq.wurstscript.ast.Element position, ProgramState globalState, ILconstObject receiver, ImMethod originalMethod) { // find correct implementation: - for (ImMethod m : mc.getMethod().getSubMethods()) { + ImMethod mostPrecise = originalMethod; + for (ImMethod m : originalMethod.getSubMethods()) { if (m.attrClass().isSubclassOf(mostPrecise.attrClass())) { - if (globalState.isInstanceOf(receiver, m.attrClass(), mc.attrTrace())) { + if (globalState.isInstanceOf(receiver, m.attrClass(), position)) { // found more precise method mostPrecise = m; } } } - // execute most precise method - ILconst[] eargs = new ILconst[args.size() + 1]; - eargs[0] = receiver; - for (int i = 0; i < args.size(); i++) { - eargs[i + 1] = args.get(i).evaluate(globalState, localState); - } - return evaluateFunc(globalState, mostPrecise.getImplementation(), mc, eargs); + return mostPrecise; } public static ILconst eval(ImMemberAccess ma, ProgramState globalState, LocalState localState) { @@ -393,8 +399,25 @@ public ILconst get() { public static ILconst eval(ImTypeVarDispatch e, ProgramState globalState, LocalState localState) { - // TODO store type arguments in localState with the required dispatch functions - throw new InterpreterException(e.attrTrace(), "Cannot evaluate " + e); + Either impl = localState.getImplementation(e.getTypeVariable(), e.getTypeClassFunc()); + if (impl == null) { + throw new InterpreterException(globalState, "Could not find implementation for " + e.getTypeVariable() + "." + e.getTypeClassFunc().getName()); + } + ILconst[] eArgs = e.getArguments().stream() + .map(arg -> arg.evaluate(globalState, localState)) + .toArray(ILconst[]::new); + + return impl.either( + (ImMethod m) -> { + ILconst receiver1 = eArgs[0]; + ILconstObject receiver = globalState.toObject(receiver1); + globalState.assertAllocated(receiver, e.attrTrace()); + ImMethod mostPreciseMethod = findMostPreciseMethod(e.attrTrace(), globalState, receiver, m); + return evaluateFunc(globalState, mostPreciseMethod.getImplementation(), e, Collections.emptyList(), eArgs); + }, + (ImFunction f) -> + evaluateFunc(globalState, f, e, Collections.emptyList(), eArgs) // TODO type var dispatch should also have type arguments? + ); } public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState localState) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 7c74152d5..4400806d3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -18,6 +18,8 @@ import java.io.File; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; public class ILInterpreter implements AbstractInterpreter { @@ -37,7 +39,7 @@ public ILInterpreter(ImProg prog, WurstGui gui, @Nullable File mapFile, boolean } public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullable Element caller, - ILconst... args) { + List typeArguments, ILconst... args) { if (Thread.currentThread().isInterrupted()) { throw new InterpreterException(globalState, "Execution interrupted"); } @@ -76,6 +78,8 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab } LocalState localState = new LocalState(); + localState.setTypeArguments(f.getTypeVariables(), typeArguments); + int i = 0; for (ImVar p : f.getParameters()) { localState.setVal(p, args[i]); @@ -184,7 +188,7 @@ public LocalState executeFunction(String funcName, @Nullable Element trace) { globalState.resetStackframes(); for (ImFunction f : prog.getFunctions()) { if (f.getName().equals(funcName)) { - return runFunc(globalState, f, trace); + return runFunc(globalState, f, trace, Collections.emptyList()); } } @@ -198,7 +202,7 @@ public void runVoidFunc(ImFunction f, @Nullable Element trace) { // this should only happen because of added stacktrace parameter args = new ILconstString[]{new ILconstString("initial call")}; } - runFunc(globalState, f, trace, args); + runFunc(globalState, f, trace, Collections.emptyList(), args); } public Element getLastStatement() { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index b2d093e1e..2f740c295 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -1,12 +1,20 @@ package de.peeeq.wurstscript.intermediatelang.interpreter; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; import de.peeeq.wurstscript.intermediatelang.ILconst; +import de.peeeq.wurstscript.jassIm.*; +import fj.data.Either; import org.eclipse.jdt.annotation.Nullable; +import java.util.List; +import java.util.Map; + public class LocalState extends State { private @Nullable ILconst returnVal = null; + private Table> typeClassImplementations = HashBasedTable.create(); public LocalState(ILconst returnVal) { this.setReturnVal(returnVal); @@ -25,4 +33,17 @@ public LocalState setReturnVal(@Nullable ILconst returnVal) { } + public Either getImplementation(ImTypeVar typeVariable, ImTypeClassFunc typeClassFunc) { + return typeClassImplementations.get(typeVariable, typeClassFunc); + } + + public void setTypeArguments(ImTypeVars typeVariables, List typeArguments) { + for (int i = 0; i < typeVariables.size() && i < typeArguments.size(); i++) { + ImTypeVar typeVariable = typeVariables.get(i); + ImTypeArgument typeArgument = typeArguments.get(i); + for (Map.Entry> e : typeArgument.getTypeClassBinding().entrySet()) { + typeClassImplementations.put(typeVariable, e.getKey(), e.getValue()); + } + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java index 4c3f2a080..680e2ad06 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java @@ -4,10 +4,7 @@ import de.peeeq.wurstio.jassinterpreter.InterpreterException; import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.ILconstArray; -import de.peeeq.wurstscript.jassIm.ImArrayLikeType; -import de.peeeq.wurstscript.jassIm.ImArrayTypeMulti; -import de.peeeq.wurstscript.jassIm.ImType; -import de.peeeq.wurstscript.jassIm.ImVar; +import de.peeeq.wurstscript.jassIm.*; import org.eclipse.jdt.annotation.Nullable; import java.util.List; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 910e78da5..01a5136eb 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -442,6 +442,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu List arguments = Lists.newArrayList(e.getArgs()); Expr leftExpr = null; boolean dynamicDispatch = false; + WurstTypeTypeParam typeParamDispatchOn = null; FunctionDefinition calledFunc = e.attrFuncDef(); @@ -453,6 +454,12 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu // TODO why would I add the implicit parameter here, if it is // not a dynamic dispatch? leftExpr = (Expr) e.attrImplicitParameter(); + + if (leftExpr.attrTyp() instanceof WurstTypeTypeParam) { + WurstTypeTypeParam tp = (WurstTypeTypeParam) leftExpr.attrTyp(); + typeParamDispatchOn = tp; + } + } // get real func def (override of module function) @@ -509,7 +516,13 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; - if (dynamicDispatch) { + if (typeParamDispatchOn != null) { + ImTypeClassFunc typeClassFunc = t.getTypeClassFuncFor((FuncDef) calledFunc); + if (receiver != null) { + imArgs.add(0, receiver); + } + call = JassIm.ImTypeVarDispatch(e, typeClassFunc, imArgs, t.getTypeVar(typeParamDispatchOn.getDef())); + } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false); @@ -545,10 +558,8 @@ private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, Fun if (!t.isTemplateTypeParameter()) { continue; } - ImType type = t.imTranslateType(tr); - // TODO handle constraints - Map> typeClassBinding = new HashMap<>(); - res.add(ImTypeArgument(type, typeClassBinding)); + ImTypeArgument imTypeArgument = t.imTranslateToTypeArgument(tr); + res.add(imTypeArgument); } return res; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 08735dafd..30aab80aa 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -58,8 +58,9 @@ public class Flatten { - public static Result flatten(ImTypeVarDispatch imTypeVarDispatch, ImTranslator translator, ImFunction f) { - throw new RuntimeException("called too early"); + public static Result flatten(ImTypeVarDispatch e, ImTranslator t, ImFunction f) { + MultiResult r = flattenExprs(t, f, e.getArguments()); + return new Result(r.stmts, ImTypeVarDispatch(e.getTrace(), e.getTypeClassFunc(), ImExprs(r.exprs), e.getTypeVariable())); } public static Result flatten(ImCast imCast, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index e59a79376..bbc1b5df7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1596,6 +1596,23 @@ public ImMethod getMethodFor(FuncDef f) { return m; } + private Map typeClassFuncForFuncDef = Maps.newLinkedHashMap(); + + public ImTypeClassFunc getTypeClassFuncFor(FuncDef f) { + ImTypeClassFunc m = typeClassFuncForFuncDef.get(f); + if (m == null) { + ImTypeVars typeVars = ImTypeVars(); + ImVars params = ImVars(); + ImType returnType = JassIm.ImVoid(); + m = JassIm.ImTypeClassFunc(f, f.getName(), typeVars, params, returnType); + typeClassFuncForFuncDef.put(f, m); + } + return m; + } + + + + public ClassManagementVars getClassManagementVarsFor(ImClass c) { return getClassManagementVars().get(c); } @@ -1656,14 +1673,14 @@ private ImFunction makeDefaultErrorFunc() { // TODO divide by zero to crash thread: -// stmts.add(JassAst.JassStmtCall("BJDebugMsg", +// stmts.add(JassAst.JassStmtCall("BJDebugMsg", // JassAst.JassExprlist(JassAst.JassExprBinary( -// JassAst.JassExprStringVal("|cffFF3A29Wurst Error:|r" + nl), +// JassAst.JassExprStringVal("|cffFF3A29Wurst Error:|r" + nl), // JassAst.JassOpPlus(), // s.getMessage().translate(translator))))); // // crash thread (divide by zero) // stmts.add(JassAst.JassStmtCall("I2S", JassAst.JassExprlist(JassAst.JassExprBinary(JassAst.JassExprIntVal("1"), JassAst.JassOpDiv(), Jas -// +// List flags = Lists.newArrayList(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java new file mode 100644 index 000000000..f38aec84e --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -0,0 +1,45 @@ +package de.peeeq.wurstscript.types; + +import de.peeeq.wurstscript.ast.FuncDef; +import de.peeeq.wurstscript.jassIm.ImFunction; +import de.peeeq.wurstscript.jassIm.ImMethod; +import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import fj.data.Either; + +import java.util.Map; + +public abstract class TypeClassInstance { + + + public static TypeClassInstance asSubtype(WurstTypeInterface interfaceType) { + return new SubtypeTypeClassInstance(interfaceType); + } + + public abstract void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding); + + static class SubtypeTypeClassInstance extends TypeClassInstance { + private WurstTypeInterface interfaceType; + + public SubtypeTypeClassInstance(WurstTypeInterface interfaceType) { + this.interfaceType = interfaceType; + } + + @Override + public void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding) { + interfaceType.getMemberMethods(interfaceType.getDef()).forEach(fl -> { + if (fl.getDef() instanceof FuncDef) { + FuncDef def = (FuncDef) fl.getDef(); + ImTypeClassFunc tcf = tr.getTypeClassFuncFor(def); + typeClassBinding.put(tcf, Either.left(tr.getMethodFor(def))); + } + }); + } + + @Override + public String toString() { + return "SubtypeTypeClassInstance<" + interfaceType + ">"; + } + } + +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java index 16015b1a7..ddcc6e889 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java @@ -97,7 +97,7 @@ public String toString() { } s.append(e._1().getName()); s.append(" -> "); - s.append(e._2().getBaseType()); + s.append(e._2().getName()); first = false; } s.append("]"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 084d1e828..853aa0040 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -1,14 +1,17 @@ package de.peeeq.wurstscript.types; +import com.google.common.collect.ImmutableList; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.ImplicitFuncs; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import de.peeeq.wurstscript.utils.Utils; import fj.data.Either; import org.eclipse.jdt.annotation.Nullable; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,7 +27,8 @@ public class WurstTypeBoundTypeParam extends WurstType { // the fromIndex and toIndex functions for old-generics: private FuncDef fromIndex; private FuncDef toIndex; - private final @Nullable Map typeConstraintFunctions; + // type class instances (null for old generics) + private final @Nullable List instances; private boolean indexInitialized = false; private Element context; @@ -36,12 +40,22 @@ public WurstTypeBoundTypeParam(TypeParamDef def, WurstType baseType, Element con this.baseType = baseType; this.context = context; if (def.getTypeParamConstraints() instanceof NoTypeParamConstraints) { - this.typeConstraintFunctions = null; + this.instances = null; } else { - this.typeConstraintFunctions = new HashMap<>(); + this.instances = Collections.emptyList(); } } + public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { + this.typeParamDef = typeParamDef; + this.baseType = baseType; + this.fromIndex = fromIndex; + this.toIndex = toIndex; + this.instances = instances; + this.indexInitialized = indexInitialized; + this.context = context; + } + @Override VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element location, VariableBinding mapping, VariablePosition variablePosition) { return baseType.matchAgainstSupertypeIntern(other, location, mapping, NONE); @@ -49,8 +63,9 @@ VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element l @Override public String getName() { - return baseType.getName(); -// return "[" + typeParamDef.getName() + ": " + baseType + "]"; +// return baseType.getName(); + String is = instances == null ? "" : " | " + Utils.printSep(", ", instances); + return "[" + typeParamDef.getName() + ": " + baseType + is + "]"; } @Override @@ -130,7 +145,7 @@ private void initIndex() { if (indexInitialized) { return; } - if (typeConstraintFunctions == null) { + if (instances == null) { if (!baseType.supportsGenerics()) { // if type does support generics natively, try to find implicit conversion functions fromIndex = ImplicitFuncs.findFromIndexFunc(baseType, context); @@ -147,7 +162,7 @@ private void initIndex() { @Override public boolean supportsGenerics() { return baseType.supportsGenerics() - || getFromIndex() != null && getToIndex() != null; + || getFromIndex() != null && getToIndex() != null; } @Override @@ -190,10 +205,6 @@ public boolean isTranslatedToInt() { return baseType.isTranslatedToInt(); } - public @Nullable Map getTypeConstraintFunctions() { - return typeConstraintFunctions; - } - public boolean isTemplateTypeParameter() { return typeParamDef.getTypeParamConstraints() instanceof TypeExprList; } @@ -201,7 +212,19 @@ public boolean isTemplateTypeParameter() { public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { ImType t = imTranslateType(tr); Map> typeClassBinding = new HashMap<>(); - // TODO add type class binding + for (TypeClassInstance instance : instances) { + instance.addTypeClassBinding(tr, typeClassBinding); + } return JassIm.ImTypeArgument(t, typeClassBinding); } + + public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { + ImmutableList newInstances = + ImmutableList.builderWithExpectedSize(instances.size() + 1) + .addAll(instances) + .add(instance) + .build(); + return new WurstTypeBoundTypeParam(typeParamDef, baseType, fromIndex, toIndex, newInstances, indexInitialized, context); + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 0eae15fcc..32778daaa 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -1,8 +1,10 @@ package de.peeeq.wurstscript.types; import de.peeeq.wurstscript.ast.Element; +import de.peeeq.wurstscript.ast.TypeExpr; import de.peeeq.wurstscript.ast.TypeExprList; import de.peeeq.wurstscript.ast.TypeParamDef; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; import de.peeeq.wurstscript.jassIm.JassIm; @@ -10,6 +12,10 @@ import fj.data.Option; import org.eclipse.jdt.annotation.Nullable; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class WurstTypeTypeParam extends WurstType { private TypeParamDef def; @@ -96,4 +102,30 @@ protected boolean isNullable() { return !hasTypeConstraints(); } + @Override + public void addMemberMethods(Element node, String name, List result) { + getMemberMethods(node) + .filter(fl -> fl.getName().equals(name)) + .collect(Collectors.toCollection(() -> result)); + } + + @Override + public Stream getMemberMethods(Element node) { + return getTypeConstraints() + .flatMap(i -> + i.getMemberMethods(node)) + .map(fl -> fl.withReceiverType(this)); + } + + private Stream getTypeConstraints() { + if (def.getTypeParamConstraints() instanceof TypeExprList) { + TypeExprList constraints = (TypeExprList) def.getTypeParamConstraints(); + return constraints.stream() + .map(TypeExpr::attrTyp) + .filter(t -> t instanceof WurstTypeInterface) + .map(t -> (WurstTypeInterface) t); + } else { + return Stream.empty(); + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index d84bb4954..3fd00ee4c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -339,6 +339,8 @@ private void check(Element e) { checkForInvalidStmts((WStatements) e); if (e instanceof StmtExitwhen) visit((StmtExitwhen) e); + if (e instanceof TypeParamDef) + checkTypeParamDef(((TypeParamDef) e)); } catch (CyclicDependencyError cde) { cde.printStackTrace(); Element element = cde.getElement(); @@ -350,6 +352,18 @@ private void check(Element e) { } } + private void checkTypeParamDef(TypeParamDef e) { + TypeParamConstraints constraints = e.getTypeParamConstraints(); + if (constraints instanceof TypeExprList) { + TypeExprList typeExprs = (TypeExprList) constraints; + for (TypeExpr te : typeExprs) { + if (!(te.attrTyp() instanceof WurstTypeInterface)) { + te.addError("Invalid type constraint " + te.attrTyp() + ". Type constraint must be an interface type."); + } + } + } + } + private void checkAbstractMethods(ClassDef c) { ImmutableMultimap nameLinks = c.attrNameLinks(); if (!c.attrIsAbstract()) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index da56c8e39..11e8ad31e 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1274,4 +1274,66 @@ public void abstractReturnT() { ); } + + @Test + public void typeClassMustBeInterface1() { + testAssertErrorsLines(false, "Could not find type ToIndex", + "package test", + "native testSuccess()", + "function foo(T x)", + " testSuccess()" + ); + } + + @Test + public void typeClassMustBeInterface2() { + testAssertErrorsLines(false, "Invalid type constraint ToIndex. Type constraint must be an interface type.", + "package test", + "native testSuccess()", + "tuple ToIndex(int x)", + "function foo(T x)", + " testSuccess()" + ); + } + + @Test + public void callMustSatisfyConstraint() { + testAssertErrorsLines(false, "Type integer does not satisfy constraint T: ToIndex", + "package test", + "native testSuccess()", + "interface ToIndex", + " function toIndex() returns int", + "function foo(T x)", + " testSuccess()", + "init", + " foo(42)" + ); + } + + @Test + @Ignore + public void simpleTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ToIndex", + " function toIndex() returns int", + "class A implements ToIndex", + " override function toIndex() returns int", + " return 42", + "function foo(T x)", + " if x.toIndex() == 42", + " testSuccess()", + "init", + " let a = new A", + " if a.toIndex() != 42", + " testFail(\"a\")", + " ToIndex i = a", + " if i.toIndex() != 42", + " testFail(\"b\")", + " foo(a)" + ); + } + } From 51ad081dff951736c4e3e36cebd10c49c63cc12a Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 1 Jan 2020 01:13:09 +0100 Subject: [PATCH 02/50] wip --- .../imtranslation/EliminateGenerics.java | 39 ++++++++++++++++--- .../translation/imtranslation/ImPrinter.java | 1 + .../lua/translation/ExprTranslation.java | 6 ++- .../lua/translation/LuaTranslator.java | 18 +++++++++ .../tests/GenericsWithTypeclassesTests.java | 1 - 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index 31e93ef0a..82d8bca41 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -8,6 +8,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtojass.ImAttrType; import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; +import fj.data.Either; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -146,7 +147,7 @@ private void moveFunctionsOutOfClass(ImClass c) { .stream() .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) .collect(Collectors.toList()); - rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables()); + rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables(), newTypeVars); } } @@ -188,11 +189,11 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) { ImFunction newF = f.copyWithRefs(); specializedFunctions.put(f, generics, newF); prog.getFunctions().add(newF); - newF.getTypeVariables().removeAll(); + List newTypeVars = newF.getTypeVariables().removeAll(); List typeVars = f.getTypeVariables(); newF.setName(f.getName() + "⟪" + generics.makeName() + "⟫"); - rewriteGenerics(newF, generics, typeVars); + rewriteGenerics(newF, generics, typeVars, newTypeVars); collectGenericUsages(newF); return newF; } @@ -253,7 +254,7 @@ private void adaptSubmethods(List oldSubMethods, ImMethod newM) { /** * Replaces all uses of the given typeVars with the type arguments given in parameter generics. */ - private void rewriteGenerics(Element element, GenericTypes generics, List typeVars) { + private void rewriteGenerics(Element element, GenericTypes generics, List typeVars, List newTypeVars) { if (generics.getTypeArguments().size() != typeVars.size()) { throw new RuntimeException("Rewrite generics with wrong sizes\n" + "generics: " + generics + "\n" + @@ -321,6 +322,32 @@ public void visit(ImDealloc e) { super.visit(e); } + @Override + public void visit(ImTypeVarDispatch e) { + super.visit(e); + ImTypeVar tv = e.getTypeVariable(); + int index = newTypeVars.indexOf(tv); + if (index < 0) { + throw new CompileError(e.attrTrace(), "Could not find type variable " + tv + " in " + newTypeVars); + } + ImTypeArgument ta = generics.getTypeArguments().get(index); + Either impl = ta.getTypeClassBinding().get(e.getTypeClassFunc()); + if (impl == null) { + throw new CompileError(e.attrTrace(), "Could not find func " + e.getTypeClassFunc().getName() + " in " + ta.getTypeClassBinding().keySet()); + } + ImExpr newExpr; + if (impl.isLeft()) { + ImMethod m = impl.left().value(); + ImExpr receiver = e.getArguments().remove(0); + e.getArguments().setParent(null); + newExpr = JassIm.ImMethodCall(e.getTrace(), m, JassIm.ImTypeArguments(), receiver, e.getArguments(), false); + } else { + ImFunction f = impl.right().value(); + e.getArguments().setParent(null); + newExpr = JassIm.ImFunctionCall(e.getTrace(), f, JassIm.ImTypeArguments(), e.getArguments(), false, CallType.NORMAL); + } + e.replaceBy(newExpr); + } }); } @@ -346,11 +373,11 @@ private ImClass specializeClass(ImClass c, GenericTypes generics) { newC.setSuperClasses(new ArrayList<>(newC.getSuperClasses())); specializedClasses.put(c, generics, newC); prog.getClasses().add(newC); - newC.getTypeVariables().removeAll(); + List newTypeVars = newC.getTypeVariables().removeAll(); newC.setName(c.getName() + "⟪" + generics.makeName() + "⟫"); List typeVars = c.getTypeVariables(); - rewriteGenerics(newC, generics, typeVars); + rewriteGenerics(newC, generics, typeVars, newTypeVars); newC.getSuperClasses().replaceAll(this::specializeType); // we don't collect generic usages to avoid infinite loops // in cases like class C { C> x; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index def61ce19..ac4fc33f4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -504,6 +504,7 @@ public static void print(ImVarargLoop e, Appendable sb, int indent) { public static void print(ImTypeVarDispatch e, Appendable sb, int indent) { append(sb, "<"); append(sb, e.getTypeVariable().getName()); + append(sb, smallHash(e.getTypeVariable())); append(sb, ">."); append(sb, e.getTypeClassFunc().getName()); printArgumentList(sb, indent, e.getArguments()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 0775dc668..7ba3a3132 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -330,8 +330,10 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat throw new Error("not implemented"); } - public static LuaExpr translate(ImTypeVarDispatch imTypeVarDispatch, LuaTranslator tr) { - throw new Error("not implemented"); + public static LuaExpr translate(ImTypeVarDispatch e, LuaTranslator tr) { + LuaVariable dict = tr.luaTypeClassDictionaryVar.getFor(e.getTypeVariable()); + LuaExprFieldAccess f = LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(dict), tr.typeClassFuncName.getFor(e.getTypeClassFunc())); + return LuaAst.LuaExprFunctionCallE(f, tr.translateExprList(e.getArguments())); } public static LuaExpr translate(ImCast imCast, LuaTranslator tr) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index 2b3906ce4..c153e7830 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -70,6 +70,24 @@ public LuaVariable initFor(ImVar a) { } }; + GetAForB luaTypeClassDictionaryVar = new GetAForB() { + @Override + public LuaVariable initFor(ImTypeVar a) { + String name = uniqueName(a.getName()); + return LuaAst.LuaVariable(name, LuaAst.LuaNoExpr()); + } + }; + + GetAForB typeClassFuncName = new GetAForB() { + @Override + public String initFor(ImTypeClassFunc a) { + return uniqueName(a.getName()); + } + }; + + + + GetAForB luaFunc = new GetAForB() { @Override diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 11e8ad31e..e79e34de6 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1311,7 +1311,6 @@ public void callMustSatisfyConstraint() { } @Test - @Ignore public void simpleTypeClass() { testAssertOkLines(true, "package test", From e7ddf901f059c7fbf1baff9c9c82c29039219e7a Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sat, 4 Jan 2020 02:19:13 +0100 Subject: [PATCH 03/50] wip --- .../parserspec/jass_im.parseq | 11 +-- .../interpreter/EvaluateExpr.java | 22 ----- .../interpreter/ILInterpreter.java | 1 - .../interpreter/LocalState.java | 15 ---- .../optimizer/SideEffectAnalyzer.java | 5 -- .../translation/imtojass/ExprTranslation.java | 3 - .../translation/imtojass/ImAttrType.java | 4 - .../translation/imtojass/TypeEquality.java | 3 - .../imtojass/TypeRewriteMatcher.java | 2 +- .../imtranslation/AssertProperty.java | 5 -- .../imtranslation/ClassTranslator.java | 6 +- .../imtranslation/ClosureTranslator.java | 6 +- .../imtranslation/EliminateGenerics.java | 30 +------ .../imtranslation/ExprTranslation.java | 17 ++-- .../translation/imtranslation/Flatten.java | 4 - .../imtranslation/GenericTypes.java | 3 - .../translation/imtranslation/ImPrinter.java | 14 +-- .../imtranslation/ImTranslator.java | 85 +++++++++++++++---- .../imtranslation/InterfaceTranslator.java | 2 +- .../lua/translation/ExprTranslation.java | 5 -- .../lua/translation/LuaTranslator.java | 6 -- .../wurstscript/types/TypeClassInstance.java | 12 --- .../types/WurstTypeBoundTypeParam.java | 7 +- .../wurstscript/types/WurstTypeTypeParam.java | 2 +- 24 files changed, 91 insertions(+), 179 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 006a6cf62..48519554c 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -9,13 +9,11 @@ ImProg( ImFunctions functions, ImMethods methods, ImClasses classes, - ImTypeClassFuncs typeClassFunctions, java.util.Map> globalInits) ImVars * ImVar ImFunctions * ImFunction ImClasses * ImClass -ImTypeClassFuncs * ImTypeClassFunc ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, boolean isBJ) @@ -46,11 +44,6 @@ ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImStmts body, java.util.List flags) -ImTypeClassFunc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, - ImTypeVars typeVariables, - ImVars parameters, - ref ImType returnType) ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, @@ -100,8 +93,6 @@ ImExpr = | ImGetStackTrace() | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr - | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImTypeClassFunc typeClassFunc, ImExprs arguments - , ref ImTypeVar typeVariable) | ImCast(ImExpr expr, ref ImType toType) // an expression which can be used on the left hand side of an assignment @@ -148,7 +139,7 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type, java.util.Map> typeClassBinding) +ImTypeArgument(ref ImType type) // helper types: diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 38ca0ed46..8c61cd50f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -398,28 +398,6 @@ public ILconst get() { } - public static ILconst eval(ImTypeVarDispatch e, ProgramState globalState, LocalState localState) { - Either impl = localState.getImplementation(e.getTypeVariable(), e.getTypeClassFunc()); - if (impl == null) { - throw new InterpreterException(globalState, "Could not find implementation for " + e.getTypeVariable() + "." + e.getTypeClassFunc().getName()); - } - ILconst[] eArgs = e.getArguments().stream() - .map(arg -> arg.evaluate(globalState, localState)) - .toArray(ILconst[]::new); - - return impl.fold( - (ImMethod m) -> { - ILconst receiver1 = eArgs[0]; - ILconstObject receiver = globalState.toObject(receiver1); - globalState.assertAllocated(receiver, e.attrTrace()); - ImMethod mostPreciseMethod = findMostPreciseMethod(e.attrTrace(), globalState, receiver, m); - return evaluateFunc(globalState, mostPreciseMethod.getImplementation(), e, Collections.emptyList(), eArgs); - }, - (ImFunction f) -> - evaluateFunc(globalState, f, e, Collections.emptyList(), eArgs) // TODO type var dispatch should also have type arguments? - ); - } - public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState localState) { ILconst res = imCast.getExpr().evaluate(globalState, localState); if (TypesHelper.isIntType(imCast.getToType())) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 9dbd285ad..64f841238 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -79,7 +79,6 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab } LocalState localState = new LocalState(); - localState.setTypeArguments(f.getTypeVariables(), typeArguments); int i = 0; for (ImVar p : f.getParameters()) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index e762fe6b8..64c08c608 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -14,7 +14,6 @@ public class LocalState extends State { private @Nullable ILconst returnVal = null; - private Table> typeClassImplementations = HashBasedTable.create(); public LocalState(ILconst returnVal) { this.setReturnVal(returnVal); @@ -32,18 +31,4 @@ public LocalState setReturnVal(@Nullable ILconst returnVal) { return this; } - - public Either getImplementation(ImTypeVar typeVariable, ImTypeClassFunc typeClassFunc) { - return typeClassImplementations.get(typeVariable, typeClassFunc); - } - - public void setTypeArguments(ImTypeVars typeVariables, List typeArguments) { - for (int i = 0; i < typeVariables.size() && i < typeArguments.size(); i++) { - ImTypeVar typeVariable = typeVariables.get(i); - ImTypeArgument typeArgument = typeArguments.get(i); - for (Map.Entry> e : typeArgument.getTypeClassBinding().entrySet()) { - typeClassImplementations.put(typeVariable, e.getKey(), e.getValue()); - } - } - } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java index b8c341a10..58abbb566 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java @@ -96,11 +96,6 @@ public Boolean case_ImGetStackTrace(ImGetStackTrace imGetStackTrace) { return true; } - @Override - public Boolean case_ImTypeVarDispatch(ImTypeVarDispatch imTypeVarDispatch) { - return true; - } - @Override public Boolean case_ImOperatorCall(ImOperatorCall e) { return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java index 6545d6694..5cdf12555 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java @@ -140,9 +140,6 @@ public static JassExpr translate(ImCompiletimeExpr e, ImToJassTranslator transla "Enable '-runcompiletimefunctions' to evaluate compiletime expressions."); } - public static JassExpr translate(ImTypeVarDispatch e, ImToJassTranslator translator) { - throw new CompileError(e, "Typevar dispatch not eliminated."); - } public static JassExpr translate(ImCast imCast, ImToJassTranslator translator) { return imCast.getExpr().translate(translator); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index 26695972d..66c542a34 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -212,10 +212,6 @@ public static ImType getType(ImCompiletimeExpr e) { return e.getExpr().attrTyp(); } - public static ImType getType(ImTypeVarDispatch e) { - return e.getTypeClassFunc().getReturnType(); - } - public static ImType getType(ImCast imCast) { return imCast.getToType(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java index d5acdfa81..ab5f74280 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java @@ -75,9 +75,6 @@ public static boolean isEqualType(ImClassType c, ImType other) { if (!x.getType().equalsType(y.getType())) { return false; } - if (!x.getTypeClassBinding().equals(y.getTypeClassBinding())) { - return false; - } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java index 0f9b9b455..ad7481653 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java @@ -53,7 +53,7 @@ public ImType case_ImArrayType(ImArrayType t) { public ImType case_ImClassType(ImClassType t) { ImTypeArguments args = t.getTypeArguments() .stream() - .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this), ta.getTypeClassBinding())) + .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(t.getClassDef(), args); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java index dc5e6669d..a5b5caf79 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java @@ -43,16 +43,11 @@ public void check(Element e) { ImFunction f = (ImFunction) e; currentFunction = f; checkType(e, (f).getReturnType()); - } else if (e instanceof ImTypeClassFunc) { - checkType(e, ((ImTypeClassFunc) e).getReturnType()); } else if (e instanceof ImMethod) { checkType(e, ((ImMethod) e).getMethodClass()); checkRooted(e, ((ImMethod) e).getImplementation()); } else if (e instanceof ImVarargLoop) { checkRooted(e, ((ImVarargLoop) e).getLoopVar()); - } else if (e instanceof ImTypeVarDispatch) { - checkRooted(e, ((ImTypeVarDispatch) e).getTypeClassFunc()); - checkRooted(e, ((ImTypeVarDispatch) e).getTypeVariable()); } else if (e instanceof ImVarAccess) { checkRooted(e, ((ImVarAccess) e).getVar()); } else if (e instanceof ImVarArrayAccess) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index d96a3afef..bb2f46d36 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -124,7 +124,7 @@ private void createDestroyMethod(List subClasses) { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } @@ -372,7 +372,7 @@ private void createNewFunc(ConstructorDef constr) { } ImTypeArguments typeArgs = ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, constrFunc, typeArgs, arguments, false, CallType.NORMAL)); @@ -412,7 +412,7 @@ private void createConstructFunc(ConstructorDef constr) { // call classInitFunc: ImTypeArguments typeArguments = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, classInitFunc, typeArguments, JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); // constructor user code diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java index b13becbdb..d55efaa8d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java @@ -64,7 +64,7 @@ public ImExpr translate() { private ImTypeArguments getClassTypeArguments() { ImTypeArguments res = JassIm.ImTypeArguments(); for (ImTypeVar typeVar : typeVars.keySet()) { - res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar), Collections.emptyMap())); + res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar))); } return res; } @@ -229,9 +229,7 @@ public ImType case_ImTypeVarRef(ImTypeVarRef t) { result.put(oldTypevar, newTypevar); c.getTypeVariables().add(newTypevar); thisType.getTypeArguments().add( - JassIm.ImTypeArgument( - JassIm.ImTypeVarRef(newTypevar), - Collections.emptyMap())); + JassIm.ImTypeArgument(JassIm.ImTypeVarRef(newTypevar))); } return JassIm.ImTypeVarRef(newTypevar); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index c034378ca..581d0d9a9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -145,7 +145,7 @@ private void moveFunctionsOutOfClass(ImClass c) { f.getTypeVariables().addAll(0, newTypeVars); List typeArgs = newTypeVars .stream() - .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) + .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta))) .collect(Collectors.toList()); rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables(), newTypeVars); } @@ -322,32 +322,6 @@ public void visit(ImDealloc e) { super.visit(e); } - @Override - public void visit(ImTypeVarDispatch e) { - super.visit(e); - ImTypeVar tv = e.getTypeVariable(); - int index = newTypeVars.indexOf(tv); - if (index < 0) { - throw new CompileError(e.attrTrace(), "Could not find type variable " + tv + " in " + newTypeVars); - } - ImTypeArgument ta = generics.getTypeArguments().get(index); - Either impl = ta.getTypeClassBinding().get(e.getTypeClassFunc()); - if (impl == null) { - throw new CompileError(e.attrTrace(), "Could not find func " + e.getTypeClassFunc().getName() + " in " + ta.getTypeClassBinding().keySet()); - } - ImExpr newExpr; - if (impl.isLeft()) { - ImMethod m = impl.getLeft(); - ImExpr receiver = e.getArguments().remove(0); - e.getArguments().setParent(null); - newExpr = JassIm.ImMethodCall(e.getTrace(), m, JassIm.ImTypeArguments(), receiver, e.getArguments(), false); - } else { - ImFunction f = impl.get(); - e.getArguments().setParent(null); - newExpr = JassIm.ImFunctionCall(e.getTrace(), f, JassIm.ImTypeArguments(), e.getArguments(), false, CallType.NORMAL); - } - e.replaceBy(newExpr); - } }); } @@ -697,7 +671,7 @@ public ImType case_ImClassType(ImClassType t) { private List specializeTypeArgs(ImTypeArguments typeArgs) { return typeArgs .stream() - .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()), ta.getTypeClassBinding())) + .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()))) .collect(Collectors.toList()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index edc80b021..2f380e803 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -451,13 +451,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu dynamicDispatch = true; } // add implicit parameter to front - // TODO why would I add the implicit parameter here, if it is - // not a dynamic dispatch? + // note: might not be dynamic, in case of extension function) leftExpr = (Expr) e.attrImplicitParameter(); if (leftExpr.attrTyp() instanceof WurstTypeTypeParam) { - WurstTypeTypeParam tp = (WurstTypeTypeParam) leftExpr.attrTyp(); - typeParamDispatchOn = tp; + typeParamDispatchOn = (WurstTypeTypeParam) leftExpr.attrTyp(); } } @@ -517,11 +515,12 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; if (typeParamDispatchOn != null) { - ImTypeClassFunc typeClassFunc = t.getTypeClassFuncFor((FuncDef) calledFunc); - if (receiver != null) { - imArgs.add(0, receiver); - } - call = JassIm.ImTypeVarDispatch(e, typeClassFunc, imArgs, t.getTypeVar(typeParamDispatchOn.getDef())); + + FuncDef calledFuncDef = (FuncDef) calledFunc; + ImMethod typeClassMethod = t.getTypeClassMethodFor(calledFuncDef); + ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, typeClassMethod.getImplementation().getTypeVariables()); + ImVar typeClassDict = t.getTypeClassDict(typeParamDispatchOn, calledFuncDef); + call = JassIm.ImMethodCall(e, typeClassMethod, typeArguments, JassIm.ImVarAccess(typeClassDict), imArgs, false); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 30aab80aa..39bf0431b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -58,10 +58,6 @@ public class Flatten { - public static Result flatten(ImTypeVarDispatch e, ImTranslator t, ImFunction f) { - MultiResult r = flattenExprs(t, f, e.getArguments()); - return new Result(r.stmts, ImTypeVarDispatch(e.getTrace(), e.getTypeClassFunc(), ImExprs(r.exprs), e.getTypeVariable())); - } public static Result flatten(ImCast imCast, ImTranslator translator, ImFunction f) { Result res = imCast.getExpr().flatten(translator, f); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java index e4c91482a..a5bf928c3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java @@ -38,9 +38,6 @@ public boolean equals(Object o) { if (!t1.getType().equalsType(t2.getType())) { return false; } - if (!t1.getTypeClassBinding().equals(t2.getTypeClassBinding())) { - return false; - } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index ac4fc33f4..c39301a59 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -501,15 +501,6 @@ public static void print(ImVarargLoop e, Appendable sb, int indent) { } - public static void print(ImTypeVarDispatch e, Appendable sb, int indent) { - append(sb, "<"); - append(sb, e.getTypeVariable().getName()); - append(sb, smallHash(e.getTypeVariable())); - append(sb, ">."); - append(sb, e.getTypeClassFunc().getName()); - printArgumentList(sb, indent, e.getArguments()); - } - public static void print(ImTypeVarRef e, Appendable sb, int indent) { append(sb, e.getTypeVariable().getName()); append(sb, smallHash(e.getTypeVariable())); @@ -552,9 +543,6 @@ public static String asString(List s) { .collect(Collectors.joining(", ")) + "]"; } - public static String asString(ImTypeClassFunc s) { - return s.getName() + smallHash(s); - } public static String asString(ImClass s) { return s.getName() + smallHash(s); @@ -565,7 +553,7 @@ public static String asString(ImMethod s) { } public static String asString(ImTypeArgument s) { - return s.getType() + "" + s.getTypeClassBinding(); + return s.getType().toString(); } public static void print(ImCast e, Appendable sb, int indent) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index bbc1b5df7..5db735d65 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -42,6 +42,7 @@ import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import de.peeeq.wurstscript.validation.WurstValidator; +import io.vavr.Tuple2; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -53,6 +54,7 @@ import static de.peeeq.wurstscript.jassIm.JassIm.*; import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*; import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath; +import static de.peeeq.wurstscript.utils.Utils.emptyList; public class ImTranslator { @@ -107,7 +109,7 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg this.wurstProg = wurstProg; this.lasttranslatedThing = wurstProg; this.isUnitTestMode = isUnitTestMode; - imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new LinkedHashMap<>()); + imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), new LinkedHashMap<>()); this.runArgs = runArgs; } @@ -611,7 +613,7 @@ public ImClassType selfType(StructureDef classDef) { public ImClassType selfType(ImClass imClass) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); } return JassIm.ImClassType(imClass, typeArgs); } @@ -1280,6 +1282,72 @@ public boolean isLuaTarget() { return runArgs.isLua(); } + private GetAForB typeClassMethodForFuncDef = new GetAForB() { + @Override + public ImMethod initFor(FuncDef f) { + ImFunction impl = JassIm.ImFunction(f, f.getName(), + ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + InterfaceDef interfaceDef = (InterfaceDef) f.attrNearestStructureDef(); + ImClassType typeClass = ImClassType(typeClassDictClass.getFor(interfaceDef), ImTypeArguments()); + return JassIm.ImMethod( + f, + typeClass, + f.getName(), + impl, + new ArrayList<>(), + true + ); + } + }; + + public ImMethod getTypeClassMethodFor(FuncDef f) { + return typeClassMethodForFuncDef.getFor(f); + } + + private GetAForB typeClassDictClass = new GetAForB() { + @Override + public ImClass initFor(InterfaceDef a) { + return JassIm.ImClass( + a, + "TypeClassDict_" + a.getName(), + ImTypeVars(), + ImVars(), + ImMethods(), + ImFunctions(), + emptyList() + ); + } + }; + + private GetAForB> varsForTypeParam = new GetAForB>() { + @Override + public List initFor(TypeParamDef a) { + WurstTypeTypeParam tp = new WurstTypeTypeParam(a); + return tp.getTypeConstraints() + .map(constraint -> { + ImClass c = typeClassDictClass.getFor(constraint.getDef()); + ImType constraintT = constraint.imTranslateType(ImTranslator.this); + String name = "type_class_dict_" + a.getName() + "_" + constraint.getName(); + return JassIm.ImVar(a, constraintT, name, false); + }) + .collect(Collectors.toList()); + } + }; + + public ImVar getTypeClassDict(WurstTypeTypeParam tp, FuncDef f) { + List vars = varsForTypeParam.getFor(tp.getDef()); + // find the var that has the function + ImMethod m = getTypeClassMethodFor(f); + for (ImVar v : vars) { + ImClassType ct = (ImClassType) v.getType(); + if (ct.getClassDef().getMethods().contains(m)) { + return v; + } + } + + throw new RuntimeException("Did not find function" + f.getName() + " in type parameter " + tp); + } + interface VarsForTupleResult { @@ -1596,19 +1664,6 @@ public ImMethod getMethodFor(FuncDef f) { return m; } - private Map typeClassFuncForFuncDef = Maps.newLinkedHashMap(); - - public ImTypeClassFunc getTypeClassFuncFor(FuncDef f) { - ImTypeClassFunc m = typeClassFuncForFuncDef.get(f); - if (m == null) { - ImTypeVars typeVars = ImTypeVars(); - ImVars params = ImVars(); - ImType returnType = JassIm.ImVoid(); - m = JassIm.ImTypeClassFunc(f, f.getName(), typeVars, params, returnType); - typeClassFuncForFuncDef.put(f, m); - } - return m; - } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java index e9dfef0f6..bdd34a330 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java @@ -67,7 +67,7 @@ public void addDestroyMethod() { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 7ba3a3132..07c64f668 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -330,11 +330,6 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat throw new Error("not implemented"); } - public static LuaExpr translate(ImTypeVarDispatch e, LuaTranslator tr) { - LuaVariable dict = tr.luaTypeClassDictionaryVar.getFor(e.getTypeVariable()); - LuaExprFieldAccess f = LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(dict), tr.typeClassFuncName.getFor(e.getTypeClassFunc())); - return LuaAst.LuaExprFunctionCallE(f, tr.translateExprList(e.getArguments())); - } public static LuaExpr translate(ImCast imCast, LuaTranslator tr) { LuaExpr translated = imCast.getExpr().translateToLua(tr); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index c153e7830..e1b877795 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -78,12 +78,6 @@ public LuaVariable initFor(ImTypeVar a) { } }; - GetAForB typeClassFuncName = new GetAForB() { - @Override - public String initFor(ImTypeClassFunc a) { - return uniqueName(a.getName()); - } - }; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index a92a6d177..f9681782b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -3,7 +3,6 @@ import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.jassIm.ImFunction; import de.peeeq.wurstscript.jassIm.ImMethod; -import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import io.vavr.control.Either; @@ -16,7 +15,6 @@ public static TypeClassInstance asSubtype(WurstTypeInterface interfaceType) { return new SubtypeTypeClassInstance(interfaceType); } - public abstract void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding); static class SubtypeTypeClassInstance extends TypeClassInstance { private WurstTypeInterface interfaceType; @@ -25,16 +23,6 @@ public SubtypeTypeClassInstance(WurstTypeInterface interfaceType) { this.interfaceType = interfaceType; } - @Override - public void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding) { - interfaceType.getMemberMethods(interfaceType.getDef()).forEach(fl -> { - if (fl.getDef() instanceof FuncDef) { - FuncDef def = (FuncDef) fl.getDef(); - ImTypeClassFunc tcf = tr.getTypeClassFuncFor(def); - typeClassBinding.put(tcf, Either.left(tr.getMethodFor(def))); - } - }); - } @Override public String toString() { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 5233fcd3a..1905b2ca8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -210,12 +210,7 @@ public boolean isTemplateTypeParameter() { } public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { - ImType t = imTranslateType(tr); - Map> typeClassBinding = new HashMap<>(); - for (TypeClassInstance instance : instances) { - instance.addTypeClassBinding(tr, typeClassBinding); - } - return JassIm.ImTypeArgument(t, typeClassBinding); + return JassIm.ImTypeArgument(imTranslateType(tr)); } public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 428ebae7f..57952ea59 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -117,7 +117,7 @@ public Stream getMemberMethods(Element node) { .map(fl -> fl.withReceiverType(this)); } - private Stream getTypeConstraints() { + public Stream getTypeConstraints() { if (def.getTypeParamConstraints() instanceof TypeExprList) { TypeExprList constraints = (TypeExprList) def.getTypeParamConstraints(); return constraints.stream() From fa23f1fc79b4469f29ceed77985006664c59e4e0 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sat, 4 Jan 2020 21:01:51 +0100 Subject: [PATCH 04/50] Revert "wip" This reverts commit e7ddf901f059c7fbf1baff9c9c82c29039219e7a. --- .../parserspec/jass_im.parseq | 11 ++- .../interpreter/EvaluateExpr.java | 22 +++++ .../interpreter/ILInterpreter.java | 1 + .../interpreter/LocalState.java | 15 ++++ .../optimizer/SideEffectAnalyzer.java | 5 ++ .../translation/imtojass/ExprTranslation.java | 3 + .../translation/imtojass/ImAttrType.java | 4 + .../translation/imtojass/TypeEquality.java | 3 + .../imtojass/TypeRewriteMatcher.java | 2 +- .../imtranslation/AssertProperty.java | 5 ++ .../imtranslation/ClassTranslator.java | 6 +- .../imtranslation/ClosureTranslator.java | 6 +- .../imtranslation/EliminateGenerics.java | 30 ++++++- .../imtranslation/ExprTranslation.java | 17 ++-- .../translation/imtranslation/Flatten.java | 4 + .../imtranslation/GenericTypes.java | 3 + .../translation/imtranslation/ImPrinter.java | 14 ++- .../imtranslation/ImTranslator.java | 85 ++++--------------- .../imtranslation/InterfaceTranslator.java | 2 +- .../lua/translation/ExprTranslation.java | 5 ++ .../lua/translation/LuaTranslator.java | 6 ++ .../wurstscript/types/TypeClassInstance.java | 12 +++ .../types/WurstTypeBoundTypeParam.java | 7 +- .../wurstscript/types/WurstTypeTypeParam.java | 2 +- 24 files changed, 179 insertions(+), 91 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 48519554c..006a6cf62 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -9,11 +9,13 @@ ImProg( ImFunctions functions, ImMethods methods, ImClasses classes, + ImTypeClassFuncs typeClassFunctions, java.util.Map> globalInits) ImVars * ImVar ImFunctions * ImFunction ImClasses * ImClass +ImTypeClassFuncs * ImTypeClassFunc ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, boolean isBJ) @@ -44,6 +46,11 @@ ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImStmts body, java.util.List flags) +ImTypeClassFunc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, + String name, + ImTypeVars typeVariables, + ImVars parameters, + ref ImType returnType) ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, @@ -93,6 +100,8 @@ ImExpr = | ImGetStackTrace() | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr + | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImTypeClassFunc typeClassFunc, ImExprs arguments + , ref ImTypeVar typeVariable) | ImCast(ImExpr expr, ref ImType toType) // an expression which can be used on the left hand side of an assignment @@ -139,7 +148,7 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type) +ImTypeArgument(ref ImType type, java.util.Map> typeClassBinding) // helper types: diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 8c61cd50f..38ca0ed46 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -398,6 +398,28 @@ public ILconst get() { } + public static ILconst eval(ImTypeVarDispatch e, ProgramState globalState, LocalState localState) { + Either impl = localState.getImplementation(e.getTypeVariable(), e.getTypeClassFunc()); + if (impl == null) { + throw new InterpreterException(globalState, "Could not find implementation for " + e.getTypeVariable() + "." + e.getTypeClassFunc().getName()); + } + ILconst[] eArgs = e.getArguments().stream() + .map(arg -> arg.evaluate(globalState, localState)) + .toArray(ILconst[]::new); + + return impl.fold( + (ImMethod m) -> { + ILconst receiver1 = eArgs[0]; + ILconstObject receiver = globalState.toObject(receiver1); + globalState.assertAllocated(receiver, e.attrTrace()); + ImMethod mostPreciseMethod = findMostPreciseMethod(e.attrTrace(), globalState, receiver, m); + return evaluateFunc(globalState, mostPreciseMethod.getImplementation(), e, Collections.emptyList(), eArgs); + }, + (ImFunction f) -> + evaluateFunc(globalState, f, e, Collections.emptyList(), eArgs) // TODO type var dispatch should also have type arguments? + ); + } + public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState localState) { ILconst res = imCast.getExpr().evaluate(globalState, localState); if (TypesHelper.isIntType(imCast.getToType())) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 64f841238..9dbd285ad 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -79,6 +79,7 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab } LocalState localState = new LocalState(); + localState.setTypeArguments(f.getTypeVariables(), typeArguments); int i = 0; for (ImVar p : f.getParameters()) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index 64c08c608..e762fe6b8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -14,6 +14,7 @@ public class LocalState extends State { private @Nullable ILconst returnVal = null; + private Table> typeClassImplementations = HashBasedTable.create(); public LocalState(ILconst returnVal) { this.setReturnVal(returnVal); @@ -31,4 +32,18 @@ public LocalState setReturnVal(@Nullable ILconst returnVal) { return this; } + + public Either getImplementation(ImTypeVar typeVariable, ImTypeClassFunc typeClassFunc) { + return typeClassImplementations.get(typeVariable, typeClassFunc); + } + + public void setTypeArguments(ImTypeVars typeVariables, List typeArguments) { + for (int i = 0; i < typeVariables.size() && i < typeArguments.size(); i++) { + ImTypeVar typeVariable = typeVariables.get(i); + ImTypeArgument typeArgument = typeArguments.get(i); + for (Map.Entry> e : typeArgument.getTypeClassBinding().entrySet()) { + typeClassImplementations.put(typeVariable, e.getKey(), e.getValue()); + } + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java index 58abbb566..b8c341a10 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java @@ -96,6 +96,11 @@ public Boolean case_ImGetStackTrace(ImGetStackTrace imGetStackTrace) { return true; } + @Override + public Boolean case_ImTypeVarDispatch(ImTypeVarDispatch imTypeVarDispatch) { + return true; + } + @Override public Boolean case_ImOperatorCall(ImOperatorCall e) { return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java index 5cdf12555..6545d6694 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java @@ -140,6 +140,9 @@ public static JassExpr translate(ImCompiletimeExpr e, ImToJassTranslator transla "Enable '-runcompiletimefunctions' to evaluate compiletime expressions."); } + public static JassExpr translate(ImTypeVarDispatch e, ImToJassTranslator translator) { + throw new CompileError(e, "Typevar dispatch not eliminated."); + } public static JassExpr translate(ImCast imCast, ImToJassTranslator translator) { return imCast.getExpr().translate(translator); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index 66c542a34..26695972d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -212,6 +212,10 @@ public static ImType getType(ImCompiletimeExpr e) { return e.getExpr().attrTyp(); } + public static ImType getType(ImTypeVarDispatch e) { + return e.getTypeClassFunc().getReturnType(); + } + public static ImType getType(ImCast imCast) { return imCast.getToType(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java index ab5f74280..d5acdfa81 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java @@ -75,6 +75,9 @@ public static boolean isEqualType(ImClassType c, ImType other) { if (!x.getType().equalsType(y.getType())) { return false; } + if (!x.getTypeClassBinding().equals(y.getTypeClassBinding())) { + return false; + } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java index ad7481653..0f9b9b455 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java @@ -53,7 +53,7 @@ public ImType case_ImArrayType(ImArrayType t) { public ImType case_ImClassType(ImClassType t) { ImTypeArguments args = t.getTypeArguments() .stream() - .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this))) + .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this), ta.getTypeClassBinding())) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(t.getClassDef(), args); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java index a5b5caf79..dc5e6669d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java @@ -43,11 +43,16 @@ public void check(Element e) { ImFunction f = (ImFunction) e; currentFunction = f; checkType(e, (f).getReturnType()); + } else if (e instanceof ImTypeClassFunc) { + checkType(e, ((ImTypeClassFunc) e).getReturnType()); } else if (e instanceof ImMethod) { checkType(e, ((ImMethod) e).getMethodClass()); checkRooted(e, ((ImMethod) e).getImplementation()); } else if (e instanceof ImVarargLoop) { checkRooted(e, ((ImVarargLoop) e).getLoopVar()); + } else if (e instanceof ImTypeVarDispatch) { + checkRooted(e, ((ImTypeVarDispatch) e).getTypeClassFunc()); + checkRooted(e, ((ImTypeVarDispatch) e).getTypeVariable()); } else if (e instanceof ImVarAccess) { checkRooted(e, ((ImVarAccess) e).getVar()); } else if (e instanceof ImVarArrayAccess) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index bb2f46d36..d96a3afef 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -124,7 +124,7 @@ private void createDestroyMethod(List subClasses) { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } @@ -372,7 +372,7 @@ private void createNewFunc(ConstructorDef constr) { } ImTypeArguments typeArgs = ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); } f.getBody().add(ImFunctionCall(trace, constrFunc, typeArgs, arguments, false, CallType.NORMAL)); @@ -412,7 +412,7 @@ private void createConstructFunc(ConstructorDef constr) { // call classInitFunc: ImTypeArguments typeArguments = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); + typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); } f.getBody().add(ImFunctionCall(trace, classInitFunc, typeArguments, JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); // constructor user code diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java index d55efaa8d..b13becbdb 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java @@ -64,7 +64,7 @@ public ImExpr translate() { private ImTypeArguments getClassTypeArguments() { ImTypeArguments res = JassIm.ImTypeArguments(); for (ImTypeVar typeVar : typeVars.keySet()) { - res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar))); + res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar), Collections.emptyMap())); } return res; } @@ -229,7 +229,9 @@ public ImType case_ImTypeVarRef(ImTypeVarRef t) { result.put(oldTypevar, newTypevar); c.getTypeVariables().add(newTypevar); thisType.getTypeArguments().add( - JassIm.ImTypeArgument(JassIm.ImTypeVarRef(newTypevar))); + JassIm.ImTypeArgument( + JassIm.ImTypeVarRef(newTypevar), + Collections.emptyMap())); } return JassIm.ImTypeVarRef(newTypevar); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index 581d0d9a9..c034378ca 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -145,7 +145,7 @@ private void moveFunctionsOutOfClass(ImClass c) { f.getTypeVariables().addAll(0, newTypeVars); List typeArgs = newTypeVars .stream() - .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta))) + .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) .collect(Collectors.toList()); rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables(), newTypeVars); } @@ -322,6 +322,32 @@ public void visit(ImDealloc e) { super.visit(e); } + @Override + public void visit(ImTypeVarDispatch e) { + super.visit(e); + ImTypeVar tv = e.getTypeVariable(); + int index = newTypeVars.indexOf(tv); + if (index < 0) { + throw new CompileError(e.attrTrace(), "Could not find type variable " + tv + " in " + newTypeVars); + } + ImTypeArgument ta = generics.getTypeArguments().get(index); + Either impl = ta.getTypeClassBinding().get(e.getTypeClassFunc()); + if (impl == null) { + throw new CompileError(e.attrTrace(), "Could not find func " + e.getTypeClassFunc().getName() + " in " + ta.getTypeClassBinding().keySet()); + } + ImExpr newExpr; + if (impl.isLeft()) { + ImMethod m = impl.getLeft(); + ImExpr receiver = e.getArguments().remove(0); + e.getArguments().setParent(null); + newExpr = JassIm.ImMethodCall(e.getTrace(), m, JassIm.ImTypeArguments(), receiver, e.getArguments(), false); + } else { + ImFunction f = impl.get(); + e.getArguments().setParent(null); + newExpr = JassIm.ImFunctionCall(e.getTrace(), f, JassIm.ImTypeArguments(), e.getArguments(), false, CallType.NORMAL); + } + e.replaceBy(newExpr); + } }); } @@ -671,7 +697,7 @@ public ImType case_ImClassType(ImClassType t) { private List specializeTypeArgs(ImTypeArguments typeArgs) { return typeArgs .stream() - .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()))) + .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()), ta.getTypeClassBinding())) .collect(Collectors.toList()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 2f380e803..edc80b021 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -451,11 +451,13 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu dynamicDispatch = true; } // add implicit parameter to front - // note: might not be dynamic, in case of extension function) + // TODO why would I add the implicit parameter here, if it is + // not a dynamic dispatch? leftExpr = (Expr) e.attrImplicitParameter(); if (leftExpr.attrTyp() instanceof WurstTypeTypeParam) { - typeParamDispatchOn = (WurstTypeTypeParam) leftExpr.attrTyp(); + WurstTypeTypeParam tp = (WurstTypeTypeParam) leftExpr.attrTyp(); + typeParamDispatchOn = tp; } } @@ -515,12 +517,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; if (typeParamDispatchOn != null) { - - FuncDef calledFuncDef = (FuncDef) calledFunc; - ImMethod typeClassMethod = t.getTypeClassMethodFor(calledFuncDef); - ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, typeClassMethod.getImplementation().getTypeVariables()); - ImVar typeClassDict = t.getTypeClassDict(typeParamDispatchOn, calledFuncDef); - call = JassIm.ImMethodCall(e, typeClassMethod, typeArguments, JassIm.ImVarAccess(typeClassDict), imArgs, false); + ImTypeClassFunc typeClassFunc = t.getTypeClassFuncFor((FuncDef) calledFunc); + if (receiver != null) { + imArgs.add(0, receiver); + } + call = JassIm.ImTypeVarDispatch(e, typeClassFunc, imArgs, t.getTypeVar(typeParamDispatchOn.getDef())); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 39bf0431b..30aab80aa 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -58,6 +58,10 @@ public class Flatten { + public static Result flatten(ImTypeVarDispatch e, ImTranslator t, ImFunction f) { + MultiResult r = flattenExprs(t, f, e.getArguments()); + return new Result(r.stmts, ImTypeVarDispatch(e.getTrace(), e.getTypeClassFunc(), ImExprs(r.exprs), e.getTypeVariable())); + } public static Result flatten(ImCast imCast, ImTranslator translator, ImFunction f) { Result res = imCast.getExpr().flatten(translator, f); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java index a5bf928c3..e4c91482a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java @@ -38,6 +38,9 @@ public boolean equals(Object o) { if (!t1.getType().equalsType(t2.getType())) { return false; } + if (!t1.getTypeClassBinding().equals(t2.getTypeClassBinding())) { + return false; + } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index c39301a59..ac4fc33f4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -501,6 +501,15 @@ public static void print(ImVarargLoop e, Appendable sb, int indent) { } + public static void print(ImTypeVarDispatch e, Appendable sb, int indent) { + append(sb, "<"); + append(sb, e.getTypeVariable().getName()); + append(sb, smallHash(e.getTypeVariable())); + append(sb, ">."); + append(sb, e.getTypeClassFunc().getName()); + printArgumentList(sb, indent, e.getArguments()); + } + public static void print(ImTypeVarRef e, Appendable sb, int indent) { append(sb, e.getTypeVariable().getName()); append(sb, smallHash(e.getTypeVariable())); @@ -543,6 +552,9 @@ public static String asString(List s) { .collect(Collectors.joining(", ")) + "]"; } + public static String asString(ImTypeClassFunc s) { + return s.getName() + smallHash(s); + } public static String asString(ImClass s) { return s.getName() + smallHash(s); @@ -553,7 +565,7 @@ public static String asString(ImMethod s) { } public static String asString(ImTypeArgument s) { - return s.getType().toString(); + return s.getType() + "" + s.getTypeClassBinding(); } public static void print(ImCast e, Appendable sb, int indent) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 5db735d65..bbc1b5df7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -42,7 +42,6 @@ import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import de.peeeq.wurstscript.validation.WurstValidator; -import io.vavr.Tuple2; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -54,7 +53,6 @@ import static de.peeeq.wurstscript.jassIm.JassIm.*; import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*; import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath; -import static de.peeeq.wurstscript.utils.Utils.emptyList; public class ImTranslator { @@ -109,7 +107,7 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg this.wurstProg = wurstProg; this.lasttranslatedThing = wurstProg; this.isUnitTestMode = isUnitTestMode; - imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), new LinkedHashMap<>()); + imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new LinkedHashMap<>()); this.runArgs = runArgs; } @@ -613,7 +611,7 @@ public ImClassType selfType(StructureDef classDef) { public ImClassType selfType(ImClass imClass) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))); + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); } return JassIm.ImClassType(imClass, typeArgs); } @@ -1282,72 +1280,6 @@ public boolean isLuaTarget() { return runArgs.isLua(); } - private GetAForB typeClassMethodForFuncDef = new GetAForB() { - @Override - public ImMethod initFor(FuncDef f) { - ImFunction impl = JassIm.ImFunction(f, f.getName(), - ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); - InterfaceDef interfaceDef = (InterfaceDef) f.attrNearestStructureDef(); - ImClassType typeClass = ImClassType(typeClassDictClass.getFor(interfaceDef), ImTypeArguments()); - return JassIm.ImMethod( - f, - typeClass, - f.getName(), - impl, - new ArrayList<>(), - true - ); - } - }; - - public ImMethod getTypeClassMethodFor(FuncDef f) { - return typeClassMethodForFuncDef.getFor(f); - } - - private GetAForB typeClassDictClass = new GetAForB() { - @Override - public ImClass initFor(InterfaceDef a) { - return JassIm.ImClass( - a, - "TypeClassDict_" + a.getName(), - ImTypeVars(), - ImVars(), - ImMethods(), - ImFunctions(), - emptyList() - ); - } - }; - - private GetAForB> varsForTypeParam = new GetAForB>() { - @Override - public List initFor(TypeParamDef a) { - WurstTypeTypeParam tp = new WurstTypeTypeParam(a); - return tp.getTypeConstraints() - .map(constraint -> { - ImClass c = typeClassDictClass.getFor(constraint.getDef()); - ImType constraintT = constraint.imTranslateType(ImTranslator.this); - String name = "type_class_dict_" + a.getName() + "_" + constraint.getName(); - return JassIm.ImVar(a, constraintT, name, false); - }) - .collect(Collectors.toList()); - } - }; - - public ImVar getTypeClassDict(WurstTypeTypeParam tp, FuncDef f) { - List vars = varsForTypeParam.getFor(tp.getDef()); - // find the var that has the function - ImMethod m = getTypeClassMethodFor(f); - for (ImVar v : vars) { - ImClassType ct = (ImClassType) v.getType(); - if (ct.getClassDef().getMethods().contains(m)) { - return v; - } - } - - throw new RuntimeException("Did not find function" + f.getName() + " in type parameter " + tp); - } - interface VarsForTupleResult { @@ -1664,6 +1596,19 @@ public ImMethod getMethodFor(FuncDef f) { return m; } + private Map typeClassFuncForFuncDef = Maps.newLinkedHashMap(); + + public ImTypeClassFunc getTypeClassFuncFor(FuncDef f) { + ImTypeClassFunc m = typeClassFuncForFuncDef.get(f); + if (m == null) { + ImTypeVars typeVars = ImTypeVars(); + ImVars params = ImVars(); + ImType returnType = JassIm.ImVoid(); + m = JassIm.ImTypeClassFunc(f, f.getName(), typeVars, params, returnType); + typeClassFuncForFuncDef.put(f, m); + } + return m; + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java index bdd34a330..e9dfef0f6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java @@ -67,7 +67,7 @@ public void addDestroyMethod() { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 07c64f668..7ba3a3132 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -330,6 +330,11 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat throw new Error("not implemented"); } + public static LuaExpr translate(ImTypeVarDispatch e, LuaTranslator tr) { + LuaVariable dict = tr.luaTypeClassDictionaryVar.getFor(e.getTypeVariable()); + LuaExprFieldAccess f = LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(dict), tr.typeClassFuncName.getFor(e.getTypeClassFunc())); + return LuaAst.LuaExprFunctionCallE(f, tr.translateExprList(e.getArguments())); + } public static LuaExpr translate(ImCast imCast, LuaTranslator tr) { LuaExpr translated = imCast.getExpr().translateToLua(tr); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index e1b877795..c153e7830 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -78,6 +78,12 @@ public LuaVariable initFor(ImTypeVar a) { } }; + GetAForB typeClassFuncName = new GetAForB() { + @Override + public String initFor(ImTypeClassFunc a) { + return uniqueName(a.getName()); + } + }; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index f9681782b..a92a6d177 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.jassIm.ImFunction; import de.peeeq.wurstscript.jassIm.ImMethod; +import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import io.vavr.control.Either; @@ -15,6 +16,7 @@ public static TypeClassInstance asSubtype(WurstTypeInterface interfaceType) { return new SubtypeTypeClassInstance(interfaceType); } + public abstract void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding); static class SubtypeTypeClassInstance extends TypeClassInstance { private WurstTypeInterface interfaceType; @@ -23,6 +25,16 @@ public SubtypeTypeClassInstance(WurstTypeInterface interfaceType) { this.interfaceType = interfaceType; } + @Override + public void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding) { + interfaceType.getMemberMethods(interfaceType.getDef()).forEach(fl -> { + if (fl.getDef() instanceof FuncDef) { + FuncDef def = (FuncDef) fl.getDef(); + ImTypeClassFunc tcf = tr.getTypeClassFuncFor(def); + typeClassBinding.put(tcf, Either.left(tr.getMethodFor(def))); + } + }); + } @Override public String toString() { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 1905b2ca8..5233fcd3a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -210,7 +210,12 @@ public boolean isTemplateTypeParameter() { } public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { - return JassIm.ImTypeArgument(imTranslateType(tr)); + ImType t = imTranslateType(tr); + Map> typeClassBinding = new HashMap<>(); + for (TypeClassInstance instance : instances) { + instance.addTypeClassBinding(tr, typeClassBinding); + } + return JassIm.ImTypeArgument(t, typeClassBinding); } public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 57952ea59..428ebae7f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -117,7 +117,7 @@ public Stream getMemberMethods(Element node) { .map(fl -> fl.withReceiverType(this)); } - public Stream getTypeConstraints() { + private Stream getTypeConstraints() { if (def.getTypeParamConstraints() instanceof TypeExprList) { TypeExprList constraints = (TypeExprList) def.getTypeParamConstraints(); return constraints.stream() From d3813a34275e64d0315c526d6a6cbb1dfd31ca6c Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sat, 4 Jan 2020 21:43:34 +0100 Subject: [PATCH 05/50] fix --- .../de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 5233fcd3a..997a9db3b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -63,9 +63,9 @@ VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element l @Override public String getName() { -// return baseType.getName(); - String is = instances == null ? "" : " | " + Utils.printSep(", ", instances); - return "[" + typeParamDef.getName() + ": " + baseType + is + "]"; + return baseType.getName(); +// String is = instances == null ? "" : " | " + Utils.printSep(", ", instances); +// return "[" + typeParamDef.getName() + ": " + baseType + is + "]"; } @Override From 7bd190ad38c58fbfce38b901c8ef80a85b223cfd Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Mon, 13 Jan 2020 20:33:49 +0100 Subject: [PATCH 06/50] wip --- .../parserspec/jass_im.parseq | 8 +++- .../AttrPossibleFunctionSignatures.java | 32 ++++++++++--- .../types/TypeClassFuncInstance.java | 18 ++++++++ .../wurstscript/types/TypeClassInstance.java | 45 ------------------- .../types/WurstTypeBoundTypeParam.java | 14 +++--- .../wurstscript/types/WurstTypeTypeParam.java | 2 +- .../tests/GenericsWithTypeclassesTests.java | 23 ++++++++++ 7 files changed, 82 insertions(+), 60 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java delete mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 006a6cf62..531cba2c4 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -148,7 +148,13 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type, java.util.Map> typeClassBinding) +ImTypeArgument(ref ImType type, java.util.Map typeClassBinding) + +ImTypeClassImpls * ImTypeClassImpl + +ImTypeClassImpl = + ImTypeClassImplFromInterface(ref ImClass typeClass, ref ImClass implementingClass) + | ImTypeClassImplFromOther(ref ImClass typeClass, ref ImTypeClassImpl otherImpl) // helper types: diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 4545dd247..bc90eb879 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -11,10 +11,7 @@ import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; import static de.peeeq.wurstscript.attributes.GenericsHelper.givenBinding; import static de.peeeq.wurstscript.attributes.GenericsHelper.typeParameters; @@ -51,6 +48,9 @@ private static ImmutableCollection findBestSignature(StmtCall List argTypes = AttrFuncDef.argumentTypes(fc); for (FunctionSignature sig : res) { FunctionSignature sig2 = sig.matchAgainstArgs(argTypes, fc); + if (sig2 == null) { + continue; + } Pair> typeClassMatched = findTypeClasses(sig2, fc); if (typeClassMatched.getB().isEmpty()) { resultBuilder2.add(typeClassMatched.getA()); @@ -107,11 +107,11 @@ private static Pair> findTypeClasses(Funct } WurstTypeBoundTypeParam matchedType = matchedTypeOpt.get(); for (WurstTypeInterface constraint : constraints) { - VariableBinding mapping2 = matchedType.matchAgainstSupertype(constraint, fc, mapping, VariablePosition.RIGHT); + VariableBinding mapping2 = findTypeClass(fc, errors, mapping, tp, matchedType, constraint); if (mapping2 == null) { errors.add(new CompileError(fc.attrSource(), "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint + ".")); } else { - mapping = mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(constraint))); + mapping = mapping2; } } } @@ -119,6 +119,26 @@ private static Pair> findTypeClasses(Funct return Pair.create(sig, errors); } + private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint) { + // option 1: the matched type is a subtype of the constraint + VariableBinding mapping2 = matchedType.matchAgainstSupertype(constraint, fc, mapping, VariablePosition.RIGHT); + if (mapping2 != null) { + return mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(constraint))); + } + // option 2: the matched type is a type param that also has the right constraint: + if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { + WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); + Optional matchingConstraint = wtp.getTypeConstraints().filter(c -> c.isSubtypeOf(constraint, fc)).findFirst(); + if (matchingConstraint.isPresent()) { + return mapping.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.fromTypeParam(wtp, matchingConstraint.get()))); + } + } + // option 3: find methods elsewhere + + + return null; + } + public static ImmutableCollection calculate(ExprNewObject fc) { TypeDef typeDef = fc.attrTypeDef(); if (!(typeDef instanceof ClassDef)) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java new file mode 100644 index 000000000..33b3ff9a0 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java @@ -0,0 +1,18 @@ +package de.peeeq.wurstscript.types; + +import de.peeeq.wurstscript.ast.FuncDef; +import de.peeeq.wurstscript.jassIm.ImFunction; +import de.peeeq.wurstscript.jassIm.ImMethod; +import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import io.vavr.control.Either; + +import java.util.Map; + +public abstract class TypeClassFuncInstance { + + abstract TypeClassFuncInstance subst(Map replacements); + + + public abstract void addTypeClassBinding(ImTranslator tr, Map typeClassBinding); +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java deleted file mode 100644 index a92a6d177..000000000 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.peeeq.wurstscript.types; - -import de.peeeq.wurstscript.ast.FuncDef; -import de.peeeq.wurstscript.jassIm.ImFunction; -import de.peeeq.wurstscript.jassIm.ImMethod; -import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; -import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; -import io.vavr.control.Either; - -import java.util.Map; - -public abstract class TypeClassInstance { - - - public static TypeClassInstance asSubtype(WurstTypeInterface interfaceType) { - return new SubtypeTypeClassInstance(interfaceType); - } - - public abstract void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding); - - static class SubtypeTypeClassInstance extends TypeClassInstance { - private WurstTypeInterface interfaceType; - - public SubtypeTypeClassInstance(WurstTypeInterface interfaceType) { - this.interfaceType = interfaceType; - } - - @Override - public void addTypeClassBinding(ImTranslator tr, Map> typeClassBinding) { - interfaceType.getMemberMethods(interfaceType.getDef()).forEach(fl -> { - if (fl.getDef() instanceof FuncDef) { - FuncDef def = (FuncDef) fl.getDef(); - ImTypeClassFunc tcf = tr.getTypeClassFuncFor(def); - typeClassBinding.put(tcf, Either.left(tr.getMethodFor(def))); - } - }); - } - - @Override - public String toString() { - return "SubtypeTypeClassInstance<" + interfaceType + ">"; - } - } - -} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 997a9db3b..25d83005b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -28,7 +28,7 @@ public class WurstTypeBoundTypeParam extends WurstType { private FuncDef fromIndex; private FuncDef toIndex; // type class instances (null for old generics) - private final @Nullable List instances; + private final @Nullable List instances; private boolean indexInitialized = false; private Element context; @@ -46,7 +46,7 @@ public WurstTypeBoundTypeParam(TypeParamDef def, WurstType baseType, Element con } } - public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { + public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { this.typeParamDef = typeParamDef; this.baseType = baseType; this.fromIndex = fromIndex; @@ -211,16 +211,16 @@ public boolean isTemplateTypeParameter() { public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { ImType t = imTranslateType(tr); - Map> typeClassBinding = new HashMap<>(); - for (TypeClassInstance instance : instances) { + Map typeClassBinding = new HashMap<>(); + for (TypeClassFuncInstance instance : instances) { instance.addTypeClassBinding(tr, typeClassBinding); } return JassIm.ImTypeArgument(t, typeClassBinding); } - public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { - ImmutableList newInstances = - ImmutableList.builderWithExpectedSize(instances.size() + 1) + public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassFuncInstance instance) { + ImmutableList newInstances = + ImmutableList.builderWithExpectedSize(instances.size() + 1) .addAll(instances) .add(instance) .build(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 428ebae7f..57952ea59 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -117,7 +117,7 @@ public Stream getMemberMethods(Element node) { .map(fl -> fl.withReceiverType(this)); } - private Stream getTypeConstraints() { + public Stream getTypeConstraints() { if (def.getTypeParamConstraints() instanceof TypeExprList) { TypeExprList constraints = (TypeExprList) def.getTypeParamConstraints(); return constraints.stream() diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index e79e34de6..977884441 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1335,4 +1335,27 @@ public void simpleTypeClass() { ); } + @Test + public void forwardTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "interface ToIndex", + " function toIndex() returns int", + "class A implements ToIndex", + " override function toIndex() returns int", + " return 42", + "function foo(T x)", + " if x.toIndex() == 42", + " testSuccess()", + "function bar(T x)", + " foo(x)", + "init", + " let a = new A", + " bar(a)" + ); + } + + + } From 0326896e1d0d8af0381246f79997a28da04cd77e Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 19 Jan 2020 01:09:50 +0100 Subject: [PATCH 07/50] wip --- .../parserspec/jass_im.parseq | 15 ++---- .../parserspec/wurstscript.parseq | 2 +- .../AttrPossibleFunctionSignatures.java | 2 + .../interpreter/LocalState.java | 10 ++-- .../imtranslation/ImTranslator.java | 48 +++++++++---------- .../lua/translation/LuaTranslator.java | 8 ---- .../types/TypeClassFuncInstance.java | 18 ------- .../wurstscript/types/TypeClassInstance.java | 46 ++++++++++++++++++ .../types/WurstTypeBoundTypeParam.java | 20 ++++---- .../types/WurstTypeClassOrInterface.java | 3 ++ 10 files changed, 94 insertions(+), 78 deletions(-) delete mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 531cba2c4..0de409e10 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -9,13 +9,11 @@ ImProg( ImFunctions functions, ImMethods methods, ImClasses classes, - ImTypeClassFuncs typeClassFunctions, java.util.Map> globalInits) ImVars * ImVar ImFunctions * ImFunction ImClasses * ImClass -ImTypeClassFuncs * ImTypeClassFunc ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, boolean isBJ) @@ -46,11 +44,6 @@ ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImStmts body, java.util.List flags) -ImTypeClassFunc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, - ImTypeVars typeVariables, - ImVars parameters, - ref ImType returnType) ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, @@ -100,7 +93,7 @@ ImExpr = | ImGetStackTrace() | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr - | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImTypeClassFunc typeClassFunc, ImExprs arguments + | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImMethod method, ImExprs arguments , ref ImTypeVar typeVariable) | ImCast(ImExpr expr, ref ImType toType) @@ -148,13 +141,13 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type, java.util.Map typeClassBinding) +ImTypeArgument(ref ImType type, ImTypeClassImpls typeClassImplementations) ImTypeClassImpls * ImTypeClassImpl ImTypeClassImpl = - ImTypeClassImplFromInterface(ref ImClass typeClass, ref ImClass implementingClass) - | ImTypeClassImplFromOther(ref ImClass typeClass, ref ImTypeClassImpl otherImpl) + ImTypeClassImplFromInterface(ref ImClassType typeClass, ref ImClassType implementingClass) + | ImTypeClassImplFromOther(ref ImClassType typeClass, ref ImTypeClassImpl otherImpl) // helper types: diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index 2aaab52eb..b47b1cae0 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -323,7 +323,7 @@ AstElementWithNameId = WPackage | NativeFunc | ModuleDef | TypeDef | ModuleInsta AstElementWithParameters = FunctionDefinition | ExtensionFuncDef | NativeFunc | TupleDef | ConstructorDef | FuncDef -AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassDef | InterfaceDef | FuncDef +AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassOrInterface | FuncDef AstElementWithArgs = StmtCall | ExprFunctionCall | ExprNewObject | ExprMemberMethod diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 466e3164d..a508c50cd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -136,7 +136,9 @@ private static VariableBinding findTypeClass(StmtCall fc, List err } // option 3: find methods elsewhere + // TODO find instance declaration + errors.add(new CompileError(fc, "Could not find type class instance " + constraint.getName() + " for type " + matchedType)); return null; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index e762fe6b8..e2f98ffcc 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -14,7 +14,7 @@ public class LocalState extends State { private @Nullable ILconst returnVal = null; - private Table> typeClassImplementations = HashBasedTable.create(); + private Table typeClassImplementations = HashBasedTable.create(); public LocalState(ILconst returnVal) { this.setReturnVal(returnVal); @@ -33,16 +33,16 @@ public LocalState setReturnVal(@Nullable ILconst returnVal) { } - public Either getImplementation(ImTypeVar typeVariable, ImTypeClassFunc typeClassFunc) { - return typeClassImplementations.get(typeVariable, typeClassFunc); + public @Nullable ImTypeClassImpl getImplementation(ImTypeVar typeVariable, ImClassType classType) { + return typeClassImplementations.get(typeVariable, classType); } public void setTypeArguments(ImTypeVars typeVariables, List typeArguments) { for (int i = 0; i < typeVariables.size() && i < typeArguments.size(); i++) { ImTypeVar typeVariable = typeVariables.get(i); ImTypeArgument typeArgument = typeArguments.get(i); - for (Map.Entry> e : typeArgument.getTypeClassBinding().entrySet()) { - typeClassImplementations.put(typeVariable, e.getKey(), e.getValue()); + for (ImTypeClassImpl e : typeArgument.getTypeClassImplementations()) { + typeClassImplementations.put(typeVariable, e.getTypeClass(), e); } } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 40be86bee..78360bca4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -107,7 +107,7 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg this.wurstProg = wurstProg; this.lasttranslatedThing = wurstProg; this.isUnitTestMode = isUnitTestMode; - imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new LinkedHashMap<>()); + imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), new LinkedHashMap<>()); this.runArgs = runArgs; } @@ -611,7 +611,8 @@ public ImClassType selfType(StructureDef classDef) { public ImClassType selfType(ImClass imClass) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + // TODO include type impls? (add constraints to ImTypeVar) + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), ImTypeClassImpls())); } return JassIm.ImClassType(imClass, typeArgs); } @@ -1570,12 +1571,10 @@ public ImClass getClassFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { ImTypeVars typeVariables = JassIm.ImTypeVars(); - if (s instanceof AstElementWithTypeParameters) { - for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { - ImTypeVar tv = getTypeVar(tp); - typeVariables.add(tv); - } + for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeExprList) { + ImTypeVar tv = getTypeVar(tp); + typeVariables.add(tv); } } @@ -1583,6 +1582,23 @@ public ImClass getClassFor(ClassOrInterface s) { }); } + private Map typeClassStructFor = Maps.newLinkedHashMap(); + + public ImClass getTypeClassStructFor(ClassOrInterface s) { + Preconditions.checkNotNull(s); + return typeClassStructFor.computeIfAbsent(s, s1 -> { + ImTypeVars typeVariables = JassIm.ImTypeVars(); + for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeExprList) { + ImTypeVar tv = getTypeVar(tp); + typeVariables.add(tv); + } + } + + return JassIm.ImClass(s1, "typeclass_" + s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + }); + } + Map methodForFuncDef = Maps.newLinkedHashMap(); @@ -1596,22 +1612,6 @@ public ImMethod getMethodFor(FuncDef f) { return m; } - private Map typeClassFuncForFuncDef = Maps.newLinkedHashMap(); - - public ImTypeClassFunc getTypeClassFuncFor(FuncDef f) { - ImTypeClassFunc m = typeClassFuncForFuncDef.get(f); - if (m == null) { - ImTypeVars typeVars = ImTypeVars(); - ImVars params = ImVars(); - ImType returnType = JassIm.ImVoid(); - m = JassIm.ImTypeClassFunc(f, f.getName(), typeVars, params, returnType); - typeClassFuncForFuncDef.put(f, m); - } - return m; - } - - - public ClassManagementVars getClassManagementVarsFor(ImClass c) { return getClassManagementVars().get(c); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index c153e7830..b59a76d41 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -78,14 +78,6 @@ public LuaVariable initFor(ImTypeVar a) { } }; - GetAForB typeClassFuncName = new GetAForB() { - @Override - public String initFor(ImTypeClassFunc a) { - return uniqueName(a.getName()); - } - }; - - GetAForB luaFunc = new GetAForB() { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java deleted file mode 100644 index 33b3ff9a0..000000000 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassFuncInstance.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.peeeq.wurstscript.types; - -import de.peeeq.wurstscript.ast.FuncDef; -import de.peeeq.wurstscript.jassIm.ImFunction; -import de.peeeq.wurstscript.jassIm.ImMethod; -import de.peeeq.wurstscript.jassIm.ImTypeClassFunc; -import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; -import io.vavr.control.Either; - -import java.util.Map; - -public abstract class TypeClassFuncInstance { - - abstract TypeClassFuncInstance subst(Map replacements); - - - public abstract void addTypeClassBinding(ImTranslator tr, Map typeClassBinding); -} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java new file mode 100644 index 000000000..79daaaf9f --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -0,0 +1,46 @@ +package de.peeeq.wurstscript.types; + + +import de.peeeq.wurstscript.jassIm.ImClassType; +import de.peeeq.wurstscript.jassIm.ImTypeClassImpl; +import de.peeeq.wurstscript.jassIm.ImTypeVar; +import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; + +public abstract class TypeClassInstance { + private final WurstTypeClassOrInterface constraint; + + public TypeClassInstance(WurstTypeClassOrInterface constraint) { + this.constraint = constraint; + } + + public static TypeClassInstance asSubtype(WurstTypeClassOrInterface subType, WurstTypeClassOrInterface constraint) { + return new TypeClassInstance(constraint) { + @Override + public ImTypeClassImpl translate(ImTranslator tr) { + return JassIm.ImTypeClassImplFromInterface(translateConstraintType(tr), subType.imTranslateToTypeClass(tr)); + } + }; + } + + protected ImClassType translateConstraintType(ImTranslator tr) { + return constraint.imTranslateToTypeClass(tr); + } + + public static TypeClassInstance fromTypeParam(WurstTypeTypeParam wtp, WurstTypeClassOrInterface wurstTypeInterface, WurstTypeClassOrInterface constraint) { + return new TypeClassInstance(constraint) { + @Override + public ImTypeClassImpl translate(ImTranslator tr) { + ImTypeVar tv = tr.getTypeVar(wtp.getDef()); + // TODO type var needs something that I can reference now ... + return JassIm.ImTypeClassImplFromOther(translateConstraintType(tr), ); + } + }; + } + + public WurstTypeClassOrInterface getConstraintType() { + return constraint; + } + + public abstract ImTypeClassImpl translate(ImTranslator tr); +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 25d83005b..f87d9ec99 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -7,8 +7,6 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; -import de.peeeq.wurstscript.utils.Utils; -import io.vavr.control.Either; import org.eclipse.jdt.annotation.Nullable; import java.util.Collections; @@ -28,7 +26,7 @@ public class WurstTypeBoundTypeParam extends WurstType { private FuncDef fromIndex; private FuncDef toIndex; // type class instances (null for old generics) - private final @Nullable List instances; + private final @Nullable List instances; private boolean indexInitialized = false; private Element context; @@ -46,7 +44,7 @@ public WurstTypeBoundTypeParam(TypeParamDef def, WurstType baseType, Element con } } - public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { + public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { this.typeParamDef = typeParamDef; this.baseType = baseType; this.fromIndex = fromIndex; @@ -211,16 +209,16 @@ public boolean isTemplateTypeParameter() { public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { ImType t = imTranslateType(tr); - Map typeClassBinding = new HashMap<>(); - for (TypeClassFuncInstance instance : instances) { - instance.addTypeClassBinding(tr, typeClassBinding); + ImTypeClassImpls typeClassImpls = JassIm.ImTypeClassImpls(); + for (TypeClassInstance instance : instances) { + typeClassImpls.add(instance.translate(tr)); } - return JassIm.ImTypeArgument(t, typeClassBinding); + return JassIm.ImTypeArgument(t, typeClassImpls); } - public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassFuncInstance instance) { - ImmutableList newInstances = - ImmutableList.builderWithExpectedSize(instances.size() + 1) + public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { + ImmutableList newInstances = + ImmutableList.builderWithExpectedSize(instances.size() + 1) .addAll(instances) .add(instance) .build(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java index 2c5c5e9de..6fcc8078d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java @@ -10,6 +10,7 @@ import de.peeeq.wurstscript.attributes.names.DefLink; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.jassIm.ImClassType; import de.peeeq.wurstscript.jassIm.ImType; import de.peeeq.wurstscript.jassIm.ImTypeArguments; import de.peeeq.wurstscript.jassIm.JassIm; @@ -141,4 +142,6 @@ public final ImType imTranslateType(ImTranslator tr) { return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); } + public ImClassType imTranslateToTypeClass(ImTranslator tr) { + } } From 625002e6b8ce79f5e85c3b4a8742298001cd4497 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 21 Jan 2020 20:55:53 +0100 Subject: [PATCH 08/50] wip --- .../parserspec/jass_im.parseq | 15 ++- .../parserspec/wurstscript.parseq | 7 +- .../AttrPossibleFunctionSignatures.java | 2 +- .../translation/imtojass/TypeEquality.java | 22 +++- .../translation/imtranslation/ImPrinter.java | 5 +- .../imtranslation/ImTranslator.java | 119 +++++++++++++++--- .../wurstscript/types/TypeClassInstance.java | 13 +- .../de/peeeq/wurstscript/utils/Utils.java | 18 +++ 8 files changed, 169 insertions(+), 32 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 0de409e10..e30fadef4 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -35,9 +35,18 @@ ImTypeVars * ImTypeVar ImTypeVar(String name) +ImTypeClassConstraints * ImTypeClassConstraint + +ImTypeClassConstraint( + @ignoreForEquality de.peeeq.wurstscript.ast.Element trace, + String name, + ImClassType classType) + + ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, + ImTypeClassConstraints typeClassConstraints, ImVars parameters, ref ImType returnType, ImVars locals, @@ -49,11 +58,15 @@ ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, + ImTypeClassConstraints typeClassConstraints, ImVars fields, ImMethods methods, ImFunctions functions, java.util.List superClasses) + +ImElementWithTypeVars = ImFunction | ImClass + ImMethods * ImMethod ImMethod(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, @@ -147,7 +160,7 @@ ImTypeClassImpls * ImTypeClassImpl ImTypeClassImpl = ImTypeClassImplFromInterface(ref ImClassType typeClass, ref ImClassType implementingClass) - | ImTypeClassImplFromOther(ref ImClassType typeClass, ref ImTypeClassImpl otherImpl) + | ImTypeClassImplFromOther(ref ImClassType typeClass, ref ImTypeClassConstraint otherImpl) // helper types: diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index b47b1cae0..e977b314c 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -117,7 +117,12 @@ TypeParamDefs * TypeParamDef TypeParamDef(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamConstraints typeParamConstraints) -TypeParamConstraints = NoTypeParamConstraints() | TypeExprList +TypeParamConstraints = NoTypeParamConstraints() | TypeParamConstraintList + +TypeParamConstraintList * TypeParamConstraint + +TypeParamConstraint(TypeExpr constraint) + WParameters * WParameter diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index a508c50cd..10946a190 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -131,7 +131,7 @@ private static VariableBinding findTypeClass(StmtCall fc, List err WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); Optional matchingConstraint = wtp.getTypeConstraints().filter(c -> c.isSubtypeOf(constraint, fc)).findFirst(); if (matchingConstraint.isPresent()) { - return mapping.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.fromTypeParam(wtp, matchingConstraint.get()))); + return mapping.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.fromTypeParam(fc, wtp, matchingConstraint.get()))); } } // option 3: find methods elsewhere diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java index d5acdfa81..17dfe099e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java @@ -1,6 +1,7 @@ package de.peeeq.wurstscript.translation.imtojass; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.utils.Utils; public class TypeEquality { @@ -75,7 +76,7 @@ public static boolean isEqualType(ImClassType c, ImType other) { if (!x.getType().equalsType(y.getType())) { return false; } - if (!x.getTypeClassBinding().equals(y.getTypeClassBinding())) { + if (!equalTypeClassImplementations(x.getTypeClassImplementations(), y.getTypeClassImplementations())) { return false; } } @@ -84,6 +85,25 @@ public static boolean isEqualType(ImClassType c, ImType other) { return false; } + private static boolean equalTypeClassImplementations(ImTypeClassImpls xs, ImTypeClassImpls ys) { + return Utils.listEquals(xs, ys, TypeEquality::equalTypeClassImplementation); + } + + private static boolean equalTypeClassImplementation(ImTypeClassImpl x, ImTypeClassImpl y) { + if (!x.getClass().equals(y.getClass())) { + return false; + } + if (x instanceof ImTypeClassImplFromInterface) { + ImTypeClassImplFromInterface xc = (ImTypeClassImplFromInterface) x; + ImTypeClassImplFromInterface yc = (ImTypeClassImplFromInterface) y; + return isEqualType(xc.getImplementingClass(), yc.getImplementingClass()); + } else { + ImTypeClassImplFromOther xc = (ImTypeClassImplFromOther) x; + ImTypeClassImplFromOther yc = (ImTypeClassImplFromOther) y; + return xc.getOtherImpl() == yc.getOtherImpl(); + } + } + public static boolean isEqualType(ImAnyType t, ImType other) { return other instanceof ImAnyType; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index ac4fc33f4..92b6d4bb9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -552,9 +552,6 @@ public static String asString(List s) { .collect(Collectors.joining(", ")) + "]"; } - public static String asString(ImTypeClassFunc s) { - return s.getName() + smallHash(s); - } public static String asString(ImClass s) { return s.getName() + smallHash(s); @@ -565,7 +562,7 @@ public static String asString(ImMethod s) { } public static String asString(ImTypeArgument s) { - return s.getType() + "" + s.getTypeClassBinding(); + return s.getType() + "" + s.; } public static void print(ImCast e, Appendable sb, int indent) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 78360bca4..16cf6baa0 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -117,9 +117,9 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg */ public ImProg translateProg() { try { - globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(getGlobalInitFunc()); - debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", + debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImTypeClassConstraints(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); calculateCompiletimeOrder(); @@ -129,11 +129,11 @@ public ImProg translateProg() { } if (mainFunc == null) { - mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(mainFunc); } if (configFunc == null) { - configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(configFunc); } finishInitFunctions(); @@ -538,7 +538,7 @@ public ImExpr getDefaultValueForJassType(ImType type) { public ImFunction initFor(StructureDef classDef) { ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false)); - ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); + ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), ImTypeClassConstraints(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); addFunction(f, classDef); return f; } @@ -622,7 +622,7 @@ public ImClassType selfType(ImClass imClass) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; @@ -632,7 +632,7 @@ public ImFunction initFor(ImClass c) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; @@ -649,6 +649,21 @@ public ImTypeVar initFor(TypeParamDef a) { } }; + // type class constraints for type parameters + private final GetAForB constraint = new GetAForB() { + @Override + public ImTypeClassConstraint initFor(TypeParamConstraint a) { + ImType t = a.getConstraint().attrTyp().imTranslateType(ImTranslator.this); + return ImTypeClassConstraint(a, Utils.printElement(a), (ImClassType) t); + } + + }; + + public ImTypeClassConstraint getConstraintFor(TypeParamConstraint c) { + return constraint.getFor(c); + } + + public ImFunction getFuncFor(TranslatedToImFunction funcDef) { if (functionMap.containsKey(funcDef)) { @@ -702,7 +717,8 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { } ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); - ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); + ImTypeClassConstraints typeClassConstraints = collectTypeClassConstraintsForFunction(funcDef); + ImFunction f = ImFunction(funcDef, name, typeVars, typeClassConstraints, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); funcDef.imCreateFuncSkeleton(this, f); addFunction(f, funcDef); @@ -815,6 +831,65 @@ public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { return typeVars; } + private ImTypeClassConstraints collectTypeClassConstraintsForFunction(TranslatedToImFunction funcDef) { + ImTypeClassConstraints constraints = ImTypeClassConstraints(); + funcDef.match(new TranslatedToImFunction.MatcherVoid() { + @Override + public void case_FuncDef(FuncDef funcDef) { + handleTypeParameters(funcDef.getTypeParameters()); + } + + + private void handleTypeParameters(TypeParamDefs tps) { + for (TypeParamDef tp : tps) { + handleTypeParameter(tp); + } + } + + private void handleTypeParameter(TypeParamDef tp) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); + for (TypeParamConstraint c : cs) { + constraints.add(getConstraintFor(c)); + } + } + } + + @Override + public void case_ConstructorDef(ConstructorDef constructorDef) { + } + + @Override + public void case_NativeFunc(NativeFunc nativeFunc) { + } + + @Override + public void case_OnDestroyDef(OnDestroyDef onDestroyDef) { + } + + @Override + public void case_TupleDef(TupleDef tupleDef) { + } + + @Override + public void case_ExprClosure(ExprClosure exprClosure) { + // TODO where to set closure parameters? + } + + @Override + public void case_InitBlock(InitBlock initBlock) { + + } + + @Override + public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { + handleTypeParameters(funcDef.getTypeParameters()); + } + }); + return constraints; + } + + private boolean isExtern(TranslatedToImFunction funcDef) { if (funcDef instanceof HasModifier) { @@ -839,7 +914,7 @@ private boolean isBJ(WPos source) { public ImFunction getInitFuncFor(WPackage p) { // TODO more precise trace - return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); + return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); } /** @@ -1117,7 +1192,7 @@ public ImFunction getConstructFunc(ConstructorDef constr) { params.add(getVarFor(p)); } - f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags()); + f = ImFunction(constr, name, ImTypeVars(), ImTypeClassConstraints(), params, ImVoid(), ImVars(), ImStmts(), flags()); addFunction(f, constr); constructorFuncs.put(constr, f); } @@ -1150,7 +1225,7 @@ public ImFunction getConstructNewFunc(ConstructorDef constr) { if (f == null) { String name = "new_" + constr.attrNearestClassDef().getName(); - f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); + f = ImFunction(constr, name, ImTypeVars(), ImTypeClassConstraints(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); addFunction(f, constr); constrNewFuncs.put(constr, f); } @@ -1561,7 +1636,7 @@ public boolean isUnitTestMode() { public ImClass getClassForClosure(ExprClosure s) { Preconditions.checkNotNull(s); - return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList())); + return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList())); } @@ -1571,14 +1646,19 @@ public ImClass getClassFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { ImTypeVars typeVariables = JassIm.ImTypeVars(); + ImTypeClassConstraints typeClassConstraints = ImTypeClassConstraints(); for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { ImTypeVar tv = getTypeVar(tp); + TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); typeVariables.add(tv); + for (TypeParamConstraint c : cs) { + typeClassConstraints.add(getConstraintFor(c)); + } } } - return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + return JassIm.ImClass(s1, s1.getName(), typeVariables, typeClassConstraints, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } @@ -1588,14 +1668,19 @@ public ImClass getTypeClassStructFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return typeClassStructFor.computeIfAbsent(s, s1 -> { ImTypeVars typeVariables = JassIm.ImTypeVars(); + ImTypeClassConstraints typeClassConstraints = ImTypeClassConstraints(); for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { ImTypeVar tv = getTypeVar(tp); + TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); typeVariables.add(tv); + for (TypeParamConstraint c : cs) { + typeClassConstraints.add(getConstraintFor(c)); + } } } - return JassIm.ImClass(s1, "typeclass_" + s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + return JassIm.ImClass(s1, s1.getName(), typeVariables, typeClassConstraints, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } @@ -1684,7 +1769,7 @@ private ImFunction makeDefaultErrorFunc() { List flags = Lists.newArrayList(); - return ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); + return ImFunction(emptyTrace, "error", ImTypeVars(), ImTypeClassConstraints(), parameters, returnType, locals, body, flags); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index 79daaaf9f..d74b0588a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -1,10 +1,8 @@ package de.peeeq.wurstscript.types; -import de.peeeq.wurstscript.jassIm.ImClassType; -import de.peeeq.wurstscript.jassIm.ImTypeClassImpl; -import de.peeeq.wurstscript.jassIm.ImTypeVar; -import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.ast.Element; +import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; public abstract class TypeClassInstance { @@ -27,13 +25,14 @@ protected ImClassType translateConstraintType(ImTranslator tr) { return constraint.imTranslateToTypeClass(tr); } - public static TypeClassInstance fromTypeParam(WurstTypeTypeParam wtp, WurstTypeClassOrInterface wurstTypeInterface, WurstTypeClassOrInterface constraint) { + public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeClassOrInterface wurstTypeInterface, WurstTypeClassOrInterface constraint) { return new TypeClassInstance(constraint) { @Override public ImTypeClassImpl translate(ImTranslator tr) { ImTypeVar tv = tr.getTypeVar(wtp.getDef()); - // TODO type var needs something that I can reference now ... - return JassIm.ImTypeClassImplFromOther(translateConstraintType(tr), ); + ImClassType constr = tr.getConstraintFor(wtp, wurstTypeInterface); + ImTypeClassConstraint otherImpl = JassIm.ImTypeClassConstraint(trace, constraint.getName(), constr); + return JassIm.ImTypeClassImplFromOther(translateConstraintType(tr), otherImpl); } }; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java index 5f302ebef..96ea3d1cf 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java @@ -10,6 +10,7 @@ import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.prettyPrint.DefaultSpacer; +import de.peeeq.wurstscript.jassIm.ImTypeClassImpls; import de.peeeq.wurstscript.jassIm.JassImElementWithName; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.WurstType; @@ -1057,6 +1058,23 @@ public static List init(List list) { return list.stream().limit(list.size() - 1).collect(Collectors.toList()); } + /** checks if two lists are equal using a custom equality function */ + public static boolean listEquals(List xs, List ys, BiPredicate cmp) { + if (xs.size() != ys.size()) { + return false; + } + Iterator xi = xs.iterator(); + Iterator yi = ys.iterator(); + while (xi.hasNext()) { + T x = xi.next(); + T y = yi.next(); + if (!cmp.test(x, y)) { + return false; + } + } + return true; + } + public static class ExecResult { private final String stdOut; private final String stdErr; From e679ff58c50e965cd091124ef28275b0b24ce1a9 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 23 Jan 2020 23:36:05 +0100 Subject: [PATCH 09/50] back to the design with parameters --- .../parserspec/jass_im.parseq | 32 +-- .../wurstio/CompiletimeFunctionRunner.java | 4 +- .../peeeq/wurstio/WurstCompilerJassImpl.java | 2 +- .../languageserver/requests/HoverInfo.java | 10 + .../AttrPossibleFunctionSignatures.java | 7 +- .../attributes/DescriptionHtml.java | 41 +++- .../attributes/prettyPrint/PrettyPrinter.java | 20 +- .../interpreter/EvaluateExpr.java | 22 -- .../interpreter/ILInterpreter.java | 1 - .../interpreter/LocalState.java | 14 -- .../optimizer/FunctionSplitter.java | 4 - .../optimizer/SideEffectAnalyzer.java | 5 - .../antlr/AntlrWurstParseTreeTransformer.java | 5 +- .../translation/imoptimizer/ImCompressor.java | 2 +- .../translation/imoptimizer/ImInliner.java | 4 +- .../translation/imoptimizer/NullSetter.java | 3 +- .../translation/imtojass/ExprTranslation.java | 4 - .../translation/imtojass/ImAttrType.java | 4 - .../imtojass/ImToJassTranslator.java | 4 +- .../translation/imtojass/TypeEquality.java | 21 -- .../imtojass/TypeRewriteMatcher.java | 4 +- .../imtranslation/AssertProperty.java | 5 - .../imtranslation/ClassManagementVars.java | 15 +- .../imtranslation/ClassTranslator.java | 12 +- .../imtranslation/ClosureTranslator.java | 10 +- .../imtranslation/CyclicFunctionRemover.java | 6 +- .../imtranslation/EliminateClasses.java | 9 +- .../imtranslation/EliminateGenerics.java | 32 +-- .../imtranslation/EliminateTuples.java | 6 +- .../imtranslation/ExprTranslation.java | 18 +- .../translation/imtranslation/Flatten.java | 15 +- .../imtranslation/FuncRefRemover.java | 4 +- .../imtranslation/GenericTypes.java | 3 - .../translation/imtranslation/ImPrinter.java | 11 +- .../imtranslation/ImTranslator.java | 213 ++++++------------ .../imtranslation/InterfaceTranslator.java | 3 +- .../imtranslation/MultiArrayEliminator.java | 19 +- .../RecycleCodeGeneratorQueue.java | 5 +- .../imtranslation/StackTraceInjector2.java | 16 +- .../imtranslation/StmtTranslation.java | 7 +- .../translation/imtranslation/VarFlag.java | 15 ++ .../imtranslation/VarargEliminator.java | 3 +- .../lua/translation/ExprTranslation.java | 5 - .../lua/translation/LuaTranslator.java | 9 +- .../wurstscript/types/TypeClassInstance.java | 15 +- .../types/WurstTypeBoundTypeParam.java | 8 +- .../types/WurstTypeClassOrInterface.java | 1 + .../de/peeeq/wurstscript/utils/Utils.java | 1 - .../wurstscript/tests/OptimizerTests.java | 16 +- 49 files changed, 258 insertions(+), 437 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarFlag.java diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index e30fadef4..a4c6bf684 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -15,7 +15,8 @@ ImVars * ImVar ImFunctions * ImFunction ImClasses * ImClass -ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, boolean isBJ) +ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, + java.util.List varFlags) ImType = ImSimpleType(String typename) @@ -35,19 +36,10 @@ ImTypeVars * ImTypeVar ImTypeVar(String name) -ImTypeClassConstraints * ImTypeClassConstraint - -ImTypeClassConstraint( - @ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, - ImClassType classType) - - -ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, +ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, - ImTypeClassConstraints typeClassConstraints, - ImVars parameters, + ImVars parameters, ref ImType returnType, ImVars locals, ImStmts body, @@ -58,8 +50,7 @@ ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, - ImTypeClassConstraints typeClassConstraints, - ImVars fields, + ImVars fields, ImMethods methods, ImFunctions functions, java.util.List superClasses) @@ -106,8 +97,6 @@ ImExpr = | ImGetStackTrace() | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr - | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImMethod method, ImExprs arguments - , ref ImTypeVar typeVariable) | ImCast(ImExpr expr, ref ImType toType) // an expression which can be used on the left hand side of an assignment @@ -154,13 +143,7 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type, ImTypeClassImpls typeClassImplementations) - -ImTypeClassImpls * ImTypeClassImpl - -ImTypeClassImpl = - ImTypeClassImplFromInterface(ref ImClassType typeClass, ref ImClassType implementingClass) - | ImTypeClassImplFromOther(ref ImClassType typeClass, ref ImTypeClassConstraint otherImpl) +ImTypeArgument(ref ImType type) // helper types: @@ -392,3 +375,6 @@ ImLExpr.isUsedAsLValue() returns boolean implemented by de.peeeq.wurstscript.translation.imtranslation.LValues.isUsedAsLValue +ImVar.isBJ() + returns boolean + implemented by de.peeeq.wurstscript.translation.imtranslation.VarFlag.isBj \ No newline at end of file diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java index 2680e6f62..d1be15aee 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java @@ -241,7 +241,7 @@ private void executeCompiletimeExpr(ImCompiletimeExpr cte) { public ImVar initFor(ILconstObject obj) { - ImVar res = JassIm.ImVar(obj.getTrace(), obj.getType(), obj.getType() + "_compiletime", false); + ImVar res = JassIm.ImVar(obj.getTrace(), obj.getType(), obj.getType() + "_compiletime", Collections.emptyList()); imProg.getGlobals().add(res); ImAlloc alloc = JassIm.ImAlloc(obj.getTrace(), obj.getType()); addCompiletimeStateInitAlloc(alloc.getTrace(), res, alloc); @@ -281,7 +281,7 @@ public ImVar initFor(IlConstHandle a) { @SuppressWarnings("unchecked") ArrayListMultimap map = (ArrayListMultimap) obj; ImType type = TypesHelper.imHashTable(); - ImVar res = JassIm.ImVar(trace, type, type + "_compiletime", false); + ImVar res = JassIm.ImVar(trace, type, type + "_compiletime", Collections.emptyList()); imProg.getGlobals().add(res); init = constantToExprHashtable(trace, res, a, map); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index b1d8a14a3..9db7b8b12 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -543,7 +543,7 @@ private void addJassHotCodeReloadCode() { ImFunction jhcr_reload = JassIm.ImFunction(trace, "jhcr_reload_on_escape", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), reloadBody, Collections.emptyList()); - ImVar trig = JassIm.ImVar(trace, TypesHelper.imTrigger(), "trig", false); + ImVar trig = JassIm.ImVar(trace, TypesHelper.imTrigger(), "trig", Collections.emptyList()); mainFunc.getLocals().add(trig); // TriggerRegisterPlayerEventEndCinematic(trig, Player(0)) stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(trig), callExtern(trace, CallType.NORMAL, "CreateTrigger"))); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java index 67fb6b941..bde562bfb 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java @@ -631,6 +631,11 @@ public List> case_ExprNull(ExprNull exprNull) { return string("The null-reference"); } + @Override + public List> case_TypeParamConstraintList(TypeParamConstraintList typeParamConstraintList) { + return string("Type parameter constraints define type classes that must be implemented for type parameters."); + } + @Override public List> case_ClassDefs(ClassDefs classDefs) { return string("A list of class definitions."); @@ -736,6 +741,11 @@ public List> case_ExprIfElse(ExprIfElse exprIfElse) return string("A conditional expression (condition ? ifTrue : ifFalse)."); } + @Override + public List> case_TypeParamConstraint(TypeParamConstraint t) { + return string(t.description()); + } + @Override public List> case_WurstDoc(WurstDoc wurstDoc) { return wurstDoc.getParent().match(this); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 10946a190..0a9e8cbb5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -124,14 +124,17 @@ private static VariableBinding findTypeClass(StmtCall fc, List err // option 1: the matched type is a subtype of the constraint VariableBinding mapping2 = matchedType.matchAgainstSupertype(constraint, fc, mapping, VariablePosition.RIGHT); if (mapping2 != null) { - return mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(constraint))); + WurstTypeClassOrInterface subType = (WurstTypeClassOrInterface) matchedType.getBaseType(); + return mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(subType, constraint))); } // option 2: the matched type is a type param that also has the right constraint: if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); Optional matchingConstraint = wtp.getTypeConstraints().filter(c -> c.isSubtypeOf(constraint, fc)).findFirst(); if (matchingConstraint.isPresent()) { - return mapping.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.fromTypeParam(fc, wtp, matchingConstraint.get()))); + TypeClassInstance instance = TypeClassInstance.fromTypeParam( + fc, wtp, matchingConstraint.get()); + return mapping.set(tp, matchedType.withTypeClassInstance(instance)); } } // option 3: find methods elsewhere diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java index 07041b397..4588788e0 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java @@ -70,8 +70,8 @@ public static String description(ConstructorDef constr) { descr = ""; } descr += "

" + - "construct(" + getParameterString(constr) + ") " - + "
" + "defined in class " + c.getName(); + "construct(" + getParameterString(constr) + ") " + + "
" + "defined in class " + c.getName(); return descr; } @@ -104,7 +104,7 @@ public static String description(NameDef n) { additionalProposalInfo = ""; } additionalProposalInfo += "

" + htmlType(n.attrTyp()) + " " + n.getName() - + "
" + "defined in " + nearestScopeName(n); + + "
" + "defined in " + nearestScopeName(n); return additionalProposalInfo; } @@ -130,7 +130,7 @@ public static String description(Annotation annotation) { FuncLink f = e.attrFuncLink(); if (f != null) { return "This is an overloaded operator:
" + - f.getDef().descriptionHtml(); + f.getDef().descriptionHtml(); } return null; } @@ -205,7 +205,7 @@ public static String description(ExprThis e) { public static String description(ExprTypeId exprTypeId) { return "typeId: returns the typeId of an object or class. The typeId is " - + "a unique number for each class in the same type hierarchy."; + + "a unique number for each class in the same type hierarchy."; } public static @Nullable String description(ExprUnary exprUnary) { @@ -214,7 +214,7 @@ public static String description(ExprTypeId exprTypeId) { public static @Nullable String description( - IdentifierWithTypeArgs identifierWithTypeArgs) { + IdentifierWithTypeArgs identifierWithTypeArgs) { return null; } @@ -223,13 +223,13 @@ public static String description(InitBlock initBlock) { } public static @Nullable String description( - IdentifierWithTypeParamDefs identifierWithTypeParamDefs) { + IdentifierWithTypeParamDefs identifierWithTypeParamDefs) { return null; } public static String description(ModAbstract modAbstract) { return "abstract: This function provides no implementation. Other classes have to provide " - + "an implementation for this method."; + + "an implementation for this method."; } public static String description(ModConstant modConstant) { @@ -243,7 +243,7 @@ public static String description(ModOverride m) { public static String description(ModStatic modStatic) { return "static: This function or variable is just like a function outside of a class. " - + "It is not bound to an instance. No dynamic dispatch is used."; + + "It is not bound to an instance. No dynamic dispatch is used."; } public static String description(ModuleUse m) { @@ -264,7 +264,7 @@ public static String description(ModuleUse m) { public static String description(OnDestroyDef s) { return "ondestroy block: These statements are executed when an object of this class " - + "is destroyed." + s.getSource().getLeftPos() + " - " + s.getSource().getRightPos(); + + "is destroyed." + s.getSource().getLeftPos() + " - " + s.getSource().getRightPos(); } public static @Nullable String description(StartFunctionStatement s) { @@ -335,7 +335,7 @@ public static String description(SwitchCase switchCase) { } public static String description( - SwitchDefaultCaseStatements switchDefaultCaseStatements) { + SwitchDefaultCaseStatements switchDefaultCaseStatements) { return "The default case for this switch statement"; } @@ -421,4 +421,23 @@ public static String description(SomeSuperConstructorCall s) { public static String description(NoTypeParamConstraints noTypeParamConstraints) { return "no type parameter constraints"; } + + public static String description(TypeParamConstraint tp) { + TypeParamDef d = findTypeParamDef(tp); + if (d == null) { + return "Type parameter constraint."; + } + return "Constraints the type parameter " + d.getName() + " to implement the type class " + tp.getConstraint().attrTyp(); + } + + private static TypeParamDef findTypeParamDef(TypeParamConstraint tp) { + Element e = tp; + while (e != null) { + if (e instanceof TypeParamDef) { + return (TypeParamDef) e; + } + e = e.getParent(); + } + return null; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index 03fb9bb01..b47021ce4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -81,8 +81,8 @@ public static void prettyPrint(ConstructorDef e, Spacer spacer, StringBuilder sb public static void prettyPrint(ConstructorDefs e, Spacer spacer, StringBuilder sb, int indent) { for (ConstructorDef constructorDef : e) { if (!constructorDef.getParameters().isEmpty() - || constructorDef.getSuperConstructorCall() instanceof SomeSuperConstructorCall - || constructorDef.getBody().size() > 2) { + || constructorDef.getSuperConstructorCall() instanceof SomeSuperConstructorCall + || constructorDef.getBody().size() > 2) { constructorDef.prettyPrint(spacer, sb, indent); } } @@ -723,11 +723,11 @@ public static void prettyPrint(Identifier identifier, Spacer spacer, StringBuild public static void prettyPrint(ExprIfElse e, Spacer spacer, StringBuilder sb, int indent) { sb.append("("); - e.getCond().prettyPrint(spacer, sb, indent+1); + e.getCond().prettyPrint(spacer, sb, indent + 1); sb.append(" ? "); - e.getIfTrue().prettyPrint(spacer, sb, indent+1); + e.getIfTrue().prettyPrint(spacer, sb, indent + 1); sb.append(" : "); - e.getIfFalse().prettyPrint(spacer, sb, indent+1); + e.getIfFalse().prettyPrint(spacer, sb, indent + 1); sb.append(")"); } @@ -759,4 +759,14 @@ public static String print(Element element) { element.prettyPrint(spacer, sb, 0); return sb.toString(); } + + public static void prettyPrint(TypeParamConstraint t, Spacer spacer, StringBuilder sb, int indent) { + t.getConstraint().prettyPrint(spacer, sb, indent); + } + + public static void prettyPrint(TypeParamConstraintList typeParamConstraints, Spacer spacer, StringBuilder sb, int indent) { + commaSeparatedList(typeParamConstraints, spacer, sb, indent); + } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 38ca0ed46..8c61cd50f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -398,28 +398,6 @@ public ILconst get() { } - public static ILconst eval(ImTypeVarDispatch e, ProgramState globalState, LocalState localState) { - Either impl = localState.getImplementation(e.getTypeVariable(), e.getTypeClassFunc()); - if (impl == null) { - throw new InterpreterException(globalState, "Could not find implementation for " + e.getTypeVariable() + "." + e.getTypeClassFunc().getName()); - } - ILconst[] eArgs = e.getArguments().stream() - .map(arg -> arg.evaluate(globalState, localState)) - .toArray(ILconst[]::new); - - return impl.fold( - (ImMethod m) -> { - ILconst receiver1 = eArgs[0]; - ILconstObject receiver = globalState.toObject(receiver1); - globalState.assertAllocated(receiver, e.attrTrace()); - ImMethod mostPreciseMethod = findMostPreciseMethod(e.attrTrace(), globalState, receiver, m); - return evaluateFunc(globalState, mostPreciseMethod.getImplementation(), e, Collections.emptyList(), eArgs); - }, - (ImFunction f) -> - evaluateFunc(globalState, f, e, Collections.emptyList(), eArgs) // TODO type var dispatch should also have type arguments? - ); - } - public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState localState) { ILconst res = imCast.getExpr().evaluate(globalState, localState); if (TypesHelper.isIntType(imCast.getToType())) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 9dbd285ad..64f841238 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -79,7 +79,6 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab } LocalState localState = new LocalState(); - localState.setTypeArguments(f.getTypeVariables(), typeArguments); int i = 0; for (ImVar p : f.getParameters()) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index e2f98ffcc..2039e60ef 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -14,7 +14,6 @@ public class LocalState extends State { private @Nullable ILconst returnVal = null; - private Table typeClassImplementations = HashBasedTable.create(); public LocalState(ILconst returnVal) { this.setReturnVal(returnVal); @@ -33,17 +32,4 @@ public LocalState setReturnVal(@Nullable ILconst returnVal) { } - public @Nullable ImTypeClassImpl getImplementation(ImTypeVar typeVariable, ImClassType classType) { - return typeClassImplementations.get(typeVariable, classType); - } - - public void setTypeArguments(ImTypeVars typeVariables, List typeArguments) { - for (int i = 0; i < typeVariables.size() && i < typeArguments.size(); i++) { - ImTypeVar typeVariable = typeVariables.get(i); - ImTypeArgument typeArgument = typeArguments.get(i); - for (ImTypeClassImpl e : typeArgument.getTypeClassImplementations()) { - typeClassImplementations.put(typeVariable, e.getTypeClass(), e); - } - } - } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java index d012731cf..696488a72 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java @@ -100,10 +100,6 @@ private List> split(List body) { private int estimateFuel(ImStmt s) { return s.match(new ImStmt.Matcher() { - @Override - public Integer case_ImTypeVarDispatch(ImTypeVarDispatch s) { - return estimateFuel(s.getArguments()) + 100; - } @Override public Integer case_ImDealloc(ImDealloc s) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java index b8c341a10..58abbb566 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java @@ -96,11 +96,6 @@ public Boolean case_ImGetStackTrace(ImGetStackTrace imGetStackTrace) { return true; } - @Override - public Boolean case_ImTypeVarDispatch(ImTypeVarDispatch imTypeVarDispatch) { - return true; - } - @Override public Boolean case_ImOperatorCall(ImOperatorCall e) { return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 9430f36ac..21298cd1a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -1306,9 +1306,10 @@ private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsCo if (tc == null) { return Ast.NoTypeParamConstraints(); } - TypeExprList res = Ast.TypeExprList(); + TypeParamConstraintList res = Ast.TypeParamConstraintList(); for (TypeExprContext t : tc.constraints) { - res.add(transformTypeExpr(t)); + TypeExpr te = transformTypeExpr(t); + res.add(Ast.TypeParamConstraint(te)); } return res; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java index c9277fc5d..3d6b5ed47 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java @@ -23,7 +23,7 @@ public void compressNames() { public void compressGlobals() { for (final ImVar global : prog.getGlobals()) { - if (global.getIsBJ()) { + if (global.isBJ()) { // no not rename bj constants continue; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java index 087cac4ef..00f0ffb3b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java @@ -110,7 +110,7 @@ private void inlineCall(ImFunction f, Element parent, int parentI, ImFunctionCal for (int pi = 0; pi < called.getParameters().size(); pi++) { ImVar param = called.getParameters().get(pi); ImExpr arg = args.get(pi); - ImVar tempVar = JassIm.ImVar(arg.attrTrace(), param.getType(), param.getName(), false); + ImVar tempVar = JassIm.ImVar(arg.attrTrace(), param.getType(), param.getName(), Collections.emptyList()); f.getLocals().add(tempVar); varSubtitutions.put(param, tempVar); // set temp var @@ -118,7 +118,7 @@ private void inlineCall(ImFunction f, Element parent, int parentI, ImFunctionCal } // add locals for (ImVar l : called.getLocals()) { - ImVar newL = JassIm.ImVar(l.getTrace(), l.getType(), l.getName(), false); + ImVar newL = JassIm.ImVar(l.getTrace(), l.getType(), l.getName(), Collections.emptyList()); f.getLocals().add(newL); varSubtitutions.put(l, newL); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java index ace873313..eb499dbe3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -114,7 +115,7 @@ private void handleReturnStmt(final ImFunction f, if (exprContainsVar(returnExpr, handleVars)) { // if the returnExpr contains some handleVar, we have to add a temporary var - ImVar tempReturn = JassIm.ImVar(imReturn.attrTrace(), returnExpr.attrTyp(), f.getName() + "tempReturn", false); + ImVar tempReturn = JassIm.ImVar(imReturn.attrTrace(), returnExpr.attrTyp(), f.getName() + "tempReturn", Collections.emptyList()); if (isHandleType(returnExpr.attrTyp())) { // use global variables for handle types prog.getGlobals().add(tempReturn); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java index 6545d6694..48d060451 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java @@ -140,10 +140,6 @@ public static JassExpr translate(ImCompiletimeExpr e, ImToJassTranslator transla "Enable '-runcompiletimefunctions' to evaluate compiletime expressions."); } - public static JassExpr translate(ImTypeVarDispatch e, ImToJassTranslator translator) { - throw new CompileError(e, "Typevar dispatch not eliminated."); - } - public static JassExpr translate(ImCast imCast, ImToJassTranslator translator) { return imCast.getExpr().translate(translator); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index 26695972d..66c542a34 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -212,10 +212,6 @@ public static ImType getType(ImCompiletimeExpr e) { return e.getExpr().attrTyp(); } - public static ImType getType(ImTypeVarDispatch e) { - return e.getTypeClassFunc().getReturnType(); - } - public static ImType getType(ImCast imCast) { return imCast.getToType(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java index 6d9869542..cfb02a560 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java @@ -208,12 +208,12 @@ JassVar getJassVarFor(ImVar v) { } else { if (isGlobal(v) && v.getType() instanceof ImSimpleType) { JassExpr initialVal = ImHelper.defaultValueForType((ImSimpleType) v.getType()).translate(this); - result = JassAst.JassInitializedVar(type, name, initialVal, v.getIsBJ()); + result = JassAst.JassInitializedVar(type, name, initialVal, v.isBJ()); } else { result = JassAst.JassSimpleVar(type, name); } } - if (isGlobal(v) && (!v.getIsBJ() || result instanceof JassInitializedVar)) { + if (isGlobal(v) && (!v.isBJ() || result instanceof JassInitializedVar)) { prog.getGlobals().add(result); } jassVars.put(v, result); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java index 17dfe099e..1f268b4bd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java @@ -76,33 +76,12 @@ public static boolean isEqualType(ImClassType c, ImType other) { if (!x.getType().equalsType(y.getType())) { return false; } - if (!equalTypeClassImplementations(x.getTypeClassImplementations(), y.getTypeClassImplementations())) { - return false; - } } return true; } return false; } - private static boolean equalTypeClassImplementations(ImTypeClassImpls xs, ImTypeClassImpls ys) { - return Utils.listEquals(xs, ys, TypeEquality::equalTypeClassImplementation); - } - - private static boolean equalTypeClassImplementation(ImTypeClassImpl x, ImTypeClassImpl y) { - if (!x.getClass().equals(y.getClass())) { - return false; - } - if (x instanceof ImTypeClassImplFromInterface) { - ImTypeClassImplFromInterface xc = (ImTypeClassImplFromInterface) x; - ImTypeClassImplFromInterface yc = (ImTypeClassImplFromInterface) y; - return isEqualType(xc.getImplementingClass(), yc.getImplementingClass()); - } else { - ImTypeClassImplFromOther xc = (ImTypeClassImplFromOther) x; - ImTypeClassImplFromOther yc = (ImTypeClassImplFromOther) y; - return xc.getOtherImpl() == yc.getOtherImpl(); - } - } public static boolean isEqualType(ImAnyType t, ImType other) { return other instanceof ImAnyType; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java index 0f9b9b455..40ac9fda5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java @@ -53,7 +53,9 @@ public ImType case_ImArrayType(ImArrayType t) { public ImType case_ImClassType(ImClassType t) { ImTypeArguments args = t.getTypeArguments() .stream() - .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this), ta.getTypeClassBinding())) + .map(ta -> { + return JassIm.ImTypeArgument(ta.getType().match(this)); + }) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(t.getClassDef(), args); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java index dc5e6669d..a5b5caf79 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java @@ -43,16 +43,11 @@ public void check(Element e) { ImFunction f = (ImFunction) e; currentFunction = f; checkType(e, (f).getReturnType()); - } else if (e instanceof ImTypeClassFunc) { - checkType(e, ((ImTypeClassFunc) e).getReturnType()); } else if (e instanceof ImMethod) { checkType(e, ((ImMethod) e).getMethodClass()); checkRooted(e, ((ImMethod) e).getImplementation()); } else if (e instanceof ImVarargLoop) { checkRooted(e, ((ImVarargLoop) e).getLoopVar()); - } else if (e instanceof ImTypeVarDispatch) { - checkRooted(e, ((ImTypeVarDispatch) e).getTypeClassFunc()); - checkRooted(e, ((ImTypeVarDispatch) e).getTypeVariable()); } else if (e instanceof ImVarAccess) { checkRooted(e, ((ImVarAccess) e).getVar()); } else if (e instanceof ImVarArrayAccess) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java index e525cb44c..9479423a6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java @@ -1,13 +1,10 @@ package de.peeeq.wurstscript.translation.imtranslation; import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.jassIm.ImClass; -import de.peeeq.wurstscript.jassIm.ImProg; -import de.peeeq.wurstscript.jassIm.ImVar; -import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.TypesHelper; -import java.lang.reflect.Type; +import java.util.Collections; public class ClassManagementVars { /** @@ -33,16 +30,16 @@ public class ClassManagementVars { public ClassManagementVars(ImClass repClass, ImTranslator translator) { Element tr = repClass.getTrace(); ImProg prog = translator.getImProg(); - free = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_nextFree", false); + free = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_nextFree", Collections.emptyList()); prog.getGlobals().add(free); - freeCount = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_firstFree", false); + freeCount = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_firstFree", Collections.emptyList()); translator.addGlobalWithInitalizer(freeCount, JassIm.ImIntVal(0)); - maxIndex = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_maxIndex", false); + maxIndex = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_maxIndex", Collections.emptyList()); translator.addGlobalWithInitalizer(maxIndex, JassIm.ImIntVal(0)); - typeId = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_typeId", false); + typeId = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_typeId", Collections.emptyList()); prog.getGlobals().add(typeId); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index d96a3afef..bc05b05df 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -124,7 +124,7 @@ private void createDestroyMethod(List subClasses) { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .map(tv -> ImTypeArgument(JassIm.ImTypeVarRef(tv))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } @@ -351,13 +351,13 @@ private void createNewFunc(ConstructorDef constr) { Map varReplacements = Maps.newLinkedHashMap(); for (WParameter p : constr.getParameters()) { - ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), false); + ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), Collections.emptyList()); varReplacements.put(translator.getVarFor(p), imP); f.getParameters().add(imP); } - ImVar thisVar = JassIm.ImVar(constr, imClassType(), "this", false); + ImVar thisVar = ImVar(constr, imClassType(), "this", Collections.emptyList()); varReplacements.put(translator.getThisVar(constr), thisVar); f.getLocals().add(thisVar); @@ -372,7 +372,7 @@ private void createNewFunc(ConstructorDef constr) { } ImTypeArguments typeArgs = ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArgs.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, constrFunc, typeArgs, arguments, false, CallType.NORMAL)); @@ -412,7 +412,7 @@ private void createConstructFunc(ConstructorDef constr) { // call classInitFunc: ImTypeArguments typeArguments = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArguments.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, classInitFunc, typeArguments, JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); // constructor user code @@ -421,7 +421,7 @@ private void createConstructFunc(ConstructorDef constr) { private void translateClassInitFunc() { ClassDef trace = classDef; - ImVar thisVar = JassIm.ImVar(trace, imClassType(), "this", false); + ImVar thisVar = ImVar(trace, imClassType(), "this", Collections.emptyList()); classInitFunc = JassIm.ImFunction(classDef, translator.getNameFor(classDef) + "_init", ImTypeVars(), ImVars(thisVar), ImVoid(), ImVars(), ImStmts(), Collections.emptyList()); imClass.getFunctions().add(classInitFunc); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java index 1ee74a06e..13e99d8b1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java @@ -45,7 +45,7 @@ public ImExpr translate() { } else { ImClass c = createClass(); ImClassType ct = JassIm.ImClassType(c, getClassTypeArguments()); - ImVar clVar = JassIm.ImVar(e, ct, "clVar", false); + ImVar clVar = JassIm.ImVar(e, ct, "clVar", Collections.emptyList()); f.getLocals().add(clVar); ImStmts stmts = JassIm.ImStmts(); // allocate closure @@ -64,7 +64,7 @@ public ImExpr translate() { private ImTypeArguments getClassTypeArguments() { ImTypeArguments res = JassIm.ImTypeArguments(); for (ImTypeVar typeVar : typeVars.keySet()) { - res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar), Collections.emptyMap())); + res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar))); } return res; } @@ -229,9 +229,7 @@ public ImType case_ImTypeVarRef(ImTypeVarRef t) { result.put(oldTypevar, newTypevar); c.getTypeVariables().add(newTypevar); thisType.getTypeArguments().add( - JassIm.ImTypeArgument( - JassIm.ImTypeVarRef(newTypevar), - Collections.emptyMap())); + JassIm.ImTypeArgument(JassIm.ImTypeVarRef(newTypevar))); } return JassIm.ImTypeVarRef(newTypevar); } @@ -271,7 +269,7 @@ private ImVarAccess closureThis() { private ImVar getClosureVarFor(ImVar var) { ImVar v = closureVars.get(var); if (v == null) { - v = JassIm.ImVar(e, var.getType(), var.getName(), false); + v = JassIm.ImVar(e, var.getType(), var.getName(), Collections.emptyList()); c.getFields().add(v); closureVars.put(var, v); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java index 74c9c1301..e9e0504fe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java @@ -46,7 +46,7 @@ private void removeCycle(List funcs) { de.peeeq.wurstscript.ast.Element trace = funcs.get(0).getTrace(); - ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", false); + ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", Collections.emptyList()); List flags = Lists.newArrayList(); @@ -212,7 +212,7 @@ private ImVar getTempReturnVar(ImType t) { String typeName = t.translateType(); ImVar r = tempReturnVars.get(typeName); if (r == null) { - r = JassIm.ImVar(t.attrTrace(), t, "tempReturn_" + typeName, false); + r = JassIm.ImVar(t.attrTrace(), t, "tempReturn_" + typeName, Collections.emptyList()); prog.getGlobals().add(r); tempReturnVars.put(typeName, r); } @@ -239,7 +239,7 @@ private void calculateNewParameters(List funcs, } } // otherwise, we have to create a new var: - ImVar newVar = JassIm.ImVar(v.getTrace(), (ImType) v.getType().copy(), v.getName(), false); + ImVar newVar = JassIm.ImVar(v.getTrace(), (ImType) v.getType().copy(), v.getName(), Collections.emptyList()); oldToNewVar.put(v, newVar); newParameters.add(newVar); pos = newParameters.size() + 1; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java index 3a817ce59..35177f911 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java @@ -133,7 +133,7 @@ public static int calculateMaxTypeId(ImProg prog) { @NotNull private ImFunction accessClassManagementVar(String funcName, ImType returnType, ImExpr defaultReturn, Function makeAccess) { Element trace = prog.getTrace(); - ImVar typeId = JassIm.ImVar(trace, TypesHelper.imInt(), "typeId", false); + ImVar typeId = JassIm.ImVar(trace, TypesHelper.imInt(), "typeId", Collections.emptyList()); ImVars parameters = JassIm.ImVars(typeId); ImVars locals = JassIm.ImVars(); Map classId = prog.attrTypeId(); @@ -218,8 +218,7 @@ private void eliminateClass(ImClass c) { // for each field, create a global array variable for (ImVar f : c.getFields()) { ImType type = ImHelper.toArray(f.getType()); - ImVar v = JassIm - .ImVar(f.getTrace(), type, f.getName(), false); + ImVar v = JassIm.ImVar(f.getTrace(), type, f.getName(), Collections.emptyList()); prog.getGlobals().add(v); fieldToArray.put(f, v); } @@ -274,7 +273,7 @@ public void createDispatchFunc(ImClass c, ImMethod m) { resultVar = null; } else { resultVar = JassIm.ImVar(df.getTrace(), returnType, m.getName() - + "_result", false); + + "_result", Collections.emptyList()); df.getLocals().add(resultVar); } @@ -564,7 +563,7 @@ private void replaceInstanceof(ImInstanceof e) { ImExpr objTypeIdExpr = objTypeId; if (useTempVar) { // use temporary variable - tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", false); + tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", Collections.emptyList()); f.getLocals().add(tempVar); objTypeIdExpr = JassIm.ImVarAccess(tempVar); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index c034378ca..63370b347 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -145,7 +145,7 @@ private void moveFunctionsOutOfClass(ImClass c) { f.getTypeVariables().addAll(0, newTypeVars); List typeArgs = newTypeVars .stream() - .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) + .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta))) .collect(Collectors.toList()); rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables(), newTypeVars); } @@ -322,32 +322,6 @@ public void visit(ImDealloc e) { super.visit(e); } - @Override - public void visit(ImTypeVarDispatch e) { - super.visit(e); - ImTypeVar tv = e.getTypeVariable(); - int index = newTypeVars.indexOf(tv); - if (index < 0) { - throw new CompileError(e.attrTrace(), "Could not find type variable " + tv + " in " + newTypeVars); - } - ImTypeArgument ta = generics.getTypeArguments().get(index); - Either impl = ta.getTypeClassBinding().get(e.getTypeClassFunc()); - if (impl == null) { - throw new CompileError(e.attrTrace(), "Could not find func " + e.getTypeClassFunc().getName() + " in " + ta.getTypeClassBinding().keySet()); - } - ImExpr newExpr; - if (impl.isLeft()) { - ImMethod m = impl.getLeft(); - ImExpr receiver = e.getArguments().remove(0); - e.getArguments().setParent(null); - newExpr = JassIm.ImMethodCall(e.getTrace(), m, JassIm.ImTypeArguments(), receiver, e.getArguments(), false); - } else { - ImFunction f = impl.get(); - e.getArguments().setParent(null); - newExpr = JassIm.ImFunctionCall(e.getTrace(), f, JassIm.ImTypeArguments(), e.getArguments(), false, CallType.NORMAL); - } - e.replaceBy(newExpr); - } }); } @@ -697,7 +671,9 @@ public ImType case_ImClassType(ImClassType t) { private List specializeTypeArgs(ImTypeArguments typeArgs) { return typeArgs .stream() - .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()), ta.getTypeClassBinding())) + .map(ta -> { + return JassIm.ImTypeArgument(specializeType(ta.getType())); + }) .collect(Collectors.toList()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java index fca20a886..7b895760d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java @@ -182,7 +182,7 @@ public void visit(ImVarArrayAccess va) { for (ImExpr ie : indexes) { if (sideEffects) { // use temp variables if there are side effects - ImVar tempIndex = JassIm.ImVar(ie.attrTrace(), TypesHelper.imInt(), "tempIndex", false); + ImVar tempIndex = JassIm.ImVar(ie.attrTrace(), TypesHelper.imInt(), "tempIndex", Collections.emptyList()); indexExprs.add(JassIm.ImVarAccess(tempIndex)); f.getLocals().add(tempIndex); ie.setParent(null); @@ -392,7 +392,7 @@ private static ImStatementExpr inSet(ImSet imSet, ImFunction f) { List tempVars = new ArrayList<>(); // 2) assign right hand side to temporary variables: for (ImExpr expr : right.getExprs()) { - ImVar temp = JassIm.ImVar(expr.attrTrace(), expr.attrTyp(), "tuple_temp", false); + ImVar temp = JassIm.ImVar(expr.attrTrace(), expr.attrTyp(), "tuple_temp", Collections.emptyList()); expr.setParent(null); stmts.add(JassIm.ImSet(expr.attrTrace(), JassIm.ImVarAccess(temp), expr)); tempVars.add(temp); @@ -452,7 +452,7 @@ private static Element inTupleSelection(ImTupleSelection ts, ImTupleExpr tupleEx // TODO maybe this assumption should be validated ... result = extractSideEffect(te, stmts); } else { - ImVar temp = JassIm.ImVar(trace, te.attrTyp(), "tupleSelection", false); + ImVar temp = JassIm.ImVar(trace, te.attrTyp(), "tupleSelection", Collections.emptyList()); f.getLocals().add(temp); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(temp), te)); result = JassIm.ImVarAccess(temp); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index edc80b021..40468f547 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -16,12 +16,10 @@ import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; -import io.vavr.control.Either; import io.vavr.control.Option; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import static de.peeeq.wurstscript.jassIm.JassIm.*; @@ -298,7 +296,7 @@ private static ImExpr translateTupleSelection(ImTranslator t, ImFunction f, Expr } else { // if tupleExpr is not an l-value (e.g. foo().x) // store result in intermediate variable first: - ImVar v = ImVar(left.attrTrace(), left.attrTyp(), "temp_tuple", false); + ImVar v = ImVar(left.attrTrace(), left.attrTyp(), "temp_tuple", Collections.emptyList()); f.getLocals().add(v); return JassIm.ImStatementExpr( JassIm.ImStmts( @@ -507,7 +505,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu if (returnReveiver) { if (leftExpr == null) throw new Error("impossible"); - tempVar = JassIm.ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", false); + tempVar = ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", Collections.emptyList()); f.getLocals().add(tempVar); stmts = JassIm.ImStmts(ImSet(e, ImVarAccess(tempVar), receiver)); receiver = JassIm.ImVarAccess(tempVar); @@ -517,11 +515,13 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; if (typeParamDispatchOn != null) { - ImTypeClassFunc typeClassFunc = t.getTypeClassFuncFor((FuncDef) calledFunc); + ImMethod method = t.getTypeClassMethodFor((FuncDef) calledFunc); if (receiver != null) { imArgs.add(0, receiver); } - call = JassIm.ImTypeVarDispatch(e, typeClassFunc, imArgs, t.getTypeVar(typeParamDispatchOn.getDef())); + ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn, calledFunc)); + call = JassIm.ImMethodCall(e, method, typeArguments, typeClassDict, imArgs, false); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); @@ -536,8 +536,6 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } if (returnReveiver) { - if (stmts == null) - throw new Error("impossible"); stmts.add(call); return JassIm.ImStatementExpr(stmts, JassIm.ImVarAccess(tempVar)); } else { @@ -685,7 +683,7 @@ public static ImExpr translate(ExprIfElse e, ImTranslator t, ImFunction f) { ImExpr ifTrue = e.getIfTrue().imTranslateExpr(t, f); ImExpr ifFalse = e.getIfFalse().imTranslateExpr(t, f); // TODO common super type of both - ImVar res = JassIm.ImVar(e, ifTrue.attrTyp(), "cond_result", false); + ImVar res = ImVar(e, ifTrue.attrTyp(), "cond_result", Collections.emptyList()); f.getLocals().add(res); return JassIm.ImStatementExpr( ImStmts( diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 30aab80aa..7cf10442b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -58,11 +58,6 @@ public class Flatten { - public static Result flatten(ImTypeVarDispatch e, ImTranslator t, ImFunction f) { - MultiResult r = flattenExprs(t, f, e.getArguments()); - return new Result(r.stmts, ImTypeVarDispatch(e.getTrace(), e.getTypeClassFunc(), ImExprs(r.exprs), e.getTypeVariable())); - } - public static Result flatten(ImCast imCast, ImTranslator translator, ImFunction f) { Result res = imCast.getExpr().flatten(translator, f); return new Result(res.stmts, ImCast(res.expr, imCast.getToType())); @@ -321,7 +316,7 @@ public static Result flatten(ImOperatorCall e, ImTranslator t, ImFunction f) { return new Result(left.stmts, JassIm.ImOperatorCall(WurstOperator.AND, ImExprs(left.expr, right.expr))); } else { ArrayList stmts = Lists.newArrayList(left.stmts); - ImVar tempVar = JassIm.ImVar(e.attrTrace(), WurstTypeBool.instance().imTranslateType(t), "andLeft", false); + ImVar tempVar = ImVar(e.attrTrace(), WurstTypeBool.instance().imTranslateType(t), "andLeft", Collections.emptyList()); f.getLocals().add(tempVar); ImStmts thenBlock = JassIm.ImStmts(); // if left is true then check right @@ -341,7 +336,7 @@ public static Result flatten(ImOperatorCall e, ImTranslator t, ImFunction f) { return new Result(left.stmts, JassIm.ImOperatorCall(WurstOperator.OR, ImExprs(left.expr, right.expr))); } else { ArrayList stmts = Lists.newArrayList(left.stmts); - ImVar tempVar = JassIm.ImVar(trace, WurstTypeBool.instance().imTranslateType(t), "andLeft", false); + ImVar tempVar = ImVar(trace, WurstTypeBool.instance().imTranslateType(t), "andLeft", Collections.emptyList()); f.getLocals().add(tempVar); // if left is true then result is ture ImStmts thenBlock = JassIm.ImStmts(ImSet(trace, ImVarAccess(tempVar), JassIm.ImBoolVal(true))); @@ -412,7 +407,7 @@ public static ResultL flattenL(ImTupleSelection e, ImTranslator t, ImFunction f) } else { // in the unlikely event that this is not an l-value (e.g. foo().x) // we create a temporary variable and store the result there - ImVar v = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "tuple_temp", false); + ImVar v = ImVar(e.attrTrace(), r.expr.attrTyp(), "tuple_temp", Collections.emptyList()); f.getLocals().add(v); stmts = new ArrayList<>(r.stmts); stmts.add(JassIm.ImSet(e.attrTrace(), ImVarAccess(v), r.expr)); @@ -487,7 +482,7 @@ private static MultiResult flattenExprs(ImTranslator t, ImFunction f, List= withStmts) { newExprs.add(r.expr); } else { - ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); + ImVar tempVar = ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", Collections.emptyList()); f.getLocals().add(tempVar); stmts.add(ImSet(e.attrTrace(), ImVarAccess(tempVar), r.expr)); newExprs.add(JassIm.ImVarAccess(tempVar)); @@ -518,7 +513,7 @@ private static MultiResultL flattenExprsL(ImTranslator t, ImFunction f, List= withStmts) { newExprs.add(r.getExpr()); } else { - ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); + ImVar tempVar = ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", Collections.emptyList()); f.getLocals().add(tempVar); stmts.add(ImSet(e.attrTrace(), ImVarAccess(tempVar), r.expr)); newExprs.add(JassIm.ImVarAccess(tempVar)); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java index 229478285..be3c6bd51 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.WurstTypeCode; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,8 +49,7 @@ public void visit(ImFuncRef imFuncRef) { g = refs.get(func); } else { // create global variable containing a reference to the function: - g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), - "ref_function_" + func.getName(), false); + g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), "ref_function_" + func.getName(), Collections.emptyList()); refs.put(func, g); tr.addGlobalWithInitalizer(g, fr.copy()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java index e4c91482a..a5bf928c3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java @@ -38,9 +38,6 @@ public boolean equals(Object o) { if (!t1.getType().equalsType(t2.getType())) { return false; } - if (!t1.getTypeClassBinding().equals(t2.getTypeClassBinding())) { - return false; - } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index 92b6d4bb9..df596b519 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -501,15 +501,6 @@ public static void print(ImVarargLoop e, Appendable sb, int indent) { } - public static void print(ImTypeVarDispatch e, Appendable sb, int indent) { - append(sb, "<"); - append(sb, e.getTypeVariable().getName()); - append(sb, smallHash(e.getTypeVariable())); - append(sb, ">."); - append(sb, e.getTypeClassFunc().getName()); - printArgumentList(sb, indent, e.getArguments()); - } - public static void print(ImTypeVarRef e, Appendable sb, int indent) { append(sb, e.getTypeVariable().getName()); append(sb, smallHash(e.getTypeVariable())); @@ -562,7 +553,7 @@ public static String asString(ImMethod s) { } public static String asString(ImTypeArgument s) { - return s.getType() + "" + s.; + return s.getType() + ""; } public static void print(ImCast e, Appendable sb, int indent) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 16cf6baa0..03ea5b6b1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -53,6 +53,7 @@ import static de.peeeq.wurstscript.jassIm.JassIm.*; import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*; import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath; +import static de.peeeq.wurstscript.utils.Utils.emptyList; public class ImTranslator { @@ -93,7 +94,7 @@ public class ImTranslator { private boolean isUnitTestMode; - private ImVar lastInitFunc = JassIm.ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", false); + private ImVar lastInitFunc = ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", Collections.emptyList()); private int compiletimeOrderCounter = 1; private final Map compiletimeFlags = new HashMap<>(); @@ -117,10 +118,9 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg */ public ImProg translateProg() { try { - globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(getGlobalInitFunc()); - debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImTypeClassConstraints(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", - false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); + debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", Collections.emptyList())), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); calculateCompiletimeOrder(); @@ -129,11 +129,11 @@ public ImProg translateProg() { } if (mainFunc == null) { - mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(mainFunc); } if (configFunc == null) { - configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(configFunc); } finishInitFunctions(); @@ -146,8 +146,8 @@ public ImProg translateProg() { } catch (Throwable t) { WLogger.severe(t); throw new RuntimeException("There was a Wurst bug in the translation of " + Utils.printElementWithSource(lasttranslatedThing) + ": " + t - .getMessage() + - "\nPlease open a ticket with source code and the error log.", t); + .getMessage() + + "\nPlease open a ticket with source code and the error log.", t); } } @@ -259,7 +259,7 @@ private void removeDuplicateNatives(ImProg imProg) { ImFunction existing = natives.get(f.getName()); if (!compatibleTypes(f, existing)) { throw new CompileError(f, "Native function definition conflicts with other native function defined in " + - existing.attrTrace().attrErrorPos()); + existing.attrTrace().attrErrorPos()); } // remove duplicate it.remove(); @@ -344,13 +344,13 @@ private void finishInitFunctions() { ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger"); if (native_DestroyTrigger != null) { getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(), - JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); } } @NotNull private ImVar prepareTrigger() { - ImVar initTrigVar = JassIm.ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", false); + ImVar initTrigVar = ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", Collections.emptyList()); getMainFunc().getLocals().add(initTrigVar); // initTrigVar = CreateTrigger() @@ -406,11 +406,11 @@ private boolean createInitFuncCall(WPackage p, ImVar initTrigVar, ImFunction ini ImFunction native_GetLocalPlayer = getNativeFunc("GetLocalPlayer"); if (native_ClearTrigger == null - || native_TriggerAddCondition == null - || native_Condition == null - || native_TriggerEvaluate == null - || native_DisplayTimedTextToPlayer == null - || native_GetLocalPlayer == null + || native_TriggerAddCondition == null + || native_Condition == null + || native_TriggerEvaluate == null + || native_DisplayTimedTextToPlayer == null + || native_GetLocalPlayer == null ) { return false; } @@ -431,21 +431,21 @@ public void visit(ImReturn imReturn) { // TriggerAddCondition(initTrigVar, Condition(function myInit)) mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs( - JassIm.ImVarAccess(initTrigVar), - ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs( - JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL) + JassIm.ImVarAccess(initTrigVar), + ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs( + JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL) ), true, CallType.NORMAL)); // if not TriggerEvaluate(initTrigVar) ... mainBody.add(JassIm.ImIf(trace, - JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs( - ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL) - )), - // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package") - JassIm.ImStmts( - imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + ".")) - ), - // else: - JassIm.ImStmts())); + JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs( + ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL) + )), + // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package") + JassIm.ImStmts( + imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + ".")) + ), + // else: + JassIm.ImStmts())); mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); return true; } @@ -491,7 +491,7 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni if (initialExpr instanceof Expr) { Expr expr = (Expr) initialExpr; ImExpr translated = expr.imTranslateExpr(this, f); - if (!v.getIsBJ()) { + if (!v.isBJ()) { // add init statement for non-bj vars // bj-vars are already initalized by blizzard f.getBody().add(ImSet(trace, ImVarAccess(v), translated)); @@ -500,8 +500,8 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni } else if (initialExpr instanceof ArrayInitializer) { ArrayInitializer arInit = (ArrayInitializer) initialExpr; List translatedExprs = arInit.getValues().stream() - .map(expr -> expr.imTranslateExpr(this, f)) - .collect(Collectors.toList()); + .map(expr -> expr.imTranslateExpr(this, f)) + .collect(Collectors.toList()); for (int i = 0; i < arInit.getValues().size(); i++) { ImExpr translated = translatedExprs.get(i); f.getBody().add(ImSet(trace, ImVarArrayAccess(trace, v, ImExprs((ImExpr) JassIm.ImIntVal(i))), translated)); @@ -536,9 +536,9 @@ public ImExpr getDefaultValueForJassType(ImType type) { @Override public ImFunction initFor(StructureDef classDef) { - ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false)); + ImVars params = ImVars(ImVar(classDef, selfType(classDef), "this", Collections.emptyList())); - ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), ImTypeClassConstraints(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); + ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); addFunction(f, classDef); return f; } @@ -550,7 +550,7 @@ public ImFunction initFor(StructureDef classDef) { public ImMethod initFor(StructureDef classDef) { ImFunction impl = destroyFunc.getFor(classDef); ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(), - impl, Lists.newArrayList(), false); + impl, Lists.newArrayList(), false); return m; } }; @@ -611,8 +611,7 @@ public ImClassType selfType(StructureDef classDef) { public ImClassType selfType(ImClass imClass) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - // TODO include type impls? (add constraints to ImTypeVar) - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), ImTypeClassImpls())); + typeArgs.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } return JassIm.ImClassType(imClass, typeArgs); } @@ -622,7 +621,7 @@ public ImClassType selfType(ImClass imClass) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; @@ -632,7 +631,7 @@ public ImFunction initFor(ImClass c) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(ImVar(c.getTrace(), TypesHelper.imInt(), "obj", Collections.emptyList())), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; @@ -649,22 +648,21 @@ public ImTypeVar initFor(TypeParamDef a) { } }; - // type class constraints for type parameters - private final GetAForB constraint = new GetAForB() { + // type class parameter for type parameters + private final GetAForB constraint = new GetAForB() { @Override - public ImTypeClassConstraint initFor(TypeParamConstraint a) { + public ImVar initFor(TypeParamConstraint a) { ImType t = a.getConstraint().attrTyp().imTranslateType(ImTranslator.this); - return ImTypeClassConstraint(a, Utils.printElement(a), (ImClassType) t); + return ImVar(a, t, Utils.printElement(a), emptyList()); } }; - public ImTypeClassConstraint getConstraintFor(TypeParamConstraint c) { + public ImVar getConstraintFor(TypeParamConstraint c) { return constraint.getFor(c); } - public ImFunction getFuncFor(TranslatedToImFunction funcDef) { if (functionMap.containsKey(funcDef)) { return functionMap.get(funcDef); @@ -717,8 +715,7 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { } ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); - ImTypeClassConstraints typeClassConstraints = collectTypeClassConstraintsForFunction(funcDef); - ImFunction f = ImFunction(funcDef, name, typeVars, typeClassConstraints, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); + ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); funcDef.imCreateFuncSkeleton(this, f); addFunction(f, funcDef); @@ -831,64 +828,6 @@ public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { return typeVars; } - private ImTypeClassConstraints collectTypeClassConstraintsForFunction(TranslatedToImFunction funcDef) { - ImTypeClassConstraints constraints = ImTypeClassConstraints(); - funcDef.match(new TranslatedToImFunction.MatcherVoid() { - @Override - public void case_FuncDef(FuncDef funcDef) { - handleTypeParameters(funcDef.getTypeParameters()); - } - - - private void handleTypeParameters(TypeParamDefs tps) { - for (TypeParamDef tp : tps) { - handleTypeParameter(tp); - } - } - - private void handleTypeParameter(TypeParamDef tp) { - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); - for (TypeParamConstraint c : cs) { - constraints.add(getConstraintFor(c)); - } - } - } - - @Override - public void case_ConstructorDef(ConstructorDef constructorDef) { - } - - @Override - public void case_NativeFunc(NativeFunc nativeFunc) { - } - - @Override - public void case_OnDestroyDef(OnDestroyDef onDestroyDef) { - } - - @Override - public void case_TupleDef(TupleDef tupleDef) { - } - - @Override - public void case_ExprClosure(ExprClosure exprClosure) { - // TODO where to set closure parameters? - } - - @Override - public void case_InitBlock(InitBlock initBlock) { - - } - - @Override - public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { - handleTypeParameters(funcDef.getTypeParameters()); - } - }); - return constraints; - } - private boolean isExtern(TranslatedToImFunction funcDef) { @@ -914,7 +853,7 @@ private boolean isBJ(WPos source) { public ImFunction getInitFuncFor(WPackage p) { // TODO more precise trace - return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImTypeClassConstraints(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); + return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); } /** @@ -971,7 +910,7 @@ public ImVar getThisVar(TranslatedToImFunction f) { if (thisVarMap.containsKey(f)) { return thisVarMap.get(f); } - ImVar v = JassIm.ImVar(f, selfType(f), "this", false); + ImVar v = ImVar(f, selfType(f), "this", Collections.emptyList()); thisVarMap.put(f, v); return v; } @@ -1020,7 +959,7 @@ public ImVar getVarFor(VarDef varDef) { name = getNameFor(varDef.attrNearestNamedScope()) + "_" + name; } boolean isBj = isBJ(varDef.getSource()); - v = JassIm.ImVar(varDef, type, name, isBj); + v = ImVar(varDef, type, name, isBj ? Collections.singletonList(VarFlag.BJ) : Collections.emptyList()); varMap.put(varDef, v); } return v; @@ -1192,7 +1131,7 @@ public ImFunction getConstructFunc(ConstructorDef constr) { params.add(getVarFor(p)); } - f = ImFunction(constr, name, ImTypeVars(), ImTypeClassConstraints(), params, ImVoid(), ImVars(), ImStmts(), flags()); + f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags()); addFunction(f, constr); constructorFuncs.put(constr, f); } @@ -1225,7 +1164,7 @@ public ImFunction getConstructNewFunc(ConstructorDef constr) { if (f == null) { String name = "new_" + constr.attrNearestClassDef().getName(); - f = ImFunction(constr, name, ImTypeVars(), ImTypeClassConstraints(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); + f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); addFunction(f, constr); constrNewFuncs.put(constr, f); } @@ -1356,6 +1295,14 @@ public boolean isLuaTarget() { return runArgs.isLua(); } + public ImMethod getTypeClassMethodFor(FuncDef calledFunc) { + throw new RuntimeException("TODO"); + } + + public ImVar getTypeClassParamFor(WurstTypeTypeParam typeParamDispatchOn, FunctionDefinition method) { + throw new RuntimeException(); + } + interface VarsForTupleResult { @@ -1462,7 +1409,7 @@ public VarsForTupleResult case_ImArrayType(ImArrayType at) { return new TupleResult(ts.build()); } // otherwise just create the array variable - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1484,7 +1431,7 @@ public VarsForTupleResult case_ImArrayTypeMulti(ImArrayTypeMulti at) { return new TupleResult(ts.build()); } // otherwise just create the array variable - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1495,19 +1442,19 @@ public VarsForTupleResult case_ImVoid(ImVoid imVoid) { @Override public VarsForTupleResult case_ImClassType(ImClassType st) { ImType type = typeConstructor.apply(st); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override public VarsForTupleResult case_ImSimpleType(ImSimpleType st) { ImType type = typeConstructor.apply(st); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override public VarsForTupleResult case_ImAnyType(ImAnyType at) { ImType type = typeConstructor.apply(at); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1538,7 +1485,7 @@ private void addVarsForType(List result, String name, ImType type, de.pee } else if (type instanceof ImVoid) { // nothing to add } else { - result.add(JassIm.ImVar(tr, type, name, false)); + result.add(ImVar(tr, type, name, Collections.emptyList())); } } @@ -1636,7 +1583,7 @@ public boolean isUnitTestMode() { public ImClass getClassForClosure(ExprClosure s) { Preconditions.checkNotNull(s); - return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), ImTypeClassConstraints(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList())); + return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList())); } @@ -1645,20 +1592,7 @@ public ImClass getClassForClosure(ExprClosure s) { public ImClass getClassFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { - ImTypeVars typeVariables = JassIm.ImTypeVars(); - ImTypeClassConstraints typeClassConstraints = ImTypeClassConstraints(); - for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - ImTypeVar tv = getTypeVar(tp); - TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); - typeVariables.add(tv); - for (TypeParamConstraint c : cs) { - typeClassConstraints.add(getConstraintFor(c)); - } - } - } - - return JassIm.ImClass(s1, s1.getName(), typeVariables, typeClassConstraints, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + return JassIm.ImClass(s1, s1.getName(), ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } @@ -1667,20 +1601,7 @@ public ImClass getClassFor(ClassOrInterface s) { public ImClass getTypeClassStructFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return typeClassStructFor.computeIfAbsent(s, s1 -> { - ImTypeVars typeVariables = JassIm.ImTypeVars(); - ImTypeClassConstraints typeClassConstraints = ImTypeClassConstraints(); - for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - ImTypeVar tv = getTypeVar(tp); - TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); - typeVariables.add(tv); - for (TypeParamConstraint c : cs) { - typeClassConstraints.add(getConstraintFor(c)); - } - } - } - - return JassIm.ImClass(s1, s1.getName(), typeVariables, typeClassConstraints, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + return JassIm.ImClass(s1, s1.getName(), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } @@ -1747,7 +1668,7 @@ public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr mes } private ImFunction makeDefaultErrorFunc() { - ImVar msgVar = JassIm.ImVar(emptyTrace, TypesHelper.imString(), "msg", false); + ImVar msgVar = ImVar(emptyTrace, TypesHelper.imString(), "msg", Collections.emptyList()); ImVars parameters = JassIm.ImVars(msgVar); ImType returnType = JassIm.ImVoid(); ImVars locals = JassIm.ImVars(); @@ -1769,7 +1690,7 @@ private ImFunction makeDefaultErrorFunc() { List flags = Lists.newArrayList(); - return ImFunction(emptyTrace, "error", ImTypeVars(), ImTypeClassConstraints(), parameters, returnType, locals, body, flags); + return ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java index e9dfef0f6..3e33bf590 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java @@ -12,7 +12,6 @@ import de.peeeq.wurstscript.types.WurstTypeInterface; import de.peeeq.wurstscript.types.WurstTypeNamedScope; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -67,7 +66,7 @@ public void addDestroyMethod() { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java index 0df79de18..69df1f30e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java @@ -8,6 +8,7 @@ import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Utils; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,7 +47,7 @@ public void run() { int size0 = arraySize.get(0); List newArrays = Lists.newArrayList(); for (int i = 0; i < size0; i++) { - ImVar newVar = JassIm.ImVar(v.getTrace(), JassIm.ImArrayType(type.getEntryType()), v.getName() + "_" + i, false); + ImVar newVar = JassIm.ImVar(v.getTrace(), JassIm.ImArrayType(type.getEntryType()), v.getName() + "_" + i, Collections.emptyList()); newArrays.add(newVar); } ImFunction setFunc = generateSetFunc(v, newArrays); @@ -168,9 +169,9 @@ private void replaceVars(Element e, Map oldToNewVar) { private ImFunction generateSetFunc(ImVar aVar, List newArrays) { ImArrayTypeMulti mtype = (ImArrayTypeMulti) aVar.getType(); ImVars locals = JassIm.ImVars(); - ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "instanceId", false); - ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "arrayIndex", false); - ImVar value = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "value", false); + ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "instanceId", Collections.emptyList()); + ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "arrayIndex", Collections.emptyList()); + ImVar value = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "value", Collections.emptyList()); ImFunctionCall error = imError(aVar, "Index out of Bounds"); ImStmts thenBlock = JassIm.ImStmts(error); ImStmts elseBlock = JassIm.ImStmts(); @@ -183,7 +184,7 @@ private ImFunction generateSetFunc(ImVar aVar, List newArrays) { ImFunction setFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_set", JassIm.ImTypeVars(), JassIm.ImVars(instanceId, arrayIndex, value), JassIm.ImVoid(), locals, body, Lists.newArrayList()); if (generateStacktraces) { - ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", false); + ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", Collections.emptyList()); setFunc.getParameters().add(stackPos); if (error.getFunc().getParameters().size() == 2) { error.getArguments().add(JassIm.ImVarAccess(stackPos)); @@ -220,10 +221,10 @@ private void generateBinSearchSet(ImStmts stmts, ImVar indexVar1, ImVar indexVar private ImFunction generateGetFunc(ImVar aVar, List newArrays) { ImArrayTypeMulti mtype = (ImArrayTypeMulti) aVar.getType(); - ImVar returnVal = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "returnVal", false); + ImVar returnVal = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "returnVal", Collections.emptyList()); ImVars locals = JassIm.ImVars(returnVal); - ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index1", false); - ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index2", false); + ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index1", Collections.emptyList()); + ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index2", Collections.emptyList()); ImFunctionCall error = imError(aVar, "Index out of Bounds"); ImStmts thenBlock = JassIm.ImStmts(error); ImStmts elseBlock = JassIm.ImStmts(); @@ -237,7 +238,7 @@ private ImFunction generateGetFunc(ImVar aVar, List newArrays) { ImFunction getFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_get", JassIm.ImTypeVars(), JassIm.ImVars(instanceId, arrayIndex), mtype.getEntryType(), locals, body, Lists.newArrayList()); if (generateStacktraces) { - ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", false); + ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", Collections.emptyList()); getFunc.getParameters().add(stackPos); if (error.getFunc().getParameters().size() == 2) { error.getArguments().add(JassIm.ImVarAccess(stackPos)); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java index fb1cc016b..207de8c7f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java @@ -3,9 +3,10 @@ import de.peeeq.wurstscript.WurstOperator; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.*; -import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Constants; +import java.util.Collections; + /** * Manages object ids in a queue. This way the time each object is * inactive is maximized and thus errors should be easier to detect @@ -20,7 +21,7 @@ public void createAllocFunc(ImTranslator translator, ImProg prog, ImClass c) { ImStmts body = f.getBody(); Element tr = c.getTrace(); - ImVar thisVar = JassIm.ImVar(tr, translator.selfType(c), "this", false); // TODO change type + ImVar thisVar = JassIm.ImVar(tr, translator.selfType(c), "this", Collections.emptyList()); // TODO change type locals.add(thisVar); ClassManagementVars mVars = translator.getClassManagementVarsFor(c); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java index ad744bd01..4252f2d17 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java @@ -8,14 +8,12 @@ import de.peeeq.datastructures.TransitiveClosure; import de.peeeq.wurstio.TimeTaker; import de.peeeq.wurstscript.WurstOperator; -import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.ast.FunctionDefinition; import de.peeeq.wurstscript.ast.NameDef; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.TypesHelper; -import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; import java.util.*; @@ -79,9 +77,9 @@ public void visit(ImFuncRef imFuncRef) { }); de.peeeq.wurstscript.ast.Element trace = prog.attrTrace(); - stackSize = JassIm.ImVar(trace, TypesHelper.imInt(), "wurst_stack_depth", false); + stackSize = JassIm.ImVar(trace, TypesHelper.imInt(), "wurst_stack_depth", Collections.emptyList()); prog.getGlobals().add(stackSize); - stack = JassIm.ImVar(trace, TypesHelper.imStringArray(), "wurst_stack", false); + stack = JassIm.ImVar(trace, TypesHelper.imStringArray(), "wurst_stack", Collections.emptyList()); prog.getGlobals().add(stack); prog.getGlobalInits().put(stackSize, Collections.singletonList(JassIm.ImIntVal(0))); @@ -193,7 +191,7 @@ public void visit(ImReturn imReturn) { if (count > 1) { tempReturnName += "_" + count; } - ImVar temp = JassIm.ImVar(trace, f.getReturnType(), tempReturnName, false); + ImVar temp = JassIm.ImVar(trace, f.getReturnType(), tempReturnName, Collections.emptyList()); f.getLocals().add(temp); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(temp), (ImExpr) returnedValue)); newReturn = JassIm.ImReturn(trace, JassIm.ImVarAccess(temp)); @@ -265,7 +263,7 @@ private void passStacktraceParams(final Multimap cal Collection callsForF = calls.get(f); // add stackPos parameter - f.getParameters().add(getStacktraceIndex(f), JassIm.ImVar(f.getTrace(), TypesHelper.imString(), STACK_POS_PARAM, false)); + f.getParameters().add(getStacktraceIndex(f), JassIm.ImVar(f.getTrace(), TypesHelper.imString(), STACK_POS_PARAM, Collections.emptyList())); // pass the stacktrace parameter at all calls for (ImFunctionCall call : callsForF) { String callPos = getCallPos(call.attrTrace().attrErrorPos()); @@ -390,11 +388,11 @@ private void rewriteErrorStatements(final Multimap } de.peeeq.wurstscript.ast.Element trace = s.attrTrace(); - ImVar traceStr = JassIm.ImVar(trace, TypesHelper.imString(), "stacktraceStr", false); + ImVar traceStr = JassIm.ImVar(trace, TypesHelper.imString(), "stacktraceStr", Collections.emptyList()); f.getLocals().add(traceStr); - ImVar traceI = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceIndex", false); + ImVar traceI = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceIndex", Collections.emptyList()); f.getLocals().add(traceI); - ImVar traceLimit = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceLimit", false); + ImVar traceLimit = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceLimit", Collections.emptyList()); f.getLocals().add(traceLimit); ImStmts stmts = JassIm.ImStmts(); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(traceStr), JassIm.ImStringVal(""))); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java index 48e57c14e..40f9c6b83 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java @@ -19,6 +19,7 @@ import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeVararg; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -84,7 +85,7 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { } else { // store from-expression in variable, so that it is only evaluated once ImExpr iterationTargetTr = iterationTarget.imTranslateExpr(t, f); - ImVar fromVar = ImVar(s, iterationTargetTr.attrTyp(), "from", false); + ImVar fromVar = ImVar(s, iterationTargetTr.attrTyp(), "from", Collections.emptyList()); f.getLocals().add(fromVar); result.add(ImSet(s, ImVarAccess(fromVar), iterationTargetTr)); fromTarget = JassIm.ImExprs(ImVarAccess(fromVar)); @@ -147,7 +148,7 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { // call XX.iterator() ImFunctionCall iteratorCall = ImFunctionCall(forIn, iteratorFuncIm, ImTypeArguments(), iterationTargetList, false, CallType.NORMAL); // create IM-variable for iterator - ImVar iteratorVar = JassIm.ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", false); + ImVar iteratorVar = ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", Collections.emptyList()); f.getLocals().add(iteratorVar); f.getLocals().add(t.getVarFor(forIn.getLoopVar())); @@ -254,7 +255,7 @@ private static ImExpr addCacheVariableSmart(ImTranslator t, ImFunction f, List newParams = new ArrayList<>(); for (int i = 0; i < argumentSize; i++) { - ImVar param = JassIm.ImVar(func.getTrace(), type, varargParam.getName() + "_" + i, false); + ImVar param = JassIm.ImVar(func.getTrace(), type, varargParam.getName() + "_" + i, Collections.emptyList()); newParams.add(param); newFunc.getParameters().add(param); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 7ba3a3132..07c64f668 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -330,11 +330,6 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat throw new Error("not implemented"); } - public static LuaExpr translate(ImTypeVarDispatch e, LuaTranslator tr) { - LuaVariable dict = tr.luaTypeClassDictionaryVar.getFor(e.getTypeVariable()); - LuaExprFieldAccess f = LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(dict), tr.typeClassFuncName.getFor(e.getTypeClassFunc())); - return LuaAst.LuaExprFunctionCallE(f, tr.translateExprList(e.getArguments())); - } public static LuaExpr translate(ImCast imCast, LuaTranslator tr) { LuaExpr translated = imCast.getExpr().translateToLua(tr); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index b59a76d41..ca5f8cfd6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -1,14 +1,12 @@ package de.peeeq.wurstscript.translation.lua.translation; import de.peeeq.datastructures.UnionFind; -import de.peeeq.wurstio.TimeTaker; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.ast.NameDef; import de.peeeq.wurstscript.ast.WPackage; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.luaAst.*; -import de.peeeq.wurstscript.translation.imoptimizer.ImOptimizer; import de.peeeq.wurstscript.translation.imtranslation.*; import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Lazy; @@ -16,7 +14,6 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import static de.peeeq.wurstscript.translation.lua.translation.ExprTranslation.WURST_SUPERTYPES; @@ -63,7 +60,7 @@ private ImProg getProg() { @Override public LuaVariable initFor(ImVar a) { String name = a.getName(); - if (!a.getIsBJ()) { + if (!a.isBJ()) { name = uniqueName(name); } return LuaAst.LuaVariable(name, LuaAst.LuaNoExpr()); @@ -228,7 +225,7 @@ private void collectPredefinedNames() { } for (ImVar global : prog.getGlobals()) { - if (global.getIsBJ()) { + if (global.isBJ()) { setNameFromTrace(global); usedNames.add(global.getName()); } @@ -585,7 +582,7 @@ private void collectSuperClasses(LuaTableFields superClasses, ImClass c, Set Date: Sat, 25 Jan 2020 01:20:50 +0100 Subject: [PATCH 10/50] add parameters for type-class-dicts --- .../AttrPossibleFunctionSignatures.java | 6 +- .../attributes/names/FuncLink.java | 51 ++++++----- .../imtranslation/ExprTranslation.java | 10 +-- .../imtranslation/FuncSkeleton.java | 11 ++- .../translation/imtranslation/ImHelper.java | 14 ++- .../translation/imtranslation/ImPrinter.java | 5 ++ .../imtranslation/ImTranslator.java | 88 ++++++++++++++++--- .../types/WurstTypeBoundTypeParam.java | 2 +- .../types/WurstTypeClassOrInterface.java | 12 ++- .../wurstscript/types/WurstTypeTypeParam.java | 36 +++++--- .../validation/WurstValidator.java | 12 +-- .../tests/GenericsWithTypeclassesTests.java | 10 +-- 12 files changed, 182 insertions(+), 75 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 0a9e8cbb5..13ea1a975 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -92,9 +92,9 @@ private static Pair> findTypeClasses(Funct for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { Option matchedTypeOpt = mapping.get(tp); List constraints = new ArrayList<>(); - if (tp.getTypeParamConstraints() instanceof TypeExprList) { - for (TypeExpr c : ((TypeExprList) tp.getTypeParamConstraints())) { - WurstType ct = c.attrTyp(); + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + WurstType ct = c.getConstraint().attrTyp(); if (ct instanceof WurstTypeInterface) { constraints.add((WurstTypeInterface) ct); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java index c801fee87..dd81dbdab 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java @@ -6,7 +6,6 @@ import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.VariableBinding; import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeTypeParam; import de.peeeq.wurstscript.types.WurstTypeVararg; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -22,33 +21,35 @@ public class FuncLink extends DefLink { private final List parameterNames; private final List parameterTypes; private final VariableBinding mapping; + private final TypeParamConstraint typeParamConstraint; public FuncLink(Visibility visibility, WScope definedIn, List typeParams, - @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType, VariableBinding mapping) { + @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType, VariableBinding mapping, TypeParamConstraint typeParamConstraint) { super(visibility, definedIn, typeParams, receiverType); this.def = def; this.returnType = returnType; this.parameterTypes = parameterTypes; this.parameterNames = parameterNames; this.mapping = mapping; + this.typeParamConstraint = typeParamConstraint; } public static FuncLink create(FunctionDefinition func, WScope definedIn) { Visibility visibiliy = calcVisibility(definedIn, func); List typeParams = typeParams(func).collect(Collectors.toList()); List lParameterNames = func.getParameters().stream() - .map(WParameter::getName) - .collect(Collectors.toList()); + .map(WParameter::getName) + .collect(Collectors.toList()); List lParameterTypes = func.getParameters().stream() - .map(WParameter::attrTyp) - .collect(Collectors.toList()); + .map(WParameter::attrTyp) + .collect(Collectors.toList()); WurstType lreturnType = func.attrReturnTyp(); WurstType lreceiverType = calcReceiverType(definedIn, func); VariableBinding mapping = VariableBinding.emptyMapping(); if (func instanceof AstElementWithTypeParameters) { mapping = mapping.withTypeVariables(((AstElementWithTypeParameters) func).getTypeParameters()); } - return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType, mapping); + return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType, mapping, null); } @@ -78,7 +79,7 @@ public FuncLink withVisibility(Visibility newVis) { if (newVis == getVisibility()) { return this; } - return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } @@ -109,8 +110,8 @@ public String toString() { if (!typeParams.isEmpty()) { result.append("<"); result.append(typeParams.stream() - .map(TypeParamDef::getName) - .collect(Collectors.joining(", "))); + .map(TypeParamDef::getName) + .collect(Collectors.joining(", "))); result.append(">"); } result.append("("); @@ -119,10 +120,10 @@ public String toString() { result.append(returnType); WPos src = def.attrSource(); result.append(" (") - .append(src.getFile()) - .append(":") - .append(src.getLine()) - .append(")"); + .append(src.getFile()) + .append(":") + .append(src.getLine()) + .append(")"); return result.toString(); } @@ -156,9 +157,9 @@ public FuncLink withTypeArgBinding(Element context, VariableBinding binding) { if (changed) { // remove type parameters that are now bound: List newTypeParams = getTypeParams().stream() - .filter(tp -> !binding.contains(tp)) - .collect(Collectors.toList()); - return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType, binding); + .filter(tp -> !binding.contains(tp)) + .collect(Collectors.toList()); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType, binding, typeParamConstraint); } else { return this; } @@ -170,7 +171,7 @@ public DefLink withGenericTypeParams(List typeParams) { return this; } ImmutableList newTypeParams = Utils.concatLists(getTypeParams(), typeParams); - return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } @Override @@ -180,7 +181,7 @@ public WurstType getTyp() { @Override public FuncLink withDef(NameDef def) { - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType(), mapping); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType(), mapping, typeParamConstraint); } private WurstType adjustType(Element context, WurstType t, VariableBinding binding) { @@ -224,7 +225,7 @@ public boolean isVarargMethod() { public FuncLink withConfigDef() { FunctionDefinition def = (FunctionDefinition) this.def.attrConfigActualNameDef(); - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } public FuncLink hidingPrivate() { @@ -291,6 +292,14 @@ public boolean hasIfNotDefinedAnnotation() { } public FuncLink withReceiverType(WurstType newReceiverType) { - return new FuncLink(getVisibility(), def, typeParams, newReceiverType, def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(getVisibility(), def, typeParams, newReceiverType, def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); + } + + public FuncLink withTypeParamConstraint(TypeParamConstraint typeParamConstraint) { + return new FuncLink(getVisibility(), def, typeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); + } + + public @Nullable TypeParamConstraint getTypeParamConstraint() { + return typeParamConstraint; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 40468f547..4c22cce54 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -440,7 +440,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu List arguments = Lists.newArrayList(e.getArgs()); Expr leftExpr = null; boolean dynamicDispatch = false; - WurstTypeTypeParam typeParamDispatchOn = null; + TypeParamConstraint typeParamDispatchOn = e.attrFuncLink().getTypeParamConstraint(); FunctionDefinition calledFunc = e.attrFuncDef(); @@ -452,12 +452,6 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu // TODO why would I add the implicit parameter here, if it is // not a dynamic dispatch? leftExpr = (Expr) e.attrImplicitParameter(); - - if (leftExpr.attrTyp() instanceof WurstTypeTypeParam) { - WurstTypeTypeParam tp = (WurstTypeTypeParam) leftExpr.attrTyp(); - typeParamDispatchOn = tp; - } - } // get real func def (override of module function) @@ -520,7 +514,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu imArgs.add(0, receiver); } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); - ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn, calledFunc)); + ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn)); call = JassIm.ImMethodCall(e, method, typeArguments, typeClassDict, imArgs, false); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java index 47d36b670..ce21b32bf 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java @@ -5,11 +5,14 @@ import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.TypesHelper; +import java.util.Collections; + public class FuncSkeleton { public static void create(ConstructorDef constr, ImTranslator translator, ImFunction f) { f.setReturnType(TypesHelper.imInt()); - ImHelper.translateParameters(constr.getParameters(), f.getParameters(), translator); + // TODO add type parameters from class + ImHelper.translateParameters(constr.getParameters(), Collections.emptyList(), f.getParameters(), translator); } public static void create(ExtensionFuncDef funcDef, ImTranslator translator, ImFunction f) { @@ -19,7 +22,7 @@ public static void create(ExtensionFuncDef funcDef, ImTranslator translator, ImF ImVar thisVar = translator.getThisVar(funcDef); thisVar.setType(funcDef.getExtendedType().attrTyp().imTranslateType(translator)); f.getParameters().add(thisVar); - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), funcDef.getTypeParameters(), f.getParameters(), translator); } public static void create(FuncDef funcDef, ImTranslator translator, ImFunction f) { @@ -30,7 +33,7 @@ public static void create(FuncDef funcDef, ImTranslator translator, ImFunction f ImVar thisVar = translator.getThisVar(funcDef); f.getParameters().add(thisVar); } - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), funcDef.getTypeParameters(), f.getParameters(), translator); } public static void create(InitBlock initBlock, ImTranslator translator, ImFunction f) { @@ -38,7 +41,7 @@ public static void create(InitBlock initBlock, ImTranslator translator, ImFuncti public static void create(NativeFunc funcDef, ImTranslator translator, ImFunction f) { f.setReturnType(funcDef.attrReturnTyp().imTranslateType(translator)); - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), Collections.emptyList(), f.getParameters(), translator); } public static void create(TupleDef tupleDef, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java index df2998686..9b8dbb384 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java @@ -1,9 +1,9 @@ package de.peeeq.wurstscript.translation.imtranslation; -import de.peeeq.wurstscript.ast.WParameter; -import de.peeeq.wurstscript.ast.WParameters; +import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.jassIm.Element; import de.peeeq.wurstscript.utils.Constants; import java.util.ArrayList; @@ -12,10 +12,18 @@ public class ImHelper { - static void translateParameters(WParameters params, ImVars result, ImTranslator t) { + static void translateParameters(WParameters params, List typeParameters, ImVars result, ImTranslator t) { for (WParameter p : params) { result.add(t.getVarFor(p)); } + for (TypeParamDef tp : typeParameters) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cList = (TypeParamConstraintList) tp.getTypeParamConstraints(); + for (TypeParamConstraint c : cList) { + result.add(t.getTypeClassParamFor(c)); + } + } + } } public static ImType toArray(ImType t) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index df596b519..2b902e353 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -354,6 +354,11 @@ public static void print(ImStmts ss, Appendable sb, int indent) { public static void print(ImVar v, Appendable sb, int indent) { + for (VarFlag varFlag : v.getVarFlags()) { + append(sb, "@"); + append(sb, varFlag); + append(sb, " "); + } v.getType().print(sb, indent); append(sb, " "); append(sb, v.getName()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 03ea5b6b1..d06bd4491 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -367,7 +367,7 @@ private ImFunction getNativeFunc(String funcName) { if (wurstFunc.isEmpty()) { return null; } - return getFuncFor((TranslatedToImFunction) Utils.getFirst(wurstFunc).getDef()); + return getFuncFor(Utils.getFirst(wurstFunc).getDef()); } private void callInitFunc(Set calledInitializers, WPackage p, ImVar initTrigVar) { @@ -504,7 +504,7 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni .collect(Collectors.toList()); for (int i = 0; i < arInit.getValues().size(); i++) { ImExpr translated = translatedExprs.get(i); - f.getBody().add(ImSet(trace, ImVarArrayAccess(trace, v, ImExprs((ImExpr) JassIm.ImIntVal(i))), translated)); + f.getBody().add(ImSet(trace, ImVarArrayAccess(trace, v, ImExprs(JassIm.ImIntVal(i))), translated)); } // add list of init-values to translatedExprs imProg.getGlobalInits().put(v, translatedExprs); @@ -514,7 +514,7 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni public void addGlobalWithInitalizer(ImVar g, ImExpr initial) { imProg.getGlobals().add(g); getGlobalInitFunc().getBody().add(ImSet(g.getTrace(), ImVarAccess(g), initial)); - imProg.getGlobalInits().put(g, Collections.singletonList((ImExpr) initial.copy())); + imProg.getGlobalInits().put(g, Collections.singletonList(initial.copy())); } @@ -723,6 +723,56 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { return f; } + private final Map typeClassFuncMap = new LinkedHashMap<>(); + + public ImFunction getTypeClassFuncFor(FuncDef funcDef) { + if (typeClassFuncMap.containsKey(funcDef)) { + return typeClassFuncMap.get(funcDef); + } + String name = getNameFor(funcDef); + List flags = flags(); + if (funcDef.attrIsCompiletime()) { + throw new CompileError(funcDef.getSource(), "Compiletime flag not supported here."); + } + + // Check if last parameter is vararg + WParameters params = ((AstElementWithParameters) funcDef).getParameters(); + if (params.size() >= 1 && params.get(params.size() - 1).attrIsVararg()) { + flags.add(IS_VARARG); + } + + + for (Modifier m : funcDef.getModifiers()) { + if (m instanceof Annotation) { + Annotation annotation = (Annotation) m; + flags.add(new FunctionFlagAnnotation(annotation.getAnnotationType())); + } + } + + ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); + ImVars parameters = ImVars(); + ImVar thisVar = getThisVar(funcDef).copy(); + parameters.add(thisVar); + for (WParameter p : params) { + parameters.add(getVarFor(p).copy()); + } + + ImFunction f = ImFunction( + funcDef, + name, + typeVars, + parameters, + funcDef.attrReturnTyp().imTranslateType(this), + ImVars(), + ImStmts(), + flags); + + + addFunction(f, funcDef); + typeClassFuncMap.put(funcDef, f); + return f; + } + private ImClass getClassForFunc(TranslatedToImFunction funcDef) { if (funcDef == null) { return null; @@ -789,7 +839,7 @@ private void handleTypeParameters(TypeParamDefs tps) { } private void handleTypeParameter(TypeParamDef tp) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { typeVars.add(typeVariable.getFor(tp)); } } @@ -829,7 +879,6 @@ public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { } - private boolean isExtern(TranslatedToImFunction funcDef) { if (funcDef instanceof HasModifier) { HasModifier f = (HasModifier) funcDef; @@ -1295,12 +1344,31 @@ public boolean isLuaTarget() { return runArgs.isLua(); } - public ImMethod getTypeClassMethodFor(FuncDef calledFunc) { - throw new RuntimeException("TODO"); + + private Map typeClassMethodForFuncDef = Maps.newLinkedHashMap(); + + public ImMethod getTypeClassMethodFor(FuncDef f) { + ImMethod m = typeClassMethodForFuncDef.get(f); + if (m == null) { + ImFunction imFunc = getTypeClassFuncFor(f); + m = JassIm.ImMethod(f, selfType(f), elementNameWithPath(f), imFunc, Lists.newArrayList(), false); + typeClassMethodForFuncDef.put(f, m); + } + return m; } - public ImVar getTypeClassParamFor(WurstTypeTypeParam typeParamDispatchOn, FunctionDefinition method) { - throw new RuntimeException(); + private Map typeClassParamFor = new LinkedHashMap<>(); + + public ImVar getTypeClassParamFor(TypeParamConstraint tc) { + ImVar v = typeClassParamFor.get(tc); + if (v == null) { + TypeParamDef tp = (TypeParamDef) tc.getParent().getParent(); + WurstTypeInterface wti = (WurstTypeInterface) tc.getConstraint().attrTyp(); + ImClassType t = wti.imTranslateToTypeClass(this); + v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + "_" + tc.getConstraint().attrTyp(), Collections.singletonList(VarFlag.SPECIALIZE)); + typeClassParamFor.put(tc, v); + } + return v; } @@ -1606,7 +1674,7 @@ public ImClass getTypeClassStructFor(ClassOrInterface s) { } - Map methodForFuncDef = Maps.newLinkedHashMap(); + private Map methodForFuncDef = Maps.newLinkedHashMap(); public ImMethod getMethodFor(FuncDef f) { ImMethod m = methodForFuncDef.get(f); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 50f1c9149..b3201fa13 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -202,7 +202,7 @@ public boolean isTranslatedToInt() { } public boolean isTemplateTypeParameter() { - return typeParamDef.getTypeParamConstraints() instanceof TypeExprList; + return typeParamDef.getTypeParamConstraints() instanceof TypeParamConstraintList; } public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java index 163f3d265..079f28d15 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java @@ -17,6 +17,7 @@ import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -133,16 +134,23 @@ VariableBinding matchAgainstSupertypeIntern(WurstType obj, @Nullable Element loc @Override public final ImType imTranslateType(ImTranslator tr) { + ImTypeArguments typeArgs = translateTypeArguments(tr); + return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); + } + + @NotNull + private ImTypeArguments translateTypeArguments(ImTranslator tr) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (WurstTypeBoundTypeParam btp : getTypeParameters()) { if (btp.isTemplateTypeParameter()) { typeArgs.add(btp.imTranslateToTypeArgument(tr)); } } - return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); + return typeArgs; } public ImClassType imTranslateToTypeClass(ImTranslator tr) { - throw new RuntimeException("TODO"); + ImTypeArguments typeArgs = translateTypeArguments(tr); + return JassIm.ImClassType(tr.getTypeClassStructFor(getDef()), typeArgs); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 57952ea59..55ad66bca 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -1,9 +1,6 @@ package de.peeeq.wurstscript.types; -import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.ast.TypeExpr; -import de.peeeq.wurstscript.ast.TypeExprList; -import de.peeeq.wurstscript.ast.TypeParamDef; +import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; @@ -81,9 +78,11 @@ public ImType imTranslateType(ImTranslator tr) { return JassIm.ImAnyType(); } - /** Using the new template generics with type constraints*/ + /** + * Using the new template generics with type constraints + */ private boolean hasTypeConstraints() { - return def.getTypeParamConstraints() instanceof TypeExprList; + return def.getTypeParamConstraints() instanceof TypeParamConstraintList; } @Override @@ -111,16 +110,29 @@ public void addMemberMethods(Element node, String name, List result) { @Override public Stream getMemberMethods(Element node) { - return getTypeConstraints() - .flatMap(i -> - i.getMemberMethods(node)) - .map(fl -> fl.withReceiverType(this)); + if (def.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); + return constraints.stream() + .flatMap((TypeParamConstraint constr) -> { + WurstType t = constr.getConstraint().attrTyp(); + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + return wti.getMemberMethods(node) + .map(f -> f.withReceiverType(this).withTypeParamConstraint(constr)); + } else { + return Stream.empty(); + } + }); + } else { + return Stream.empty(); + } } public Stream getTypeConstraints() { - if (def.getTypeParamConstraints() instanceof TypeExprList) { - TypeExprList constraints = (TypeExprList) def.getTypeParamConstraints(); + if (def.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); return constraints.stream() + .map(TypeParamConstraint::getConstraint) .map(TypeExpr::attrTyp) .filter(t -> t instanceof WurstTypeInterface) .map(t -> (WurstTypeInterface) t); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index 4fbac4155..baf02342e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -354,11 +354,11 @@ private void check(Element e) { private void checkTypeParamDef(TypeParamDef e) { TypeParamConstraints constraints = e.getTypeParamConstraints(); - if (constraints instanceof TypeExprList) { - TypeExprList typeExprs = (TypeExprList) constraints; - for (TypeExpr te : typeExprs) { - if (!(te.attrTyp() instanceof WurstTypeInterface)) { - te.addError("Invalid type constraint " + te.attrTyp() + ". Type constraint must be an interface type."); + if (constraints instanceof TypeParamConstraintList) { + TypeParamConstraintList typeExprs = (TypeParamConstraintList) constraints; + for (TypeParamConstraint te : typeExprs) { + if (!(te.getConstraint().attrTyp() instanceof WurstTypeInterface)) { + te.addError("Invalid type constraint " + te.getConstraint().attrTyp() + ". Type constraint must be an interface type."); } } } @@ -1552,7 +1552,7 @@ public VariableBinding case_ExprMemberMethodDotDot(ExprMemberMethodDotDot e) { WurstType typ = boundTyp.getBaseType(); TypeParamDef tp = t._1(); - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { // new style generics } else { // old style generics diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 977884441..6d27bb7b3 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1326,11 +1326,11 @@ public void simpleTypeClass() { " testSuccess()", "init", " let a = new A", - " if a.toIndex() != 42", - " testFail(\"a\")", - " ToIndex i = a", - " if i.toIndex() != 42", - " testFail(\"b\")", +// " if a.toIndex() != 42", +// " testFail(\"a\")", +// " ToIndex i = a", +// " if i.toIndex() != 42", +// " testFail(\"b\")", " foo(a)" ); } From f0a2e9ad636777589be07d7d60acaf9094d4b0bd Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sat, 25 Jan 2020 19:46:05 +0100 Subject: [PATCH 11/50] add type-class arguments --- .../peeeq/wurstio/WurstCompilerJassImpl.java | 4 ++ .../imtranslation/ClassesOptimizer.java | 37 ++++++++++ .../imtranslation/ExprTranslation.java | 25 +++++++ .../imtranslation/ImTranslator.java | 17 +++-- .../imtranslation/TLDTranslation.java | 4 +- .../imtranslation/TypeClassTranslator.java | 67 +++++++++++++++++++ .../wurstscript/types/TypeClassInstance.java | 13 ++-- .../types/WurstTypeBoundTypeParam.java | 3 + 8 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 9db7b8b12..201832f03 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -411,9 +411,13 @@ public JassProg transformProgToJass() { new EliminateGenerics(imTranslator2, imProg2).transform(); printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); + + // eliminate classes beginPhase(2, "translate classes"); + ClassesOptimizer.optimizeProg(imTranslator2); + new EliminateClasses(imTranslator2, imProg2, !runArgs.isUncheckedDispatch()).eliminateClasses(); imTranslator2.assertProperties(); printDebugImProg("./test-output/im " + stage++ + "_classesEliminated.im"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java new file mode 100644 index 000000000..cef105684 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java @@ -0,0 +1,37 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +/** + * optimizes classes, dynamic dispatch, etc. + * before running EliminateClasses + */ +public class ClassesOptimizer { + public static void optimizeProg(ImTranslator tr) { + optimizeStatelessClasses(tr); + + optimizeDispatch(tr); + } + + /** + * Search for classes without attributes and replace them with + * a single-instance. + * + * This is a necessary optimization for the translation of type classes, + * as otherwise there would be memory leaks. + * A typeclass dict is always a class without attributes. + */ + private static void optimizeStatelessClasses(ImTranslator tr) { + // TODO implement optimization + } + + /** + * Analyze the program and find places where the receiver of a method + * is guaranteed to not be null or de-allocated. + * Mark these method calls as unchecked dispatches. + * Unchecked dispatches are later translated to simplified code: If + * there are no sub-methods, they can be directly translated to + * function calls. + */ + private static void optimizeDispatch(ImTranslator tr) { + // TODO implement optimization + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 4c22cce54..8c58905df 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -514,11 +514,13 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu imArgs.add(0, receiver); } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn)); call = JassIm.ImMethodCall(e, method, typeArguments, typeClassDict, imArgs, false); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false); } else { ImFunction calledImFunc = t.getFuncFor(calledFunc); @@ -526,6 +528,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu imArgs.add(0, receiver); } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables(), imArgs); call = ImFunctionCall(e, calledImFunc, typeArguments, imArgs, false, CallType.NORMAL); } @@ -537,6 +540,28 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } } + /** + * For each type parameter constraint adds an argument containing the type class dictionary. + */ + private static void addTypeClassDictArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables, ImExprs imArgs) { + VariableBinding mapping = sig.getMapping(); + for (ImTypeVar tv : typeVariables) { + TypeParamDef tp = tr.getTypeParamDef(tv); + Option to = mapping.get(tp); + if (to.isEmpty()) { + throw new CompileError(location, "Type variable " + tp.getName() + " not bound in mapping."); + } + WurstTypeBoundTypeParam t = to.get(); + if (!t.isTemplateTypeParameter()) { + continue; + } + for (TypeClassInstance instance : t.getInstances()) { + ImExpr arg = instance.translate(location, tr); + imArgs.add(arg); + } + } + } + private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables) { ImTypeArguments res = ImTypeArguments(); VariableBinding mapping = sig.getMapping(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index d06bd4491..6ad36b565 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -751,8 +751,14 @@ public ImFunction getTypeClassFuncFor(FuncDef funcDef) { ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); ImVars parameters = ImVars(); - ImVar thisVar = getThisVar(funcDef).copy(); - parameters.add(thisVar); + // add parameter for type class struct + ImClass typeClassStruct = getTypeClassStructFor(funcDef.attrNearestClassOrInterface()); + parameters.add(JassIm.ImVar(funcDef, selfType(typeClassStruct), "thisDict", emptyList())); + + if (!funcDef.attrIsStatic()) { + ImVar thisVar = getThisVar(funcDef).copy(); + parameters.add(thisVar); + } for (WParameter p : params) { parameters.add(getVarFor(p).copy()); } @@ -768,7 +774,6 @@ public ImFunction getTypeClassFuncFor(FuncDef funcDef) { flags); - addFunction(f, funcDef); typeClassFuncMap.put(funcDef, f); return f; } @@ -1351,7 +1356,8 @@ public ImMethod getTypeClassMethodFor(FuncDef f) { ImMethod m = typeClassMethodForFuncDef.get(f); if (m == null) { ImFunction imFunc = getTypeClassFuncFor(f); - m = JassIm.ImMethod(f, selfType(f), elementNameWithPath(f), imFunc, Lists.newArrayList(), false); + ImClass typeClassStruct = getTypeClassStructFor(f.attrNearestClassOrInterface()); + m = JassIm.ImMethod(f, selfType(typeClassStruct), elementNameWithPath(f), imFunc, Lists.newArrayList(), false); typeClassMethodForFuncDef.put(f, m); } return m; @@ -1372,6 +1378,7 @@ public ImVar getTypeClassParamFor(TypeParamConstraint tc) { } + interface VarsForTupleResult { default Iterable allValues() { @@ -1669,7 +1676,7 @@ public ImClass getClassFor(ClassOrInterface s) { public ImClass getTypeClassStructFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return typeClassStructFor.computeIfAbsent(s, s1 -> { - return JassIm.ImClass(s1, s1.getName(), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + return JassIm.ImClass(s1, "TypeClassDict_" + s1.getName(), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java index c960824dc..4c3d07cf2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java @@ -5,7 +5,6 @@ import de.peeeq.wurstscript.jassIm.ImStmt; import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.WurstTypeClass; -import org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -73,6 +72,7 @@ public static void translate(ClassDef classDef, ImTranslator translator) { translate(extendedClass.getClassDef(), translator); } ClassTranslator.translate(classDef, translator); + TypeClassTranslator.translateClassOrInterface(classDef, translator); translator.setTranslated(classDef); } @@ -112,7 +112,7 @@ public static void translate(InitBlock initBlock, ImTranslator translator) { public static void translate(InterfaceDef interfaceDef, ImTranslator translator) { new InterfaceTranslator(interfaceDef, translator).translate(); - + TypeClassTranslator.translateClassOrInterface(interfaceDef, translator); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java new file mode 100644 index 000000000..916697c2f --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java @@ -0,0 +1,67 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeClassOrInterface; + +import javax.sound.midi.Receiver; +import java.util.stream.Collectors; + +public class TypeClassTranslator { + + public static void translateClassOrInterface(ClassOrInterface def, ImTranslator tr) { + ImClass dict = tr.getTypeClassStructFor(def); + tr.imProg().getClasses().add(dict); + + WurstTypeClassOrInterface wurstType = (WurstTypeClassOrInterface) def.attrTyp(); + + // add super types: + for (WurstTypeClassOrInterface directSupertype : wurstType.directSupertypes()) { + ImClassType imSuperType = directSupertype.imTranslateToTypeClass(tr); + dict.getSuperClasses().add(imSuperType); + } + + // add methods + for (FuncDef method : def.getMethods()) { + ImMethod imMethod = tr.getTypeClassMethodFor(method); + dict.getMethods().add(imMethod); + + // TODO add overrides + + + } + + // add functions + for (FuncDef method : def.getMethods()) { + ImFunction imFunc = tr.getTypeClassFuncFor(method); + dict.getFunctions().add(imFunc); + + ImTypeArguments typeArgs = imFunc.getTypeVariables().stream() + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + ImExprs args = imFunc.getParameters().stream() + .skip(1) // skip dict-this parameter + .map(JassIm::ImVarAccess) + .collect(Collectors.toCollection(JassIm::ImExprs)); + + + ImExpr result; + // add method call for the real method + if (method.attrIsStatic()) { + ImFunction originalFunc = tr.getFuncFor(method); + result = JassIm.ImFunctionCall(method, originalFunc, typeArgs, args, false, CallType.NORMAL); + } else { + ImMethod m = tr.getMethodFor(method); + ImExpr receiver = args.remove(0); + result = JassIm.ImMethodCall(method, m, typeArgs, receiver, args, false); + + } + if (imFunc.getReturnType() instanceof ImVoid) { + imFunc.getBody().add(result); + } else { + imFunc.getBody().add(JassIm.ImReturn(method, result)); + } + } + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index c50487287..c90d258b2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -5,6 +5,8 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import static de.peeeq.wurstscript.utils.Utils.emptyList; + public abstract class TypeClassInstance { private final WurstTypeClassOrInterface constraint; @@ -15,8 +17,11 @@ public TypeClassInstance(WurstTypeClassOrInterface constraint) { public static TypeClassInstance asSubtype(WurstTypeClassOrInterface subType, WurstTypeClassOrInterface constraint) { return new TypeClassInstance(constraint) { @Override - public ImExpr translate(ImTranslator tr) { - throw new RuntimeException("TODO"); + public ImExpr translate(Element trace, ImTranslator tr) { + // using can alloc here, which we will later rewrite to a constant + // variable access (the dict class has no fields and no constructor/destructor code) + ImClassType ct = subType.imTranslateToTypeClass(tr); + return JassIm.ImAlloc(trace, ct); } }; } @@ -28,7 +33,7 @@ protected ImClassType translateConstraintType(ImTranslator tr) { public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeClassOrInterface constraint) { return new TypeClassInstance(constraint) { @Override - public ImExpr translate(ImTranslator tr) { + public ImExpr translate(Element trace, ImTranslator tr) { throw new RuntimeException("TODO"); } }; @@ -38,5 +43,5 @@ public WurstTypeClassOrInterface getConstraintType() { return constraint; } - public abstract ImExpr translate(ImTranslator tr); + public abstract ImExpr translate(Element trace, ImTranslator tr); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index b3201fa13..bbbabd1cf 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -219,4 +219,7 @@ public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) return new WurstTypeBoundTypeParam(typeParamDef, baseType, fromIndex, toIndex, newInstances, indexInitialized, context); } + public List getInstances() { + return instances; + } } From a2fe29b9cdffc40ed1f7d222836717593963abf2 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sat, 25 Jan 2020 23:47:37 +0100 Subject: [PATCH 12/50] singleton optimization --- .../peeeq/wurstio/WurstCompilerJassImpl.java | 1 + .../imtranslation/ClassesOptimizer.java | 112 +++++++++++++++++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 201832f03..3471f5a29 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -417,6 +417,7 @@ public JassProg transformProgToJass() { beginPhase(2, "translate classes"); ClassesOptimizer.optimizeProg(imTranslator2); + printDebugImProg("./test-output/im " + stage++ + "_classes_optimized.im"); new EliminateClasses(imTranslator2, imProg2, !runArgs.isUncheckedDispatch()).eliminateClasses(); imTranslator2.assertProperties(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java index cef105684..70733d336 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java @@ -1,8 +1,19 @@ package de.peeeq.wurstscript.translation.imtranslation; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import de.peeeq.wurstscript.WurstOperator; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.TypesHelper; + +import java.util.*; +import java.util.stream.Collectors; + /** * optimizes classes, dynamic dispatch, etc. * before running EliminateClasses + * + * Precondition: Generics eliminated */ public class ClassesOptimizer { public static void optimizeProg(ImTranslator tr) { @@ -14,13 +25,112 @@ public static void optimizeProg(ImTranslator tr) { /** * Search for classes without attributes and replace them with * a single-instance. + * If the class has no attributes, the only way to distinguish different + * instances of the class is by using equality or casting to int. * * This is a necessary optimization for the translation of type classes, * as otherwise there would be memory leaks. * A typeclass dict is always a class without attributes. */ private static void optimizeStatelessClasses(ImTranslator tr) { - // TODO implement optimization + ImProg prog = tr.getImProg(); + + Multimap subClasses = transitiveReflexiveSubclasses(prog); + + Set statelessClasses = prog.getClasses().stream() + .filter(c -> c.getFields().isEmpty() + && subClasses.get(c).size() == 1) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + Multimap allocs = LinkedHashMultimap.create(); + Multimap deAllocs = LinkedHashMultimap.create(); + prog.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImAlloc imAlloc) { + super.visit(imAlloc); + ImClass classDef = imAlloc.getClazz().getClassDef(); + if (statelessClasses.contains(classDef)) { + allocs.put(classDef, imAlloc); + } + } + + @Override + public void visit(ImDealloc deAlloc) { + super.visit(deAlloc); + ImClass classDef = deAlloc.getClazz().getClassDef(); + if (statelessClasses.contains(classDef)) { + deAllocs.put(classDef, deAlloc); + } + } + + @Override + public void visit(ImOperatorCall c) { + WurstOperator op = c.getOp(); + if (op == WurstOperator.EQ || op == WurstOperator.NOTEQ) { + for (ImExpr arg : c.getArguments()) { + markNotStateless(arg.attrTyp()); + } + } + } + + private void markNotStateless(ImType attrTyp) { + if (attrTyp instanceof ImClassType) { + ImClassType ct = (ImClassType) attrTyp; + markNotStateless(ct.getClassDef()); + } + } + + private void markNotStateless(ImClass classDef) { + statelessClasses.removeAll(subClasses.get(classDef)); + } + + @Override + public void visit(ImCast c) { + if (c.getToType().equalsType(TypesHelper.imInt())) { + markNotStateless(c.getExpr().attrTyp()); + } + } + }); + + for (ImClass c : statelessClasses) { + Collection cAllocs = allocs.get(c); + if (cAllocs.isEmpty()) { + continue; + } + // create singleton variable + ImClassType classType = JassIm.ImClassType(c, JassIm.ImTypeArguments()); + ImVar singletonVar = JassIm.ImVar(c.getTrace(), classType, c.getName() + "_singleton", Collections.emptyList()); + tr.addGlobalWithInitalizer(singletonVar, JassIm.ImAlloc(c.getTrace(), classType)); + + // replace all allocations with singleton variable + for (ImAlloc cAlloc : cAllocs) { + cAlloc.replaceBy(JassIm.ImVarAccess(singletonVar)); + } + // remove deAllocs + for (ImDealloc deAlloc : deAllocs.get(c)) { + deAlloc.replaceBy(JassIm.ImNull(JassIm.ImVoid())); + } + + } + } + + /** + * map from class to all subclasses and itself + */ + private static Multimap transitiveReflexiveSubclasses(ImProg prog) { + LinkedHashMultimap res = LinkedHashMultimap.create(); + for (ImClass c : prog.getClasses()) { + addSuperClasses(res, c, c); + } + + return res; + } + + private static void addSuperClasses(LinkedHashMultimap res, ImClass subClass, ImClass superClass) { + res.put(superClass, subClass); + for (ImClassType superSuperClass : superClass.getSuperClasses()) { + addSuperClasses(res, subClass, superSuperClass.getClassDef()); + } } /** From 2f05cf09f730a4bff753b91bad86dba2686d908a Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 26 Jan 2020 23:13:18 +0100 Subject: [PATCH 13/50] wip --- .../imtranslation/ImTranslator.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 6ad36b565..2b2286e4e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1667,7 +1667,14 @@ public ImClass getClassForClosure(ExprClosure s) { public ImClass getClassFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { - return JassIm.ImClass(s1, s1.getName(), ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + ImTypeVars typeVariables = JassIm.ImTypeVars(); + for (TypeParamDef tp : s.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + ImTypeVar tv = getTypeVar(tp); + typeVariables.add(tv); + } + } + return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } @@ -1676,7 +1683,14 @@ public ImClass getClassFor(ClassOrInterface s) { public ImClass getTypeClassStructFor(ClassOrInterface s) { Preconditions.checkNotNull(s); return typeClassStructFor.computeIfAbsent(s, s1 -> { - return JassIm.ImClass(s1, "TypeClassDict_" + s1.getName(), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + ImTypeVars typeVariables = JassIm.ImTypeVars(); + for (TypeParamDef tp : s.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + ImTypeVar tv = getTypeVar(tp); + typeVariables.add(tv.copy()); + } + } + return JassIm.ImClass(s1, "TypeClassDict_" + s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } From 452e603e8c00c42e02e9cd0145f1ec9c949a86aa Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 28 Jan 2020 00:07:52 +0100 Subject: [PATCH 14/50] again changing design more like Rust, but reusing interfaces for traits --- .../parserspec/jass_im.parseq | 2 + .../parserspec/wurstscript.parseq | 5 ++ .../antlr/de/peeeq/wurstscript/antlr/Wurst.g4 | 8 +++ .../requests/DocumentSymbolRequest.java | 5 ++ .../languageserver/requests/HoverInfo.java | 5 ++ .../requests/SymbolInformationRequest.java | 5 ++ .../wurstscript/attributes/AttrNearest.java | 3 + .../AttrPossibleFunctionSignatures.java | 72 +++++++++++++++---- .../attributes/DescriptionHtml.java | 4 ++ .../attributes/names/NameLinks.java | 13 ++++ .../wurstscript/attributes/names/VarLink.java | 2 +- .../attributes/prettyPrint/PrettyPrinter.java | 11 +++ .../interpreter/EvaluateExpr.java | 10 +++ .../optimizer/FunctionSplitter.java | 5 ++ .../optimizer/SideEffectAnalyzer.java | 5 ++ .../antlr/AntlrWurstParseTreeTransformer.java | 22 ++++++ .../translation/imtojass/ExprTranslation.java | 4 ++ .../translation/imtojass/ImAttrType.java | 4 ++ .../translation/imtranslation/Flatten.java | 5 ++ .../translation/imtranslation/ImPrinter.java | 6 ++ .../imtranslation/ImTranslator.java | 3 + .../imtranslation/TLDTranslation.java | 5 +- .../imtranslation/TypeClassTranslator.java | 54 +------------- .../lua/translation/ExprTranslation.java | 30 ++++++++ .../wurstscript/types/TypeClassInstance.java | 23 ++++-- .../wurstscript/types/WurstTypeInterface.java | 2 +- .../wurstscript/types/WurstTypeTypeParam.java | 11 +++ .../tests/GenericsWithTypeclassesTests.java | 33 ++++----- 28 files changed, 264 insertions(+), 93 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index a4c6bf684..f973d5649 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -98,6 +98,8 @@ ImExpr = | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr | ImCast(ImExpr expr, ref ImType toType) + | ImTypeClassDictValue(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType classType, ImExprs arguments) + // an expression which can be used on the left hand side of an assignment ImLExpr = diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index e977b314c..bcc894442 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -36,6 +36,7 @@ WEntity = | ModuleDef(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamDefs typeParameters, ClassDefs innerClasses, FuncDefs methods, GlobalVarDefs vars, ConstructorDefs constructors, ModuleInstanciations p_moduleInstanciations, ModuleUses moduleUses, OnDestroyDef onDestroy) + | InstanceDecl(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, TypeParamDefs typeParameters, TypeExpr implementedInterface, FuncDefs methods) @@ -511,6 +512,10 @@ Element.attrNearestScope() returns @Nullable WScope implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestScope +TypeParamConstraint.parentTypeParam() + returns TypeParamDef + implemented by de.peeeq.wurstscript.attributes.AttrNearest.parentTypeParam + WScope.attrNextScope "returns the scope surrounding this scope" returns @Nullable WScope implemented by de.peeeq.wurstscript.attributes.AttrNearest.nextScope diff --git a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 index 8bc9e740d..5ab24b87b 100644 --- a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 +++ b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 @@ -112,8 +112,16 @@ entity: | interfaceDef | tupleDef | extensionFuncDef + | instanceDeclaration ; +instanceDeclaration: + 'instance' (name=ID? typeParams 'implements')? implemented=typeExpr + NL (STARTBLOCK + funcDef* + ENDBLOCK)? + ; + interfaceDef: modifiersWithDoc 'interface' name=ID typeParams ('extends' extended+=typeExpr (',' extended+=typeExpr)*)? diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java index 92b5e89e0..3b8910ff8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java @@ -118,6 +118,11 @@ public void case_TupleDef(TupleDef tupleDef) { add(tupleDef.getName(), SymbolKind.Class); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + // ignore + } + @Override public void case_FuncDef(FuncDef funcDef) { SymbolKind kind = funcDef.attrIsDynamicClassMember() ? SymbolKind.Method : SymbolKind.Function; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java index bde562bfb..9f80d3a23 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java @@ -776,6 +776,11 @@ public List> case_ModVararg(ModVararg modVararg) { return string("Declares the parameter to be a array of variable length"); } + @Override + public List> case_InstanceDecl(InstanceDecl instanceDecl) { + return string("An instance declaration for the type-class " + instanceDecl.getImplementedInterface().attrTyp()); + } + @Override public List> case_TypeExprResolved(TypeExprResolved typeExprResolved) { return typeExpr(typeExprResolved); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java index 33e69de75..be72e2f2d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java @@ -111,6 +111,11 @@ public void case_TupleDef(TupleDef tupleDef) { add(tupleDef.getName(), SymbolKind.Class); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + // ignore + } + @Override public void case_FuncDef(FuncDef funcDef) { SymbolKind kind = funcDef.attrIsDynamicClassMember() ? SymbolKind.Method : SymbolKind.Function; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java index 517585082..0e35fc0c1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java @@ -159,4 +159,7 @@ public static ClassOrInterface nearestClassOrInterface(Element node) { } + public static TypeParamDef parentTypeParam(TypeParamConstraint tp) { + return (TypeParamDef) tp.getParent().getParent(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 13ea1a975..894e11d76 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -8,10 +8,13 @@ import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static de.peeeq.wurstscript.attributes.GenericsHelper.givenBinding; import static de.peeeq.wurstscript.attributes.GenericsHelper.typeParameters; @@ -96,7 +99,13 @@ private static Pair> findTypeClasses(Funct for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { WurstType ct = c.getConstraint().attrTyp(); if (ct instanceof WurstTypeInterface) { - constraints.add((WurstTypeInterface) ct); + WurstTypeInterface wti = (WurstTypeInterface) ct; + + TypeParamDef lastTypeParam = Utils.getLast(wti.getDef().getTypeParameters()); + wti = (WurstTypeInterface) wti.setTypeArgs(wti.getTypeArgBinding() + .set(lastTypeParam, new WurstTypeBoundTypeParam(lastTypeParam, new WurstTypeTypeParam(tp), c))); + + constraints.add(wti); } } } @@ -120,14 +129,13 @@ private static Pair> findTypeClasses(Funct return Pair.create(sig, errors); } - private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint) { - // option 1: the matched type is a subtype of the constraint - VariableBinding mapping2 = matchedType.matchAgainstSupertype(constraint, fc, mapping, VariablePosition.RIGHT); - if (mapping2 != null) { - WurstTypeClassOrInterface subType = (WurstTypeClassOrInterface) matchedType.getBaseType(); - return mapping2.set(tp, matchedType.withTypeClassInstance(TypeClassInstance.asSubtype(subType, constraint))); - } - // option 2: the matched type is a type param that also has the right constraint: + private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { + WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); + System.out.println("mapping = " + mapping); + System.out.println("constraint1 = " + constraint1); + System.out.println("constraint = " + constraint); + + // option 1: the matched type is a type param that also has the right constraint: if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); Optional matchingConstraint = wtp.getTypeConstraints().filter(c -> c.isSubtypeOf(constraint, fc)).findFirst(); @@ -137,12 +145,50 @@ private static VariableBinding findTypeClass(StmtCall fc, List err return mapping.set(tp, matchedType.withTypeClassInstance(instance)); } } - // option 3: find methods elsewhere + // option 2: find instance declarations + // TODO create index to make this faster and use normal scoped lookup (ony search imports) + WurstModel model = fc.getModel(); + List instances = model.stream() + .flatMap(cu -> cu.getPackages().stream()) + .flatMap(p -> p.getElements().stream()) + .filter(e -> e instanceof InstanceDecl) + .map(e -> (InstanceDecl) e) + .flatMap(instance -> { + WurstType instanceType = instance.getImplementedInterface().attrTyp(); + System.out.println("checking instance " + instanceType + " // " + constraint); + VariableBinding match = constraint.matchAgainstSupertype(instanceType, fc, VariableBinding.emptyMapping(), VariablePosition.RIGHT); + + if (match == null) { + return Stream.empty(); + } + instanceType = instanceType.setTypeArgs(match); + + List typeArgs = new ArrayList<>(); + List deps = new ArrayList<>(); + + // TODO resolve dependencies - // TODO find instance declaration + TypeClassInstance result = TypeClassInstance.fromInstance(instance, typeArgs, deps, (WurstTypeInterface) instanceType); - errors.add(new CompileError(fc, "Could not find type class instance " + constraint.getName() + " for type " + matchedType)); - return null; + return Stream.of(result); + + }).collect(Collectors.toList()); + + if (instances.isEmpty()) { + errors.add(new CompileError(fc, + "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint.getName())); + // "Could not find type class instance " + constraint.getName() + " for type " + matchedType)); + return null; + } else { + if (instances.size() > 1) { + errors.add(new CompileError(fc, + "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + + Utils.printSep("\n", instances))); + + } + TypeClassInstance instance = Utils.getFirst(instances); + return mapping.set(tp, matchedType.withTypeClassInstance(instance)); + } } public static ImmutableCollection calculate(ExprNewObject fc) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java index 4588788e0..694e2391b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java @@ -440,4 +440,8 @@ private static TypeParamDef findTypeParamDef(TypeParamConstraint tp) { } return null; } + + public static String description(InstanceDecl instanceDecl) { + return "Type class instance declaration."; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java index bd054032b..3360630ba 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java @@ -16,6 +16,7 @@ import java.util.*; import java.util.Map.Entry; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class NameLinks { @@ -48,6 +49,7 @@ public static ImmutableMultimap calculate(ClassOrModuleOrModule addNamesFromImplementedInterfaces(result, classType, overrideCheckResults); } + addTypeParametersIfAny(result::put, c); reportOverrideErrors(overrideCheckResults); return ImmutableMultimap.copyOf(result); } @@ -96,6 +98,7 @@ public static ImmutableMultimap calculate(InterfaceDef i) { Map> overrideCheckResults = initOverrideMap(result); addNamesFromExtendedInterfaces(result, i.attrTypI(), overrideCheckResults); reportOverrideErrors(overrideCheckResults); + addTypeParametersIfAny(result::put, i); return ImmutableMultimap.copyOf(result); } @@ -170,9 +173,19 @@ public static ImmutableMultimap calculate(AstElementWithBody c) WScope s = (WScope) c; addVarDefIfAny(result, s); addParametersIfAny(result, s); + addTypeParametersIfAny(result::put, s); return result.build(); } + private static void addTypeParametersIfAny(BiConsumer result, WScope s) { + if (s instanceof AstElementWithTypeParameters) { + AstElementWithTypeParameters tps = (AstElementWithTypeParameters) s; + for (TypeParamDef tp : tps.getTypeParameters()) { + result.accept(tp.getName(), VarLink.create(tp, s)); + } + } + } + private static void addVarDefIfAny(Builder result, WScope s) { if (s instanceof LoopStatementWithVarDef) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java index aaf01b603..cbe2d6837 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java @@ -145,7 +145,7 @@ public VarLink withTypeArgBinding(Element context, VariableBinding binding) { } Deferred newType = type.map(oldType -> oldType.setTypeArgs(binding)); boolean changed = newType != type; - WurstType newReceiverType = getReceiverType().setTypeArgs(binding); + WurstType newReceiverType = getReceiverType() == null ? null : getReceiverType().setTypeArgs(binding); changed |= newReceiverType != getReceiverType(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index b47021ce4..387e8f575 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -388,6 +388,17 @@ public static void prettyPrint(InterfaceDef e, Spacer spacer, StringBuilder sb, e.getMethods().prettyPrint(spacer, sb, indent); } + + public static void prettyPrint(InstanceDecl e, Spacer spacer, StringBuilder sb, int indent) { + printIndent(sb, indent); + sb.append("instance"); + e.getTypeParameters().prettyPrint(spacer, sb, indent); + spacer.addSpace(sb); + e.getImplementedInterface().prettyPrint(spacer, sb, indent); + sb.append("\n"); + e.getMethods().prettyPrint(spacer, sb, indent + 1); + } + public static void prettyPrint(JassGlobalBlock e, Spacer spacer, StringBuilder sb, int indent) { } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 8c61cd50f..e26154aff 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -420,4 +420,14 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } return res; } + + public static ILconst eval(ImTypeClassDictValue e, ProgramState globalState, LocalState localState) { + ILconstObject obj = globalState.allocate(e.getClassType(), e.attrTrace()); + int i = 0; + for (ImExpr arg : e.getArguments()) { + ILconst argV = arg.evaluate(globalState, localState); + obj.set(e.getClassType().getClassDef().getFields().get(i), Collections.emptyList(), argV); + } + return obj; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java index 696488a72..b0d4c7274 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java @@ -234,6 +234,11 @@ public Integer case_ImIf(ImIf s) { return 1 + estimateFuel(s.getCondition()) + Math.max(estimateFuel(s.getThenBlock()), estimateFuel(s.getElseBlock())); } + @Override + public Integer case_ImTypeClassDictValue(ImTypeClassDictValue imTypeClassDictValue) { + return 1; + } + @Override public Integer case_ImCast(ImCast s) { return estimateFuel(s.getExpr()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java index 58abbb566..202ae2895 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java @@ -61,6 +61,11 @@ public Boolean case_ImTupleSelection(ImTupleSelection e) { return quickcheckHasSideeffects(e.getTupleExpr()); } + @Override + public Boolean case_ImTypeClassDictValue(ImTypeClassDictValue e) { + return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); + } + @Override public Boolean case_ImInstanceof(ImInstanceof e) { return quickcheckHasSideeffects(e.getObj()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 21298cd1a..4c4157b4e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -288,6 +288,8 @@ private WPackage transformPackage(WpackageContext p) { return transformTupleDef(e.tupleDef()); } else if (e.extensionFuncDef() != null) { return transformExtensionFuncDef(e.extensionFuncDef()); + } else if (e.instanceDeclaration() != null) { + return transformInstanceDeclaration(e.instanceDeclaration()); } if (e.exception != null) { @@ -301,6 +303,26 @@ private WPackage transformPackage(WpackageContext p) { } } + private WEntity transformInstanceDeclaration(InstanceDeclarationContext i) { + return Ast.InstanceDecl( + source(i), + transformTypeParams(i.typeParams()), + transformTypeExpr(i.implemented), + transformFuncDefs(i.funcDef()) + ); + } + + private FuncDefs transformFuncDefs(List funcDef) { + FuncDefs res = Ast.FuncDefs(); + if (funcDef == null) { + return res; + } + for (FuncDefContext f : funcDef) { + res.add(transformFuncDef(f)); + } + return res; + } + private WEntity transformExtensionFuncDef(ExtensionFuncDefContext f) { WPos src = source(f); Modifiers modifiers = transformModifiers(f.modifiersWithDoc()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java index 48d060451..358edc43f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java @@ -143,4 +143,8 @@ public static JassExpr translate(ImCompiletimeExpr e, ImToJassTranslator transla public static JassExpr translate(ImCast imCast, ImToJassTranslator translator) { return imCast.getExpr().translate(translator); } + + public static JassExpr translate(ImTypeClassDictValue e, ImToJassTranslator translator) { + throw new CompileError(e.getTrace().attrSource(), "Dependent type classes should be elimnated: " + e); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index 66c542a34..d5c1e0070 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -215,4 +215,8 @@ public static ImType getType(ImCompiletimeExpr e) { public static ImType getType(ImCast imCast) { return imCast.getToType(); } + + public static ImType getType(ImTypeClassDictValue e) { + return e.getClassType(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 7cf10442b..ae9beb618 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -88,6 +88,11 @@ public static Result flatten(ImAlloc e, ImTranslator translator, ImFunction f) { return new Result(e); } + public static Result flatten(ImTypeClassDictValue e, ImTranslator translator, ImFunction f) { + MultiResult r = flattenExprs(translator, f, e.getArguments()); + return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClassType(), JassIm.ImExprs(e.getArguments()))); + } + public static class Result { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index 2b902e353..101459534 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -572,4 +572,10 @@ public static void print(ImCast e, Appendable sb, int indent) { public static void print(ImAnyType at, Appendable sb, int indent) { append(sb, "any"); } + + public static void print(ImTypeClassDictValue e, Appendable sb, int indent) { + append(sb, "TypeClassDict"); + e.getClassType().print(sb, indent); + printArgumentList(sb, indent, e.getArguments()); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 2b2286e4e..5366169fa 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1377,6 +1377,9 @@ public ImVar getTypeClassParamFor(TypeParamConstraint tc) { return v; } + public ImClass getInstanceClassFor(InstanceDecl decl) { + throw new RuntimeException("TODOO"); + } interface VarsForTupleResult { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java index 4c3d07cf2..ff482d4e1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java @@ -72,7 +72,6 @@ public static void translate(ClassDef classDef, ImTranslator translator) { translate(extendedClass.getClassDef(), translator); } ClassTranslator.translate(classDef, translator); - TypeClassTranslator.translateClassOrInterface(classDef, translator); translator.setTranslated(classDef); } @@ -112,7 +111,6 @@ public static void translate(InitBlock initBlock, ImTranslator translator) { public static void translate(InterfaceDef interfaceDef, ImTranslator translator) { new InterfaceTranslator(interfaceDef, translator).translate(); - TypeClassTranslator.translateClassOrInterface(interfaceDef, translator); } @@ -134,4 +132,7 @@ public static void translate(ModuleInstanciation moduleInstanciation, ImTranslat // nothing to do? } + public static void translate(InstanceDecl instanceDecl, ImTranslator translator) { + TypeClassTranslator.translateTypeClass(instanceDecl, translator); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java index 916697c2f..498106646 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java @@ -10,58 +10,8 @@ public class TypeClassTranslator { - public static void translateClassOrInterface(ClassOrInterface def, ImTranslator tr) { - ImClass dict = tr.getTypeClassStructFor(def); - tr.imProg().getClasses().add(dict); - WurstTypeClassOrInterface wurstType = (WurstTypeClassOrInterface) def.attrTyp(); - - // add super types: - for (WurstTypeClassOrInterface directSupertype : wurstType.directSupertypes()) { - ImClassType imSuperType = directSupertype.imTranslateToTypeClass(tr); - dict.getSuperClasses().add(imSuperType); - } - - // add methods - for (FuncDef method : def.getMethods()) { - ImMethod imMethod = tr.getTypeClassMethodFor(method); - dict.getMethods().add(imMethod); - - // TODO add overrides - - - } - - // add functions - for (FuncDef method : def.getMethods()) { - ImFunction imFunc = tr.getTypeClassFuncFor(method); - dict.getFunctions().add(imFunc); - - ImTypeArguments typeArgs = imFunc.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) - .collect(Collectors.toCollection(JassIm::ImTypeArguments)); - ImExprs args = imFunc.getParameters().stream() - .skip(1) // skip dict-this parameter - .map(JassIm::ImVarAccess) - .collect(Collectors.toCollection(JassIm::ImExprs)); - - - ImExpr result; - // add method call for the real method - if (method.attrIsStatic()) { - ImFunction originalFunc = tr.getFuncFor(method); - result = JassIm.ImFunctionCall(method, originalFunc, typeArgs, args, false, CallType.NORMAL); - } else { - ImMethod m = tr.getMethodFor(method); - ImExpr receiver = args.remove(0); - result = JassIm.ImMethodCall(method, m, typeArgs, receiver, args, false); - - } - if (imFunc.getReturnType() instanceof ImVoid) { - imFunc.getBody().add(result); - } else { - imFunc.getBody().add(JassIm.ImReturn(method, result)); - } - } + public static void translateTypeClass(InstanceDecl instanceDecl, ImTranslator translator) { + throw new RuntimeException("TODO"); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 07c64f668..1b94254b9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -158,6 +158,36 @@ && isStringType(right.attrTyp())) { throw new Error("not implemented: " + e); } + public static LuaExpr translate(ImTypeClassDictValue e, LuaTranslator tr) { + ImClass c = e.getClassType().getClassDef(); + LuaMethod m = tr.luaClassInitMethod.getFor(c); + LuaVariable classVar = tr.luaClassVar.getFor(c); + + LuaStatements body = LuaAst.LuaStatements(); + LuaVariable r = LuaAst.LuaVariable("res", LuaAst.LuaExprMethodCall( + LuaAst.LuaExprVarAccess(classVar), + m, + LuaAst.LuaExprlist() + )); + body.add(r); + int i = 0; + for (ImVar field : c.getFields()) { + if (i >= e.getArguments().size()) { + break; + } + ImExpr arg = e.getArguments().get(i); + + LuaVariable lField = tr.luaVar.getFor(field); + body.add(LuaAst.LuaAssignment( + LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(r), lField.getName()), + arg.translateToLua(tr))); + } + body.add(LuaAst.LuaReturn(LuaAst.LuaExprVarAccess(r))); + return LuaAst.LuaExprFunctionCallE( + LuaAst.LuaExprFunctionAbstraction(LuaAst.LuaParams(), body), + LuaAst.LuaExprlist()); + } + static class TupleFunc { final ImTupleType tupleType; final LuaFunction func; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index c90d258b2..bc045ccce 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -2,9 +2,12 @@ import de.peeeq.wurstscript.ast.Element; +import de.peeeq.wurstscript.ast.InstanceDecl; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import java.util.List; + import static de.peeeq.wurstscript.utils.Utils.emptyList; public abstract class TypeClassInstance { @@ -14,14 +17,22 @@ public TypeClassInstance(WurstTypeClassOrInterface constraint) { this.constraint = constraint; } - public static TypeClassInstance asSubtype(WurstTypeClassOrInterface subType, WurstTypeClassOrInterface constraint) { + public static TypeClassInstance fromInstance(InstanceDecl decl, List typeArgs, List dependencies, WurstTypeInterface constraint) { return new TypeClassInstance(constraint) { @Override public ImExpr translate(Element trace, ImTranslator tr) { - // using can alloc here, which we will later rewrite to a constant - // variable access (the dict class has no fields and no constructor/destructor code) - ImClassType ct = subType.imTranslateToTypeClass(tr); - return JassIm.ImAlloc(trace, ct); + ImClass c = tr.getInstanceClassFor(decl); + ImTypeArguments imTypeArgs = JassIm.ImTypeArguments(); + for (WurstType ta : typeArgs) { + imTypeArgs.add(JassIm.ImTypeArgument(ta.imTranslateType(tr))); + } + ImClassType ct = JassIm.ImClassType(c, imTypeArgs); + ImExprs args = JassIm.ImExprs(); + for (TypeClassInstance dep : dependencies) { + ImExpr d = dep.translate(trace, tr); + args.add(d); + } + return JassIm.ImTypeClassDictValue(trace, ct, args); } }; } @@ -30,7 +41,7 @@ protected ImClassType translateConstraintType(ImTranslator tr) { return constraint.imTranslateToTypeClass(tr); } - public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeClassOrInterface constraint) { + public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeInterface constraint) { return new TypeClassInstance(constraint) { @Override public ImExpr translate(Element trace, ImTranslator tr) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java index e83baf8ee..0430b0a1c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java @@ -60,7 +60,7 @@ public WurstType dynamic() { } @Override - public WurstType replaceTypeVars(List newTypes) { + public WurstTypeInterface replaceTypeVars(List newTypes) { return new WurstTypeInterface(getInterfaceDef(), newTypes); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 55ad66bca..f984524b1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -6,6 +6,7 @@ import de.peeeq.wurstscript.jassIm.ImType; import de.peeeq.wurstscript.jassIm.JassIm; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import de.peeeq.wurstscript.utils.Utils; import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; @@ -115,8 +116,18 @@ public Stream getMemberMethods(Element node) { return constraints.stream() .flatMap((TypeParamConstraint constr) -> { WurstType t = constr.getConstraint().attrTyp(); + if (t instanceof WurstTypeInterface) { WurstTypeInterface wti = (WurstTypeInterface) t; + + // adjust last type parameter to be the type from the contraint + // e.g. requires implementation Foo + VariableBinding binding = wti.getTypeArgBinding(); + TypeParamDef lastParam = Utils.getLast(wti.getDef().getTypeParameters()); + VariableBinding newBinding = binding.set(lastParam, new WurstTypeBoundTypeParam(lastParam, this, node)); + wti = (WurstTypeInterface) wti.setTypeArgs(newBinding); + + return wti.getMemberMethods(node) .map(f -> f.withReceiverType(this).withTypeParamConstraint(constr)); } else { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 6d27bb7b3..508c36f9c 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1316,21 +1316,17 @@ public void simpleTypeClass() { "package test", "native testSuccess()", "native testFail(string s)", - "interface ToIndex", - " function toIndex() returns int", - "class A implements ToIndex", - " override function toIndex() returns int", - " return 42", - "function foo(T x)", - " if x.toIndex() == 42", + "interface ToIndex", + " function toIndex(T x) returns int", // 5 + "class A", + "instance ToIndex", + " function toIndex(A x) returns int", + " return 42", + "function foo(Q x)", // 10 + " if Q.toIndex(x) == 42", " testSuccess()", "init", " let a = new A", -// " if a.toIndex() != 42", -// " testFail(\"a\")", -// " ToIndex i = a", -// " if i.toIndex() != 42", -// " testFail(\"b\")", " foo(a)" ); } @@ -1340,13 +1336,14 @@ public void forwardTypeClass() { testAssertOkLines(true, "package test", "native testSuccess()", - "interface ToIndex", - " function toIndex() returns int", - "class A implements ToIndex", - " override function toIndex() returns int", - " return 42", + "interface ToIndex", + " function toIndex(T elem) returns int", + "class A", + "instance ToIndex", + " function toIndex(A x) returns int", + " return 42", "function foo(T x)", - " if x.toIndex() == 42", + " if T.toIndex(x) == 42", " testSuccess()", "function bar(T x)", " foo(x)", From 0aa3a368ab65483dde4d7eba6a3df221153ba9df Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 29 Jan 2020 01:00:27 +0100 Subject: [PATCH 15/50] type class translation --- .../parserspec/wurstscript.parseq | 11 ++ .../attributes/AttrImplicitParameter.java | 6 + .../attributes/AttrIsClassMember.java | 2 +- .../wurstscript/attributes/AttrNearest.java | 10 ++ .../AttrPossibleFunctionSignatures.java | 8 +- .../attributes/AttrTypeExprType.java | 22 +++ .../attributes/AttrVarDefType.java | 7 +- .../wurstscript/attributes/ReadVariables.java | 6 + .../attributes/names/NameLinks.java | 18 +++ .../attributes/names/OtherLink.java | 5 + .../attributes/names/TypeNameLinks.java | 8 ++ .../imtranslation/EliminateGenerics.java | 2 +- .../imtranslation/ExprTranslation.java | 10 +- .../translation/imtranslation/ImPrinter.java | 2 +- .../imtranslation/ImTranslator.java | 132 +++++------------- .../imtranslation/TypeClassTranslator.java | 40 +++++- .../wurstscript/types/TypeClassInstance.java | 8 +- .../types/WurstTypeClassOrInterface.java | 6 +- .../wurstscript/types/WurstTypeTypeParam.java | 21 ++- .../validation/WurstValidator.java | 4 +- .../tests/GenericsWithTypeclassesTests.java | 2 +- 21 files changed, 184 insertions(+), 146 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index bcc894442..0a7c87c40 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -288,6 +288,7 @@ WScope = | WBlock | WEntities | ExprClosure + | InstanceDecl PackageOrGlobal = WPackage | CompilationUnit @@ -356,6 +357,8 @@ StructureDef = ClassOrModuleOrModuleInstanciation | ClassOrInterface ClassOrInterface = ClassDef | InterfaceDef +ClassOrInterfaceOrInstance = ClassOrInterface | InstanceDecl + ClassOrModuleInstanciation = ClassDef | ModuleInstanciation ClassOrModuleOrModuleInstanciation = ClassOrModule | ClassOrModuleInstanciation @@ -516,6 +519,10 @@ TypeParamConstraint.parentTypeParam() returns TypeParamDef implemented by de.peeeq.wurstscript.attributes.AttrNearest.parentTypeParam +TypeParamConstraint.attrConstraintTyp() + returns de.peeeq.wurstscript.types.WurstType + implemented by de.peeeq.wurstscript.attributes.AttrTypeExprType.constraintType + WScope.attrNextScope "returns the scope surrounding this scope" returns @Nullable WScope implemented by de.peeeq.wurstscript.attributes.AttrNearest.nextScope @@ -537,6 +544,10 @@ Element.attrNearestClassOrInterface() returns @Nullable ClassOrInterface implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrInterface +Element.attrNearestClassOrInterfaceOrInstance() + returns @Nullable ClassOrInterfaceOrInstance + implemented by de.peeeq.wurstscript.attributes.AttrNearest.attrNearestClassOrInterfaceOrInstance + Element.attrNearestClassOrModule() returns @Nullable ClassOrModule implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrModule diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java index 9402be839..839e6907e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeTypeParam; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -78,6 +79,11 @@ static OptExpr getFunctionCallImplicitParameter(FunctionCall e, FuncLink calledF if (res != null) { return res; } + if (hasReceiver.getLeft().attrTyp() instanceof WurstTypeTypeParam) { + // for type parameters we have no implicit parameter here, + // (one is added later in the translation when constraints are resolved) + return Ast.NoExpr(); + } } if (calledFunc == null) { return Ast.NoExpr(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java index 5359a9a4c..33df1db28 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java @@ -26,7 +26,7 @@ public static boolean calculate(FuncDef f) { if (f.attrNearestNamedScope() instanceof StructureDef) { return !f.attrIsStatic(); } - return false; + return f.attrNearestClassOrInterfaceOrInstance() instanceof InstanceDecl; } public static boolean calculate(ExtensionFuncDef f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java index 0e35fc0c1..9c5c46481 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java @@ -162,4 +162,14 @@ public static ClassOrInterface nearestClassOrInterface(Element node) { public static TypeParamDef parentTypeParam(TypeParamConstraint tp) { return (TypeParamDef) tp.getParent().getParent(); } + + public static ClassOrInterfaceOrInstance attrNearestClassOrInterfaceOrInstance(Element e) { + while (e != null) { + if (e instanceof ClassOrInterfaceOrInstance) { + return (ClassOrInterfaceOrInstance) e; + } + e = e.getParent(); + } + return null; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 894e11d76..957066f4c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -10,7 +10,6 @@ import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import io.vavr.control.Option; -import org.eclipse.jdt.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; @@ -97,15 +96,12 @@ private static Pair> findTypeClasses(Funct List constraints = new ArrayList<>(); if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { - WurstType ct = c.getConstraint().attrTyp(); + WurstType ct = c.attrConstraintTyp(); if (ct instanceof WurstTypeInterface) { WurstTypeInterface wti = (WurstTypeInterface) ct; + constraints.add(wti); - TypeParamDef lastTypeParam = Utils.getLast(wti.getDef().getTypeParameters()); - wti = (WurstTypeInterface) wti.setTypeArgs(wti.getTypeArgBinding() - .set(lastTypeParam, new WurstTypeBoundTypeParam(lastTypeParam, new WurstTypeTypeParam(tp), c))); - constraints.add(wti); } } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java index 5e331a4dd..3c621eb21 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java @@ -97,4 +97,26 @@ public static WurstType calculate(TypeExprResolved e) { } + public static WurstType constraintType(TypeParamConstraint c) { + WurstType t = c.getConstraint().attrTyp(); + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + + TypeParamDefs tps = wti.getDef().getTypeParameters(); + if (tps.isEmpty()) { + c.addError("Constraint " + t + " must be an interface type with at least one type parameter.\n" + + "Maybe change interface " + t + " to interface " + t + " ?"); + } else { + TypeParamDef lastTypeParam = Utils.getLast(tps); + TypeParamDef tp = c.parentTypeParam(); + // TODO check that type param is not yet set + wti = (WurstTypeInterface) wti.setTypeArgs(wti.getTypeArgBinding() + .set(lastTypeParam, new WurstTypeBoundTypeParam(lastTypeParam, new WurstTypeTypeParam(tp, false), c))); + } + return wti; + } else { + c.addError("Invalid type constraint " + t + ". Type constraint must be an interface type."); + return WurstTypeUnknown.instance(); + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java index 30ce51b69..0db7a71d2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java @@ -3,7 +3,6 @@ import com.google.common.collect.Lists; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; -import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.types.*; import org.eclipse.jdt.annotation.Nullable; @@ -80,7 +79,7 @@ public static WurstType getParameterTypeFromClosureType(WShortParameter p, int p public static WurstTypeClass calculate(ClassDef c) { List typeArgs = Lists.newArrayList(); for (TypeParamDef tp : c.getTypeParameters()) { - WurstTypeTypeParam typParam = new WurstTypeTypeParam(tp); + WurstTypeTypeParam typParam = new WurstTypeTypeParam(tp, false); typeArgs.add(new WurstTypeBoundTypeParam(tp, typParam, tp)); } return new WurstTypeClass(c, typeArgs, true); @@ -155,13 +154,13 @@ public static WurstType calculate(FunctionDefinition f) { } public static WurstType calculate(TypeParamDef t) { - return new WurstTypeTypeParam(t); + return new WurstTypeTypeParam(t, true); } public static WurstTypeInterface calculate(InterfaceDef i) { List typeArgs = Lists.newArrayList(); for (TypeParamDef tp : i.getTypeParameters()) { - WurstTypeTypeParam tpType = new WurstTypeTypeParam(tp); + WurstTypeTypeParam tpType = new WurstTypeTypeParam(tp, false); typeArgs.add(new WurstTypeBoundTypeParam(tp, tpType, tp)); } return new WurstTypeInterface(i, typeArgs, true); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java index aeec4d6e2..08b6264c5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java @@ -158,6 +158,10 @@ public static ImmutableList calculate(InterfaceDef e) { return generic(e); } + public static ImmutableList calculate(InstanceDecl e) { + return generic(e); + } + public static ImmutableList calculate(ModuleDef e) { return generic(e); } @@ -201,4 +205,6 @@ public static ImmutableList calculate(ExprEmpty exprEmpty) { public static ImmutableList calculate(ExprIfElse e) { return generic(e); } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java index 3360630ba..4467986dd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java @@ -7,7 +7,9 @@ import com.google.common.collect.Multimap; import de.peeeq.wurstscript.WLogger; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeClass; +import de.peeeq.wurstscript.types.WurstTypeInt; import de.peeeq.wurstscript.types.WurstTypeInterface; import de.peeeq.wurstscript.utils.Utils; import de.peeeq.wurstscript.validation.WurstValidator; @@ -21,6 +23,8 @@ public class NameLinks { + + static private class OverrideCheckResult { // does this override some other function boolean doesOverride = false; @@ -102,6 +106,20 @@ public static ImmutableMultimap calculate(InterfaceDef i) { return ImmutableMultimap.copyOf(result); } + + public static ImmutableMultimap calculate(InstanceDecl i) { + Multimap result = HashMultimap.create(); + addDefinedNames(result, i, i.getMethods()); + Map> overrideCheckResults = initOverrideMap(result); + WurstType implementedI = i.getImplementedInterface().attrTyp(); + if (implementedI instanceof WurstTypeInterface) { + addNamesFromExtendedInterfaces(result, (WurstTypeInterface) implementedI, overrideCheckResults); + } + reportOverrideErrors(overrideCheckResults); + addTypeParametersIfAny(result::put, i); + return ImmutableMultimap.copyOf(result); + } + private static void addNamesFromExtendedInterfaces(Multimap result, WurstTypeInterface iType, Map> overrideCheckResults) { for (WurstTypeInterface superI : iType.extendedInterfaces()) { addNewNameLinks(result, overrideCheckResults, superI.nameLinks(), false); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java index 85a3cd482..4814abf1d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java @@ -119,6 +119,11 @@ public CompilationUnit attrCompilationUnit() { return null; } + @Override + public @Nullable ClassOrInterfaceOrInstance attrNearestClassOrInterfaceOrInstance() { + return null; + } + @Override public @Nullable ClassOrModule attrNearestClassOrModule() { return null; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java index f734ff836..1f192f749 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java @@ -45,6 +45,12 @@ public static ImmutableMultimap calculate(InterfaceDef i) { return result.build(); } + public static ImmutableMultimap calculate(InstanceDecl i) { + ImmutableMultimap.Builder result = ImmutableSetMultimap.builder(); + addTypeParametersIfAny(result, i); + return result.build(); + } + public static ImmutableMultimap calculate(NativeFunc nativeFunc) { return ImmutableMultimap.of(); } @@ -116,4 +122,6 @@ private static void addJassTypes(ImmutableMultimap.Builder res public static ImmutableMultimap calculate(ExprClosure exprClosure) { return ImmutableMultimap.of(); } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index 63370b347..f94047dfc 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -400,7 +400,7 @@ public void visit(ImVar v) { super.visit(v); if (isGenericType(v.getType())) { if (containsTypeVariable(v.getType())) { - throw new CompileError(v, "Var should not have type variables."); + throw new CompileError(v, "Var " + v + " should not have type variables."); } genericsUses.add(new GenericVar(v)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 8c58905df..65c5de9aa 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -7,6 +7,7 @@ import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.names.OtherLink; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.jassIm.ImClass; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.jassIm.ImExprs; @@ -509,10 +510,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; if (typeParamDispatchOn != null) { - ImMethod method = t.getTypeClassMethodFor((FuncDef) calledFunc); - if (receiver != null) { - imArgs.add(0, receiver); - } + System.out.println("TypeParam dispatch for " + PrettyPrinter.print(e) + "\n" + typeParamDispatchOn); + ImMethod method = t.getMethodFor((FuncDef) calledFunc); +// if (receiver != null) { +// imArgs.add(0, receiver); +// } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn)); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index 101459534..6e51d5174 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -574,7 +574,7 @@ public static void print(ImAnyType at, Appendable sb, int indent) { } public static void print(ImTypeClassDictValue e, Appendable sb, int indent) { - append(sb, "TypeClassDict"); + append(sb, "Dict#"); e.getClassType().print(sb, indent); printArgumentList(sb, indent, e.getArguments()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 5366169fa..84e92d8f4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -536,7 +536,7 @@ public ImExpr getDefaultValueForJassType(ImType type) { @Override public ImFunction initFor(StructureDef classDef) { - ImVars params = ImVars(ImVar(classDef, selfType(classDef), "this", Collections.emptyList())); + ImVars params = ImVars(ImVar(classDef, selfTypeS(classDef), "this", Collections.emptyList())); ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); addFunction(f, classDef); @@ -549,7 +549,7 @@ public ImFunction initFor(StructureDef classDef) { @Override public ImMethod initFor(StructureDef classDef) { ImFunction impl = destroyFunc.getFor(classDef); - ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(), + ImMethod m = JassIm.ImMethod(classDef, selfTypeS(classDef), "destroy" + classDef.getName(), impl, Lists.newArrayList(), false); return m; } @@ -600,11 +600,16 @@ public ImType case_ExtensionFuncDef(ExtensionFuncDef f) { } private ImClassType selfType(FuncDef f) { - return selfType(f.attrNearestClassOrInterface()); + return selfType(f.attrNearestClassOrInterfaceOrInstance()); } - public ImClassType selfType(StructureDef classDef) { - ImClass imClass = getClassFor(classDef.attrNearestClassOrInterface()); + public ImClassType selfType(ClassOrInterfaceOrInstance classDef) { + ImClass imClass = getClassFor(classDef.attrNearestClassOrInterfaceOrInstance()); + return selfType(imClass); + } + + public ImClassType selfTypeS(StructureDef classDef) { + ImClass imClass = getClassFor(classDef.attrNearestClassOrInterfaceOrInstance()); return selfType(imClass); } @@ -652,7 +657,7 @@ public ImTypeVar initFor(TypeParamDef a) { private final GetAForB constraint = new GetAForB() { @Override public ImVar initFor(TypeParamConstraint a) { - ImType t = a.getConstraint().attrTyp().imTranslateType(ImTranslator.this); + ImType t = a.attrConstraintTyp().imTranslateType(ImTranslator.this); return ImVar(a, t, Utils.printElement(a), emptyList()); } @@ -723,60 +728,6 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { return f; } - private final Map typeClassFuncMap = new LinkedHashMap<>(); - - public ImFunction getTypeClassFuncFor(FuncDef funcDef) { - if (typeClassFuncMap.containsKey(funcDef)) { - return typeClassFuncMap.get(funcDef); - } - String name = getNameFor(funcDef); - List flags = flags(); - if (funcDef.attrIsCompiletime()) { - throw new CompileError(funcDef.getSource(), "Compiletime flag not supported here."); - } - - // Check if last parameter is vararg - WParameters params = ((AstElementWithParameters) funcDef).getParameters(); - if (params.size() >= 1 && params.get(params.size() - 1).attrIsVararg()) { - flags.add(IS_VARARG); - } - - - for (Modifier m : funcDef.getModifiers()) { - if (m instanceof Annotation) { - Annotation annotation = (Annotation) m; - flags.add(new FunctionFlagAnnotation(annotation.getAnnotationType())); - } - } - - ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); - ImVars parameters = ImVars(); - // add parameter for type class struct - ImClass typeClassStruct = getTypeClassStructFor(funcDef.attrNearestClassOrInterface()); - parameters.add(JassIm.ImVar(funcDef, selfType(typeClassStruct), "thisDict", emptyList())); - - if (!funcDef.attrIsStatic()) { - ImVar thisVar = getThisVar(funcDef).copy(); - parameters.add(thisVar); - } - for (WParameter p : params) { - parameters.add(getVarFor(p).copy()); - } - - ImFunction f = ImFunction( - funcDef, - name, - typeVars, - parameters, - funcDef.attrReturnTyp().imTranslateType(this), - ImVars(), - ImStmts(), - flags); - - - typeClassFuncMap.put(funcDef, f); - return f; - } private ImClass getClassForFunc(TranslatedToImFunction funcDef) { if (funcDef == null) { @@ -791,7 +742,11 @@ public ImClass case_TupleDef(TupleDef tupleDef) { @Override public ImClass case_FuncDef(FuncDef funcDef) { if (funcDef.attrIsDynamicClassMember()) { - return getClassFor(funcDef.attrNearestClassOrInterface()); + return getClassFor(funcDef.attrNearestClassOrInterfaceOrInstance()); + } + @Nullable WScope nearestScope = funcDef.getParent().attrNearestScope(); + if (nearestScope instanceof InstanceDecl) { + return getClassFor((InstanceDecl) nearestScope); } return null; } @@ -1350,37 +1305,20 @@ public boolean isLuaTarget() { } - private Map typeClassMethodForFuncDef = Maps.newLinkedHashMap(); - - public ImMethod getTypeClassMethodFor(FuncDef f) { - ImMethod m = typeClassMethodForFuncDef.get(f); - if (m == null) { - ImFunction imFunc = getTypeClassFuncFor(f); - ImClass typeClassStruct = getTypeClassStructFor(f.attrNearestClassOrInterface()); - m = JassIm.ImMethod(f, selfType(typeClassStruct), elementNameWithPath(f), imFunc, Lists.newArrayList(), false); - typeClassMethodForFuncDef.put(f, m); - } - return m; - } - private Map typeClassParamFor = new LinkedHashMap<>(); public ImVar getTypeClassParamFor(TypeParamConstraint tc) { ImVar v = typeClassParamFor.get(tc); if (v == null) { TypeParamDef tp = (TypeParamDef) tc.getParent().getParent(); - WurstTypeInterface wti = (WurstTypeInterface) tc.getConstraint().attrTyp(); - ImClassType t = wti.imTranslateToTypeClass(this); - v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + "_" + tc.getConstraint().attrTyp(), Collections.singletonList(VarFlag.SPECIALIZE)); + WurstTypeInterface wti = (WurstTypeInterface) tc.attrConstraintTyp(); + ImClassType t = wti.imTranslateType(this); + v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + "_" + tc.attrConstraintTyp(), Collections.singletonList(VarFlag.SPECIALIZE)); typeClassParamFor.put(tc, v); } return v; } - public ImClass getInstanceClassFor(InstanceDecl decl) { - throw new RuntimeException("TODOO"); - } - interface VarsForTupleResult { @@ -1665,9 +1603,9 @@ public ImClass getClassForClosure(ExprClosure s) { } - private Map classForStructureDef = Maps.newLinkedHashMap(); + private Map classForStructureDef = Maps.newLinkedHashMap(); - public ImClass getClassFor(ClassOrInterface s) { + public ImClass getClassFor(ClassOrInterfaceOrInstance s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { ImTypeVars typeVariables = JassIm.ImTypeVars(); @@ -1677,23 +1615,23 @@ public ImClass getClassFor(ClassOrInterface s) { typeVariables.add(tv); } } - return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); - }); - } + String name = s.match(new ClassOrInterfaceOrInstance.Matcher() { + @Override + public String case_InstanceDecl(InstanceDecl i) { + return "TypeClassDict_" + i.getImplementedInterface().attrTyp(); + } - private Map typeClassStructFor = Maps.newLinkedHashMap(); + @Override + public String case_ClassDef(ClassDef c) { + return c.getName(); + } - public ImClass getTypeClassStructFor(ClassOrInterface s) { - Preconditions.checkNotNull(s); - return typeClassStructFor.computeIfAbsent(s, s1 -> { - ImTypeVars typeVariables = JassIm.ImTypeVars(); - for (TypeParamDef tp : s.getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - ImTypeVar tv = getTypeVar(tp); - typeVariables.add(tv.copy()); + @Override + public String case_InterfaceDef(InterfaceDef i) { + return i.getName(); } - } - return JassIm.ImClass(s1, "TypeClassDict_" + s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + }); + return JassIm.ImClass(s1, name, typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java index 498106646..63415152b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java @@ -1,17 +1,47 @@ package de.peeeq.wurstscript.translation.imtranslation; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeClassOrInterface; +import de.peeeq.wurstscript.validation.WurstValidator; -import javax.sound.midi.Receiver; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; public class TypeClassTranslator { - public static void translateTypeClass(InstanceDecl instanceDecl, ImTranslator translator) { - throw new RuntimeException("TODO"); + public static void translateTypeClass(InstanceDecl instance, ImTranslator tr) { + ImClass c = tr.getClassFor(instance); + tr.getImProg().getClasses().add(c); + + WurstType implementedInterface = instance.getImplementedInterface().attrTyp(); + c.getSuperClasses().add((ImClassType) implementedInterface.imTranslateType(tr)); + + + for (FuncDef method : instance.getMethods()) { + translateMethod(instance, c, method, implementedInterface, tr); + } + + } + + private static void translateMethod(InstanceDecl instance, ImClass c, FuncDef funcDef, WurstType implementedInterface, ImTranslator tr) { + ImFunction func = tr.getFuncFor(funcDef); + func.getBody().addAll(tr.translateStatements(func, funcDef.getBody())); + + ImMethod m = tr.getMethodFor(funcDef); + c.getMethods().add(m); + + m.setImplementation(func); + + List superMethods = new ArrayList<>(); + implementedInterface.addMemberMethods(funcDef, funcDef.getName(), superMethods); + for (FuncLink superMethod : superMethods) { + if (WurstValidator.canOverride(funcDef.createFuncLink(instance), superMethod, false)) { + OverrideUtils.addOverride(tr, (FuncDef) superMethod.getDef(), c, m, funcDef, implementedInterface.getTypeArgBinding()); + } + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index bc045ccce..e70649590 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -8,8 +8,6 @@ import java.util.List; -import static de.peeeq.wurstscript.utils.Utils.emptyList; - public abstract class TypeClassInstance { private final WurstTypeClassOrInterface constraint; @@ -21,7 +19,7 @@ public static TypeClassInstance fromInstance(InstanceDecl decl, List return new TypeClassInstance(constraint) { @Override public ImExpr translate(Element trace, ImTranslator tr) { - ImClass c = tr.getInstanceClassFor(decl); + ImClass c = tr.getClassFor(decl); ImTypeArguments imTypeArgs = JassIm.ImTypeArguments(); for (WurstType ta : typeArgs) { imTypeArgs.add(JassIm.ImTypeArgument(ta.imTranslateType(tr))); @@ -37,10 +35,6 @@ public ImExpr translate(Element trace, ImTranslator tr) { }; } - protected ImClassType translateConstraintType(ImTranslator tr) { - return constraint.imTranslateToTypeClass(tr); - } - public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeInterface constraint) { return new TypeClassInstance(constraint) { @Override diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java index 079f28d15..ef415a703 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java @@ -133,7 +133,7 @@ VariableBinding matchAgainstSupertypeIntern(WurstType obj, @Nullable Element loc } @Override - public final ImType imTranslateType(ImTranslator tr) { + public final ImClassType imTranslateType(ImTranslator tr) { ImTypeArguments typeArgs = translateTypeArguments(tr); return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); } @@ -149,8 +149,4 @@ private ImTypeArguments translateTypeArguments(ImTranslator tr) { return typeArgs; } - public ImClassType imTranslateToTypeClass(ImTranslator tr) { - ImTypeArguments typeArgs = translateTypeArguments(tr); - return JassIm.ImClassType(tr.getTypeClassStructFor(getDef()), typeArgs); - } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index f984524b1..f9afa4cdd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -16,10 +16,12 @@ public class WurstTypeTypeParam extends WurstType { - private TypeParamDef def; + private final boolean isStaticRef; + private final TypeParamDef def; - public WurstTypeTypeParam(TypeParamDef t) { + public WurstTypeTypeParam(TypeParamDef t, boolean isStaticRef) { this.def = t; + this.isStaticRef = isStaticRef; } @Override @@ -115,19 +117,11 @@ public Stream getMemberMethods(Element node) { TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); return constraints.stream() .flatMap((TypeParamConstraint constr) -> { - WurstType t = constr.getConstraint().attrTyp(); + WurstType t = constr.attrConstraintTyp(); if (t instanceof WurstTypeInterface) { WurstTypeInterface wti = (WurstTypeInterface) t; - // adjust last type parameter to be the type from the contraint - // e.g. requires implementation Foo - VariableBinding binding = wti.getTypeArgBinding(); - TypeParamDef lastParam = Utils.getLast(wti.getDef().getTypeParameters()); - VariableBinding newBinding = binding.set(lastParam, new WurstTypeBoundTypeParam(lastParam, this, node)); - wti = (WurstTypeInterface) wti.setTypeArgs(newBinding); - - return wti.getMemberMethods(node) .map(f -> f.withReceiverType(this).withTypeParamConstraint(constr)); } else { @@ -151,4 +145,9 @@ public Stream getTypeConstraints() { return Stream.empty(); } } + + @Override + public boolean isStaticRef() { + return isStaticRef; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index baf02342e..fa41a3def 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -357,9 +357,7 @@ private void checkTypeParamDef(TypeParamDef e) { if (constraints instanceof TypeParamConstraintList) { TypeParamConstraintList typeExprs = (TypeParamConstraintList) constraints; for (TypeParamConstraint te : typeExprs) { - if (!(te.getConstraint().attrTyp() instanceof WurstTypeInterface)) { - te.addError("Invalid type constraint " + te.getConstraint().attrTyp() + ". Type constraint must be an interface type."); - } + te.attrConstraintTyp(); } } } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 508c36f9c..194b88907 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1316,7 +1316,7 @@ public void simpleTypeClass() { "package test", "native testSuccess()", "native testFail(string s)", - "interface ToIndex", + "interface ToIndex", " function toIndex(T x) returns int", // 5 "class A", "instance ToIndex", From c03c8615740612bab9e7b153fd8017aff0875e41 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 29 Jan 2020 23:42:26 +0100 Subject: [PATCH 16/50] some real progress on type classes --- .../peeeq/wurstio/WurstCompilerJassImpl.java | 5 +- .../AttrPossibleFunctionSignatures.java | 9 +- .../imtranslation/EliminateTypeClasses.java | 243 ++++++++++++++++++ .../imtranslation/ExprTranslation.java | 1 - .../wurstscript/types/TypeClassInstance.java | 21 +- .../wurstscript/types/WurstTypeTypeParam.java | 11 +- .../tests/GenericsWithTypeclassesTests.java | 4 +- 7 files changed, 265 insertions(+), 29 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 3471f5a29..5b0e55b4b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -410,7 +410,10 @@ public JassProg transformProgToJass() { beginPhase(2, "Eliminate generics"); new EliminateGenerics(imTranslator2, imProg2).transform(); printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); - + if (!runArgs.isLua()) { + EliminateTypeClasses.transform(imTranslator2); + } + printDebugImProg("./test-output/im " + stage++ + "_typeClassesEliminated.im"); // eliminate classes diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 957066f4c..5ad526da3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -127,17 +127,15 @@ private static Pair> findTypeClasses(Funct private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); - System.out.println("mapping = " + mapping); - System.out.println("constraint1 = " + constraint1); - System.out.println("constraint = " + constraint); // option 1: the matched type is a type param that also has the right constraint: if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); - Optional matchingConstraint = wtp.getTypeConstraints().filter(c -> c.isSubtypeOf(constraint, fc)).findFirst(); + Optional matchingConstraint = wtp.getTypeConstraints().stream() + .filter(c -> c.attrConstraintTyp().isSubtypeOf(constraint, fc)).findFirst(); if (matchingConstraint.isPresent()) { TypeClassInstance instance = TypeClassInstance.fromTypeParam( - fc, wtp, matchingConstraint.get()); + fc, matchingConstraint.get()); return mapping.set(tp, matchedType.withTypeClassInstance(instance)); } } @@ -151,7 +149,6 @@ private static VariableBinding findTypeClass(StmtCall fc, List err .map(e -> (InstanceDecl) e) .flatMap(instance -> { WurstType instanceType = instance.getImplementedInterface().attrTyp(); - System.out.println("checking instance " + instanceType + " // " + constraint); VariableBinding match = constraint.matchAgainstSupertype(instanceType, fc, VariableBinding.emptyMapping(), VariablePosition.RIGHT); if (match == null) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java new file mode 100644 index 000000000..dc10755c5 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -0,0 +1,243 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import com.google.common.collect.*; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.jassIm.*; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Eliminates ImTypeClassDictValue from the program. + *

+ * The algorithm works like this: + * Initialize a list of all ImTypeClassDictValue with no dynamic values. + * Maintain this list invariant and while the list is not empty: + *

+ * Look at where the dict is used: + * When used as a function parameter: specialize the function + * When used as a method parameter: specialize the method (including submethods and implementation) + * When used as an alloc parameter: specialize the whole class + * When used as receiver in a method call: Specialize method (if no submethods, replace by function call) + * No other uses should be possible --> compilation error. + *

+ * Keep Mappings from Function (etc.) and type to specialized version such that every specialization + * is only done once. + *

+ * Replace the call to the generic function with a call to the specialized one. + *

+ * When specializing a parameter to a type: + * - Replace all uses of the parameter with a concrete dict + * - Add the concrete dicts to the work list + */ +public class EliminateTypeClasses { + private final ImTranslator tr; + private final ArrayDeque workList = new ArrayDeque<>(); + private final Table specializedFunctions = HashBasedTable.create(); + private final Map outOfClassFuncs = new LinkedHashMap<>(); + + public EliminateTypeClasses(ImTranslator tr) { + this.tr = tr; + } + + public static void transform(ImTranslator tr) { + new EliminateTypeClasses(tr).run(); + + + } + + private void run() { + ImProg prog = tr.getImProg(); + + prog.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImTypeClassDictValue dv) { + // no super visit + if (!dynamicArgsStream(dv).findAny().isPresent()) { + workList.add(dv); + } + } + }); + + while (!workList.isEmpty()) { + ImTypeClassDictValue dictV = workList.removeFirst(); + doSpecialize(dictV); + + } + } + + private void doSpecialize(ImTypeClassDictValue dictV) { + Element parent = dictV.getParent(); + if (parent instanceof ImExprs) { + Element parent2 = parent.getParent(); + if (parent2 instanceof ImFunctionCall) { + ImFunctionCall fc = (ImFunctionCall) parent2; + int index = fc.getArguments().indexOf(dictV); + ImFunction specializedFunc = getSpecializeFunc(fc.getFunc(), key(dictV, index)); + fc.getArguments().remove(index); + fc.setFunc(specializedFunc); + return; + } + } else if (parent instanceof ImMethodCall) { + ImMethodCall mc = (ImMethodCall) parent; + assert mc.getReceiver() == dictV; + + ImMethod m = findMostConcreteMethod(mc.getMethod(), dictV.getClassType()); + + // allow to move + mc.getTypeArguments().setParent(null); + mc.getArguments().setParent(null); + + if (!m.getSubMethods().isEmpty()) { + throw new CompileError(dictV.getTrace(), + "Could not specialize method call: " + mc + " to " + m); + } + mc.replaceBy(JassIm.ImFunctionCall( + mc.getTrace(), + getOutOfClassFunc(m.getImplementation()), + mc.getTypeArguments(), + mc.getArguments(), + false, + CallType.NORMAL + )); + + return; + } + + + throw new CompileError(dictV.getTrace(), + "Unhandled parent for dict: " + parent + " // " + parent.getClass()); + } + + private ImFunction getOutOfClassFunc(ImFunction impl) { + ImFunction res = outOfClassFuncs.get(impl); + if (res != null) { + return res; + } + ImFunction copy = impl.copyWithRefs(); + // remove implicit parameter + copy.getParameters().remove(0); + outOfClassFuncs.put(impl, copy); + return copy; + } + + private ImMethod findMostConcreteMethod(ImMethod method, ImClassType classType) { + ImMethod result = method; + for (ImMethod sub : method.getSubMethods()) { + if (isSubClass(classType, sub.getMethodClass()) && isSubClass(sub.getMethodClass(), result.getMethodClass())) { + result = sub; + } + } + return result; + } + + private boolean isSubClass(ImClassType sub, ImClassType sup) { + return isSubClass(sub.getClassDef(), sup.getClassDef()); + } + + private boolean isSubClass(ImClass sub, ImClass sup) { + return sub == sup + || sub.getSuperClasses().stream() + .anyMatch(sc -> isSubClass(sc.getClassDef(), sup)); + } + + private ImFunction getSpecializeFunc(ImFunction func, TypeClassInstanceKey key) { + ImFunction res = specializedFunctions.get(func, key); + if (res != null) { + return res; + } + res = specializeFunc(func, key); + specializedFunctions.put(func, key, res); + return res; + } + + private ImFunction specializeFunc(ImFunction func, TypeClassInstanceKey key) { + ImFunction copy = func.copyWithRefs(); + copy.setName(func.getName() + "_" + key); + ImVar removedParam = copy.getParameters().remove(key.index); + Set uses = new HashSet<>(); + copy.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImVarAccess va) { + super.visit(va); + if (va.getVar() == removedParam) { + uses.add(va); + } + } + }); + + for (ImVarAccess use : uses) { + ImTypeClassDictValue newDict = key.makeDictValue(use.attrTrace()); + use.replaceBy(newDict); + workList.add(newDict); + } + tr.getImProg().getFunctions().add(copy); + return copy; + } + + + private static Stream dynamicArgsStream(ImTypeClassDictValue dv) { + return dv.getArguments().stream().flatMap(EliminateTypeClasses::dynamicArgsStreamE); + } + + private static Stream dynamicArgsStreamE(ImExpr a) { + if (a instanceof ImVarAccess) { + return Stream.of(((ImVarAccess) a).getVar()); + } else if (a instanceof ImTypeClassDictValue) { + return dynamicArgsStream((ImTypeClassDictValue) a); + } + throw new RuntimeException("invalid TypeClassDictValue " + a + " // " + a.getClass()); + } + + private static List dynamicArgs(ImTypeClassDictValue dv) { + return dynamicArgsStream(dv).collect(Collectors.toList()); + } + + private TypeClassInstanceKey key(ImTypeClassDictValue dictV, int index) { + return new TypeClassInstanceKey(dictV.getClassType().getClassDef(), index, + dictV.getArguments().stream() + .map(v -> key((ImTypeClassDictValue) v, index)) + .collect(Collectors.toList())); + } + + static class TypeClassInstanceKey { + private final ImClass base; + private int index; + private final List args; + + TypeClassInstanceKey(ImClass base, int index, List args) { + this.base = base; + this.index = index; + this.args = args; + } + + @Override + public String toString() { + return "#" + base + "@" + index + args; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TypeClassInstanceKey that = (TypeClassInstanceKey) o; + return index == that.index && + base.equals(that.base) && + args.equals(that.args); + } + + @Override + public int hashCode() { + return Objects.hash(base, index, args); + } + + public ImTypeClassDictValue makeDictValue(de.peeeq.wurstscript.ast.Element trace) { + ImExprs args2 = args.stream() + .map(x -> x.makeDictValue(trace)) + .collect(Collectors.toCollection(JassIm::ImExprs)); + return JassIm.ImTypeClassDictValue(trace, JassIm.ImClassType(base, JassIm.ImTypeArguments()), args2); + } + } + +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 65c5de9aa..98435c7b0 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -510,7 +510,6 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; if (typeParamDispatchOn != null) { - System.out.println("TypeParam dispatch for " + PrettyPrinter.print(e) + "\n" + typeParamDispatchOn); ImMethod method = t.getMethodFor((FuncDef) calledFunc); // if (receiver != null) { // imArgs.add(0, receiver); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index e70649590..9dc5e2a27 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -3,20 +3,17 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.InstanceDecl; +import de.peeeq.wurstscript.ast.StmtCall; +import de.peeeq.wurstscript.ast.TypeParamConstraint; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import java.util.List; public abstract class TypeClassInstance { - private final WurstTypeClassOrInterface constraint; - - public TypeClassInstance(WurstTypeClassOrInterface constraint) { - this.constraint = constraint; - } public static TypeClassInstance fromInstance(InstanceDecl decl, List typeArgs, List dependencies, WurstTypeInterface constraint) { - return new TypeClassInstance(constraint) { + return new TypeClassInstance() { @Override public ImExpr translate(Element trace, ImTranslator tr) { ImClass c = tr.getClassFor(decl); @@ -35,18 +32,18 @@ public ImExpr translate(Element trace, ImTranslator tr) { }; } - public static TypeClassInstance fromTypeParam(Element trace, WurstTypeTypeParam wtp, WurstTypeInterface constraint) { - return new TypeClassInstance(constraint) { + public static TypeClassInstance fromTypeParam(Element trace, TypeParamConstraint constraint) { + return new TypeClassInstance() { @Override public ImExpr translate(Element trace, ImTranslator tr) { - throw new RuntimeException("TODO"); + ImVar param = tr.getTypeClassParamFor(constraint); + // TODO if it is a class field do something different + return JassIm.ImVarAccess(param); } }; } - public WurstTypeClassOrInterface getConstraintType() { - return constraint; - } + public abstract ImExpr translate(Element trace, ImTranslator tr); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index f9afa4cdd..a8a3ea97c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -10,6 +10,7 @@ import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -133,16 +134,12 @@ public Stream getMemberMethods(Element node) { } } - public Stream getTypeConstraints() { + public List getTypeConstraints() { if (def.getTypeParamConstraints() instanceof TypeParamConstraintList) { TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); - return constraints.stream() - .map(TypeParamConstraint::getConstraint) - .map(TypeExpr::attrTyp) - .filter(t -> t instanceof WurstTypeInterface) - .map(t -> (WurstTypeInterface) t); + return constraints; } else { - return Stream.empty(); + return Collections.emptyList(); } } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 194b88907..37f9bebde 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1301,8 +1301,8 @@ public void callMustSatisfyConstraint() { testAssertErrorsLines(false, "Type integer does not satisfy constraint T: ToIndex", "package test", "native testSuccess()", - "interface ToIndex", - " function toIndex() returns int", + "interface ToIndex", + " function toIndex(T x) returns int", "function foo(T x)", " testSuccess()", "init", From b162a4f20a637a4034611a3d5bad3f2148585ff7 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 30 Jan 2020 22:15:22 +0100 Subject: [PATCH 17/50] fixed optimization --- .../imtranslation/ClassManagementVars.java | 4 ++-- .../imtranslation/ClassesOptimizer.java | 2 +- .../imtranslation/FuncRefRemover.java | 2 +- .../translation/imtranslation/ImTranslator.java | 16 +++++++++++++++- .../tests/GenericsWithTypeclassesTests.java | 4 ++-- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java index 9479423a6..427daba9e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java @@ -34,10 +34,10 @@ public ClassManagementVars(ImClass repClass, ImTranslator translator) { prog.getGlobals().add(free); freeCount = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_firstFree", Collections.emptyList()); - translator.addGlobalWithInitalizer(freeCount, JassIm.ImIntVal(0)); + translator.addGlobalWithInitializerFront(freeCount, JassIm.ImIntVal(0)); maxIndex = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_maxIndex", Collections.emptyList()); - translator.addGlobalWithInitalizer(maxIndex, JassIm.ImIntVal(0)); + translator.addGlobalWithInitializerFront(maxIndex, JassIm.ImIntVal(0)); typeId = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_typeId", Collections.emptyList()); prog.getGlobals().add(typeId); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java index 70733d336..98ab90c6e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java @@ -100,7 +100,7 @@ public void visit(ImCast c) { // create singleton variable ImClassType classType = JassIm.ImClassType(c, JassIm.ImTypeArguments()); ImVar singletonVar = JassIm.ImVar(c.getTrace(), classType, c.getName() + "_singleton", Collections.emptyList()); - tr.addGlobalWithInitalizer(singletonVar, JassIm.ImAlloc(c.getTrace(), classType)); + tr.addGlobalWithInitializer(singletonVar, JassIm.ImAlloc(c.getTrace(), classType)); // replace all allocations with singleton variable for (ImAlloc cAlloc : cAllocs) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java index be3c6bd51..bf2b689d8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java @@ -51,7 +51,7 @@ public void visit(ImFuncRef imFuncRef) { // create global variable containing a reference to the function: g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), "ref_function_" + func.getName(), Collections.emptyList()); refs.put(func, g); - tr.addGlobalWithInitalizer(g, fr.copy()); + tr.addGlobalWithInitializer(g, fr.copy()); } fr.replaceBy(JassIm.ImVarAccess(g)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 84e92d8f4..9f2df6586 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -511,12 +511,26 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni } } - public void addGlobalWithInitalizer(ImVar g, ImExpr initial) { + public void addGlobalWithInitializer(ImVar g, ImExpr initial) { imProg.getGlobals().add(g); getGlobalInitFunc().getBody().add(ImSet(g.getTrace(), ImVarAccess(g), initial)); imProg.getGlobalInits().put(g, Collections.singletonList(initial.copy())); } + public void addGlobalWithInitializerFront(ImVar g, ImExpr initial) { + imProg.getGlobals().add(g); + imProg.getGlobalInits().put(g, Collections.singletonList(initial.copy())); + ImStmts body = getGlobalInitFunc().getBody(); + ImStatementExpr init; + if (body.isEmpty() || !(body.get(0) instanceof ImStatementExpr)) { + init = JassIm.ImStatementExpr(JassIm.ImStmts(), JassIm.ImNull(ImVoid())); + body.add(0, init); + } else { + init = (ImStatementExpr) body.get(0); + } + init.getStatements().add(ImSet(g.getTrace(), ImVarAccess(g), initial)); + } + public ImExpr getDefaultValueForJassType(ImType type) { if (type instanceof ImSimpleType) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 37f9bebde..049589b6a 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1209,8 +1209,8 @@ public void capturedType() { // #490 "init", " F plus1 = x -> x + 1", // line 9 " F plus2 = twice(plus1)", - " F shout = twice((string s) -> s + \"!\")", // line 11 - " if shout.apply(\"hello\") == \"hello!!\" and plus2.apply(1) == 3", +// " F shout = twice((string s) -> s + \"!\")", // line 11 + " if plus2.apply(1) == 3", " testSuccess()" ); } From c496f81a54a979d9420d2e28f079f34ccdc7978c Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 30 Jan 2020 22:28:50 +0100 Subject: [PATCH 18/50] find subclass instances --- .../AttrPossibleFunctionSignatures.java | 5 +- .../wurstscript/types/TypeClassInstance.java | 12 + .../de/peeeq/wurstscript/utils/Utils.java | 9 +- .../tests/GenericsWithTypeclassesTests.java | 1652 +++++++++-------- 4 files changed, 871 insertions(+), 807 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 5ad526da3..9443b643b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -149,8 +149,7 @@ private static VariableBinding findTypeClass(StmtCall fc, List err .map(e -> (InstanceDecl) e) .flatMap(instance -> { WurstType instanceType = instance.getImplementedInterface().attrTyp(); - VariableBinding match = constraint.matchAgainstSupertype(instanceType, fc, VariableBinding.emptyMapping(), VariablePosition.RIGHT); - + VariableBinding match = instanceType.matchAgainstSupertype(constraint, fc, VariableBinding.emptyMapping(), VariablePosition.LEFT); if (match == null) { return Stream.empty(); } @@ -175,7 +174,7 @@ private static VariableBinding findTypeClass(StmtCall fc, List err } else { if (instances.size() > 1) { errors.add(new CompileError(fc, - "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + + "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + Utils.printSep("\n", instances))); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index 9dc5e2a27..9f57dff3d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -7,6 +7,8 @@ import de.peeeq.wurstscript.ast.TypeParamConstraint; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import de.peeeq.wurstscript.utils.Utils; +import org.eclipse.xtend.lib.annotations.ToString; import java.util.List; @@ -29,6 +31,11 @@ public ImExpr translate(Element trace, ImTranslator tr) { } return JassIm.ImTypeClassDictValue(trace, ct, args); } + + @Override + public String toString() { + return "Instance " + decl.getImplementedInterface().attrTyp() + " " + Utils.printElementSource(decl); + } }; } @@ -40,6 +47,11 @@ public ImExpr translate(Element trace, ImTranslator tr) { // TODO if it is a class field do something different return JassIm.ImVarAccess(param); } + + @Override + public String toString() { + return "Instance from constraint of type parameter " + constraint.parentTypeParam().getName() + " " + Utils.printElementSource(constraint); + } }; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java index ca90968c8..729b39779 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java @@ -471,8 +471,13 @@ public static boolean isEmptyCU(@Nullable CompilationUnit cu) { public static String printElementWithSource(Element e) { WPos src = e.attrSource(); - return printElement(e) + " (" + src.getFile() + ":" - + src.getLine() + ")"; + return printElement(e) + " " + printElementSource(e); + } + + public static String printElementSource(Element e) { + WPos src = e.attrSource(); + return "(" + src.getFile() + ":" + + src.getLine() + ")"; } public static int[] copyArray(int[] ar) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 049589b6a..a88829291 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -9,54 +9,54 @@ public class GenericsWithTypeclassesTests extends WurstScriptTest { @Test public void identity() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " int x = identity(3)", - " string s = identity(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " int x = identity(3)", + " string s = identity(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void identityTrans() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity1(A a) returns A", - " return a", - " function identity2(B a) returns B", - " return identity1(a)", - " function identity3(C a) returns C", - " return identity2(a)", - " init", - " int x = identity3(3)", - " string s = identity3(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity1(A a) returns A", + " return a", + " function identity2(B a) returns B", + " return identity1(a)", + " function identity3(C a) returns C", + " return identity2(a)", + " init", + " int x = identity3(3)", + " string s = identity3(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void identityRec() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(int i, A a) returns A", - " if i > 0", - " return identity(i - 1, a)", - " return a", - " init", - " int x = identity(5, 3)", - " string s = identity(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(int i, A a) returns A", + " if i > 0", + " return identity(i - 1, a)", + " return a", + " init", + " int x = identity(5, 3)", + " string s = identity(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @@ -64,59 +64,59 @@ public void identityRec() { @Ignore // TODO public void identityRecTypeCreation() { testAssertErrorsLines(true, "some error message", - "package test", - " native testSuccess()", - " class C", - " construct(T t)", - " function blub(int i, A a) returns int", - " if i <= 0", - " return 0", - " return 1 + blub>(i-1, new C(a))", - " init", - " int x = blub(5, 3)", - " if x == 5", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class C", + " construct(T t)", + " function blub(int i, A a) returns int", + " if i <= 0", + " return 0", + " return 1 + blub>(i-1, new C(a))", + " init", + " int x = blub(5, 3)", + " if x == 5", + " testSuccess()", + "endpackage" ); } @Test public void identityRecMut() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity1(int i, A a) returns A", - " if i > 0", - " return identity2(i - 1, a)", - " return a", - " function identity2(int i, A a) returns A", - " if i > 0", - " return identity1(i - 1, a)", - " return a", - " init", - " int x = identity1(5, 3)", - " string s = identity1(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity1(int i, A a) returns A", + " if i > 0", + " return identity2(i - 1, a)", + " return a", + " function identity2(int i, A a) returns A", + " if i > 0", + " return identity1(i - 1, a)", + " return a", + " init", + " int x = identity1(5, 3)", + " string s = identity1(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void extensionFunc() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function boolean.choice(A x, A y) returns A", - " if this", - " return x", - " return y", - " init", - " int x = true.choice(5, 3)", - " string s = false.choice(\"a\", \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function boolean.choice(A x, A y) returns A", + " if this", + " return x", + " return y", + " init", + " int x = true.choice(5, 3)", + " string s = false.choice(\"a\", \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", + "endpackage" ); } @@ -124,56 +124,53 @@ public void extensionFunc() { @Test public void extensionFuncReceiver() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function A.choice(boolean b, A y) returns A", - " if b", - " return this", - " return y", - " init", - " int x = (5).choice(true, 3)", - " string s = \"a\".choice(false, \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function A.choice(boolean b, A y) returns A", + " if b", + " return this", + " return y", + " init", + " int x = (5).choice(true, 3)", + " string s = \"a\".choice(false, \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", + "endpackage" ); } @Test public void genericsDispatch() { testAssertOkLines(true, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "init", - " Cell x = new Cell()", - " Cell y = new Cell()", - " x.o = 3", - " y.o = \"a\"", - " if x.o == 3 and y.o == \"a\"", - " testSuccess()" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "init", + " Cell x = new Cell()", + " Cell y = new Cell()", + " x.o = 3", + " y.o = \"a\"", + " if x.o == 3 and y.o == \"a\"", + " testSuccess()" ); } - - - @Test public void identity2() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class C", - " function identity(A a) returns A", - " return a", - " init", - " C a = new C()", - " C b = identity(a)", - " if a == b", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class C", + " function identity(A a) returns A", + " return a", + " init", + " C a = new C()", + " C b = identity(a)", + " if a == b", + " testSuccess()", + "endpackage" ); } @@ -181,95 +178,95 @@ public void identity2() { @Test public void function() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class List", - " function iterator() returns Iterator", - " return new Iterator(this)", - " class Iterator", - " S t", - " construct(List t)", - " int x = 1", - " function hasNext() returns boolean", - " return true", - " function next() returns S", - " return t", - " class A", - " class B", - " class C", - " init", - " List a = new List()", + "package test", + " native testSuccess()", + " class List", + " function iterator() returns Iterator", + " return new Iterator(this)", + " class Iterator", + " S t", + " construct(List t)", + " int x = 1", + " function hasNext() returns boolean", + " return true", + " function next() returns S", + " return t", + " class A", + " class B", + " class C", + " init", + " List a = new List()", // " for B b in a", - " Iterator iterator = a.iterator()", - " while iterator.hasNext()", - " B b = iterator.next()", - " testSuccess()", - "endpackage" + " Iterator iterator = a.iterator()", + " while iterator.hasNext()", + " B b = iterator.next()", + " testSuccess()", + "endpackage" ); } @Test public void testSubtypeGenericClass() { testAssertOkLines(false, - "package test", - " class A", - " class B extends A", - " init", - " A x = new B", - "endpackage" + "package test", + " class A", + " class B extends A", + " init", + " A x = new B", + "endpackage" ); } @Test public void testSubtypeGenericClass2() { testAssertOkLines(false, - "package test", - " class A", - " class B extends A", - " function foo()", - " A x = new B", - "endpackage" + "package test", + " class A", + " class B extends A", + " function foo()", + " A x = new B", + "endpackage" ); } @Test public void testSubtypeGenericInterface() { testAssertOkLines(false, - "package test", - " interface I", - " class B implements I", - " init", - " I x = new B", - "endpackage" + "package test", + " interface I", + " class B implements I", + " init", + " I x = new B", + "endpackage" ); } @Test public void identityFail1() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " real x = identity(3.14)", - " if x == 3.14", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " real x = identity(3.14)", + " if x == 3.14", + " testSuccess()", + "endpackage" ); } @Test public void identityFail2() { testAssertErrorsLines(true, "Cannot assign C to real", - "package test", - " function identity(A a) returns A", - " return a", - " class C", - " int y", - " init", - " real x = identity(new C())", - "endpackage" + "package test", + " function identity(A a) returns A", + " return a", + " class C", + " int y", + " init", + " real x = identity(new C())", + "endpackage" ); } @@ -277,162 +274,162 @@ public void identityFail2() { @Test public void cellExample() { testAssertErrorsLines(true, "Wrong parameter type", - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " class A", - " class B", - " init", - " Cell c = new Cell()", - " c.set(new B())", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " class A", + " class B", + " init", + " Cell c = new Cell()", + " c.set(new B())", + "endpackage" ); } @Test public void implicitConversions() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " if c.get() == bla(5, 3)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " if c.get() == bla(5, 3)", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions2() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " c.set(c.get())", - " if c.get() == bla(5, 3)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " c.set(c.get())", + " if c.get() == bla(5, 3)", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions3() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " public interface FoldClosure", - " function apply(T t, Q q) returns Q", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " function fold(Q start, FoldClosure f) returns Q", - " return f.apply(elem, start)", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " let x = c.fold(2, (e, a) -> e.z + a)", - " if x == 7", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " public interface FoldClosure", + " function apply(T t, Q q) returns Q", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " function fold(Q start, FoldClosure f) returns Q", + " return f.apply(elem, start)", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " let x = c.fold(2, (e, a) -> e.z + a)", + " if x == 7", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions4() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface TFunc", - " abstract function run(T t)", - "", - "function runFunc(TFunc func)", - " func.run(false)", - "", - "init", - " runFunc( (bool b) -> begin", - " testSuccess()", - " end )" + "package test", + "native testSuccess()", + "interface TFunc", + " abstract function run(T t)", + "", + "function runFunc(TFunc func)", + " func.run(false)", + "", + "init", + " runFunc( (bool b) -> begin", + " testSuccess()", + " end )" ); } @Test public void implicitConversions5() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "@extern native R2I(real r) returns int", - "@extern native R2S(real r) returns string", - "native println(string s)", - "interface F", - " function apply(A a) returns R", - "class Cell", - " T elem", - " construct(T t)", - " this.elem = t", - " function get() returns T", - " return elem", - " function map(F f) returns Cell", - " return new Cell(f.apply(elem))", - "function real.assertEquals(real expected)", - " if this == expected", - " testSuccess()", - " else", - " println(R2S(this))", - "init", - " let a = new Cell(5)", - " let b = a.map(i -> i*10.)", - " b.get().assertEquals(50)" + "package test", + "native testSuccess()", + "@extern native R2I(real r) returns int", + "@extern native R2S(real r) returns string", + "native println(string s)", + "interface F", + " function apply(A a) returns R", + "class Cell", + " T elem", + " construct(T t)", + " this.elem = t", + " function get() returns T", + " return elem", + " function map(F f) returns Cell", + " return new Cell(f.apply(elem))", + "function real.assertEquals(real expected)", + " if this == expected", + " testSuccess()", + " else", + " println(R2S(this))", + "init", + " let a = new Cell(5)", + " let b = a.map(i -> i*10.)", + " b.get().assertEquals(50)" ); } @Test public void implicitConversionsFail() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(3,4))", - " if c.get() == bla(3,4)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(3,4))", + " if c.get() == bla(3,4)", + " testSuccess()", + "endpackage" ); } @@ -440,157 +437,156 @@ public void implicitConversionsFail() { @Test public void implicitConversionsAssign() { testAssertOkLines(false, - "type unit extends handle", - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " init", - " Cell c = new Cell()", - " Cell c2 = c", - "endpackage" + "type unit extends handle", + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " init", + " Cell c = new Cell()", + " Cell c2 = c", + "endpackage" ); } @Test public void nativeTypes() { testAssertOkLines(false, - "type effect extends handle", - "package Test", - "class L", - "init", - " L l = new L()" + "type effect extends handle", + "package Test", + "class L", + "init", + " L l = new L()" ); } - @Test public void implicitConversionFailSimple() { // see bug #121 testAssertErrorsLines(false, "cast expression not defined for expression type int", - "type effect extends handle", - "package Test", + "type effect extends handle", + "package Test", - "class List", - " function get() returns T", - " return 0 castTo T", + "class List", + " function get() returns T", + " return 0 castTo T", - "init", - " List fxs = new List()", - " let f = fxs.get()", - "endpackage"); + "init", + " List fxs = new List()", + " let f = fxs.get()", + "endpackage"); } @Test public void cast() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "class A", - " function foo() returns int", - " return 5", - "class B extends A", - " override function foo() returns int", - " return 6", - "init", - " Cell c = new Cell()", - " c.o = new B()", - " B b = c.o castTo B" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "class A", + " function foo() returns int", + " return 5", + "class B extends A", + " override function foo() returns int", + " return 6", + "init", + " Cell c = new Cell()", + " c.o = new B()", + " B b = c.o castTo B" ); } @Test public void genericsDispatch2() { testAssertOkLines(true, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "class A", - " function foo() returns int", - " return 5", - "class B extends A", - " override function foo() returns int", - " return 6", - "init", - " Cell c = new Cell()", - " c.o = new B()", - " if c.o.foo() == 6", - " testSuccess()" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "class A", + " function foo() returns int", + " return 5", + "class B extends A", + " override function foo() returns int", + " return 6", + "init", + " Cell c = new Cell()", + " c.o = new B()", + " if c.o.foo() == 6", + " testSuccess()" ); } @Test public void genericsSubstitute1() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "class A", - " function bla(T t)", - "class B extends A", - "class C", - "init", - " let b = new B", - " b.bla(new C)" + "package Test", + "native testSuccess()", + "class A", + " function bla(T t)", + "class B extends A", + "class C", + "init", + " let b = new B", + " b.bla(new C)" ); } @Test public void genericsSubstitute2() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "interface I", - " function bla(T t, S s)", - " skip", - "class A implements I", - "class B extends A", - "class C", - "class D", - "init", - " let b = new B", - " b.bla(new D, new C)" + "package Test", + "native testSuccess()", + "interface I", + " function bla(T t, S s)", + " skip", + "class A implements I", + "class B extends A", + "class C", + "class D", + "init", + " let b = new B", + " b.bla(new D, new C)" ); } @Test public void genericsSubstitute3() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "interface I", - " function bla(T t, S s)", - " skip", - "interface J extends I", - " function foo()", - " skip", - "class A implements J", - "class B extends A", - "class C", - "class D", - "init", - " let b = new B", - " b.bla(new D, new C)" + "package Test", + "native testSuccess()", + "interface I", + " function bla(T t, S s)", + " skip", + "interface J extends I", + " function foo()", + " skip", + "class A implements J", + "class B extends A", + "class C", + "class D", + "init", + " let b = new B", + " b.bla(new D, new C)" ); } @Test public void genericsSubstitute() { testAssertOkLines(false, - "package Test", - "class A", - " function bla(T a)", - "class B extends A", - " function do()", - " bla(new MyType)", - "class MyType" + "package Test", + "class A", + " function bla(T a)", + "class B extends A", + " function do()", + " bla(new MyType)", + "class MyType" ); } @@ -598,13 +594,13 @@ public void genericsSubstitute() { @Test public void genericsSubstitute_override() { testAssertOkLines(false, - "package Test", - "class A", - " function bla(T a)", - "class B extends A", - " override function bla(MyType t)", - " skip", - "class MyType" + "package Test", + "class A", + " function bla(T a)", + "class B extends A", + " override function bla(MyType t)", + " skip", + "class MyType" ); } @@ -613,16 +609,16 @@ public void genericsSubstitute_override() { @Test public void genericsSubstitute_override_interface() { testAssertOkLines(false, - "package Test", - "interface I", - " function bla(S s, T t)", - "interface J extends I", - " function foo(T t)", - "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", - "class MyType" + "package Test", + "interface I", + " function bla(S s, T t)", + "interface J extends I", + " function foo(T t)", + "class B implements J", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", + "class MyType" ); } @@ -630,16 +626,16 @@ public void genericsSubstitute_override_interface() { @Test public void genericsSubstitute_override_interface_fail() { testAssertErrorsLines(false, "Parameter int s should have type MyType to override function bla", - "package Test", - "interface I", - " function bla(S s, T t)", - "interface J extends I", - " function foo(T t)", - "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", - "class MyType" + "package Test", + "interface I", + " function bla(S s, T t)", + "interface J extends I", + " function foo(T t)", + "class B implements J", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", + "class MyType" ); } @@ -648,203 +644,203 @@ public void genericsSubstitute_override_interface_fail() { @Test public void genericMethod1() { testAssertOkLines(false, - "package Test", - "class Blub", - "function bla(Blub t)", - "init", - " bla(new Blub)" + "package Test", + "class Blub", + "function bla(Blub t)", + "init", + " bla(new Blub)" ); } @Test public void genericExtensionMethod1() { testAssertOkLines(false, - "package Test", - "class Blub", - "function Blub.bla()", - " skip", - "init", - " new Blub.bla()" + "package Test", + "class Blub", + "function Blub.bla()", + " skip", + "init", + " new Blub.bla()" ); } @Test public void genericReturnOverride() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package Test", - "interface I", - " function f() returns T", - "class C implements I", - " function f() returns T", - " return null" + "package Test", + "interface I", + " function f() returns T", + "class C implements I", + " function f() returns T", + " return null" ); } @Test public void genericReturnOverride2() { testAssertOkLines(false, - "package Test", - "interface I", - " function f(S t) returns S", - "class C implements I", - " function f(T t) returns T", - " return t" + "package Test", + "interface I", + " function f(S t) returns S", + "class C implements I", + " function f(T t) returns T", + " return t" ); } @Test public void genericRecursive() { testAssertOkLines(false, - "package Test", - "public class C", - " C x", - " function foo()", - " this.x.x = null" + "package Test", + "public class C", + " C x", + " function foo()", + " this.x.x = null" ); } @Test public void genericRecursive2() { testAssertOkLines(false, - "package Test", - "public class C", - " C x", - " function foo()", - " C c = new C", - " c.x.x = null" + "package Test", + "public class C", + " C x", + " function foo()", + " C c = new C", + " c.x.x = null" ); } @Test public void genericChain1() { testAssertOkLines(false, - "package Test", - "class A", - "public class C", - " K x", - "init", - " C>> c = null", - " c.x.x.x = new A" + "package Test", + "class A", + "public class C", + " K x", + "init", + " C>> c = null", + " c.x.x.x = new A" ); } @Test public void genericChain1Err() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " K x", - "init", - " C>> c = null", - " c.x.x = new A" + "package Test", + "class A", + "public class C", + " K x", + "init", + " C>> c = null", + " c.x.x = new A" ); } @Test public void genericChain2() { testAssertOkLines(false, - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>>" ); } @Test public void genericChain2ErrA() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>>>" ); } @Test public void genericChain2ErrB() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>" ); } @Test public void implicitsWithClass() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface Comparison", - " function leq(T t, T u) returns bool", - "class BoolComp implements Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "Comparison bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "interface Comparison", + " function leq(T t, T u) returns bool", + "class BoolComp implements Comparison", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "Comparison bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClass2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class Comparison", - " function leq(T t, T u) returns bool", - " return true", - "class BoolComp extends Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "Comparison bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "class Comparison", + " function leq(T t, T u) returns bool", + " return true", + "class BoolComp extends Comparison", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "Comparison bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClass3() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class Comparison", - " function leq(T t, T u) returns bool", - " return true", - "class ComparisonX extends Comparison", - "class BoolComp extends ComparisonX", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "ComparisonX bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "class Comparison", + " function leq(T t, T u) returns bool", + " return true", + "class ComparisonX extends Comparison", + "class BoolComp extends ComparisonX", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "ComparisonX bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClosures() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface Comparison", - " function leq(T t, T u) returns bool", - "Comparison bc = (bool a, bool b) -> not a or b", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "interface Comparison", + " function leq(T t, T u) returns bool", + "Comparison bc = (bool a, bool b) -> not a or b", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -852,25 +848,25 @@ public void implicitsWithClosures() { @Test public void genericForIn() { testAssertErrorsLines(true, "cast expression not defined for expression type int", - "package test", - "native testSuccess()", - "class C", - " function iterator() returns Iterator", - " return new Iterator()", - "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", - " function close()", - " destroy this", - "init", - " let c = new C", - " for i in c", - " if i == 5", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function iterator() returns Iterator", + " return new Iterator()", + "class Iterator", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", + " function close()", + " destroy this", + "init", + " let c = new C", + " for i in c", + " if i == 5", + " testSuccess()" ); } @@ -878,164 +874,164 @@ public void genericForIn() { @Ignore public void genericForFrom() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function iterator() returns Iterator", - " return new Iterator()", - "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", - "init", - " let c = new C", - " let iter = c.iterator()", - " for i from iter", - " if i == 5", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function iterator() returns Iterator", + " return new Iterator()", + "class Iterator", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", + "init", + " let c = new C", + " let iter = c.iterator()", + " for i from iter", + " if i == 5", + " testSuccess()" ); } @Test public void genericOverload() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", - "init", - " let c = new C(1)", - " c.foo(1)" + "package test", + "native testSuccess()", + "class C", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", + "init", + " let c = new C(1)", + " c.foo(1)" ); } @Test public void genericOverload2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", - " function test()", - " let c = new C(1)", - " c.foo(1)", - "init", - " new C(1).test()" + "package test", + "native testSuccess()", + "class C", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", + " function test()", + " let c = new C(1)", + " c.foo(1)", + "init", + " new C(1).test()" ); } @Test public void inferType() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "function id(int x) returns int", - " return x", - "class C", - " var x = id(4)", - "init", - " let x= new C", - " if x.x == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "function id(int x) returns int", + " return x", + "class C", + " var x = id(4)", + "init", + " let x= new C", + " if x.x == 4", + " testSuccess()" ); } @Test public void simpleFunctionCall() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function foo() returns int", - " return 4", - "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function foo() returns int", + " return 4", + "init", + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @Test public void simpleFunctionCall2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function foo() returns int", - " return bar()", - " function bar() returns int", - " return 4", - "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function foo() returns int", + " return bar()", + " function bar() returns int", + " return 4", + "init", + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @Test public void genericFunctionOverload() { // #628 testAssertOkLines(false, - "package test", - "native testSuccess()", - "class LinkedList", - " T x", - "public function LinkedList.foo(string separator) returns string", - " this.foo(s -> s, separator) // Doesn't work", - " this.foo2(s -> s, separator) // Works", - " return separator", - "interface ToStringClosure", - " function apply(A a) returns A", - "public function LinkedList.foo(ToStringClosure cls, string separator)", - "public function LinkedList.foo2(ToStringClosure cls, string separator)", - "init", - " let x = new LinkedList", - " x.foo(\"a\")" + "package test", + "native testSuccess()", + "class LinkedList", + " T x", + "public function LinkedList.foo(string separator) returns string", + " this.foo(s -> s, separator) // Doesn't work", + " this.foo2(s -> s, separator) // Works", + " return separator", + "interface ToStringClosure", + " function apply(A a) returns A", + "public function LinkedList.foo(ToStringClosure cls, string separator)", + "public function LinkedList.foo2(ToStringClosure cls, string separator)", + "init", + " let x = new LinkedList", + " x.foo(\"a\")" ); } @Test public void extensionFunc2() { // #718 testAssertOkLines(false, - "package test", - "native testSuccess()", - "public function T.foo() returns T", - " return this", - "init", - " let x = \"hello\".foo()", - " if x == \"hello\"", - " testSuccess()" + "package test", + "native testSuccess()", + "public function T.foo() returns T", + " return this", + "init", + " let x = \"hello\".foo()", + " if x == \"hello\"", + " testSuccess()" ); } @Test public void strangeFoldl() { // #655 testAssertOkLines(false, - "package test", - "native testSuccess()", - "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return startValue", - "interface FoldClosure", - " function run(X t, Y q) returns Y", - "init", - " let x = new LinkedList", - " x.foldl(0, (x, y) -> x + y)" + "package test", + "native testSuccess()", + "class LinkedList", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return startValue", + "interface FoldClosure", + " function run(X t, Y q) returns Y", + "init", + " let x = new LinkedList", + " x.foldl(0, (x, y) -> x + y)" ); } @@ -1043,57 +1039,57 @@ public void strangeFoldl() { // #655 @Ignore public void normalFoldlInfer() { // #657 testAssertOkLines(true, - "package test", - "native testSuccess()", - "@extern native I2S(int i) returns string", - "string array s", - "int s_max = -1", - "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return predicate.run(x, startValue)", - " function toString() returns string", - " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", - " return fold + \"]\"", - "interface FoldClosure", - " function run(T t, Q q) returns Q", - "init", - " let x = new LinkedList", - " x.x = 5", - " if x.toString() == \"[5,]\"", - " testSuccess()" + "package test", + "native testSuccess()", + "@extern native I2S(int i) returns string", + "string array s", + "int s_max = -1", + "class LinkedList", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return predicate.run(x, startValue)", + " function toString() returns string", + " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", + " return fold + \"]\"", + "interface FoldClosure", + " function run(T t, Q q) returns Q", + "init", + " let x = new LinkedList", + " x.x = 5", + " if x.toString() == \"[5,]\"", + " testSuccess()" ); } @Test public void inheritField() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " int someInt", - "class B extends A", - " construct()", - " someInt = 1", - "init", - " if new B().someInt == 1", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class A", + " int someInt", + "class B extends A", + " construct()", + " someInt = 1", + "init", + " if new B().someInt == 1", + " testSuccess()" ); } @Test public void inheritField2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " int someInt", - "class B extends A", - " construct()", - " someInt = 1", - "init", - " if new B().someInt == 1", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class A", + " int someInt", + "class B extends A", + " construct()", + " someInt = 1", + "init", + " if new B().someInt == 1", + " testSuccess()" ); } @@ -1101,63 +1097,63 @@ public void inheritField2() { @Test public void inheritMethod() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " function someInt()", - " testSuccess()", - "class B extends A", - " construct()", - " someInt()", - "init", - " new B()" + "package test", + "native testSuccess()", + "abstract class A", + " function someInt()", + " testSuccess()", + "class B extends A", + " construct()", + " someInt()", + "init", + " new B()" ); } @Test public void nullWithGeneric() { testAssertErrorsLines(false, "Cannot compare types T with null", - "package test", - "native testSuccess()", - "function foo(T t)", - " if t == null", - " testSuccess()", - "init", - " foo(null)" + "package test", + "native testSuccess()", + "function foo(T t)", + " if t == null", + " testSuccess()", + "init", + " foo(null)" ); } @Test public void missingTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "function foo() returns T", - " return null", - "init", - " let x = foo()" + "package test", + "function foo() returns T", + " return null", + "init", + " let x = foo()" ); } @Test public void missingTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "class C", - " function foo() returns T", - " return null", - "init", - " let c = new C", - " let x = c.foo()" + "package test", + "class C", + " function foo() returns T", + " return null", + "init", + " let c = new C", + " let x = c.foo()" ); } @Test public void missingTypeArgsConstructor() { testAssertErrorsLines(false, "Cannot infer type for type parameter T", - "package test", - "class C", - "init", - " let c = new C" + "package test", + "class C", + "init", + " let c = new C" ); } @@ -1165,112 +1161,112 @@ public void missingTypeArgsConstructor() { @Test public void tooManyTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "function foo() returns T", - " return null", - "init", - " let x = foo()" + "package test", + "function foo() returns T", + " return null", + "init", + " let x = foo()" ); } @Test public void tooManyTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "class C", - " function foo() returns T", - " return null", - "init", - " let c = new C", - " let x = c.foo()" + "package test", + "class C", + " function foo() returns T", + " return null", + "init", + " let c = new C", + " let x = c.foo()" ); } @Test public void tooManyTypeArgsConstructor() { testAssertErrorsLines(false, "Too many type arguments given", - "package test", - "class C", - "init", - " let c = new C" + "package test", + "class C", + "init", + " let c = new C" ); } @Test public void capturedType() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "native println(string s)", - "interface F", - " function apply(A a) returns R", - "function twice(F f) returns F", - " return x -> f.apply(f.apply(x))", // line 7 - "init", - " F plus1 = x -> x + 1", // line 9 - " F plus2 = twice(plus1)", + "package test", + "native testSuccess()", + "native println(string s)", + "interface F", + " function apply(A a) returns R", + "function twice(F f) returns F", + " return x -> f.apply(f.apply(x))", // line 7 + "init", + " F plus1 = x -> x + 1", // line 9 + " F plus2 = twice(plus1)", // " F shout = twice((string s) -> s + \"!\")", // line 11 - " if plus2.apply(1) == 3", - " testSuccess()" + " if plus2.apply(1) == 3", + " testSuccess()" ); } @Test public void severalSubMethods() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "native println(string s)", - "class B", - " function id(T x) returns T", - " return x", - "class A extends B", - " override function id(Y y) returns Y", - " return y", - "init", - " B a = new A", - " B b = new A", - " if a.id(4) == 4 and b.id(2) == 2", - " testSuccess()" + "package test", + "native testSuccess()", + "native println(string s)", + "class B", + " function id(T x) returns T", + " return x", + "class A extends B", + " override function id(Y y) returns Y", + " return y", + "init", + " B a = new A", + " B b = new A", + " if a.id(4) == 4 and b.id(2) == 2", + " testSuccess()" ); } @Test public void simpleCastTest() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class A", - " class B extends A", - " int x", - " function get() returns A", - " let r = new B", - " r.x = 5", - " return r", - " init", - " if (get() castTo B).x == 5", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class A", + " class B extends A", + " int x", + " function get() returns A", + " let r = new B", + " r.x = 5", + " return r", + " init", + " if (get() castTo B).x == 5", + " testSuccess()", + "endpackage" ); } @Test public void abstractReturnT() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class F", - " abstract function get() returns T", - "class X extends F", - " T t", - " construct(T t)", - " this.t = t", - " override function get() returns T", - " return t", - "init", - " F x = new X(42)", - " if x.get() == 42", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class F", + " abstract function get() returns T", + "class X extends F", + " T t", + " construct(T t)", + " this.t = t", + " override function get() returns T", + " return t", + "init", + " F x = new X(42)", + " if x.get() == 42", + " testSuccess()" ); } @@ -1353,6 +1349,58 @@ public void forwardTypeClass() { ); } + @Test + public void subTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(T x) returns int", + "interface B extends A", + " function y(T x) returns int", + "class C", + "instance B", + " function x(C x) returns int", + " return 42", + " function y(C x) returns int", + " return 43", + "function foo(Q x)", + " if Q.x(x) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } + + @Test + public void typeClassAmbiguous() { + testAssertErrorsLines(true, "There are multiple instances", + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(T x) returns int", + "interface B extends A", + " function y(T x) returns int", + "class C", + "instance A", + " function x(C x) returns int", + " return 17", + "instance B", + " function x(C x) returns int", + " return 42", + " function y(C x) returns int", + " return 43", + "function foo(Q x)", + " if Q.x(x) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } } From 2f200e27543038e2492f1f588cca579ad0dd9888 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 31 Jan 2020 00:52:55 +0100 Subject: [PATCH 19/50] first dependent instances working --- .../parserspec/jass_im.parseq | 4 +- .../parserspec/wurstscript.parseq | 2 +- .../peeeq/wurstio/WurstCompilerJassImpl.java | 2 + .../attributes/AttrCallSignature.java | 6 -- .../AttrPossibleFunctionSignatures.java | 73 ++++++++++++++----- .../interpreter/EvaluateExpr.java | 5 +- .../translation/imtojass/ImAttrType.java | 3 +- .../imtranslation/AssertProperty.java | 2 +- .../imtranslation/EliminateGenerics.java | 8 ++ .../imtranslation/EliminateTypeClasses.java | 47 ++++++++++-- .../imtranslation/ExprTranslation.java | 15 +++- .../translation/imtranslation/Flatten.java | 2 +- .../translation/imtranslation/ImPrinter.java | 2 +- .../imtranslation/ImTranslator.java | 45 +++++++++--- .../imtranslation/TypeClassTranslator.java | 8 ++ .../lua/translation/ExprTranslation.java | 2 +- .../tests/GenericsWithTypeclassesTests.java | 27 +++++++ 17 files changed, 195 insertions(+), 58 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index f973d5649..d4d2b1de4 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -98,7 +98,7 @@ ImExpr = | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr | ImCast(ImExpr expr, ref ImType toType) - | ImTypeClassDictValue(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType classType, ImExprs arguments) + // an expression which can be used on the left hand side of an assignment @@ -126,7 +126,7 @@ ImClassRelatedExprWithClass = | ImInstanceof(ImExpr obj, ref ImClassType clazz) | ImTypeIdOfObj(ImExpr obj, ref ImClassType clazz) | ImTypeIdOfClass(ref ImClassType clazz) - + | ImTypeClassDictValue(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType clazz, ImExprs arguments) diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index 0a7c87c40..eb2d8ab97 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -330,7 +330,7 @@ AstElementWithNameId = WPackage | NativeFunc | ModuleDef | TypeDef | ModuleInsta AstElementWithParameters = FunctionDefinition | ExtensionFuncDef | NativeFunc | TupleDef | ConstructorDef | FuncDef -AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassOrInterface | FuncDef +AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassOrInterfaceOrInstance | FuncDef AstElementWithArgs = StmtCall | ExprFunctionCall | ExprNewObject | ExprMemberMethod diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 5b0e55b4b..7683327d2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -619,6 +619,8 @@ private void printDebugImProg(String debugFile) { } catch (IOException e) { ErrorReporting.instance.handleSevere(e, getCompleteSourcecode()); } + // basic sanity check + getImTranslator().assertProperties(AssertProperty.rooted(getImProg())); } private WurstModel mergeCompilationUnits(List compilationUnits) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java index 02d865e41..0f2c80873 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java @@ -17,12 +17,6 @@ public static CallSignature calculate(ExprFunctionCall c) { } public static CallSignature calculate(ExprMemberMethod c) { - Expr receiver = c.getLeft(); - if (receiver.attrTyp().isStaticRef()) { - // if we use a static ref like A.foo() - // then there is no real receiver - receiver = null; - } return new CallSignature(c.attrImplicitParameter(), c.getArgs()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 9443b643b..c78b73145 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -9,7 +9,9 @@ import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; +import io.vavr.Tuple2; import io.vavr.control.Option; +import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.stream.Collectors; @@ -67,8 +69,8 @@ private static ImmutableCollection findBestSignature(StmtCall if (res2.isEmpty()) { // no signature matches precisely --> try to match as good as possible ImmutableList match3 = res.stream() - .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) - .collect(ImmutableList.toImmutableList()); + .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) + .collect(ImmutableList.toImmutableList()); if (match3.isEmpty()) { return ImmutableList.of(); @@ -80,8 +82,8 @@ private static ImmutableCollection findBestSignature(StmtCall } return match3.stream() - .map(ArgsMatchResult::getSig) - .collect(ImmutableList.toImmutableList()); + .map(ArgsMatchResult::getSig) + .collect(ImmutableList.toImmutableList()); } } else { return res2; @@ -93,18 +95,7 @@ private static Pair> findTypeClasses(Funct VariableBinding mapping = sig.getMapping(); for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { Option matchedTypeOpt = mapping.get(tp); - List constraints = new ArrayList<>(); - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { - WurstType ct = c.attrConstraintTyp(); - if (ct instanceof WurstTypeInterface) { - WurstTypeInterface wti = (WurstTypeInterface) ct; - constraints.add(wti); - - - } - } - } + List constraints = getConstraints(tp); if (matchedTypeOpt.isEmpty()) { if (!constraints.isEmpty()) { errors.add(new CompileError(fc.attrSource(), "Type parameter " + tp.getName() + " is not bound, so type constraints cannot be solved.")); @@ -125,6 +116,23 @@ private static Pair> findTypeClasses(Funct return Pair.create(sig, errors); } + @NotNull + private static List getConstraints(TypeParamDef tp) { + List constraints = new ArrayList<>(); + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + WurstType ct = c.attrConstraintTyp(); + if (ct instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) ct; + constraints.add(wti); + + + } + } + } + return constraints; + } + private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); @@ -149,14 +157,39 @@ private static VariableBinding findTypeClass(StmtCall fc, List err .map(e -> (InstanceDecl) e) .flatMap(instance -> { WurstType instanceType = instance.getImplementedInterface().attrTyp(); - VariableBinding match = instanceType.matchAgainstSupertype(constraint, fc, VariableBinding.emptyMapping(), VariablePosition.LEFT); + VariableBinding initialMapping = VariableBinding.emptyMapping().withTypeVariables(instance.getTypeParameters()); + VariableBinding match = instanceType.matchAgainstSupertype(constraint, fc, initialMapping, VariablePosition.LEFT); + + if (match == null) { return Stream.empty(); } instanceType = instanceType.setTypeArgs(match); - List typeArgs = new ArrayList<>(); + + for (Tuple2 m : match) { + TypeParamDef instanceTp = m._1(); + WurstTypeBoundTypeParam mType = m._2(); + List instanceConstraints = getConstraints(instanceTp); + for (WurstTypeInterface instanceConstraint : instanceConstraints) { + VariableBinding match2 = findTypeClass(fc, errors, match, instanceTp, mType, instanceConstraint); + if (match2 == null) { + return Stream.empty(); + } + match = match2; + } + } + List deps = new ArrayList<>(); + List typeArgs = new ArrayList<>(); + for (TypeParamDef instanceTp : instance.getTypeParameters()) { + WurstTypeBoundTypeParam i = match.get(instanceTp).get(); + deps.addAll(i.getInstances()); + if (instanceTp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + typeArgs.add(i); + } + } + // TODO resolve dependencies @@ -174,8 +207,8 @@ private static VariableBinding findTypeClass(StmtCall fc, List err } else { if (instances.size() > 1) { errors.add(new CompileError(fc, - "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + - Utils.printSep("\n", instances))); + "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + + Utils.printSep("\n", instances))); } TypeClassInstance instance = Utils.getFirst(instances); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index e26154aff..72eeefe35 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -9,7 +9,6 @@ import de.peeeq.wurstscript.intermediatelang.*; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.TypesHelper; -import io.vavr.control.Either; import org.eclipse.jdt.annotation.Nullable; import java.util.ArrayList; @@ -422,11 +421,11 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } public static ILconst eval(ImTypeClassDictValue e, ProgramState globalState, LocalState localState) { - ILconstObject obj = globalState.allocate(e.getClassType(), e.attrTrace()); + ILconstObject obj = globalState.allocate(e.getClazz(), e.attrTrace()); int i = 0; for (ImExpr arg : e.getArguments()) { ILconst argV = arg.evaluate(globalState, localState); - obj.set(e.getClassType().getClassDef().getFields().get(i), Collections.emptyList(), argV); + obj.set(e.getClazz().getClassDef().getFields().get(i), Collections.emptyList(), argV); } return obj; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index d5c1e0070..3038f5bd7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -4,7 +4,6 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.*; -import java.util.ArrayList; import java.util.List; public class ImAttrType { @@ -217,6 +216,6 @@ public static ImType getType(ImCast imCast) { } public static ImType getType(ImTypeClassDictValue e) { - return e.getClassType(); + return e.getClazz(); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java index a5b5caf79..c14182a06 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java @@ -91,7 +91,7 @@ public void checkRooted(Element location, Element el) { } Element parent = e.getParent(); if (parent == null) { - break; + throw new CompileError(location,"Element " + el + " has no parent"); } checkContains(location, parent, e); if (parent instanceof ImFunction && parent != currentFunction) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index f94047dfc..2fbdb28bc 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -468,6 +468,14 @@ public void visit(ImTypeIdOfClass f) { } } + @Override + public void visit(ImTypeClassDictValue f) { + super.visit(f); + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + }); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java index dc10755c5..5f259aab9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -1,6 +1,7 @@ package de.peeeq.wurstscript.translation.imtranslation; import com.google.common.collect.*; +import de.peeeq.datastructures.Partitions; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; @@ -35,7 +36,7 @@ public class EliminateTypeClasses { private final ImTranslator tr; private final ArrayDeque workList = new ArrayDeque<>(); private final Table specializedFunctions = HashBasedTable.create(); - private final Map outOfClassFuncs = new LinkedHashMap<>(); + private final Table outOfClassFuncs = HashBasedTable.create(); public EliminateTypeClasses(ImTranslator tr) { this.tr = tr; @@ -83,7 +84,7 @@ private void doSpecialize(ImTypeClassDictValue dictV) { ImMethodCall mc = (ImMethodCall) parent; assert mc.getReceiver() == dictV; - ImMethod m = findMostConcreteMethod(mc.getMethod(), dictV.getClassType()); + ImMethod m = findMostConcreteMethod(mc.getMethod(), dictV.getClazz()); // allow to move mc.getTypeArguments().setParent(null); @@ -95,13 +96,24 @@ private void doSpecialize(ImTypeClassDictValue dictV) { } mc.replaceBy(JassIm.ImFunctionCall( mc.getTrace(), - getOutOfClassFunc(m.getImplementation()), + getOutOfClassFunc(m.getImplementation(), key(dictV, 0)), mc.getTypeArguments(), mc.getArguments(), false, CallType.NORMAL )); + return; + } else if (parent instanceof ImMemberAccess) { + ImMemberAccess ma = (ImMemberAccess) parent; + ImVar field = ma.getVar(); + int index = ((ImVars) field.getParent()).indexOf(field); + ImExpr tc = dictV.getArguments().get(index); + ImExpr copy = tc.copyWithRefs(); + ma.replaceBy(copy); + if (copy instanceof ImTypeClassDictValue) { + workList.add((ImTypeClassDictValue) copy); + } return; } @@ -110,15 +122,33 @@ private void doSpecialize(ImTypeClassDictValue dictV) { "Unhandled parent for dict: " + parent + " // " + parent.getClass()); } - private ImFunction getOutOfClassFunc(ImFunction impl) { - ImFunction res = outOfClassFuncs.get(impl); + private ImFunction getOutOfClassFunc(ImFunction impl, TypeClassInstanceKey key) { + ImFunction res = outOfClassFuncs.get(impl, key); if (res != null) { return res; } ImFunction copy = impl.copyWithRefs(); + tr.getImProg().getFunctions().add(copy); // remove implicit parameter - copy.getParameters().remove(0); - outOfClassFuncs.put(impl, copy); + ImVar thisVar = copy.getParameters().remove(0); + List vars = new ArrayList<>(); + copy.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImVarAccess v) { + + if (v.getVar() == thisVar) { + vars.add(v); + } + } + }); + + for (ImVarAccess va : vars) { + ImTypeClassDictValue newDict = key.makeDictValue(va.attrTrace()); + va.replaceBy(newDict); + workList.add(newDict); + } + + outOfClassFuncs.put(impl, key, copy); return copy; } @@ -195,12 +225,13 @@ private static List dynamicArgs(ImTypeClassDictValue dv) { } private TypeClassInstanceKey key(ImTypeClassDictValue dictV, int index) { - return new TypeClassInstanceKey(dictV.getClassType().getClassDef(), index, + return new TypeClassInstanceKey(dictV.getClazz().getClassDef(), index, dictV.getArguments().stream() .map(v -> key((ImTypeClassDictValue) v, index)) .collect(Collectors.toList())); } + // TODO remove index here and make another class for parameter index static class TypeClassInstanceKey { private final ImClass base; private int index; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 98435c7b0..b5fe33a17 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -516,7 +516,20 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu // } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); - ImVarAccess typeClassDict = JassIm.ImVarAccess(t.getTypeClassParamFor(typeParamDispatchOn)); + ImVar typeClassParam = t.getTypeClassParamFor(typeParamDispatchOn); + ImExpr typeClassDict; + if (typeClassParam.getParent().getParent() instanceof ImFunction) { + // parameter + typeClassDict = JassIm.ImVarAccess(typeClassParam); + } else { + // field + ImVar thisVar = t.getThisVarForNode(f, e); + typeClassDict = JassIm.ImMemberAccess(e, + JassIm.ImVarAccess(thisVar), + JassIm.ImTypeArguments(), + typeClassParam, + JassIm.ImExprs()); + } call = JassIm.ImMethodCall(e, method, typeArguments, typeClassDict, imArgs, false); } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index ae9beb618..a7c37be24 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -90,7 +90,7 @@ public static Result flatten(ImAlloc e, ImTranslator translator, ImFunction f) { public static Result flatten(ImTypeClassDictValue e, ImTranslator translator, ImFunction f) { MultiResult r = flattenExprs(translator, f, e.getArguments()); - return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClassType(), JassIm.ImExprs(e.getArguments()))); + return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClazz(), JassIm.ImExprs(e.getArguments()))); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index 6e51d5174..2e30c20f1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -575,7 +575,7 @@ public static void print(ImAnyType at, Appendable sb, int indent) { public static void print(ImTypeClassDictValue e, Appendable sb, int indent) { append(sb, "Dict#"); - e.getClassType().print(sb, indent); + e.getClazz().print(sb, indent); printArgumentList(sb, indent, e.getArguments()); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 9f2df6586..fc080e7ca 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -101,7 +101,7 @@ public class ImTranslator { private final Map compiletimeExpressionsOrder = new HashMap<>(); de.peeeq.wurstscript.ast.Element lasttranslatedThing; - private boolean debug = false; + private boolean debug = true; private final RunArgs runArgs; public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArgs) { @@ -121,6 +121,7 @@ public ImProg translateProg() { globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(getGlobalInitFunc()); debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", Collections.emptyList())), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); + imProg.getFunctions().add(debugPrintFunction); calculateCompiletimeOrder(); @@ -946,7 +947,7 @@ public ImVar getThisVar(ImFunction f, ExprSuper e) { return getThisVarForNode(f, e); } - private ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) { + ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) { de.peeeq.wurstscript.ast.Element node = node1; while (node != null) { if (node instanceof TranslatedToImFunction && !(node instanceof ExprClosure)) { @@ -1327,7 +1328,8 @@ public ImVar getTypeClassParamFor(TypeParamConstraint tc) { TypeParamDef tp = (TypeParamDef) tc.getParent().getParent(); WurstTypeInterface wti = (WurstTypeInterface) tc.attrConstraintTyp(); ImClassType t = wti.imTranslateType(this); - v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + "_" + tc.attrConstraintTyp(), Collections.singletonList(VarFlag.SPECIALIZE)); + int i = ((TypeParamConstraintList) tp.getTypeParamConstraints()).indexOf(tc); + v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + ((i > 0) ? i : ""), Collections.singletonList(VarFlag.SPECIALIZE)); typeClassParamFor.put(tc, v); } return v; @@ -1558,18 +1560,26 @@ public void assertProperties(Set properties, Element e) { checkVar(((ElementWithVar) e).getVar(), properties); } properties.forEach(p -> p.check(e)); - if (properties.contains(AssertProperty.NOTUPLES)) { - - } - if (properties.contains(AssertProperty.FLAT)) { - - } for (int i = 0; i < e.size(); i++) { Element child = e.get(i); if (child.getParent() == null) { throw new Error("Child " + i + " (" + child + ") of " + e + " not attached to tree"); } - assertProperties(properties, child); + try { + assertProperties(properties, child); + } catch (Throwable t) { + de.peeeq.wurstscript.ast.Element trace = e.attrTrace(); + if (trace instanceof WurstModel || trace instanceof NoExpr) { + throw t; + } + WPos source; + if (t instanceof CompileError) { + source = ((CompileError) t).getSource(); + } else { + source = trace.attrSource(); + } + throw new CompileError(source, "When checking element:\n" + e, CompileError.ErrorType.ERROR, t); + } } } @@ -1632,7 +1642,19 @@ public ImClass getClassFor(ClassOrInterfaceOrInstance s) { String name = s.match(new ClassOrInterfaceOrInstance.Matcher() { @Override public String case_InstanceDecl(InstanceDecl i) { - return "TypeClassDict_" + i.getImplementedInterface().attrTyp(); + WurstType t = i.getImplementedInterface().attrTyp(); + String name = "TypeClassDict"; + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + name += "_" + wti.getDef().getName(); + if (!wti.getTypeParameters().isEmpty()) { + WurstTypeBoundTypeParam last = Utils.getLast(wti.getTypeParameters()); + name += "_" + last.getBaseType().getName().replaceAll("[<>]", ""); + } + + + } + return name; } @Override @@ -1706,6 +1728,7 @@ public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr mes if (ef == null) { Optional f = findErrorFunc().map(this::getFuncFor); ef = errorFunc = f.orElseGet(this::makeDefaultErrorFunc); + imProg.getFunctions().add(errorFunc); } ImExprs arguments = JassIm.ImExprs(message); return ImFunctionCall(trace, ef, ImTypeArguments(), arguments, false, CallType.NORMAL); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java index 63415152b..0cec51e4f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java @@ -19,6 +19,14 @@ public static void translateTypeClass(InstanceDecl instance, ImTranslator tr) { WurstType implementedInterface = instance.getImplementedInterface().attrTyp(); c.getSuperClasses().add((ImClassType) implementedInterface.imTranslateType(tr)); + for (TypeParamDef tp : instance.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint constraint : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + ImVar v = tr.getTypeClassParamFor(constraint); + c.getFields().add(v); + } + } + } for (FuncDef method : instance.getMethods()) { translateMethod(instance, c, method, implementedInterface, tr); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 1b94254b9..71df41032 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -159,7 +159,7 @@ && isStringType(right.attrTyp())) { } public static LuaExpr translate(ImTypeClassDictValue e, LuaTranslator tr) { - ImClass c = e.getClassType().getClassDef(); + ImClass c = e.getClazz().getClassDef(); LuaMethod m = tr.luaClassInitMethod.getFor(c); LuaVariable classVar = tr.luaClassVar.getFor(c); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index a88829291..76c95a010 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1403,4 +1403,31 @@ public void typeClassAmbiguous() { } + @Test + public void buildInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(X x) returns int", + "interface B", + " function y(Y y) returns int", + "class C", + "instance A", + " function x(C c) returns int", + " return 41", + "instance implements B", + " function y(T t) returns int", + " return T.x(t) + 1", + "function foo(Q q)", + " if Q.y(q) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } + + } From 3aecaa2b51a3fc789f04a2d8dae4a2f844627d7d Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 31 Jan 2020 01:30:58 +0100 Subject: [PATCH 20/50] removed debug --- .../translation/imtranslation/ImTranslator.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index fc080e7ca..d977e1e92 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -101,7 +101,7 @@ public class ImTranslator { private final Map compiletimeExpressionsOrder = new HashMap<>(); de.peeeq.wurstscript.ast.Element lasttranslatedThing; - private boolean debug = true; + private boolean debug = false; private final RunArgs runArgs; public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArgs) { @@ -1728,7 +1728,6 @@ public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr mes if (ef == null) { Optional f = findErrorFunc().map(this::getFuncFor); ef = errorFunc = f.orElseGet(this::makeDefaultErrorFunc); - imProg.getFunctions().add(errorFunc); } ImExprs arguments = JassIm.ImExprs(message); return ImFunctionCall(trace, ef, ImTypeArguments(), arguments, false, CallType.NORMAL); @@ -1757,7 +1756,9 @@ private ImFunction makeDefaultErrorFunc() { List flags = Lists.newArrayList(); - return ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); + ImFunction result = ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); + imProg.getFunctions().add(result); + return result; } From dc677abd675a46f9698c7f702515cf3d5f1f418a Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 31 Jan 2020 21:24:33 +0100 Subject: [PATCH 21/50] change syntax to 'implements' keyword --- .../main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 | 2 +- .../antlr/AntlrWurstParseTreeTransformer.java | 12 +++++++++++- .../tests/GenericsWithTypeclassesTests.java | 14 +++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 index 5ab24b87b..5e04e58a7 100644 --- a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 +++ b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 @@ -116,7 +116,7 @@ entity: ; instanceDeclaration: - 'instance' (name=ID? typeParams 'implements')? implemented=typeExpr + 'implements' implemented=typeExpr ('for' (params+=typeParam (',' params+=typeParam)*))? NL (STARTBLOCK funcDef* ENDBLOCK)? diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 4c4157b4e..1d6fcdae8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -306,12 +306,22 @@ private WPackage transformPackage(WpackageContext p) { private WEntity transformInstanceDeclaration(InstanceDeclarationContext i) { return Ast.InstanceDecl( source(i), - transformTypeParams(i.typeParams()), + transformInstanceTypeParams(i.params), transformTypeExpr(i.implemented), transformFuncDefs(i.funcDef()) ); } + private TypeParamDefs transformInstanceTypeParams(List params) { + TypeParamDefs result = Ast.TypeParamDefs(); + if (params != null) { + for (TypeParamContext p : params) { + result.add(transformTypeParam(p)); + } + } + return result; + } + private FuncDefs transformFuncDefs(List funcDef) { FuncDefs res = Ast.FuncDefs(); if (funcDef == null) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 76c95a010..98b138aaa 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1315,7 +1315,7 @@ public void simpleTypeClass() { "interface ToIndex", " function toIndex(T x) returns int", // 5 "class A", - "instance ToIndex", + "implements ToIndex", " function toIndex(A x) returns int", " return 42", "function foo(Q x)", // 10 @@ -1335,7 +1335,7 @@ public void forwardTypeClass() { "interface ToIndex", " function toIndex(T elem) returns int", "class A", - "instance ToIndex", + "implements ToIndex", " function toIndex(A x) returns int", " return 42", "function foo(T x)", @@ -1360,7 +1360,7 @@ public void subTypeClass() { "interface B extends A", " function y(T x) returns int", "class C", - "instance B", + "implements B", " function x(C x) returns int", " return 42", " function y(C x) returns int", @@ -1385,10 +1385,10 @@ public void typeClassAmbiguous() { "interface B extends A", " function y(T x) returns int", "class C", - "instance A", + "implements A", " function x(C x) returns int", " return 17", - "instance B", + "implements B", " function x(C x) returns int", " return 42", " function y(C x) returns int", @@ -1414,10 +1414,10 @@ public void buildInstance() { "interface B", " function y(Y y) returns int", "class C", - "instance A", + "implements A", " function x(C c) returns int", " return 41", - "instance implements B", + "implements B for T: A", " function y(T t) returns int", " return T.x(t) + 1", "function foo(Q q)", From 8ea93e752b11afb7dcd09c56887042e26e1bd3af Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 2 Feb 2020 00:42:42 +0100 Subject: [PATCH 22/50] first class with constraints --- .../peeeq/wurstio/WurstCompilerJassImpl.java | 6 +- .../imtranslation/ClassTranslator.java | 69 +++++++++++++++++-- .../imtranslation/EliminateTypeClasses.java | 26 ++++++- .../imtranslation/ExprTranslation.java | 10 ++- .../imtranslation/ImTranslator.java | 2 +- .../wurstscript/types/WurstTypeTypeParam.java | 5 ++ .../de/peeeq/wurstscript/utils/Utils.java | 18 +++++ .../tests/GenericsWithTypeclassesTests.java | 26 +++++++ .../wurstscript/tests/WurstScriptTest.java | 40 ++++++++--- 9 files changed, 178 insertions(+), 24 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 7683327d2..7d9c00fd9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -411,7 +411,11 @@ public JassProg transformProgToJass() { new EliminateGenerics(imTranslator2, imProg2).transform(); printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); if (!runArgs.isLua()) { - EliminateTypeClasses.transform(imTranslator2); + try { + EliminateTypeClasses.transform(imTranslator2); + } finally { + printDebugImProg("./test-output/at_crash.im"); + } } printDebugImProg("./test-output/im " + stage++ + "_typeClassesEliminated.im"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index bc05b05df..37e441c6b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -14,7 +14,9 @@ import de.peeeq.wurstscript.jassIm.ImVarAccess; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,6 +35,7 @@ public class ClassTranslator { private ImClass imClass; private ImProg prog; private ImFunction classInitFunc; + private final List typeConstraintVars = new ArrayList<>(); public ClassTranslator(ClassDef classDef, ImTranslator translator) { this.classDef = classDef; @@ -70,6 +73,7 @@ private void translate() { List subClasses = translator.getSubClasses(classDef); // order is important here + addTypeConstraintVars(classDef); translateMethods(classDef, subClasses); translateVars(classDef); translateClassInitFunc(); @@ -79,6 +83,18 @@ private void translate() { } + private void addTypeConstraintVars(ClassDef classDef) { + for (TypeParamDef tp : classDef.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cList = (TypeParamConstraintList) tp.getTypeParamConstraints(); + for (TypeParamConstraint c : cList) { + ImVar v = translator.getTypeClassParamFor(c); + typeConstraintVars.add(v); + } + } + } + imClass.getFields().addAll(typeConstraintVars); + } private void addSuperClasses() { @@ -124,8 +140,8 @@ private void createDestroyMethod(List subClasses) { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> ImTypeArgument(JassIm.ImTypeVarRef(tv))) - .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + .map(tv -> ImTypeArgument(JassIm.ImTypeVarRef(tv))) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } @@ -167,7 +183,14 @@ private boolean hasOwnDestroy(ModuleInstanciation mi) { private void createOnDestroyMethod() { OnDestroyDef onDestroy = classDef.getOnDestroy(); ImFunction f = translator.getFuncFor(onDestroy); - addOnDestroyActions(f, f.getBody(), classDef, translator.getThisVar(onDestroy)); + ImVar thisVar = translator.getThisVar(onDestroy); + addOnDestroyActions(f, f.getBody(), classDef, thisVar); + // destroy type + for (ImVar tc : typeConstraintVars) { + ImClassType t = (ImClassType) tc.getType(); + f.getBody().add(JassIm.ImDealloc(onDestroy, t, + JassIm.ImMemberAccess(onDestroy, JassIm.ImVarAccess(thisVar), JassIm.ImTypeArguments(), tc, JassIm.ImExprs()))); + } } private void addOnDestroyActions(ImFunction f, List addTo, ClassOrModuleInstanciation c, ImVar thisVar) { @@ -344,21 +367,29 @@ private void translateConstructor(ConstructorDef constr) { createConstructFunc(constr); } - + /** + * The new function contains the generic part: + * 1. allocating memory + * 2. calling the construct function + * 3. return a reference to the new instance + */ private void createNewFunc(ConstructorDef constr) { ConstructorDef trace = constr; ImFunction f = translator.getConstructNewFunc(constr); - Map varReplacements = Maps.newLinkedHashMap(); for (WParameter p : constr.getParameters()) { ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), Collections.emptyList()); - varReplacements.put(translator.getVarFor(p), imP); f.getParameters().add(imP); } + // add type constraint parameters: + List typeConstraintParams = this.typeConstraintVars.stream() + .map(ImVar::copy) + .collect(Collectors.toList()); + f.getParameters().addAll(typeConstraintParams); + ImVar thisVar = ImVar(constr, imClassType(), "this", Collections.emptyList()); - varReplacements.put(translator.getThisVar(constr), thisVar); f.getLocals().add(thisVar); // allocate class @@ -383,10 +414,31 @@ private void createNewFunc(ConstructorDef constr) { } + /** + * The construct function contains the class-specific code: + * - calls super constructor (if there is a super-class) + * - call function to init class variables + * - custom user defined constructor code + */ private void createConstructFunc(ConstructorDef constr) { ConstructorDef trace = constr; ImFunction f = translator.getConstructFunc(constr); ImVar thisVar = translator.getThisVar(constr); + + // add type constraint parameters: + List typeConstraintParams = this.typeConstraintVars.stream() + .map(ImVar::copy) + .collect(Collectors.toList()); + f.getParameters().addAll(typeConstraintParams); + + // set typeConstraintParams + for (Pair p : Utils.zip(typeConstraintVars, typeConstraintParams)) { + ImVar pField = p.getA(); + ImVar pParam = p.getB(); + f.getBody().add(ImSet(trace, ImMemberAccess(trace, ImVarAccess(thisVar), JassIm.ImTypeArguments(), pField, JassIm.ImExprs()), + JassIm.ImVarAccess(pParam))); + } + ConstructorDef superConstr = constr.attrSuperConstructor(); if (superConstr != null) { // call super constructor @@ -407,8 +459,11 @@ private void createConstructFunc(ConstructorDef constr) { } } } + // TODO add super-constructor type constraint parameters f.getBody().add(ImFunctionCall(trace, superConstrFunc, typeArgs, arguments, false, CallType.NORMAL)); } + + // call classInitFunc: ImTypeArguments typeArguments = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java index 5f259aab9..46ab38f20 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -19,7 +19,7 @@ * Look at where the dict is used: * When used as a function parameter: specialize the function * When used as a method parameter: specialize the method (including submethods and implementation) - * When used as an alloc parameter: specialize the whole class + * When assigned to a class field: That must be in a constructor. Specialize the class with all the assignments used in the constructor. * When used as receiver in a method call: Specialize method (if no submethods, replace by function call) * No other uses should be possible --> compilation error. *

@@ -37,6 +37,7 @@ public class EliminateTypeClasses { private final ArrayDeque workList = new ArrayDeque<>(); private final Table specializedFunctions = HashBasedTable.create(); private final Table outOfClassFuncs = HashBasedTable.create(); + private final Map typeClassSpecialization = new HashMap<>(); public EliminateTypeClasses(ImTranslator tr) { this.tr = tr; @@ -117,9 +118,28 @@ private void doSpecialize(ImTypeClassDictValue dictV) { return; } + // if used in other places (e.g. class parameters), don't monomorphize and + // just use the dynamic version here: + dictV.replaceBy(typeClassAlloc(dictV)); + } + + /** + * Dynamically allocate an instance of this type-class-instance. + */ + private Element typeClassAlloc(ImTypeClassDictValue dictV) { + ImClass clazz = getSpecializedTypeClassDict(key(dictV, 0)); + return JassIm.ImAlloc(dictV.getTrace(), JassIm.ImClassType(clazz, JassIm.ImTypeArguments())); + } - throw new CompileError(dictV.getTrace(), - "Unhandled parent for dict: " + parent + " // " + parent.getClass()); + private ImClass getSpecializedTypeClassDict(TypeClassInstanceKey key) { + + return typeClassSpecialization.computeIfAbsent(key, k -> { + if (key.args.isEmpty()) { + return key.base; + } else { + throw new RuntimeException("TODO specialize: " + key); + } + }); } private ImFunction getOutOfClassFunc(ImFunction impl, TypeClassInstanceKey key) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index b5fe33a17..ac1f6d73b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -445,7 +445,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu FunctionDefinition calledFunc = e.attrFuncDef(); - if (e.attrImplicitParameter() instanceof Expr) { + if (typeParamDispatchOn == null && e.attrImplicitParameter() instanceof Expr) { if (isCalledOnDynamicRef(e) && calledFunc instanceof FuncDef) { dynamicDispatch = true; } @@ -624,7 +624,13 @@ public static ImExpr translateIntern(ExprNewObject e, ImTranslator t, ImFunction WurstTypeClass wurstType = (WurstTypeClass) e.attrTyp(); ImClass imClass = t.getClassFor(wurstType.getClassDef()); ImTypeArguments typeArgs = getFunctionCallTypeArguments(t, sig, e, imClass.getTypeVariables()); - return ImFunctionCall(e, constructorImFunc, typeArgs, translateExprs(e.getArgs(), t, f), false, CallType.NORMAL); + ImExprs args = translateExprs(e.getArgs(), t, f); + + // translate type constraints: + ImTypeVars tps = t.getClassFor(constructorFunc.attrNearestClassDef()).getTypeVariables(); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, tps, args); + + return ImFunctionCall(e, constructorImFunc, typeArgs, args, false, CallType.NORMAL); } public static ImExprOpt translate(NoExpr e, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index d977e1e92..54b75784c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1325,7 +1325,7 @@ public boolean isLuaTarget() { public ImVar getTypeClassParamFor(TypeParamConstraint tc) { ImVar v = typeClassParamFor.get(tc); if (v == null) { - TypeParamDef tp = (TypeParamDef) tc.getParent().getParent(); + TypeParamDef tp = tc.parentTypeParam(); WurstTypeInterface wti = (WurstTypeInterface) tc.attrConstraintTyp(); ImClassType t = wti.imTranslateType(this); int i = ((TypeParamConstraintList) tp.getTypeParamConstraints()).indexOf(tc); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index a8a3ea97c..047509675 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -147,4 +147,9 @@ public List getTypeConstraints() { public boolean isStaticRef() { return isStaticRef; } + + @Override + public boolean allowsDynamicDispatch() { + return false; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java index 729b39779..4ce496afe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java @@ -28,6 +28,7 @@ import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; public class Utils { @@ -1079,6 +1080,23 @@ public static boolean listEquals(List xs, List ys, BiPredicate c return true; } + public static Iterable> zip(Collection as, Collection bs) { + return () -> new Iterator>() { + Iterator ia = as.iterator(); + Iterator ib = bs.iterator(); + + @Override + public boolean hasNext() { + return ia.hasNext() && ib.hasNext(); + } + + @Override + public Pair next() { + return Pair.create(ia.next(), ib.next()); + } + }; + } + public static class ExecResult { private final String stdOut; private final String stdErr; diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 98b138aaa..435174f07 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1429,5 +1429,31 @@ public void buildInstance() { ); } + @Test + public void classParameterConstraint() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "init", + " let c = new C", + " c.set(\"42\")", + " if c.get() == 42", + " testSuccess()" + ); + } + } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java index 9d8342bf3..c52eb6543 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java @@ -22,6 +22,7 @@ import de.peeeq.wurstscript.jassinterpreter.TestFailException; import de.peeeq.wurstscript.jassinterpreter.TestSuccessException; import de.peeeq.wurstscript.jassprinter.JassPrinter; +import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.translation.lua.translation.LuaTranslator; import de.peeeq.wurstscript.luaAst.LuaCompilationUnit; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; @@ -116,7 +117,26 @@ TestConfig expectError(String expectedError) { CompilationResult lines(String... lines) { String testName = UtilsIO.getMethodName(WurstScriptTest.class.getName()); additionalCompilationUnits.add(new CU(testName, Utils.join(lines, "\n") + "\n")); - return run(); + try { + return run(); + } catch (CompileError e) { + WPos source = e.getSource(); + if (source.getFile().equals(testName) + && source.getLine() >= 1 + && source.getLine() <= lines.length) { + StringBuilder msg = new StringBuilder(); + for (int i = Math.max(0, source.getLine() - 3); i < source.getLine(); i++) { + msg.append(lines[i]).append("\n"); + } + msg.append(Utils.repeat(' ', source.getStartColumn() - 1)); + msg.append(Utils.repeat('^', source.getEndColumn() - source.getStartColumn())); + msg.append("\n"); + msg.append(e.getMessage()); + throw new CompileError(source, msg.toString(), e.getErrorType(), e); + } else { + throw e; + } + } } CompilationResult compilationUnits(CU... units) { @@ -133,7 +153,7 @@ CompilationResult run() { } else { List errors = res.getGui().getErrorList(); if (errors.stream() - .noneMatch(e -> e.getMessage().toLowerCase().contains(expectedError.toLowerCase()))) { + .noneMatch(e -> e.getMessage().toLowerCase().contains(expectedError.toLowerCase()))) { for (CompileError error : errors) { System.err.println("Unexpected error:" + error); } @@ -347,15 +367,15 @@ private void testWithInliningAndOptimizations(String name, boolean executeProg, } private void testWithInliningAndOptimizationsAndStacktraces(String name, boolean executeProg, boolean executeTests, WurstGui gui, - WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) throws Error { + WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) throws Error { // test with inlining and local optimization compiler.setRunArgs(runArgs.with("-inline", "-localOptimizations", "-stacktraces")); translateAndTest(name + "_stacktraceinlopt", executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); } private void testWithInlining(String name, boolean executeProg, boolean executeTests, WurstGui gui - , WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms - , RunArgs runArgs) throws Error { + , WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms + , RunArgs runArgs) throws Error { // test with inlining compiler.setRunArgs(runArgs.with("-inline")); translateAndTest(name + "_inl", executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); @@ -370,7 +390,7 @@ private void testWithLocalOptimizations(String name, boolean executeProg, boolea private void testWithoutInliningAndOptimization(String name, boolean executeProg, boolean executeTests, WurstGui gui, WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) - throws Error { + throws Error { compiler.setRunArgs(runArgs); // test without inlining and optimization translateAndTest(name, executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); @@ -504,8 +524,8 @@ WurstModel parseFiles(Iterable inputFiles, Map inputs, boolean withStdLib, WurstCompilerJassImpl compiler) { List inputList = inputs.entrySet().stream() - .map(e -> new CU(e.getKey(), e.getValue())) - .collect(Collectors.toList()); + .map(e -> new CU(e.getKey(), e.getValue())) + .collect(Collectors.toList()); return parseFiles(inputFiles, inputList, withStdLib, compiler); } @@ -553,7 +573,7 @@ private void executeImProg(WurstGui gui, ImProg imProg) throws TestFailException } private void executeJassProg(JassProg prog) - throws TestFailException { + throws TestFailException { try { // run the interpreter with the optimized program JassInterpreter interpreter = new JassInterpreter(); @@ -571,7 +591,7 @@ private void executeTests(WurstGui gui, ImTranslator translator, ImProg imProg) RunTests.TestResult res = runTests.runTests(translator, imProg, null, null); if (res.getPassedTests() < res.getTotalTests()) { throw new Error("tests failed: " + res.getPassedTests() + " / " + res.getTotalTests() + "\n" + - gui.getErrors()); + gui.getErrors()); } } From 13bfc734852cde948fc93c7fb124cd79b114d107 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 2 Feb 2020 00:48:54 +0100 Subject: [PATCH 23/50] add missing super-visit --- .../wurstscript/translation/imtranslation/ClassesOptimizer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java index 98ab90c6e..fb97208c4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java @@ -65,6 +65,7 @@ public void visit(ImDealloc deAlloc) { @Override public void visit(ImOperatorCall c) { + super.visit(c); WurstOperator op = c.getOp(); if (op == WurstOperator.EQ || op == WurstOperator.NOTEQ) { for (ImExpr arg : c.getArguments()) { @@ -86,6 +87,7 @@ private void markNotStateless(ImClass classDef) { @Override public void visit(ImCast c) { + super.visit(c); if (c.getToType().equalsType(TypesHelper.imInt())) { markNotStateless(c.getExpr().attrTyp()); } From 500c2507ad0811cd5d376f46b601e834e20b9ea6 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 2 Feb 2020 01:00:15 +0100 Subject: [PATCH 24/50] fixed NPE and tests --- .../translation/imtranslation/ExprTranslation.java | 13 ++++++++++++- .../java/tests/wurstscript/tests/ClassesTests.java | 3 ++- .../tests/wurstscript/tests/NewFeatureTests.java | 6 ++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index ac1f6d73b..0862c6616 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; @@ -18,6 +19,7 @@ import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; import io.vavr.control.Option; +import org.eclipse.jdt.annotation.Nullable; import java.util.Collections; import java.util.List; @@ -441,7 +443,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu List arguments = Lists.newArrayList(e.getArgs()); Expr leftExpr = null; boolean dynamicDispatch = false; - TypeParamConstraint typeParamDispatchOn = e.attrFuncLink().getTypeParamConstraint(); + TypeParamConstraint typeParamDispatchOn = getTypeParamConstraint(e); FunctionDefinition calledFunc = e.attrFuncDef(); @@ -554,6 +556,15 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } } + @Nullable + private static TypeParamConstraint getTypeParamConstraint(FunctionCall e) { + FuncLink funcLink = e.attrFuncLink(); + if (funcLink == null) { + return null; + } + return funcLink.getTypeParamConstraint(); + } + /** * For each type parameter constraint adds an argument containing the type class dictionary. */ diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java index e4fee456e..d99f81a21 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java @@ -574,8 +574,9 @@ public void destroyed() { "package test", " native testSuccess()", " class A", + " int x = 7", " function foo() returns int", - " return 7", + " return x", " init", " A a = new A()", " let b = a", diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java index fbb3d9c4b..25c2efdc2 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java @@ -510,6 +510,7 @@ public void instanceCount() { "native testSuccess()", "native instanceCount(int typeId) returns int", "class A", + " int x = 42", "init", " let a = new A", " let b = new A", @@ -520,7 +521,7 @@ public void instanceCount() { " destroy a", " destroy e", " let count2 = instanceCount(A.typeId)", - " if count1 == 5 and count2 == 3", + " if count1 == 5 and count2 == 3 and b.x == 42", " testSuccess()" ); } @@ -534,6 +535,7 @@ public void instanceMaxCount() { "native testSuccess()", "native maxInstanceCount(int typeId) returns int", "class A", + " int x = 42", "init", " let a = new A", " let b = new A", @@ -544,7 +546,7 @@ public void instanceMaxCount() { " destroy a", " destroy e", " let count2 = maxInstanceCount(A.typeId)", - " if count1 == 5 and count2 == 5", + " if count1 == 5 and count2 == 5 and b.x == 42", " testSuccess()" ); } From 949a2ce10b903550072cc9dcb7bcc24fe22bb014 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 2 Feb 2020 15:11:33 +0100 Subject: [PATCH 25/50] dependent constraints for classes --- .../imtranslation/EliminateTypeClasses.java | 158 +++++++++++++++--- .../tests/GenericsWithTypeclassesTests.java | 35 +++- 2 files changed, 170 insertions(+), 23 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java index 46ab38f20..f201269fd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -4,6 +4,9 @@ import de.peeeq.datastructures.Partitions; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.TypeClassInstance; +import org.eclipse.xtend.lib.annotations.ToString; +import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.stream.Collectors; @@ -35,7 +38,7 @@ public class EliminateTypeClasses { private final ImTranslator tr; private final ArrayDeque workList = new ArrayDeque<>(); - private final Table specializedFunctions = HashBasedTable.create(); + private final Table specializedFunctions = HashBasedTable.create(); private final Table outOfClassFuncs = HashBasedTable.create(); private final Map typeClassSpecialization = new HashMap<>(); @@ -97,7 +100,7 @@ private void doSpecialize(ImTypeClassDictValue dictV) { } mc.replaceBy(JassIm.ImFunctionCall( mc.getTrace(), - getOutOfClassFunc(m.getImplementation(), key(dictV, 0)), + getOutOfClassFunc(m.getImplementation(), key(dictV)), mc.getTypeArguments(), mc.getArguments(), false, @@ -127,19 +130,95 @@ private void doSpecialize(ImTypeClassDictValue dictV) { * Dynamically allocate an instance of this type-class-instance. */ private Element typeClassAlloc(ImTypeClassDictValue dictV) { - ImClass clazz = getSpecializedTypeClassDict(key(dictV, 0)); + ImClass clazz = getSpecializedTypeClassDict(key(dictV)); return JassIm.ImAlloc(dictV.getTrace(), JassIm.ImClassType(clazz, JassIm.ImTypeArguments())); } private ImClass getSpecializedTypeClassDict(TypeClassInstanceKey key) { return typeClassSpecialization.computeIfAbsent(key, k -> { - if (key.args.isEmpty()) { - return key.base; - } else { - throw new RuntimeException("TODO specialize: " + key); - } + if (key.args.isEmpty()) { + return key.base; + } else { + return specializeClass(key); + } + }); + } + + private ImClass specializeClass(TypeClassInstanceKey key) { + ImProg imProg = tr.getImProg(); + ImClass base = key.base; + // create new subclass + ImClass sub = JassIm.ImClass( + base.getTrace(), + base.getName() + key.args, + JassIm.ImTypeVars(), + JassIm.ImVars(), + JassIm.ImMethods(), + JassIm.ImFunctions(), + Collections.singletonList(JassIm.ImClassType(base, JassIm.ImTypeArguments())) + ); + imProg.getClasses().add(sub); + + + // specialize methods using the fields + final ImVars baseFields = base.getFields(); + + + List toSpecialize = imProg.getMethods() + .stream() + .filter(m -> m.getMethodClass().getClassDef() == base + && !collectFieldAccesses(m.getImplementation(), baseFields).isEmpty()) + .collect(Collectors.toList()); + + + // if one of the fields is used, create a new submethod: + for (ImMethod m : toSpecialize) { + + ImFunction impl = m.getImplementation(); + + + ImFunction newImpl = impl.copyWithRefs(); + List fieldAccesses = collectFieldAccesses(newImpl, baseFields); + for (ImMemberAccess fa : fieldAccesses) { + int index = baseFields.indexOf(fa.getVar()); + TypeClassInstanceKey tc = key.args.get(index); + ImTypeClassDictValue dictValue = tc.makeDictValue(fa.getTrace()); + fa.replaceBy(dictValue); + workList.add(dictValue); + } + + ImMethod newM = JassIm.ImMethod( + m.getTrace(), + JassIm.ImClassType(sub, JassIm.ImTypeArguments()), + m.getName(), + newImpl, + new ArrayList<>(), + false + ); + + m.getSubMethods().add(newM); + + imProg.getFunctions().add(newImpl); + imProg.getMethods().add(newM); + } + + return sub; + } + + @NotNull + private List collectFieldAccesses(ImFunction impl, ImVars baseFields) { + List fieldAccesses = new ArrayList<>(); + impl.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImMemberAccess e) { + super.visit(e); + if (baseFields.contains(e.getVar())) { + fieldAccesses.add(e); + } + } }); + return fieldAccesses; } private ImFunction getOutOfClassFunc(ImFunction impl, TypeClassInstanceKey key) { @@ -192,7 +271,7 @@ private boolean isSubClass(ImClass sub, ImClass sup) { .anyMatch(sc -> isSubClass(sc.getClassDef(), sup)); } - private ImFunction getSpecializeFunc(ImFunction func, TypeClassInstanceKey key) { + private ImFunction getSpecializeFunc(ImFunction func, ParameterSpecializationKey key) { ImFunction res = specializedFunctions.get(func, key); if (res != null) { return res; @@ -202,7 +281,7 @@ private ImFunction getSpecializeFunc(ImFunction func, TypeClassInstanceKey key) return res; } - private ImFunction specializeFunc(ImFunction func, TypeClassInstanceKey key) { + private ImFunction specializeFunc(ImFunction func, ParameterSpecializationKey key) { ImFunction copy = func.copyWithRefs(); copy.setName(func.getName() + "_" + key); ImVar removedParam = copy.getParameters().remove(key.index); @@ -218,7 +297,7 @@ public void visit(ImVarAccess va) { }); for (ImVarAccess use : uses) { - ImTypeClassDictValue newDict = key.makeDictValue(use.attrTrace()); + ImTypeClassDictValue newDict = key.key.makeDictValue(use.attrTrace()); use.replaceBy(newDict); workList.add(newDict); } @@ -244,28 +323,64 @@ private static List dynamicArgs(ImTypeClassDictValue dv) { return dynamicArgsStream(dv).collect(Collectors.toList()); } - private TypeClassInstanceKey key(ImTypeClassDictValue dictV, int index) { - return new TypeClassInstanceKey(dictV.getClazz().getClassDef(), index, + private ParameterSpecializationKey key(ImTypeClassDictValue dictV, int index) { + return new ParameterSpecializationKey(index, + new TypeClassInstanceKey(dictV.getClazz().getClassDef(), + dictV.getArguments().stream() + .map(v -> key((ImTypeClassDictValue) v)) + .collect(Collectors.toList()))); + } + + private TypeClassInstanceKey key(ImTypeClassDictValue dictV) { + return new TypeClassInstanceKey(dictV.getClazz().getClassDef(), dictV.getArguments().stream() - .map(v -> key((ImTypeClassDictValue) v, index)) + .map(v -> key((ImTypeClassDictValue) v)) .collect(Collectors.toList())); } + static class ParameterSpecializationKey { + private int index; + private TypeClassInstanceKey key; + + public ParameterSpecializationKey(int index, TypeClassInstanceKey key) { + this.index = index; + this.key = key; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParameterSpecializationKey that = (ParameterSpecializationKey) o; + return index == that.index && + key.equals(that.key); + } + + @Override + public int hashCode() { + return Objects.hash(index, key); + } + + @Override + public String toString() { + return key + "@" + index; + } + } + // TODO remove index here and make another class for parameter index static class TypeClassInstanceKey { private final ImClass base; - private int index; + private final List args; - TypeClassInstanceKey(ImClass base, int index, List args) { + TypeClassInstanceKey(ImClass base, List args) { this.base = base; - this.index = index; this.args = args; } @Override public String toString() { - return "#" + base + "@" + index + args; + return "#" + base + args; } @Override @@ -273,14 +388,13 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TypeClassInstanceKey that = (TypeClassInstanceKey) o; - return index == that.index && - base.equals(that.base) && - args.equals(that.args); + return Objects.equals(base, that.base) && + Objects.equals(args, that.args); } @Override public int hashCode() { - return Objects.hash(base, index, args); + return Objects.hash(base, args); } public ImTypeClassDictValue makeDictValue(de.peeeq.wurstscript.ast.Element trace) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 435174f07..284d9a9ce 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1452,7 +1452,40 @@ public void classParameterConstraint() { " c.set(\"42\")", " if c.get() == 42", " testSuccess()" - ); + ); + } + + @Test + public void classParameterConstraintBuild() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class Cell", + " T elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "implements ToInt> for T: ToInt", + " function toInt(Cell c) returns int", + " return T.toInt(c.elem)", + "init", + " let cell = new Cell", + " cell.elem = \"42\"", + " let c = new C>", + " c.set(cell)", + " if c.get() == 42", + " testSuccess()" + ); } From 895a31ff836c2724515beb476a18b0c96290534e Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 2 Feb 2020 22:57:04 +0100 Subject: [PATCH 26/50] some memory usage tweaks for travis --- .../peeeq/wurstio/WurstCompilerJassImpl.java | 10 +++- .../peeeq/wurstscript/attributes/AttrPos.java | 6 +- .../jass/AntlrJassParseTreeTransformer.java | 14 +++-- .../jurst/AntlrJurstParseTreeTransformer.java | 14 +++-- .../de/peeeq/wurstscript/parser/WPos.java | 57 +++++++++++++------ .../antlr/AntlrWurstParseTreeTransformer.java | 16 +++--- .../wurstscript/tests/WurstScriptTest.java | 1 + 7 files changed, 77 insertions(+), 41 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index 7d9c00fd9..0e6c4ffe5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -346,7 +346,7 @@ private void resolveImport(Function addCompilationUnit, S private CompilationUnit loadLibPackage(Function addCompilationUnit, String imp) { File file = getLibs().get(imp); if (file == null) { - gui.sendError(new CompileError(new WPos("", null, 0, 0), "Could not find lib-package " + imp + ". Are you missing your wurst.dependencies file?")); + gui.sendError(new CompileError(WPos.noSource(), "Could not find lib-package " + imp + ". Are you missing your wurst.dependencies file?")); return Ast.CompilationUnit(new CompilationUnitInfo(errorHandler), Ast.JassToplevelDeclarations(), Ast.WPackages()); } else { return addCompilationUnit.apply(file); @@ -831,4 +831,12 @@ public LuaCompilationUnit transformProgToLua() { LuaCompilationUnit luaCode = luaTranslator.translate(); return luaCode; } + + /** + * Clears the intermediate output programs to allow collection by garbage collector + */ + public void clear() { + imProg = null; + prog = null; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java index 73c95c7d7..0dbee6fa8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java @@ -31,7 +31,7 @@ public static WPos getPos(Element e) { min = Math.min(min, childSource.getLeftPos()); max = Math.max(max, childSource.getRightPos()); } - return new WPos(e.get(0).attrSource().getFile(), e.get(0).attrSource().getLineOffsets(), min, max); + return new WPos(e.get(0).attrSource().getFileInfo(), min, max); } // if no childs exist, search a parent element with a explicit position return getParentSource(e); @@ -49,7 +49,7 @@ public static WPos getPos(WImports e) { max = Math.max(max, childSource.getRightPos()); } if (min != Integer.MAX_VALUE) { - return new WPos(e.get(0).attrSource().getFile(), e.get(0).attrSource().getLineOffsets(), min, max); + return new WPos(e.get(0).attrSource().getFileInfo(), min, max); } else { return getParentSource(e); } @@ -62,7 +62,7 @@ private static WPos getParentSource(Element e) { if (parent instanceof AstElementWithSource) { WPos parentSource = ((AstElementWithSource) parent).getSource(); // use parent position but with size -1, so we do not go into this - return new WPos(parentSource.getFile(), parentSource.getLineOffsets(), parentSource.getLeftPos(), parentSource.getLeftPos() - 1); + return new WPos(parentSource.getFileInfo(), parentSource.getLeftPos(), parentSource.getLeftPos() - 1); } parent = parent.getParent(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java index c9b181428..6720d06e2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java @@ -18,15 +18,17 @@ public class AntlrJassParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; public AntlrJassParseTreeTransformer(String file, ErrorHandler cuErrorHandler, LineOffsets lineOffsets) { this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(JassParser.CompilationUnitContext cu) { @@ -70,7 +72,7 @@ private String text(@Nullable Token t) { private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -496,12 +498,12 @@ private WParameter transformFormalParameter(JassParser.FormalParamContext p, } private WPos source(ParserRuleContext p) { - return new WPos(file, lineOffsets, p.start.getStartIndex(), + return new WPos(fileInfo, p.start.getStartIndex(), p.stop.getStopIndex() + 1); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java index 4f7c3460f..653c90e05 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java @@ -20,9 +20,10 @@ public class AntlrJurstParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; private boolean isJassCode = true; public AntlrJurstParseTreeTransformer(String file, @@ -30,6 +31,7 @@ public AntlrJurstParseTreeTransformer(String file, this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(CompilationUnitContext cu) { @@ -74,7 +76,7 @@ private JassToplevelDeclaration transformJassToplevelDecl( private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -1272,12 +1274,12 @@ private WImport transformImport(WImportContext i) { } private WPos source(ParserRuleContext p) { - return new WPos(file, lineOffsets, p.start.getStartIndex(), + return new WPos(fileInfo, p.start.getStartIndex(), p.stop.getStopIndex() + 1); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java index 6a020e389..902c12af8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java @@ -7,24 +7,45 @@ import java.util.regex.Pattern; public class WPos { - private final String file; - private final @Nullable LineOffsets lineOffsets; + + private FileInfo fileInfo; private final int leftPos; private final int rightPos; - public WPos(String file, @Nullable LineOffsets lineOffsets, int leftPos, int rightPos) { - this.file = file; - this.lineOffsets = lineOffsets; + public static WPos noSource() { + return new WPos(new FileInfo("", null), 0, 0); + } + + public FileInfo getFileInfo() { + return fileInfo; + } + + public static class FileInfo { + private final String file; + private final @Nullable LineOffsets lineOffsets; + + public FileInfo(String file, @Nullable LineOffsets lineOffsets) { + this.file = file; + this.lineOffsets = lineOffsets; + } + } + + public WPos(String file, LineOffsets lo, int leftPos, int rightPos) { + this(new FileInfo(file, lo), leftPos, rightPos); + } + + public WPos(FileInfo fileInfo, int leftPos, int rightPos) { + this.fileInfo = fileInfo; this.leftPos = leftPos; this.rightPos = rightPos; } public String getFile() { - return file; + return fileInfo.file; } public @Nullable LineOffsets getLineOffsets() { - return lineOffsets; + return fileInfo.lineOffsets; } public int getLeftPos() { @@ -37,35 +58,35 @@ public int getRightPos() { public int getLine() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getLine(leftPos); } public int getEndLine() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getLine(rightPos); } public int getStartColumn() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getColumn(leftPos); } public int getEndColumn() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getColumn(rightPos); } public WPos withRightPos(int rightPos) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(fileInfo, leftPos, rightPos); } @Override @@ -75,13 +96,13 @@ public String toString() { } public String print() { - return "[" + file + " line " + getLine() + "]"; + return "[" + getFile() + " line " + getLine() + "]"; } public String printShort() { Pattern p = Pattern.compile("^.*[/\\\\]([^/\\\\]+)\\.[^.]*$"); - String shortFile = file; - Matcher m = p.matcher(file); + String shortFile = getFile(); + Matcher m = p.matcher(getFile()); if (m.find()) { shortFile = m.group(1); } @@ -89,11 +110,11 @@ public String printShort() { } public WPos withLeftPos(int leftPos) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(fileInfo, leftPos, rightPos); } public WPos withFile(String file) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(new FileInfo(file, fileInfo.lineOffsets), leftPos, rightPos); } public String shortFile() { @@ -107,7 +128,7 @@ public String shortFile() { * makes this position artificial by setting the rightPost = leftPos-1 */ public WPos artificial() { - return new WPos(file, lineOffsets, leftPos, leftPos - 1); + return new WPos(fileInfo, leftPos, leftPos - 1); } /** diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 1d6fcdae8..99924b0c8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -22,15 +22,17 @@ public class AntlrWurstParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; public AntlrWurstParseTreeTransformer(String file, ErrorHandler cuErrorHandler, LineOffsets lineOffsets) { this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(CompilationUnitContext cu) { @@ -74,14 +76,14 @@ private JassToplevelDeclaration transformJassToplevelDecl( private Identifier text(@Nullable Token t) { if (t == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(t), t.getText()); } private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -1362,11 +1364,11 @@ private WPos source(ParserRuleContext p) { } else { stopIndex = p.stop.getStopIndex() + 1; } - return new WPos(file, lineOffsets, p.start.getStartIndex(), stopIndex); + return new WPos(fileInfo, p.start.getStartIndex(), stopIndex); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java index c52eb6543..79c96286c 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java @@ -467,6 +467,7 @@ private void translateAndTestLua(String name, boolean executeProg, WurstGui gui, private void translateAndTest(String name, boolean executeProg, boolean executeTests, WurstGui gui, WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms) throws Error { + compiler.clear(); ImProg imProg = compiler.translateProgToIm(model); From 5e15802c882a9ca04b2f03529efb3fd44a888cdb Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Mon, 3 Feb 2020 00:26:47 +0100 Subject: [PATCH 27/50] increase heap size for travis --- de.peeeq.wurstscript/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index ba1633526..38cd22332 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -213,7 +213,7 @@ compileJava.dependsOn gen test { // set minimal heap size required to run tests: - jvmArgs = ['-Xms256m'] + maxHeapSize = "1G" useTestNG() { suites 'src/test/resources/AllTestsSuite.xml' From 2bd09281e6bf49c6e7304b759ceb789c4774c8e0 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 4 Feb 2020 21:24:31 +0100 Subject: [PATCH 28/50] super-type constraint with dependency --- .../de/peeeq/wurstscript/TypeClasses.java | 157 ++++++++++++++++++ .../AttrPossibleFunctionSignatures.java | 135 +-------------- .../interpreter/ProgramState.java | 5 +- .../imtranslation/ClassTranslator.java | 8 +- .../imtranslation/EliminateGenerics.java | 9 +- .../imtranslation/ExprTranslation.java | 17 +- .../wurstscript/types/TypeClassInstance.java | 28 +++- .../types/WurstTypeBoundTypeParam.java | 5 + .../types/WurstTypeNamedScope.java | 46 ++++- .../tests/GenericsWithTypeclassesTests.java | 61 +++++++ 10 files changed, 315 insertions(+), 156 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java new file mode 100644 index 000000000..2fa288230 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -0,0 +1,157 @@ +package de.peeeq.wurstscript; + +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.types.*; +import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; +import io.vavr.Tuple2; +import io.vavr.control.Option; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TypeClasses { + public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { + List errors = new ArrayList<>(); + VariableBinding mapping = sig.getMapping(); + for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { + Option matchedTypeOpt = mapping.get(tp); + List constraints = getConstraints(tp); + if (matchedTypeOpt.isEmpty()) { + if (!constraints.isEmpty()) { + errors.add(new CompileError(fc.attrSource(), "Type parameter " + tp.getName() + " is not bound, so type constraints cannot be solved.")); + } + continue; + } + WurstTypeBoundTypeParam matchedType = matchedTypeOpt.get(); + for (WurstTypeInterface constraint : constraints) { + VariableBinding mapping2 = findTypeClass(fc, errors, mapping, tp, matchedType, constraint); + if (mapping2 == null) { + errors.add(new CompileError(fc.attrSource(), "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint + ".")); + } else { + mapping = mapping2; + } + } + } + sig = sig.setTypeArgs(fc, mapping); + return Pair.create(sig, errors); + } + + @NotNull + public static List getConstraints(TypeParamDef tp) { + List constraints = new ArrayList<>(); + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + WurstType ct = c.attrConstraintTyp(); + if (ct instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) ct; + constraints.add(wti); + + + } + } + } + return constraints; + } + + /** + * Finds an instance of a type class + * @param location + * @param errors + * @param mapping + * @param tp + * @param matchedType + * @param constraint1 + * @return the updated type var mapping if any was found + * + * TODO change to stream return type to allow backtracking + */ + public static VariableBinding findTypeClass(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { + WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); + + // option 1: the matched type is a type param that also has the right constraint: + if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { + WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); + Optional matchingConstraint = wtp.getTypeConstraints().stream() + .filter(c -> c.attrConstraintTyp().isSubtypeOf(constraint, location)).findFirst(); + if (matchingConstraint.isPresent()) { + TypeClassInstance instance = TypeClassInstance.fromTypeParam( + location, matchingConstraint.get()); + return mapping.set(tp, matchedType.withTypeClassInstance(instance)); + } + } + // option 2: find instance declarations + // TODO create index to make this faster and use normal scoped lookup (ony search imports) + WurstModel model = location.getModel(); + List instances = model.stream() + .flatMap(cu -> cu.getPackages().stream()) + .flatMap(p -> p.getElements().stream()) + .filter(e -> e instanceof InstanceDecl) + .map(e -> (InstanceDecl) e) + .flatMap(instance -> { + WurstType instanceType = instance.getImplementedInterface().attrTyp(); + VariableBinding initialMapping = VariableBinding.emptyMapping().withTypeVariables(instance.getTypeParameters()); + VariableBinding match = instanceType.matchAgainstSupertype(constraint, location, initialMapping, VariablePosition.LEFT); + + + if (match == null) { + return Stream.empty(); + } + instanceType = instanceType.setTypeArgs(match); + + + + for (Tuple2 m : match) { + TypeParamDef instanceTp = m._1(); + WurstTypeBoundTypeParam mType = m._2(); + List instanceConstraints = getConstraints(instanceTp); + for (WurstTypeInterface instanceConstraint : instanceConstraints) { + VariableBinding match2 = findTypeClass(location, errors, match, instanceTp, mType, instanceConstraint); + if (match2 == null) { + return Stream.empty(); + } + match = match2; + } + } + + List deps = new ArrayList<>(); + List typeArgs = new ArrayList<>(); + for (TypeParamDef instanceTp : instance.getTypeParameters()) { + WurstTypeBoundTypeParam i = match.get(instanceTp).get(); + deps.addAll(i.getInstances()); + if (instanceTp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + typeArgs.add(i); + } + } + + + // TODO resolve dependencies + + TypeClassInstance result = TypeClassInstance.fromInstance(instance, typeArgs, deps, (WurstTypeInterface) instanceType); + + return Stream.of(result); + + }).collect(Collectors.toList()); + + if (instances.isEmpty()) { + errors.add(new CompileError(location, + "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint.getName())); + // "Could not find type class instance " + constraint.getName() + " for type " + matchedType)); + return null; + } else { + if (instances.size() > 1) { + errors.add(new CompileError(location, + "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + + Utils.printSep("\n", instances))); + + } + TypeClassInstance instance = Utils.getFirst(instances); + return mapping.set(tp, matchedType.withTypeClassInstance(instance)); + } + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index c78b73145..502249486 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -3,19 +3,14 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; import de.peeeq.wurstscript.utils.Pair; -import de.peeeq.wurstscript.utils.Utils; -import io.vavr.Tuple2; -import io.vavr.control.Option; -import org.jetbrains.annotations.NotNull; import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static de.peeeq.wurstscript.attributes.GenericsHelper.givenBinding; import static de.peeeq.wurstscript.attributes.GenericsHelper.typeParameters; @@ -56,7 +51,7 @@ private static ImmutableCollection findBestSignature(StmtCall if (sig2 == null) { continue; } - Pair> typeClassMatched = findTypeClasses(sig2, fc); + Pair> typeClassMatched = TypeClasses.findTypeClasses(sig2, fc); if (typeClassMatched.getB().isEmpty()) { resultBuilder2.add(typeClassMatched.getA()); } else { @@ -90,132 +85,6 @@ private static ImmutableCollection findBestSignature(StmtCall } } - private static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { - List errors = new ArrayList<>(); - VariableBinding mapping = sig.getMapping(); - for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { - Option matchedTypeOpt = mapping.get(tp); - List constraints = getConstraints(tp); - if (matchedTypeOpt.isEmpty()) { - if (!constraints.isEmpty()) { - errors.add(new CompileError(fc.attrSource(), "Type parameter " + tp.getName() + " is not bound, so type constraints cannot be solved.")); - } - continue; - } - WurstTypeBoundTypeParam matchedType = matchedTypeOpt.get(); - for (WurstTypeInterface constraint : constraints) { - VariableBinding mapping2 = findTypeClass(fc, errors, mapping, tp, matchedType, constraint); - if (mapping2 == null) { - errors.add(new CompileError(fc.attrSource(), "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint + ".")); - } else { - mapping = mapping2; - } - } - } - sig = sig.setTypeArgs(fc, mapping); - return Pair.create(sig, errors); - } - - @NotNull - private static List getConstraints(TypeParamDef tp) { - List constraints = new ArrayList<>(); - if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { - WurstType ct = c.attrConstraintTyp(); - if (ct instanceof WurstTypeInterface) { - WurstTypeInterface wti = (WurstTypeInterface) ct; - constraints.add(wti); - - - } - } - } - return constraints; - } - - private static VariableBinding findTypeClass(StmtCall fc, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { - WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); - - // option 1: the matched type is a type param that also has the right constraint: - if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { - WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); - Optional matchingConstraint = wtp.getTypeConstraints().stream() - .filter(c -> c.attrConstraintTyp().isSubtypeOf(constraint, fc)).findFirst(); - if (matchingConstraint.isPresent()) { - TypeClassInstance instance = TypeClassInstance.fromTypeParam( - fc, matchingConstraint.get()); - return mapping.set(tp, matchedType.withTypeClassInstance(instance)); - } - } - // option 2: find instance declarations - // TODO create index to make this faster and use normal scoped lookup (ony search imports) - WurstModel model = fc.getModel(); - List instances = model.stream() - .flatMap(cu -> cu.getPackages().stream()) - .flatMap(p -> p.getElements().stream()) - .filter(e -> e instanceof InstanceDecl) - .map(e -> (InstanceDecl) e) - .flatMap(instance -> { - WurstType instanceType = instance.getImplementedInterface().attrTyp(); - VariableBinding initialMapping = VariableBinding.emptyMapping().withTypeVariables(instance.getTypeParameters()); - VariableBinding match = instanceType.matchAgainstSupertype(constraint, fc, initialMapping, VariablePosition.LEFT); - - - if (match == null) { - return Stream.empty(); - } - instanceType = instanceType.setTypeArgs(match); - - - for (Tuple2 m : match) { - TypeParamDef instanceTp = m._1(); - WurstTypeBoundTypeParam mType = m._2(); - List instanceConstraints = getConstraints(instanceTp); - for (WurstTypeInterface instanceConstraint : instanceConstraints) { - VariableBinding match2 = findTypeClass(fc, errors, match, instanceTp, mType, instanceConstraint); - if (match2 == null) { - return Stream.empty(); - } - match = match2; - } - } - - List deps = new ArrayList<>(); - List typeArgs = new ArrayList<>(); - for (TypeParamDef instanceTp : instance.getTypeParameters()) { - WurstTypeBoundTypeParam i = match.get(instanceTp).get(); - deps.addAll(i.getInstances()); - if (instanceTp.getTypeParamConstraints() instanceof TypeParamConstraintList) { - typeArgs.add(i); - } - } - - - // TODO resolve dependencies - - TypeClassInstance result = TypeClassInstance.fromInstance(instance, typeArgs, deps, (WurstTypeInterface) instanceType); - - return Stream.of(result); - - }).collect(Collectors.toList()); - - if (instances.isEmpty()) { - errors.add(new CompileError(fc, - "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint.getName())); - // "Could not find type class instance " + constraint.getName() + " for type " + matchedType)); - return null; - } else { - if (instances.size() > 1) { - errors.add(new CompileError(fc, - "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + - Utils.printSep("\n", instances))); - - } - TypeClassInstance instance = Utils.getFirst(instances); - return mapping.set(tp, matchedType.withTypeClassInstance(instance)); - } - } - public static ImmutableCollection calculate(ExprNewObject fc) { TypeDef typeDef = fc.attrTypeDef(); if (!(typeDef instanceof ClassDef)) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java index a93ce4332..46d193eca 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java @@ -5,6 +5,7 @@ import de.peeeq.wurstio.jassinterpreter.InterpreterException; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.gui.WurstGui; import de.peeeq.wurstscript.intermediatelang.*; import de.peeeq.wurstscript.jassIm.*; @@ -102,7 +103,9 @@ public void deallocate(ILconstObject obj, ImClass clazz, Element trace) { public void assertAllocated(ILconstObject obj, Element trace) { if (obj == null) { - throw new InterpreterException(trace, "Null pointer dereference"); + throw new InterpreterException(trace, "Null pointer dereference in " + + PrettyPrinter.print(trace) + ); } if (obj.isDestroyed()) { throw new InterpreterException(trace, "Object already destroyed"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index 37e441c6b..918fa2b43 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -354,7 +354,7 @@ private WurstTypeClass getExtendedClassType(ClassDef subC) { private ImFunction createStaticCallFunc(FuncDef funcDef) { ImFunction f = translator.getFuncFor(funcDef); f.getBody().addAll(translator.translateStatements(f, funcDef.getBody())); - // TODO add return for abstract function + // add dummy return for abstract function if (funcDef.attrIsAbstract() && !(funcDef.attrReturnType() instanceof WurstTypeVoid)) { f.getBody().add(ImReturn(funcDef, funcDef.attrReturnType().getDefaultValue(translator))); } @@ -456,10 +456,14 @@ private void createConstructFunc(ConstructorDef constr) { for (WurstTypeBoundTypeParam bt : extendedTypeC.getTypeParameters()) { if (bt.isTemplateTypeParameter()) { typeArgs.add(bt.imTranslateToTypeArgument(translator)); + // add super-constructor type constraint parameters: + for (TypeClassInstance instance : bt.getInstances()) { + arguments.add(instance.translate(trace, thisVar, translator)); + } } } } - // TODO add super-constructor type constraint parameters + f.getBody().add(ImFunctionCall(trace, superConstrFunc, typeArgs, arguments, false, CallType.NORMAL)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index 2fbdb28bc..fa6c5e066 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -8,7 +8,6 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtojass.ImAttrType; import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; -import io.vavr.control.Either; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -100,13 +99,12 @@ private static ImClassType adaptToSuperclass(ImClassType ct, ImClass owningClass } private static Iterable superTypes(ImClassType ct) { - GenericTypes generics = new GenericTypes(ct.getTypeArguments()); List typeVars = ct.getClassDef().getTypeVariables(); return () -> ct.getClassDef() .getSuperClasses() .stream() - .map(sc -> (ImClassType) transformType(sc, generics, typeVars)) + .map(sc -> (ImClassType) ImAttrType.substituteType(sc, ct.getTypeArguments(), typeVars)) .iterator(); } @@ -435,6 +433,7 @@ public void visit(ImFunction f) { @Override public void visit(ImAlloc f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -442,6 +441,7 @@ public void visit(ImAlloc f) { @Override public void visit(ImDealloc f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -449,6 +449,7 @@ public void visit(ImDealloc f) { @Override public void visit(ImInstanceof f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -456,6 +457,7 @@ public void visit(ImInstanceof f) { @Override public void visit(ImTypeIdOfObj f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -463,6 +465,7 @@ public void visit(ImTypeIdOfObj f) { @Override public void visit(ImTypeIdOfClass f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 0862c6616..c5de91199 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -517,7 +517,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu // imArgs.add(0, receiver); // } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); - addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs, getThisVar(f)); ImVar typeClassParam = t.getTypeClassParamFor(typeParamDispatchOn); ImExpr typeClassDict; if (typeClassParam.getParent().getParent() instanceof ImFunction) { @@ -536,7 +536,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } else if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); - addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs, getThisVar(f)); call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false); } else { ImFunction calledImFunc = t.getFuncFor(calledFunc); @@ -544,7 +544,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu imArgs.add(0, receiver); } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables()); - addTypeClassDictArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables(), imArgs); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables(), imArgs, getThisVar(f)); call = ImFunctionCall(e, calledImFunc, typeArguments, imArgs, false, CallType.NORMAL); } @@ -556,6 +556,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } } + @org.jetbrains.annotations.Nullable + private static ImVar getThisVar(ImFunction f) { + return f.getParameters().isEmpty() ? null : Utils.getFirst(f.getParameters()); + } + @Nullable private static TypeParamConstraint getTypeParamConstraint(FunctionCall e) { FuncLink funcLink = e.attrFuncLink(); @@ -568,7 +573,7 @@ private static TypeParamConstraint getTypeParamConstraint(FunctionCall e) { /** * For each type parameter constraint adds an argument containing the type class dictionary. */ - private static void addTypeClassDictArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables, ImExprs imArgs) { + private static void addTypeClassDictArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables, ImExprs imArgs, ImVar thisVar) { VariableBinding mapping = sig.getMapping(); for (ImTypeVar tv : typeVariables) { TypeParamDef tp = tr.getTypeParamDef(tv); @@ -581,7 +586,7 @@ private static void addTypeClassDictArguments(ImTranslator tr, FunctionSignature continue; } for (TypeClassInstance instance : t.getInstances()) { - ImExpr arg = instance.translate(location, tr); + ImExpr arg = instance.translate(location, thisVar, tr); imArgs.add(arg); } } @@ -639,7 +644,7 @@ public static ImExpr translateIntern(ExprNewObject e, ImTranslator t, ImFunction // translate type constraints: ImTypeVars tps = t.getClassFor(constructorFunc.attrNearestClassDef()).getTypeVariables(); - addTypeClassDictArguments(t, e.attrFunctionSignature(), e, tps, args); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, tps, args, getThisVar(f)); return ImFunctionCall(e, constructorImFunc, typeArgs, args, false, CallType.NORMAL); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index 9f57dff3d..e2ee2c57e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.ast.InstanceDecl; import de.peeeq.wurstscript.ast.StmtCall; import de.peeeq.wurstscript.ast.TypeParamConstraint; +import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import de.peeeq.wurstscript.utils.Utils; @@ -17,7 +18,7 @@ public abstract class TypeClassInstance { public static TypeClassInstance fromInstance(InstanceDecl decl, List typeArgs, List dependencies, WurstTypeInterface constraint) { return new TypeClassInstance() { @Override - public ImExpr translate(Element trace, ImTranslator tr) { + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { ImClass c = tr.getClassFor(decl); ImTypeArguments imTypeArgs = JassIm.ImTypeArguments(); for (WurstType ta : typeArgs) { @@ -26,7 +27,7 @@ public ImExpr translate(Element trace, ImTranslator tr) { ImClassType ct = JassIm.ImClassType(c, imTypeArgs); ImExprs args = JassIm.ImExprs(); for (TypeClassInstance dep : dependencies) { - ImExpr d = dep.translate(trace, tr); + ImExpr d = dep.translate(trace, thisVar, tr); args.add(d); } return JassIm.ImTypeClassDictValue(trace, ct, args); @@ -42,10 +43,20 @@ public String toString() { public static TypeClassInstance fromTypeParam(Element trace, TypeParamConstraint constraint) { return new TypeClassInstance() { @Override - public ImExpr translate(Element trace, ImTranslator tr) { + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { ImVar param = tr.getTypeClassParamFor(constraint); - // TODO if it is a class field do something different - return JassIm.ImVarAccess(param); + if (isLocalVar(param)) { + return JassIm.ImVarAccess(param); + } else { + if (thisVar == null) { + throw new CompileError(trace, "Cannot construct " + this + ", because there is no 'this' available"); + } + // if it is a class field, access the field: + return JassIm.ImMemberAccess(trace, + JassIm.ImVarAccess(thisVar), + JassIm.ImTypeArguments(), + param, JassIm.ImExprs()); + } } @Override @@ -55,7 +66,12 @@ public String toString() { }; } + private static boolean isLocalVar(ImVar param) { + return param != null + && param.getParent() != null + && param.getParent().getParent() instanceof ImFunction; + } - public abstract ImExpr translate(Element trace, ImTranslator tr); + public abstract ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index bbbabd1cf..4f7c1e409 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -7,6 +7,7 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; import java.util.Collections; @@ -168,6 +169,10 @@ protected boolean isNullable() { @Override public WurstTypeBoundTypeParam setTypeArgs(VariableBinding typeParamMapping) { +// Option m = typeParamMapping.get(typeParamDef); +// if (m.isDefined()) { +// return m.get(); +// } return this.withBaseType(baseType.setTypeArgs(typeParamMapping)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java index ffebb8882..53752c75b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java @@ -3,12 +3,17 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.*; +import io.vavr.Tuple2; +import io.vavr.control.Option; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; public abstract class WurstTypeNamedScope extends WurstType { @@ -119,19 +124,50 @@ public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs) { typeArgs.addError("Expected " + typeParameters.size() + " type arguments, but got " + typeArgs.size()); } + List errors = new ArrayList<>(); + VariableBinding mapping = VariableBinding.emptyMapping(); + for (int i = 0; i < typeArgs.size() && i < typeParameters.size(); i++) { WurstTypeBoundTypeParam tp = typeParameters.get(i); TypeParamDef tpDef = tp.getTypeParamDef(); TypeExpr typeArg = typeArgs.get(i); WurstType baseType = typeArg.attrTyp().dynamic(); - typeParams.add(new WurstTypeBoundTypeParam(tpDef, baseType, typeArg)); + WurstTypeBoundTypeParam wtbt = new WurstTypeBoundTypeParam(tpDef, baseType, typeArg); + mapping = mapping.set(tpDef, wtbt); + } + + for (int i = 0; i < typeArgs.size() && i < typeParameters.size(); i++) { + WurstTypeBoundTypeParam tp = typeParameters.get(i); + TypeParamDef tpDef = tp.getTypeParamDef(); + WurstTypeBoundTypeParam wtbt = mapping.get(tpDef).get(); + TypeExpr typeArg = typeArgs.get(i); + for (WurstTypeInterface constraint : TypeClasses.getConstraints(tpDef)) { + VariableBinding mapping2 = TypeClasses.findTypeClass( + typeArg, + errors, + mapping, + tpDef, + wtbt, + constraint + ); + if (mapping2 != null) { + mapping = mapping2; + } + } + } + + for (CompileError error : errors) { + typeArgs.get(0).getErrorHandler().sendError(error); } -// List newTypes = node.getTypeArgs().stream() -// .map((TypeExpr te) -> te.attrTyp().dynamic()) -// .collect(Collectors.toList()); - return replaceTypeVars(typeParams); +// return setTypeArgs(mapping); + VariableBinding finalMapping = mapping; + List newTypes = + typeParameters.stream() + .map(t -> finalMapping.get(t.getTypeParamDef()).get()) + .collect(Collectors.toList()); + return replaceTypeVars(newTypes); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 284d9a9ce..074b8374c 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1488,5 +1488,66 @@ public void classParameterConstraintBuild() { ); } + @Test + public void subtypeConstraint1() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class D extends C", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "init", + " let d = new D", + " d.set(\"42\")", + " if d.get() == 42", + " testSuccess()" + ); + } + + @Test + public void subtypeConstraintDependent() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class D extends C", + "class Cell", + " T elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "implements ToInt> for T: ToInt", + " function toInt(Cell c) returns int", + " return T.toInt(c.elem)", + "init", + " let d = new D>", + " let c = new Cell", + " c.elem = \"42\"", + " d.set(c)", + " if d.get() == 42", + " testSuccess()" + ); + } + } From 39f85125d09add15babd6da058630a370d0e8805 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 9 Feb 2020 14:21:47 +0100 Subject: [PATCH 29/50] iterator example --- .../antlr/de/peeeq/wurstscript/antlr/Wurst.g4 | 1 + .../de/peeeq/wurstscript/TypeClasses.java | 15 ++++- .../attributes/AttrFunctionSignature.java | 8 +-- .../attributes/IsDynamicContext.java | 14 +++-- .../attributes/prettyPrint/PrettyPrinter.java | 6 ++ .../antlr/AntlrWurstParseTreeTransformer.java | 16 ++++-- .../imtranslation/EliminateTypeClasses.java | 33 +++++++---- .../types/WurstTypeBoundTypeParam.java | 2 +- .../types/WurstTypeNamedScope.java | 25 +++++++-- .../tests/GenericsWithTypeclassesTests.java | 55 +++++++++++++++++++ 10 files changed, 141 insertions(+), 34 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 index 5e04e58a7..1b60a3ff9 100644 --- a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 +++ b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 @@ -1,3 +1,4 @@ + grammar Wurst; @header { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 2fa288230..6bf0fb522 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -2,11 +2,13 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import io.vavr.Tuple2; import io.vavr.control.Option; +import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -94,11 +96,15 @@ public static VariableBinding findTypeClass(Element location, List .filter(e -> e instanceof InstanceDecl) .map(e -> (InstanceDecl) e) .flatMap(instance -> { - WurstType instanceType = instance.getImplementedInterface().attrTyp(); + WurstType instanceType; + try { + instanceType = instance.getImplementedInterface().attrTyp(); + } catch (CyclicDependencyError e) { + return Stream.empty(); + } VariableBinding initialMapping = VariableBinding.emptyMapping().withTypeVariables(instance.getTypeParameters()); VariableBinding match = instanceType.matchAgainstSupertype(constraint, location, initialMapping, VariablePosition.LEFT); - if (match == null) { return Stream.empty(); } @@ -123,7 +129,10 @@ public static VariableBinding findTypeClass(Element location, List List typeArgs = new ArrayList<>(); for (TypeParamDef instanceTp : instance.getTypeParameters()) { WurstTypeBoundTypeParam i = match.get(instanceTp).get(); - deps.addAll(i.getInstances()); + @Nullable List is = i.getInstances(); + if (is != null) { + deps.addAll(is); + } if (instanceTp.getTypeParamConstraints() instanceof TypeParamConstraintList) { typeArgs.add(i); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java index 3d72653d0..5adbcbecb 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java @@ -1,11 +1,11 @@ package de.peeeq.wurstscript.attributes; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; -import de.peeeq.wurstscript.types.FunctionSignature; -import de.peeeq.wurstscript.types.VariableBinding; -import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeUnknown; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; +import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; +import io.vavr.Tuple2; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java index 233f2c61f..91a522cd2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java @@ -26,19 +26,23 @@ public static boolean calculate(Element origElem) { if (origElem.isSubtreeOf(a.getExtendedClass())) { return true; } + if (origElem.isSubtreeOf(a.getImplementsList())) { + return true; + } + if (origElem.isSubtreeOf(a.getTypeParameters())) { + return true; + } } if (elem instanceof InterfaceDef) { InterfaceDef a = (InterfaceDef) elem; if (origElem.isSubtreeOf(a.getExtendsList())) { return true; } - } - if (elem instanceof ClassDef) { - ClassDef a = (ClassDef) elem; - if (origElem.isSubtreeOf(a.getImplementsList())) { + if (origElem.isSubtreeOf(a.getTypeParameters())) { return true; } - } else if (elem instanceof ModuleUse) { + } + if (elem instanceof ModuleUse) { ModuleUse mu = (ModuleUse) elem; if (origElem.isSubtreeOf(mu.getTypeArgs())) { return true; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index 387e8f575..3df967d94 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -581,6 +581,12 @@ public static void prettyPrint(TypeExprArray e, Spacer spacer, StringBuilder sb, } public static void prettyPrint(TypeExprList e, Spacer spacer, StringBuilder sb, int indent) { + if (e.isEmpty()) { + return; + } + sb.append("<"); + commaSeparatedList(e, spacer, sb, indent); + sb.append(">"); } public static void prettyPrint(TypeExprResolved e, Spacer spacer, StringBuilder sb, int indent) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 99924b0c8..63246b079 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -318,7 +318,7 @@ private TypeParamDefs transformInstanceTypeParams(List params) TypeParamDefs result = Ast.TypeParamDefs(); if (params != null) { for (TypeParamContext p : params) { - result.add(transformTypeParam(p)); + result.add(transformTypeParam(p, true)); } } return result; @@ -1324,21 +1324,25 @@ private TypeParamDefs transformTypeParams(TypeParamsContext typeParams) { TypeParamDefs result = Ast.TypeParamDefs(); if (typeParams != null && typeParams.params != null) { for (TypeParamContext p : typeParams.params) { - result.add(transformTypeParam(p)); + result.add(transformTypeParam(p, false)); } } return result; } - private TypeParamDef transformTypeParam(TypeParamContext p) { + private TypeParamDef transformTypeParam(TypeParamContext p, boolean defaultsToNewGenerics) { Modifiers modifiers = Ast.Modifiers(); - TypeParamConstraints typeParamClasses = tranformTypeParamConstraints(p.typeParamConstraints()); + TypeParamConstraints typeParamClasses = tranformTypeParamConstraints(p.typeParamConstraints(), defaultsToNewGenerics); return Ast.TypeParamDef(source(p), modifiers, text(p.name), typeParamClasses); } - private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsContext tc) { + private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsContext tc, boolean defaultsToNewGenerics) { if (tc == null) { - return Ast.NoTypeParamConstraints(); + if (defaultsToNewGenerics) { + return Ast.TypeParamConstraintList(); + } else { + return Ast.NoTypeParamConstraints(); + } } TypeParamConstraintList res = Ast.TypeParamConstraintList(); for (TypeExprContext t : tc.constraints) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java index f201269fd..0ae4df570 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -1,11 +1,9 @@ package de.peeeq.wurstscript.translation.imtranslation; import com.google.common.collect.*; -import de.peeeq.datastructures.Partitions; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.jassIm.*; -import de.peeeq.wurstscript.types.TypeClassInstance; -import org.eclipse.xtend.lib.annotations.ToString; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -55,7 +53,17 @@ public static void transform(ImTranslator tr) { private void run() { ImProg prog = tr.getImProg(); - prog.accept(new Element.DefaultVisitor() { + addTypeClassDictValuesToWorkList(prog); + + while (!workList.isEmpty()) { + ImTypeClassDictValue dictV = workList.removeFirst(); + doSpecialize(dictV); + + } + } + + private void addTypeClassDictValuesToWorkList(Element e) { + e.accept(new Element.DefaultVisitor() { @Override public void visit(ImTypeClassDictValue dv) { // no super visit @@ -64,17 +72,14 @@ public void visit(ImTypeClassDictValue dv) { } } }); - - while (!workList.isEmpty()) { - ImTypeClassDictValue dictV = workList.removeFirst(); - doSpecialize(dictV); - - } } private void doSpecialize(ImTypeClassDictValue dictV) { Element parent = dictV.getParent(); - if (parent instanceof ImExprs) { + if (parent == null) { + System.out.println("already replaced1: " + dictV); + return; + } else if (parent instanceof ImExprs) { Element parent2 = parent.getParent(); if (parent2 instanceof ImFunctionCall) { ImFunctionCall fc = (ImFunctionCall) parent2; @@ -86,6 +91,10 @@ private void doSpecialize(ImTypeClassDictValue dictV) { } } else if (parent instanceof ImMethodCall) { ImMethodCall mc = (ImMethodCall) parent; + if (mc.getParent() == null) { + System.out.println("already replaced2: " + mc); + return; + } assert mc.getReceiver() == dictV; ImMethod m = findMostConcreteMethod(mc.getMethod(), dictV.getClazz()); @@ -199,6 +208,7 @@ private ImClass specializeClass(TypeClassInstanceKey key) { m.getSubMethods().add(newM); + addTypeClassDictValuesToWorkList(newImpl); imProg.getFunctions().add(newImpl); imProg.getMethods().add(newM); } @@ -301,6 +311,7 @@ public void visit(ImVarAccess va) { use.replaceBy(newDict); workList.add(newDict); } + addTypeClassDictValuesToWorkList(copy); tr.getImProg().getFunctions().add(copy); return copy; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 4f7c1e409..e812a0b4c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -224,7 +224,7 @@ public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) return new WurstTypeBoundTypeParam(typeParamDef, baseType, fromIndex, toIndex, newInstances, indexInitialized, context); } - public List getInstances() { + public @Nullable List getInstances() { return instances; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java index 53752c75b..8a64cee4f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java @@ -1,12 +1,14 @@ package de.peeeq.wurstscript.types; import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.*; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import io.vavr.Tuple2; import io.vavr.control.Option; import org.eclipse.jdt.annotation.NonNull; @@ -113,15 +115,29 @@ public WurstType setTypeArgs(@NonNull VariableBinding typeParamBounds) { abstract public WurstType replaceTypeVars(List newTypes); - public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs) { + public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs1) { + + List typeArgs = typeArgs1; + + if (typeArgs1.getParent().getParent() instanceof TypeParamConstraint) { + // if used as type parameter constraint, then add type param as last type argument + TypeParamConstraint constraint = (TypeParamConstraint) typeArgs1.getParent().getParent(); + WurstTypeTypeParam tp = new WurstTypeTypeParam(constraint.parentTypeParam(), true); + typeArgs = ImmutableList.builder() + .addAll(typeArgs) + .add(Ast.TypeExprResolved(typeArgs1.attrSource(), tp)) + .build(); + } + if (typeArgs.isEmpty()) { // TODO replace with unknown types? return this; } - List typeParams = new ArrayList<>(); + + if (typeArgs.size() != typeParameters.size()) { - typeArgs.addError("Expected " + typeParameters.size() + " type arguments, but got " + typeArgs.size()); + typeArgs1.addError("Expected " + typeParameters.size() + " type arguments, but got " + typeArgs.size()); } List errors = new ArrayList<>(); @@ -165,7 +181,8 @@ public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs) { VariableBinding finalMapping = mapping; List newTypes = typeParameters.stream() - .map(t -> finalMapping.get(t.getTypeParamDef()).get()) + .map(t -> finalMapping.get(t.getTypeParamDef()) + .getOrElse(() -> new WurstTypeBoundTypeParam(t.getTypeParamDef(), WurstTypeUnknown.instance(), typeArgs1))) .collect(Collectors.toList()); return replaceTypeVars(newTypes); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 074b8374c..885dcf4c8 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1549,5 +1549,60 @@ public void subtypeConstraintDependent() { ); } + @Test + public void iteratorExample() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface Iterable, T:>", +// "interface Iterable", + " function iterator(T t) returns I", + "interface Iterator", + " function hasNext(I i) returns bool", + " function next(I i) returns T", + "interface Group", + " function zero() returns T", + " function plus(T a, T b) returns T", + "function sum, I: Iterator>(C coll) returns T", + " var res = T.zero()", + " let iter = C.iterator(coll)", + " while I.hasNext(iter)", + " res = T.plus(res, I.next(iter))", + " return res", + "implements Group", + " function zero() returns int", + " return 0", + " function plus(int a, int b) returns int", + " return a + b", + "class List", + " T elem", + " List next", + " construct(T elem, List next)", + " this.elem = elem", + " this.next = next", + "class ListIterator", + " List current", + " construct(List list)", + " this.current = list", + "implements Iterator> for T", + " function hasNext(ListIterator iter) returns bool", + " return iter.current != null", + " function next(ListIterator iter) returns T", + " let res = iter.current.elem", + " iter.current = iter.current.next", + " return res", + "implements Iterable, List> for E", + " function iterator(List list) returns ListIterator", + " return new ListIterator(list)", + "init", + " let list = new List(100, new List(20, new List(3, null)))", +// " if sum(list) == 123", + " if sum, ListIterator>(list) == 123", + " testSuccess()" + ); + } + } From a278a254481bd6ef2f7a3c93684e051fdd9ca09d Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 9 Feb 2020 14:46:24 +0100 Subject: [PATCH 30/50] namespacing --- .../parserspec/wurstscript.parseq | 11 +++++- .../antlr/de/peeeq/wurstscript/antlr/Wurst.g4 | 2 +- .../de/peeeq/wurstscript/TypeClasses.java | 39 +++++++++++++++---- .../antlr/AntlrWurstParseTreeTransformer.java | 1 + .../imtranslation/EliminateTypeClasses.java | 2 - .../validation/WurstValidator.java | 5 +++ 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index eb2d8ab97..765f6364e 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -36,7 +36,7 @@ WEntity = | ModuleDef(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamDefs typeParameters, ClassDefs innerClasses, FuncDefs methods, GlobalVarDefs vars, ConstructorDefs constructors, ModuleInstanciations p_moduleInstanciations, ModuleUses moduleUses, OnDestroyDef onDestroy) - | InstanceDecl(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, TypeParamDefs typeParameters, TypeExpr implementedInterface, FuncDefs methods) + | InstanceDecl(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, TypeParamDefs typeParameters, TypeExpr implementedInterface, FuncDefs methods) @@ -317,7 +317,7 @@ Modifier = // ElementWithBody = FunctionImplementation | InitBlock | ConstructorDef | OnDestroyDef //ElementWithModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition -HasModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition +HasModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition | InstanceDecl HasTypeArgs = ExprNewObject | FunctionCall | ModuleUse | StmtCall | TypeExprSimple AstElementWithFuncName = ExprFunctionCall | ExprMemberMethod | ExprFuncRefc @@ -914,6 +914,13 @@ WPackage.attrExportedTypeNameLinks returns com.google.common.collect.ImmutableMultimap implemented by de.peeeq.wurstscript.attributes.names.Exports.exportedTypeNameLinks +WPackage.attrTypeClasses + returns java.util.List + implemented by de.peeeq.wurstscript.TypeClasses.availableTypeClasses + +WPackage.attrDefinedTypeClasses + returns java.util.List + implemented by de.peeeq.wurstscript.TypeClasses.definedTypeClasses Element.lookupType(String name, boolean showErrors) returns @Nullable TypeDef diff --git a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 index 1b60a3ff9..4dfc9f987 100644 --- a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 +++ b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 @@ -117,7 +117,7 @@ entity: ; instanceDeclaration: - 'implements' implemented=typeExpr ('for' (params+=typeParam (',' params+=typeParam)*))? + modifiersWithDoc 'implements' implemented=typeExpr ('for' (params+=typeParam (',' params+=typeParam)*))? NL (STARTBLOCK funcDef* ENDBLOCK)? diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 6bf0fb522..008b9d823 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -1,5 +1,6 @@ package de.peeeq.wurstscript; +import de.peeeq.immutablecollections.ImmutableList; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; @@ -63,6 +64,7 @@ public static List getConstraints(TypeParamDef tp) { /** * Finds an instance of a type class + * * @param location * @param errors * @param mapping @@ -70,7 +72,7 @@ public static List getConstraints(TypeParamDef tp) { * @param matchedType * @param constraint1 * @return the updated type var mapping if any was found - * + *

* TODO change to stream return type to allow backtracking */ public static VariableBinding findTypeClass(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { @@ -90,11 +92,12 @@ public static VariableBinding findTypeClass(Element location, List // option 2: find instance declarations // TODO create index to make this faster and use normal scoped lookup (ony search imports) WurstModel model = location.getModel(); - List instances = model.stream() - .flatMap(cu -> cu.getPackages().stream()) - .flatMap(p -> p.getElements().stream()) - .filter(e -> e instanceof InstanceDecl) - .map(e -> (InstanceDecl) e) + @Nullable PackageOrGlobal wPackageG = location.attrNearestPackage(); + if (!(wPackageG instanceof WPackage)) { + return null; + } + WPackage wPackage = (WPackage) wPackageG; + List instances = wPackage.attrTypeClasses().stream() .flatMap(instance -> { WurstType instanceType; try { @@ -111,7 +114,6 @@ public static VariableBinding findTypeClass(Element location, List instanceType = instanceType.setTypeArgs(match); - for (Tuple2 m : match) { TypeParamDef instanceTp = m._1(); WurstTypeBoundTypeParam mType = m._2(); @@ -163,4 +165,27 @@ public static VariableBinding findTypeClass(Element location, List return mapping.set(tp, matchedType.withTypeClassInstance(instance)); } } + + public static List availableTypeClasses(WPackage p) { + return Stream.concat( + p.attrDefinedTypeClasses().stream(), + p.getImports().stream() + .map(WImport::attrImportedPackage) + .flatMap(ip -> { + if (ip == null) { + return Stream.empty(); + } else { + return ip.attrDefinedTypeClasses().stream() + .filter(InstanceDecl::attrIsPublic); + } + })) + .collect(Collectors.toList()); + } + + public static List definedTypeClasses(WPackage p) { + return p.getElements().stream() + .filter(e -> e instanceof InstanceDecl) + .map(e -> (InstanceDecl) e) + .collect(Collectors.toList()); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 63246b079..eec68104e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -308,6 +308,7 @@ private WPackage transformPackage(WpackageContext p) { private WEntity transformInstanceDeclaration(InstanceDeclarationContext i) { return Ast.InstanceDecl( source(i), + transformModifiers(i.modifiersWithDoc()), transformInstanceTypeParams(i.params), transformTypeExpr(i.implemented), transformFuncDefs(i.funcDef()) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java index 0ae4df570..95debbb0f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -77,7 +77,6 @@ public void visit(ImTypeClassDictValue dv) { private void doSpecialize(ImTypeClassDictValue dictV) { Element parent = dictV.getParent(); if (parent == null) { - System.out.println("already replaced1: " + dictV); return; } else if (parent instanceof ImExprs) { Element parent2 = parent.getParent(); @@ -92,7 +91,6 @@ private void doSpecialize(ImTypeClassDictValue dictV) { } else if (parent instanceof ImMethodCall) { ImMethodCall mc = (ImMethodCall) parent; if (mc.getParent() == null) { - System.out.println("already replaced2: " + mc); return; } assert mc.getReceiver() == dictV; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index fa41a3def..9e0bd6382 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -1840,6 +1840,11 @@ public void case_EnumMember(EnumMember enumMember) { check(); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + check(VisibilityPublic.class); + } + }); if (error.length() > 0) { if (m.attrSource().getFile().endsWith(".jurst")) { From ae31eb164332b2b6e6d2de005508507d3a820cbc Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 9 Feb 2020 16:13:56 +0100 Subject: [PATCH 31/50] Type Check: All functions implemented correctly --- .../de/peeeq/wurstscript/TypeClasses.java | 31 +++++++++++++++++++ .../attributes/names/NameLinks.java | 4 +-- .../de/peeeq/wurstscript/utils/Utils.java | 4 +++ .../validation/WurstValidator.java | 3 ++ .../tests/GenericsWithTypeclassesTests.java | 14 +++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 008b9d823..aea06536b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -1,8 +1,11 @@ package de.peeeq.wurstscript; +import com.google.common.collect.ImmutableMultimap; import de.peeeq.immutablecollections.ImmutableList; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.names.DefLink; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Pair; @@ -188,4 +191,32 @@ public static List definedTypeClasses(WPackage p) { .map(e -> (InstanceDecl) e) .collect(Collectors.toList()); } + + public static void checkInstance(InstanceDecl c) { + ImmutableMultimap nameLinks = c.attrNameLinks(); + if (!c.attrIsAbstract()) { + StringBuilder toImplement = new StringBuilder(); + // should have no abstract methods + for (DefLink link : nameLinks.values()) { + NameDef f = link.getDef(); + if (f.attrIsAbstract()) { + if (f.attrNearestStructureDef() == c) { + Element loc = f.getModifiers().stream() + .filter(m -> m instanceof ModAbstract) + .map(x -> x) + .findFirst() + .orElse(f); + loc.addError(Utils.printElementWithSource(c) + " cannot have abstract functions like " + f.getName()); + } else if (link instanceof FuncLink) { + toImplement.append("\n "); + toImplement.append(((FuncLink) link).printFunctionTemplate()); + } + } + } + if (toImplement.length() > 0) { + c.addError(Utils.printElementWithSource(c) + " must implement the following functions:" + toImplement); + } + } + + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java index 4467986dd..d94649d3f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java @@ -9,7 +9,6 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeClass; -import de.peeeq.wurstscript.types.WurstTypeInt; import de.peeeq.wurstscript.types.WurstTypeInterface; import de.peeeq.wurstscript.utils.Utils; import de.peeeq.wurstscript.validation.WurstValidator; @@ -113,7 +112,8 @@ public static ImmutableMultimap calculate(InstanceDecl i) { Map> overrideCheckResults = initOverrideMap(result); WurstType implementedI = i.getImplementedInterface().attrTyp(); if (implementedI instanceof WurstTypeInterface) { - addNamesFromExtendedInterfaces(result, (WurstTypeInterface) implementedI, overrideCheckResults); + WurstTypeInterface wti = (WurstTypeInterface) implementedI; + addNewNameLinks(result, overrideCheckResults, wti.nameLinks(), false); } reportOverrideErrors(overrideCheckResults); addTypeParametersIfAny(result::put, i); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java index 4ce496afe..79fcf08c7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java @@ -10,6 +10,7 @@ import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.prettyPrint.DefaultSpacer; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.jassIm.JassImElementWithName; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.WurstType; @@ -317,6 +318,9 @@ public static String printElement(@Nullable Element e) { name += "}"; } type = "type"; + } else if (e instanceof InstanceDecl) { + InstanceDecl i = (InstanceDecl) e; + return "Type class instance for " + PrettyPrinter.print(i.getImplementedInterface()); } return type + " " + name; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index 9e0bd6382..3d3fbe555 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -2,6 +2,7 @@ import com.google.common.collect.*; import de.peeeq.wurstio.utils.FileUtils; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.WLogger; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CofigOverridePackages; @@ -341,6 +342,8 @@ private void check(Element e) { visit((StmtExitwhen) e); if (e instanceof TypeParamDef) checkTypeParamDef(((TypeParamDef) e)); + if (e instanceof InstanceDecl) + TypeClasses.checkInstance(((InstanceDecl) e)); } catch (CyclicDependencyError cde) { cde.printStackTrace(); Element element = cde.getElement(); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 885dcf4c8..c2143507e 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1604,5 +1604,19 @@ public void iteratorExample() { ); } + @Test + public void missingFunction() { + testAssertErrorsLines(false, "must implement the following functions", + "package test", + "interface Blub", + " function foo(T x) returns int", + " function bar(T x) returns int", + "class A", + "implements Blub", + " function foo(A x) returns int", + " return 42" + ); + } + } From 02b06d6c95cca23cda97837f6b216e644b5efe82 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 9 Feb 2020 17:15:09 +0100 Subject: [PATCH 32/50] added simple divergence check --- .../de/peeeq/wurstscript/TypeClasses.java | 24 ++++++++++++++++--- .../tests/GenericsWithTypeclassesTests.java | 22 +++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index aea06536b..785c7c583 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -8,9 +8,11 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.types.*; +import de.peeeq.wurstscript.utils.Lazy; import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import io.vavr.Tuple2; +import io.vavr.collection.HashMap; import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -22,6 +24,12 @@ import java.util.stream.Stream; public class TypeClasses { + /** + * How often an instance declaration can be used in a single derivation. + */ + private static final int DERIVATION_MAX_INSTANCE_USES = 10; + + public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { List errors = new ArrayList<>(); VariableBinding mapping = sig.getMapping(); @@ -73,12 +81,16 @@ public static List getConstraints(TypeParamDef tp) { * @param mapping * @param tp * @param matchedType - * @param constraint1 + * @param constraint * @return the updated type var mapping if any was found *

* TODO change to stream return type to allow backtracking */ - public static VariableBinding findTypeClass(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1) { + public static VariableBinding findTypeClass(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint) { + return findTypeClassH(location, errors, mapping, tp, matchedType, constraint, HashMap.empty()); + } + + private static VariableBinding findTypeClassH(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1, HashMap uses) { WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); // option 1: the matched type is a type param that also has the right constraint: @@ -102,6 +114,12 @@ public static VariableBinding findTypeClass(Element location, List WPackage wPackage = (WPackage) wPackageG; List instances = wPackage.attrTypeClasses().stream() .flatMap(instance -> { + int instanceUses = uses.getOrElse(instance, 0); + if (instanceUses > 10) { + return Stream.empty(); + } + Lazy> uses2 = Lazy.create(() -> uses.put(instance, instanceUses + 1)); + WurstType instanceType; try { instanceType = instance.getImplementedInterface().attrTyp(); @@ -122,7 +140,7 @@ public static VariableBinding findTypeClass(Element location, List WurstTypeBoundTypeParam mType = m._2(); List instanceConstraints = getConstraints(instanceTp); for (WurstTypeInterface instanceConstraint : instanceConstraints) { - VariableBinding match2 = findTypeClass(location, errors, match, instanceTp, mType, instanceConstraint); + VariableBinding match2 = findTypeClassH(location, errors, match, instanceTp, mType, instanceConstraint, uses2.get()); if (match2 == null) { return Stream.empty(); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index c2143507e..5c0bc9072 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1619,4 +1619,26 @@ public void missingFunction() { } + @Test + public void cyclicImplements() { + testAssertErrorsLines(false, "Type A does not satisfy constraint Y: Apfel", + "package test", + "interface Apfel", + " function a(T x) returns int", + "interface Birne", + " function b(T x) returns int", + "class A", + "implements Apfel for X: Birne", + " function a(X t) returns int", + " return X.b(t)", + "implements Birne for Y: Apfel", + " function b(Y t) returns int", + " return Y.a(t)", + "function foo(T t)", + "init", + " let a = new A()", + " foo(a)" + ); + } + } From 464c9b0a32e04f75f64c5af2ed9c02e08aaddc62 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Sun, 9 Feb 2020 23:38:28 +0100 Subject: [PATCH 33/50] basic support for derived ToIndex for object types --- .../de/peeeq/wurstscript/TypeClasses.java | 41 +++++-- .../interpreter/EvaluateExpr.java | 4 +- .../interpreter/RunStatement.java | 6 ++ .../imtranslation/EliminateClasses.java | 5 + .../imtranslation/ImTranslator.java | 102 +++++++++++++++--- .../wurstscript/types/TypeClassInstance.java | 35 +++++- .../tests/GenericsWithTypeclassesTests.java | 52 +++++++++ 7 files changed, 221 insertions(+), 24 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 785c7c583..9c69e9017 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -6,7 +6,6 @@ import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.DefLink; import de.peeeq.wurstscript.attributes.names.FuncLink; -import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Lazy; import de.peeeq.wurstscript.utils.Pair; @@ -17,9 +16,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -29,6 +26,7 @@ public class TypeClasses { */ private static final int DERIVATION_MAX_INSTANCE_USES = 10; + private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "AnyRef"); public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { List errors = new ArrayList<>(); @@ -94,6 +92,31 @@ private static VariableBinding findTypeClassH(Element location, List matchingConstraint = wtp.getTypeConstraints().stream() @@ -104,7 +127,12 @@ private static VariableBinding findTypeClassH(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, HashMap uses, WurstTypeInterface constraint) { // TODO create index to make this faster and use normal scoped lookup (ony search imports) WurstModel model = location.getModel(); @Nullable PackageOrGlobal wPackageG = location.attrNearestPackage(); @@ -171,9 +199,6 @@ private static VariableBinding findTypeClassH(Element location, List 1) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 72eeefe35..82dab5714 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -410,7 +410,9 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } } if (res instanceof ILconstInt) { - if (imCast.getToType() instanceof ImClassType) { + if (imCast.getToType() instanceof ImClassType + || imCast.getToType() instanceof ImAnyType + || imCast.getToType() instanceof ImTypeVarRef) { return globalState.getObjectByIndex(((ILconstInt) res).getVal()); } if (imCast.getToType() instanceof IlConstHandle) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java index 73ebdb506..05e5b0756 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java @@ -52,6 +52,9 @@ public static void run(ImReturn s, ProgramState globalState, LocalState localSta if (s.getReturnValue() instanceof ImExpr) { ImExpr e = (ImExpr) s.getReturnValue(); r = e.evaluate(globalState, localState); + if (r == null) { + throw new InterpreterException(s.getTrace(), "Returned value was null."); + } } throw new ReturnException(r); } @@ -59,6 +62,9 @@ public static void run(ImReturn s, ProgramState globalState, LocalState localSta public static void run(ImSet s, ProgramState globalState, LocalState localState) { ILaddress v = s.getLeft().evaluateLvalue(globalState, localState); ILconst right = s.getRight().evaluate(globalState, localState); + if (right == null) { + throw new InterpreterException(s.getTrace(), "Right hand side of assignment was null."); + } v.set(right); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java index 35177f911..48c0d1f98 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java @@ -200,6 +200,11 @@ private ImType eliminateClassTypes(ImType imType) { public ImType case_ImClassType(ImClassType t) { return imInt(); } + + @Override + public ImType case_ImAnyType(ImAnyType t) { + return imInt(); + } }); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 54b75784c..802cbff65 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -52,6 +52,7 @@ import static de.peeeq.wurstscript.jassIm.JassIm.*; import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*; +import static de.peeeq.wurstscript.types.TypesHelper.imInt; import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath; import static de.peeeq.wurstscript.utils.Utils.emptyList; @@ -345,7 +346,7 @@ private void finishInitFunctions() { ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger"); if (native_DestroyTrigger != null) { getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(), - JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL)); } } @@ -427,27 +428,27 @@ public void visit(ImReturn imReturn) { } }); de.peeeq.wurstscript.ast.Element trace = initFunc.getTrace(); - initFunc.getBody().add(JassIm.ImReturn(trace, JassIm.ImBoolVal(true))); + initFunc.getBody().add(ImReturn(trace, JassIm.ImBoolVal(true))); // TriggerAddCondition(initTrigVar, Condition(function myInit)) mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs( - JassIm.ImVarAccess(initTrigVar), + ImVarAccess(initTrigVar), ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs( JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL) ), true, CallType.NORMAL)); // if not TriggerEvaluate(initTrigVar) ... mainBody.add(JassIm.ImIf(trace, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs( - ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL) + ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL) )), // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package") - JassIm.ImStmts( + ImStmts( imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + ".")) ), // else: - JassIm.ImStmts())); - mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + ImStmts())); + mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL)); return true; } @@ -524,7 +525,7 @@ public void addGlobalWithInitializerFront(ImVar g, ImExpr initial) { ImStmts body = getGlobalInitFunc().getBody(); ImStatementExpr init; if (body.isEmpty() || !(body.get(0) instanceof ImStatementExpr)) { - init = JassIm.ImStatementExpr(JassIm.ImStmts(), JassIm.ImNull(ImVoid())); + init = JassIm.ImStatementExpr(ImStmts(), JassIm.ImNull(ImVoid())); body.add(0, init); } else { init = (ImStatementExpr) body.get(0); @@ -641,7 +642,7 @@ public ImClassType selfType(ImClass imClass) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), imInt(), JassIm.ImVars(), ImStmts(), Collections.emptyList()); } }; @@ -651,7 +652,7 @@ public ImFunction initFor(ImClass c) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(ImVar(c.getTrace(), TypesHelper.imInt(), "obj", Collections.emptyList())), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(ImVar(c.getTrace(), imInt(), "obj", Collections.emptyList())), TypesHelper.imVoid(), JassIm.ImVars(), ImStmts(), Collections.emptyList()); } }; @@ -1335,6 +1336,83 @@ public ImVar getTypeClassParamFor(TypeParamConstraint tc) { return v; } + private ImClass genericObjectTypeClassInstance; + + public ImClass getGenericObjectTypeClassInstance() { + if (genericObjectTypeClassInstance == null) { + ImMethods methods = JassIm.ImMethods(); + ImFunctions functions = JassIm.ImFunctions(); + de.peeeq.wurstscript.ast.Element t = wurstProg; + +// ImTypeVar typeParam = JassIm.ImTypeVar("T"); + ImClass res = JassIm.ImClass( + t, + "GenericObjectTypeClass", + JassIm.ImTypeVars(), + JassIm.ImVars(), + methods, + functions, + new ArrayList<>() + ); + + // add toIndex function: + ImVar thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + ImVar p = JassIm.ImVar(t, ImAnyType(), "x", emptyList()); + ImFunction toIndex = JassIm.ImFunction(t, + "toIndex", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz, p), + imInt(), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImCast(ImVarAccess(p), imInt())) + ), + emptyList()); + functions.add(toIndex); + methods.add(JassIm.ImMethod(t, + classType(res), + "toIndex", + toIndex, + emptyList(), + false)); + + // add fromIndex function: + thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + p = JassIm.ImVar(t, imInt(), "x", emptyList()); + ImFunction fromIndex = JassIm.ImFunction(t, + "fromIndex", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz, p), + ImAnyType(), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImCast(ImVarAccess(p), ImAnyType())) + ), + emptyList()); + functions.add(fromIndex); + methods.add(JassIm.ImMethod(t, + classType(res), + "fromIndex", + fromIndex, + emptyList(), + false)); + + imProg.getClasses().add(res); + + genericObjectTypeClassInstance = res; + } + return genericObjectTypeClassInstance; + } + + @NotNull + private ImClassType classType(ImClass res) { + ImTypeArguments args = ImTypeArguments(); + for (ImTypeVar t : res.getTypeVariables()) { + args.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(t))); + } + return JassIm.ImClassType(res, args); + } + interface VarsForTupleResult { @@ -1738,10 +1816,10 @@ private ImFunction makeDefaultErrorFunc() { ImVars parameters = JassIm.ImVars(msgVar); ImType returnType = JassIm.ImVoid(); ImVars locals = JassIm.ImVars(); - ImStmts body = JassIm.ImStmts(); + ImStmts body = ImStmts(); // print message: - body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(msgVar)), false, CallType.NORMAL)); + body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(ImVarAccess(msgVar)), false, CallType.NORMAL)); // TODO divide by zero to crash thread: diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index e2ee2c57e..b0ef67bdd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -1,10 +1,8 @@ package de.peeeq.wurstscript.types; +import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.ast.InstanceDecl; -import de.peeeq.wurstscript.ast.StmtCall; -import de.peeeq.wurstscript.ast.TypeParamConstraint; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; @@ -72,6 +70,37 @@ private static boolean isLocalVar(ImVar param) { && param.getParent().getParent() instanceof ImFunction; } + public static TypeClassInstance fromObject(WurstTypeClassOrInterface objectType, InterfaceDef supI) { + return new TypeClassInstance() { + @Override + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { + ImClass sup = tr.getClassFor(supI); + ImClass cd = tr.getGenericObjectTypeClassInstance(); + // add instance as superclass if not present yet + if (cd.getSuperClasses().stream().noneMatch(sc -> sc.getClassDef() == sup)) { + cd.getSuperClasses().add(JassIm.ImClassType(sup, + JassIm.ImTypeArguments(JassIm.ImTypeArgument(objectType.imTranslateType(tr))))); + // add sub methods + for (FuncDef m : supI.getMethods()) { + ImMethod imMethod = tr.getMethodFor(m); + for (ImMethod cdM : cd.getMethods()) { + if (cdM.getName().equals(m.getName())) { + imMethod.getSubMethods().add(cdM); + } + } + } + } + ImClassType c = JassIm.ImClassType(cd, JassIm.ImTypeArguments()); + return JassIm.ImTypeClassDictValue(trace, c, JassIm.ImExprs()); + } + + @Override + public String toString() { + return "Object type class instance"; + } + }; + } + public abstract ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 5c0bc9072..afa799e8f 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1641,4 +1641,56 @@ public void cyclicImplements() { ); } + @Test + public void anyRefClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface AnyRef", + " function toIndex(T x) returns int", + " function fromIndex(int index) returns T", + "class A", + "class Cell", + " int x", + " function set(T x)", + " this.x = T.toIndex(x)", + " function get() returns T", + " return T.fromIndex(this.x)", + "init", + " let a = new A", + " let c = new Cell", + " c.set(a)", + " if c.get() == a", + " testSuccess()" + ); + } + + @Test + @Ignore + public void toIndexFromIndexClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int index) returns T", + "class A", + "class Cell", + " int x", + " function set(T x)", + " this.x = T.toIndex(x)", + " function get() returns T", + " return T.fromIndex(this.x)", + "init", + " let a = new A", + " let c = new Cell", + " c.set(a)", + " if c.get() == a", + " testSuccess()" + ); + } + } From 08dd2072bafe16e9c428c673c508fd56106ba8a9 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 11 Feb 2020 00:03:18 +0100 Subject: [PATCH 34/50] support for multiple constraints --- .../de/peeeq/wurstscript/TypeClasses.java | 10 ++++--- .../wurstscript/types/VariableBinding.java | 7 +++++ .../tests/GenericsWithTypeclassesTests.java | 26 ++++++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 9c69e9017..ca1ad624c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -107,7 +107,7 @@ private static VariableBinding findTypeClassH(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, HashMap uses, WurstTypeInterface constraint) { // TODO create index to make this faster and use normal scoped lookup (ony search imports) - WurstModel model = location.getModel(); + @Nullable PackageOrGlobal wPackageG = location.attrNearestPackage(); if (!(wPackageG instanceof WPackage)) { return null; @@ -154,6 +154,8 @@ private static VariableBinding findInstanceDeclarations(Element location, List getErrors() { return errors; } + + public VariableBinding withTypeClassInstance(TypeParamDef tp, WurstTypeBoundTypeParam matchedType, TypeClassInstance instance) { + WurstTypeBoundTypeParam bound = get(tp) + .getOrElse(matchedType); + bound = bound.withTypeClassInstance(instance); + return set(tp, bound); + } } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index afa799e8f..7585a8f9b 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1327,6 +1327,31 @@ public void simpleTypeClass() { ); } + + @Test + public void twoTypeClasses() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface Plus", + " function plus(T x, T y) returns T", + "interface Times", + " function times(T x, T y) returns T", + "implements Plus", + " function plus(int x, int y) returns int", + " return x + y", + "implements Times", + " function times(int x, int y) returns int", + " return x * y", + "function calc(Q x) returns Q", + " return Q.plus(x, Q.times(x, x))", + "init", + " if calc(6) == 42", + " testSuccess()" + ); + } + @Test public void forwardTypeClass() { testAssertOkLines(true, @@ -1667,7 +1692,6 @@ public void anyRefClass() { } @Test - @Ignore public void toIndexFromIndexClass() { testAssertOkLines(true, "package test", From ca656781ac1ebfa6cdd1ec6cd37587200026ad3b Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 11 Feb 2020 23:47:01 +0100 Subject: [PATCH 35/50] fixed tests --- .../intermediatelang/interpreter/EvaluateExpr.java | 1 - .../intermediatelang/interpreter/ILInterpreter.java | 7 ++++++- .../intermediatelang/interpreter/RunStatement.java | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 82dab5714..580979c4b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -411,7 +411,6 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } if (res instanceof ILconstInt) { if (imCast.getToType() instanceof ImClassType - || imCast.getToType() instanceof ImAnyType || imCast.getToType() instanceof ImTypeVarRef) { return globalState.getObjectByIndex(((ILconstInt) res).getVal()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 64f841238..efa7b890e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -159,7 +159,12 @@ private static LocalState runBuiltinFunction(ProgramState globalState, ImFunctio StringBuilder errors = new StringBuilder(); for (NativesProvider natives : globalState.getNativeProviders()) { try { - return new LocalState(natives.invoke(f.getName(), args)); + ILconst res = natives.invoke(f.getName(), args); + if (res == null && !(f.getReturnType() instanceof ImVoid)) { + // use default value for function stub + res = ImHelper.defaultValueForComplexType(f.getReturnType()).evaluate(globalState, new LocalState()); + } + return new LocalState(res); } catch (NoSuchNativeException e) { errors.append("\n").append(e.getMessage()); // ignore diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java index 05e5b0756..08f5f4c5b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java @@ -53,7 +53,7 @@ public static void run(ImReturn s, ProgramState globalState, LocalState localSta ImExpr e = (ImExpr) s.getReturnValue(); r = e.evaluate(globalState, localState); if (r == null) { - throw new InterpreterException(s.getTrace(), "Returned value was null."); + throw new InterpreterException(s.getTrace(), "Returned value was null"); } } throw new ReturnException(r); From 9a45d209dad751c6da4280edf1bb0b9e71bf1e44 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 12 Feb 2020 21:36:35 +0100 Subject: [PATCH 36/50] fixed classcast exception --- .../main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java | 1 + 1 file changed, 1 insertion(+) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java index 0430b0a1c..18700f178 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java @@ -67,6 +67,7 @@ public WurstTypeInterface replaceTypeVars(List newTypes public ImmutableList extendedInterfaces() { return interfaceDef.getExtendsList().stream() + .filter(i -> i instanceof WurstTypeInterface) .map(i -> (WurstTypeInterface) i.attrTyp().setTypeArgs(getTypeArgBinding())) .filter(i -> i.level() < level()) .collect(ImmutableList.toImmutableList()); From 53250f49c123deec69843021d09165e2eab2063e Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 12 Feb 2020 21:44:46 +0100 Subject: [PATCH 37/50] default type class for classes --- .../de/peeeq/wurstscript/TypeClasses.java | 2 +- .../imtranslation/ImTranslator.java | 20 +++++++++++++++++ .../tests/GenericsWithTypeclassesTests.java | 22 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index ca1ad624c..757392934 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -26,7 +26,7 @@ public class TypeClasses { */ private static final int DERIVATION_MAX_INSTANCE_USES = 10; - private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "AnyRef"); + private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "Default", "AnyRef"); public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { List errors = new ArrayList<>(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 802cbff65..acef582ad 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1397,6 +1397,26 @@ public ImClass getGenericObjectTypeClassInstance() { emptyList(), false)); + // add defaultValue function: + thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + ImFunction defaultValue = JassIm.ImFunction(t, + "defaultValue", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz), + ImAnyType(), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImNull(classType(res))) + ), + emptyList()); + functions.add(defaultValue); + methods.add(JassIm.ImMethod(t, + classType(res), + "defaultValue", + defaultValue, + emptyList(), + false)); + imProg.getClasses().add(res); genericObjectTypeClassInstance = res; diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 7585a8f9b..c4a6edd22 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1717,4 +1717,26 @@ public void toIndexFromIndexClass() { ); } + @Test + public void defaultForClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface Default", + " function defaultValue() returns T", + "class A", + "function d() returns X", + " return X.defaultValue()", + "implements Default", + " function defaultValue() returns int", + " return 42", + "init", + " let a = d()", + " let i = d()", + " if a == null and i == 42", + " testSuccess()" + ); + } + } From aae43e0e3740997e06bc4a9aded7f15917734270 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 12 Feb 2020 22:06:59 +0100 Subject: [PATCH 38/50] fix 464c9b0 --- .../java/de/peeeq/wurstscript/types/WurstTypeInterface.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java index 18700f178..3980a4008 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java @@ -67,8 +67,9 @@ public WurstTypeInterface replaceTypeVars(List newTypes public ImmutableList extendedInterfaces() { return interfaceDef.getExtendsList().stream() + .map(i -> i.attrTyp().setTypeArgs(getTypeArgBinding())) .filter(i -> i instanceof WurstTypeInterface) - .map(i -> (WurstTypeInterface) i.attrTyp().setTypeArgs(getTypeArgBinding())) + .map(i -> (WurstTypeInterface) i) .filter(i -> i.level() < level()) .collect(ImmutableList.toImmutableList()); } From 4311d89ff70c2316b6894e396e9d1ef8fde3bfc8 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 12 Feb 2020 22:39:17 +0100 Subject: [PATCH 39/50] prefer overloads without type class constraints --- .../wurstscript/attributes/AttrFuncDef.java | 17 +++++++++++++++++ .../AttrPossibleFunctionSignatures.java | 4 ---- .../attributes/names/NameLink.java | 14 ++++++++++++++ .../tests/GenericsWithTypeclassesTests.java | 19 +++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java index 0c03fe752..65f425687 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java @@ -6,6 +6,7 @@ import de.peeeq.wurstscript.WurstOperator; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; +import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.names.Visibility; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; @@ -324,10 +325,26 @@ private static List useLocalPackageIfPossible(FuncRef node, return Utils.getFirst(funcs); } + // if still ambiguous, remove methods with type classes to be resolved + funcs = filterMethodsWithTypeClasses(node, funcs); + if (funcs.size() == 1) { + return Utils.getFirst(funcs); + } + node.addError("Call to function " + funcName + " is ambiguous. Alternatives are:\n" + Utils.printAlternatives(funcs)); return Utils.getFirst(funcs); } + private static List filterMethodsWithTypeClasses(Expr node, List funcs) { + List funcs2 = funcs.stream() + .filter(n -> !n.hasTypeClassConstraints()) + .collect(Collectors.toList()); + if (funcs2.isEmpty()) { + return funcs; + } + return funcs2; + } + private static List filterByParameters(Element node, List argumentTypes, List funcs) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 502249486..d5e99eefb 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -54,10 +54,6 @@ private static ImmutableCollection findBestSignature(StmtCall Pair> typeClassMatched = TypeClasses.findTypeClasses(sig2, fc); if (typeClassMatched.getB().isEmpty()) { resultBuilder2.add(typeClassMatched.getA()); - } else { - for (CompileError err : typeClassMatched.getB()) { - fc.getErrorHandler().sendError(err); - } } } ImmutableCollection res2 = resultBuilder2.build(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java index 61946031c..df777d3a1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java @@ -102,6 +102,20 @@ public List getTypeParams() { return typeParams; } + public boolean hasTypeClassConstraints() { + if (getDef() instanceof AstElementWithTypeParameters) + for (TypeParamDef tp : ((AstElementWithTypeParameters) getDef()).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); + if (!cs.isEmpty()) { + return true; + } + } + } + return false; + } + + public NameLink hidingPrivate() { if (visibility == Visibility.PRIVATE_HERE) { return withVisibility(Visibility.PRIVATE_OTHER); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index c4a6edd22..cb0dc3ed4 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1739,4 +1739,23 @@ public void defaultForClass() { ); } + @Test + public void overloadMiss() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native I2S(int i) returns string", + "interface Show", + " function toString(T t) returns string", + "function T.toString() returns string", + " return T.toString(this)", + "function int.toString() returns string", + " return I2S(this)", + "init", + " if 42 .toString() == \"42\"", + " testSuccess()" + ); + } + } From 9cc999dee1e090841e8ef250cda755c6a568673c Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Wed, 12 Feb 2020 22:49:34 +0100 Subject: [PATCH 40/50] overloading --- .../attributes/AttrFunctionSignature.java | 17 ++++++++++++++ .../wurstscript/types/FunctionSignature.java | 12 ++++++++++ .../tests/GenericsWithTypeclassesTests.java | 22 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java index 5adbcbecb..264a1cded 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java @@ -55,6 +55,13 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + candidates = filterByHasTypeClasses(candidates); + if (candidates.isEmpty()) { + // parameters match for no element, just return the first signature + return Utils.getFirst(sigs); + } else if (candidates.size() == 1) { + return candidates.get(0); + } @@ -70,6 +77,16 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + private static List filterByHasTypeClasses(List candidates) { + List res = candidates.stream() + .filter(sig -> !sig.hasTypeClassConstraints()) + .collect(Collectors.toList()); + if (res.isEmpty()) { + return candidates; + } + return res; + } + private static List filterByIfNotDefinedAnnotation(List candidates) { return candidates.stream() .filter(sig -> !sig.hasIfNotDefinedAnnotation()) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java index 771c456d8..bc9611b0b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java @@ -264,6 +264,18 @@ public boolean hasIfNotDefinedAnnotation() { return false; } + public boolean hasTypeClassConstraints() { + for (TypeParamDef tp : getDefinitionTypeVariables()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList list = (TypeParamConstraintList) tp.getTypeParamConstraints(); + if (!list.isEmpty()) { + return true; + } + } + } + return false; + } + public static class ArgsMatchResult { private final FunctionSignature sig; private final ImmutableList errors; diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index cb0dc3ed4..8c2fee0d2 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1758,4 +1758,26 @@ public void overloadMiss() { ); } + @Test + public void overloadMiss2() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native I2S(int i) returns string", + "interface Show", + " function toString(T t) returns string", + "function T.toString() returns string", + " return T.toString(this)", + "function int.toString() returns string", + " return I2S(this)", + "implements Show", + " function toString(int i) returns string", + " return i.toString()", + "init", + " if 42 .toString() == \"42\"", + " testSuccess()" + ); + } + } From b20a0b3578634520a27e94c411c7b74dca1f47ad Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 13 Feb 2020 21:41:20 +0100 Subject: [PATCH 41/50] fix override check --- .../attributes/AttrFunctionSignature.java | 3 ++- .../peeeq/wurstscript/attributes/AttrPos.java | 4 ++++ .../wurstscript/validation/WurstValidator.java | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java index 264a1cded..2345dddec 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java @@ -47,6 +47,7 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + sigs = candidates; candidates = filterByIfNotDefinedAnnotation(candidates); if (candidates.isEmpty()) { // parameters match for no element, just return the first signature @@ -55,6 +56,7 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + sigs = candidates; candidates = filterByHasTypeClasses(candidates); if (candidates.isEmpty()) { // parameters match for no element, just return the first signature @@ -64,7 +66,6 @@ private static FunctionSignature filterSigs( } - if (argTypes.stream().noneMatch(t -> t instanceof WurstTypeUnknown)) { // only show overloading error, if type for all arguments could be determined StringBuilder alternatives = new StringBuilder(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java index 0dbee6fa8..dd739eabe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java @@ -85,6 +85,10 @@ public static WPos getErrorPos(Element e) { return e.attrSource(); } + public static WPos getErrorPos(InstanceDecl e) { + return e.getImplementedInterface().attrErrorPos(); + } + public static WPos getErrorPos(SomeSuperConstructorCall e) { return e.getKeywordSource(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index 3d3fbe555..413dca31d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -1786,7 +1786,11 @@ public void case_FuncDef(FuncDef f) { } } } else { - check(VisibilityPublic.class, Annotation.class); + if (isInInstanceDecl(f)) { + check(VisibilityPublic.class, Annotation.class, ModOverride.class); + } else { + check(VisibilityPublic.class, Annotation.class); + } } if (f.attrIsCompiletime()) { if (f.getParameters().size() > 0) { @@ -1860,6 +1864,17 @@ public void case_InstanceDecl(InstanceDecl instanceDecl) { } } + private boolean isInInstanceDecl(FuncDef f) { + Element e = f; + while (e != null) { + if (e instanceof InstanceDecl) { + return true; + } + e = e.getParent(); + } + return false; + } + private static String printMod(Class c) { String name = c.getName().toLowerCase(); name = name.replaceFirst("^.*\\.", ""); From 3c09717e2e5e407ea636e65ee3947d4859ea5518 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 13 Feb 2020 21:55:09 +0100 Subject: [PATCH 42/50] re-enable lua tests (disabled by accident?) --- .../src/test/java/tests/wurstscript/tests/WurstScriptTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java index 79c96286c..08ab20217 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java @@ -60,7 +60,7 @@ class TestConfig { private List additionalCompilationUnits = new ArrayList<>(); private boolean stopOnFirstError = true; private boolean runCompiletimeFunctions; - private boolean testLua = false; + private boolean testLua = true; TestConfig(String name) { this.name = name; From 1e263e0b01e690a712571e3f88d744a59a95f065 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 13 Feb 2020 22:21:39 +0100 Subject: [PATCH 43/50] removed any-type --- .../wurstscript/translation/imtranslation/ImTranslator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index acef582ad..435086004 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1383,10 +1383,10 @@ public ImClass getGenericObjectTypeClassInstance() { "fromIndex", JassIm.ImTypeVars(), JassIm.ImVars(thiz, p), - ImAnyType(), + classType(res), JassIm.ImVars(), ImStmts( - ImReturn(t, ImCast(ImVarAccess(p), ImAnyType())) + ImReturn(t, ImCast(ImVarAccess(p), classType(res))) ), emptyList()); functions.add(fromIndex); @@ -1403,7 +1403,7 @@ public ImClass getGenericObjectTypeClassInstance() { "defaultValue", JassIm.ImTypeVars(), JassIm.ImVars(thiz), - ImAnyType(), + classType(res), JassIm.ImVars(), ImStmts( ImReturn(t, ImNull(classType(res))) From ade96438a8e608ded190dd8987e7393ccbc2aca1 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Thu, 13 Feb 2020 23:13:49 +0100 Subject: [PATCH 44/50] remove restriction to certain tests --- de.peeeq.wurstscript/build.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index 38cd22332..78aaa0ba1 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -215,9 +215,7 @@ test { // set minimal heap size required to run tests: maxHeapSize = "1G" - useTestNG() { - suites 'src/test/resources/AllTestsSuite.xml' - } + useTestNG() } // delete the generated sources on clean From 040b04faead51f108d5e0178fd376ae0353dd8b0 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 14 Feb 2020 00:23:36 +0100 Subject: [PATCH 45/50] fixed bugs --- .../de/peeeq/wurstscript/TypeClasses.java | 4 +- .../AttrPossibleFunctionSignatures.java | 53 +++++++++++++------ .../imtranslation/ClosureTranslator.java | 7 +-- .../translation/imtranslation/Flatten.java | 2 +- .../imtranslation/ImTranslator.java | 8 +-- .../imtranslation/OverrideUtils.java | 3 +- .../lua/translation/ExprTranslation.java | 4 +- .../lua/translation/RemoveGarbage.java | 6 +++ .../wurstscript/types/FunctionSignature.java | 2 + .../tests/GenericsWithTypeclassesTests.java | 26 +++++++++ 10 files changed, 82 insertions(+), 33 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index 757392934..c97a9ba38 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -43,9 +43,7 @@ public static Pair> findTypeClasses(Functi WurstTypeBoundTypeParam matchedType = matchedTypeOpt.get(); for (WurstTypeInterface constraint : constraints) { VariableBinding mapping2 = findTypeClass(fc, errors, mapping, tp, matchedType, constraint); - if (mapping2 == null) { - errors.add(new CompileError(fc.attrSource(), "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint + ".")); - } else { + if (mapping2 != null) { mapping = mapping2; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index d5e99eefb..968d9badd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -9,8 +9,10 @@ import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; import java.util.*; +import java.util.stream.Collectors; import static de.peeeq.wurstscript.attributes.GenericsHelper.givenBinding; import static de.peeeq.wurstscript.attributes.GenericsHelper.typeParameters; @@ -45,6 +47,7 @@ public static ImmutableCollection calculate(FunctionCall fc) private static ImmutableCollection findBestSignature(StmtCall fc, ImmutableCollection res) { ImmutableCollection.Builder resultBuilder2 = ImmutableList.builder(); + ImmutableCollection.Builder>> typeClassErrors = ImmutableList.builder(); List argTypes = AttrFuncDef.argumentTypesPre(fc); for (FunctionSignature sig : res) { FunctionSignature sig2 = sig.matchAgainstArgs(argTypes, fc); @@ -54,30 +57,46 @@ private static ImmutableCollection findBestSignature(StmtCall Pair> typeClassMatched = TypeClasses.findTypeClasses(sig2, fc); if (typeClassMatched.getB().isEmpty()) { resultBuilder2.add(typeClassMatched.getA()); + } else { + typeClassErrors.add(typeClassMatched); } } ImmutableCollection res2 = resultBuilder2.build(); - if (res2.isEmpty()) { - // no signature matches precisely --> try to match as good as possible - ImmutableList match3 = res.stream() - .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) + if (!res2.isEmpty()) { + return res2; + } + // if no precise matches, check if there are any matches that just miss some type class constraints: + ImmutableCollection>> typeClassErrorsL = typeClassErrors.build(); + if (!typeClassErrorsL.isEmpty()) { + CompileError err = typeClassErrorsL.stream() + .flatMap(x -> x.getB().stream()) + .findFirst() + .get(); + + fc.getErrorHandler().sendError(err); + + return typeClassErrorsL.stream() + .map(Pair::getA) .collect(ImmutableList.toImmutableList()); + } - if (match3.isEmpty()) { - return ImmutableList.of(); - } else { - // add errors from best match (minimal badness) - ArgsMatchResult min = Collections.min(match3, Comparator.comparing(ArgsMatchResult::getBadness)); - for (CompileError c : min.getErrors()) { - fc.getErrorHandler().sendError(c); - } + // no signature matches precisely --> try to match as good as possible + ImmutableList match3 = res.stream() + .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) + .collect(ImmutableList.toImmutableList()); - return match3.stream() - .map(ArgsMatchResult::getSig) - .collect(ImmutableList.toImmutableList()); - } + if (match3.isEmpty()) { + return ImmutableList.of(); } else { - return res2; + // add errors from best match (minimal badness) + ArgsMatchResult min = Collections.min(match3, Comparator.comparing(ArgsMatchResult::getBadness)); + for (CompileError c : min.getErrors()) { + fc.getErrorHandler().sendError(c); + } + + return match3.stream() + .map(ArgsMatchResult::getSig) + .collect(ImmutableList.toImmutableList()); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java index 13e99d8b1..f60439bb6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java @@ -11,10 +11,7 @@ import de.peeeq.wurstscript.translation.imtojass.TypeRewriter; import de.peeeq.wurstscript.types.*; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import static java.util.Collections.singletonList; @@ -153,7 +150,7 @@ private ImClass createClass() { tr.getImProg().getFunctions().remove(impl); c.getFunctions().add(impl); ImClassType methodClass = JassIm.ImClassType(c, JassIm.ImTypeArguments()); - ImMethod m = JassIm.ImMethod(e, methodClass, superMethod.getName(), impl, JassIm.ImMethods(), false); + ImMethod m = JassIm.ImMethod(e, methodClass, superMethod.getName(), impl, new ArrayList<>(), false); c.getMethods().add(m); OverrideUtils.addOverrideClosure(tr, superMethod, m, e); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index a7c37be24..390632698 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -90,7 +90,7 @@ public static Result flatten(ImAlloc e, ImTranslator translator, ImFunction f) { public static Result flatten(ImTypeClassDictValue e, ImTranslator translator, ImFunction f) { MultiResult r = flattenExprs(translator, f, e.getArguments()); - return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClazz(), JassIm.ImExprs(e.getArguments()))); + return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClazz(), JassIm.ImExprs(r.exprs))); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 435086004..536984a8c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1373,7 +1373,7 @@ public ImClass getGenericObjectTypeClassInstance() { classType(res), "toIndex", toIndex, - emptyList(), + new ArrayList<>(), false)); // add fromIndex function: @@ -1394,7 +1394,7 @@ public ImClass getGenericObjectTypeClassInstance() { classType(res), "fromIndex", fromIndex, - emptyList(), + new ArrayList<>(), false)); // add defaultValue function: @@ -1408,13 +1408,13 @@ public ImClass getGenericObjectTypeClassInstance() { ImStmts( ImReturn(t, ImNull(classType(res))) ), - emptyList()); + new ArrayList<>()); functions.add(defaultValue); methods.add(JassIm.ImMethod(t, classType(res), "defaultValue", defaultValue, - emptyList(), + new ArrayList<>(), false)); imProg.getClasses().add(res); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java index 8115aee9f..6cc76edbe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java @@ -116,8 +116,7 @@ public static void addOverride( ImFunction implementation = JassIm.ImFunction(e, subMethod.getName() + "_wrapper", JassIm.ImTypeVars(), parameters, rType, locals, body, flags); tr.getImProg().getFunctions().add(implementation); - List subMethods = Collections.emptyList(); - ImMethod wrapperMethod = JassIm.ImMethod(e, subMethod.getMethodClass(), subMethod.getName() + "_wrapper", implementation, subMethods, false); + ImMethod wrapperMethod = JassIm.ImMethod(e, subMethod.getMethodClass(), subMethod.getName() + "_wrapper", implementation, new ArrayList<>(), false); subClass.getMethods().add(wrapperMethod); superMethodIm.getSubMethods().add(wrapperMethod); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 71df41032..928aaa58e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -86,7 +86,8 @@ public static LuaExpr translate(ImIntVal e, LuaTranslator tr) { } public static LuaExpr translate(ImMemberAccess e, LuaTranslator tr) { - LuaExpr res = LuaAst.LuaExprFieldAccess(e.getReceiver().translateToLua(tr), e.getVar().getName()); + LuaVariable luaV = tr.luaVar.getFor(e.getVar()); + LuaExpr res = LuaAst.LuaExprFieldAccess(e.getReceiver().translateToLua(tr), luaV.getName()); if (!e.getIndexes().isEmpty()) { LuaExprlist indexes = LuaAst.LuaExprlist(); for (ImExpr index : e.getIndexes()) { @@ -172,6 +173,7 @@ public static LuaExpr translate(ImTypeClassDictValue e, LuaTranslator tr) { body.add(r); int i = 0; for (ImVar field : c.getFields()) { + System.out.println("field: " + field); if (i >= e.getArguments().size()) { break; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/RemoveGarbage.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/RemoveGarbage.java index a300b5e3f..f92642c73 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/RemoveGarbage.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/RemoveGarbage.java @@ -169,6 +169,12 @@ public void visit(ImTypeIdOfClass e) { visitClass(e.getClazz().getClassDef(), used); } + @Override + public void visit(ImTypeClassDictValue e) { + super.visit(e); + visitClass(e.getClazz().getClassDef(), used); + } + @Override public void visit(ImMethodCall e) { super.visit(e); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java index bc9611b0b..a9b614979 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java @@ -3,10 +3,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.parser.WPos; +import de.peeeq.wurstscript.utils.Pair; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 8c2fee0d2..6ccaaea6d 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1780,4 +1780,30 @@ public void overloadMiss2() { ); } + + @Test + public void typeClassFindSupertypeInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } } From 4937df8f5346e1cc61bae5963836c048278d86f0 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 14 Feb 2020 00:44:36 +0100 Subject: [PATCH 46/50] correct handling of public imports --- .../de/peeeq/wurstscript/TypeClasses.java | 43 +++++++++++++------ .../tests/GenericsWithTypeclassesTests.java | 24 +++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java index c97a9ba38..65f321b9b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -26,7 +26,7 @@ public class TypeClasses { */ private static final int DERIVATION_MAX_INSTANCE_USES = 10; - private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "Default", "AnyRef"); + private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "ConvertIndex", "Default", "AnyRef"); public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { List errors = new ArrayList<>(); @@ -213,19 +213,34 @@ private static VariableBinding findInstanceDeclarations(Element location, List availableTypeClasses(WPackage p) { - return Stream.concat( - p.attrDefinedTypeClasses().stream(), - p.getImports().stream() - .map(WImport::attrImportedPackage) - .flatMap(ip -> { - if (ip == null) { - return Stream.empty(); - } else { - return ip.attrDefinedTypeClasses().stream() - .filter(InstanceDecl::attrIsPublic); - } - })) - .collect(Collectors.toList()); + List result = new ArrayList<>(p.attrDefinedTypeClasses()); + Set visited = new HashSet<>(); + for (WImport imp : p.getImports()) { + @Nullable WPackage impP = imp.attrImportedPackage(); + collectImportedInstances(result, visited, impP); + } + return result; + } + + private static void collectImportedInstances(List result, Set visited, WPackage p) { + if (p == null) { + return; + } + if (visited.contains(p)) { + return; + } + visited.add(p); + for (InstanceDecl inst : p.attrDefinedTypeClasses()) { + if (inst.attrIsPublic()) { + result.add(inst); + } + } + for (WImport imp : p.getImports()) { + if (imp.getIsPublic()) { + @Nullable WPackage impP = imp.attrImportedPackage(); + collectImportedInstances(result, visited, impP); + } + } } public static List definedTypeClasses(WPackage p) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 6ccaaea6d..9dc71c950 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1806,4 +1806,28 @@ public void typeClassFindSupertypeInstance() { " testSuccess()" ); } + + @Test + public void instanceViaPublicImport() { + testAssertOkLines(true, + "package a", + "public interface ToIndex", + " function toIndex(T x) returns int", + "public implements ToIndex", + " function toIndex(int x) returns int", + " return x", + "endpackage", + "package b", + "import public a", + "endpackage", + "package test", + "import b", + "native testSuccess()", + "function foo(Q x)", + " if Q.toIndex(x) == 42", + " testSuccess()", + "init", + " foo(42)" + ); + } } From 8723ef1d57934ea6cccaa6e240ca0ce2b6ca2bfa Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 14 Feb 2020 00:55:38 +0100 Subject: [PATCH 47/50] fix lua: protect main --- .../lua/translation/LuaTranslator.java | 9 ++++-- .../tests/GenericsWithTypeclassesTests.java | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index ca5f8cfd6..5219935d1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -24,7 +24,7 @@ public class LuaTranslator { final LuaCompilationUnit luaModel; private final Set usedNames = new HashSet<>(Arrays.asList( // reserved function names - "print", "tostring", "error", + "print", "tostring", "error", "main", // keywords: "and", "break", @@ -82,7 +82,9 @@ public LuaVariable initFor(ImTypeVar a) { @Override public LuaFunction initFor(ImFunction a) { String name = a.getName(); - if (!a.isExtern() && !a.isBj() && !a.isNative()) { + if (!a.isExtern() && !a.isBj() && !a.isNative() + && imTr.getMainFunc() != a + && imTr.getConfFunc() != a) { name = uniqueName(name); } @@ -501,8 +503,9 @@ private void createClassInitFunction(ImClass c, LuaVariable classVar, LuaMethod LuaTableFields initialFieldValues = LuaAst.LuaTableFields(); LuaVariable newInst = LuaAst.LuaVariable("new_inst", LuaAst.LuaTableConstructor(initialFieldValues)); for (ImVar field : c.getFields()) { + LuaVariable fieldL = luaVar.getFor(field); initialFieldValues.add( - LuaAst.LuaTableNamedField(field.getName(), defaultValue(field.getType())) + LuaAst.LuaTableNamedField(fieldL.getName(), defaultValue(field.getType())) ); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 9dc71c950..018092f9c 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1807,6 +1807,35 @@ public void typeClassFindSupertypeInstance() { ); } + @Test + public void typeClassExtendedInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + // TODO should work without these 2 lines: + " function toIndex(T x) returns int", + " function fromIndex(int i) returns T", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + @Test public void instanceViaPublicImport() { testAssertOkLines(true, From 2303938ead53f8d96868308727ee103df259626f Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 14 Feb 2020 01:23:44 +0100 Subject: [PATCH 48/50] add correct submethods --- .../java/de/peeeq/wurstscript/types/TypeClassInstance.java | 6 +++--- .../wurstscript/tests/GenericsWithTypeclassesTests.java | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java index b0ef67bdd..8d143684f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -81,14 +81,14 @@ public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { cd.getSuperClasses().add(JassIm.ImClassType(sup, JassIm.ImTypeArguments(JassIm.ImTypeArgument(objectType.imTranslateType(tr))))); // add sub methods - for (FuncDef m : supI.getMethods()) { - ImMethod imMethod = tr.getMethodFor(m); + supI.attrTyp().getMemberMethods(trace).forEach(m -> { + ImMethod imMethod = tr.getMethodFor((FuncDef) m.getDef()); for (ImMethod cdM : cd.getMethods()) { if (cdM.getName().equals(m.getName())) { imMethod.getSubMethods().add(cdM); } } - } + }); } ImClassType c = JassIm.ImClassType(cd, JassIm.ImTypeArguments()); return JassIm.ImTypeClassDictValue(trace, c, JassIm.ImExprs()); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 018092f9c..6fedcdaec 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1814,9 +1814,6 @@ public void typeClassExtendedInstance() { "native testSuccess()", "native testFail(string s)", "interface ConvertIndex extends ToIndex, FromIndex", - // TODO should work without these 2 lines: - " function toIndex(T x) returns int", - " function fromIndex(int i) returns T", "interface ToIndex", " function toIndex(T x) returns int", "interface FromIndex", From 1bcc0c1f48bd51cbe69664d6e8c695e13b5f7503 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 14 Feb 2020 01:34:38 +0100 Subject: [PATCH 49/50] one more test for subclasses --- .../tests/GenericsWithTypeclassesTests.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 6fedcdaec..08799b7ca 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -1833,6 +1833,34 @@ public void typeClassExtendedInstance() { ); } + @Test + public void dependentTypeClassSub() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return bar(x)", + "function bar(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + @Test public void instanceViaPublicImport() { testAssertOkLines(true, From 14bdfd1199e28d4ab3871c2b063f0ffe48abc513 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Fri, 21 Feb 2020 01:22:48 +0100 Subject: [PATCH 50/50] fixed bug in for-loop translation (wip) --- .../imtranslation/StmtTranslation.java | 93 +- .../tests/GenericsWithTypeclassesTests.java | 1145 +++++++++-------- 2 files changed, 675 insertions(+), 563 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java index 40f9c6b83..56709c93e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java @@ -9,15 +9,13 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.jassIm.ImExprs; import de.peeeq.wurstscript.jassIm.ImFunction; -import de.peeeq.wurstscript.jassIm.ImFunctionCall; import de.peeeq.wurstscript.jassIm.ImIf; import de.peeeq.wurstscript.jassIm.ImReturn; import de.peeeq.wurstscript.jassIm.ImSet; import de.peeeq.wurstscript.jassIm.ImStmts; import de.peeeq.wurstscript.jassIm.ImVar; -import de.peeeq.wurstscript.types.TypesHelper; -import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeVararg; +import de.peeeq.wurstscript.types.*; +import io.vavr.control.Option; import java.util.Collections; import java.util.List; @@ -73,10 +71,6 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { FuncLink nextFunc = nextFuncOpt.get(); FuncLink hasNextFunc = hasNextFuncOpt.get(); - // get the iterator function in the intermediate language - ImFunction nextFuncIm = t.getFuncFor(nextFunc.getDef()); - ImFunction hasNextFuncIm = t.getFuncFor(hasNextFunc.getDef()); - f.getLocals().add(t.getVarFor(s.getLoopVar())); ImExprs fromTarget; @@ -93,10 +87,9 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { ImStmts imBody = ImStmts(); // exitwhen not #hasNext() - imBody.add(ImExitwhen(s, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(s, hasNextFuncIm, ImTypeArguments(), fromTarget, false, CallType - .NORMAL))))); + imBody.add(ImExitwhen(s, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(functionCall(s, hasNextFunc, fromTarget, t))))); // elem = next() - ImFunctionCall nextCall = ImFunctionCall(s, nextFuncIm, ImTypeArguments(), fromTarget.copy(), false, CallType.NORMAL); + ImExpr nextCall = functionCall(s, nextFunc, fromTarget.copy(), t); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(s, t, nextCall, nextReturn, loopVarType); @@ -131,11 +124,6 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { // Type of loop Variable: WurstType loopVarType = forIn.getLoopVar().attrTyp(); - // get the iterator function in the intermediate language - ImFunction iteratorFuncIm = t.getFuncFor(iteratorFunc.getDef()); - ImFunction nextFuncIm = t.getFuncFor(nextFunc.getDef()); - ImFunction hasNextFuncIm = t.getFuncFor(hasNextFunc.getDef()); - // translate target: ImExprs iterationTargetList; if (forIn.getIn().attrTyp().isStaticRef()) { @@ -146,7 +134,8 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { } // call XX.iterator() - ImFunctionCall iteratorCall = ImFunctionCall(forIn, iteratorFuncIm, ImTypeArguments(), iterationTargetList, false, CallType.NORMAL); + // TODO use method calls if method, set type args (reuse code if possible) + ImExpr iteratorCall = functionCall(forIn, iteratorFunc, iterationTargetList, t); // create IM-variable for iterator ImVar iteratorVar = ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", Collections.emptyList()); @@ -160,11 +149,11 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { ImStmts imBody = ImStmts(); // exitwhen not #hasNext() - imBody.add(ImExitwhen(forIn, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(forIn, hasNextFuncIm, ImTypeArguments(), JassIm.ImExprs - (JassIm - .ImVarAccess(iteratorVar)), false, CallType.NORMAL))))); + imBody.add(ImExitwhen(forIn, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(functionCall(forIn, hasNextFunc, JassIm.ImExprs + (JassIm + .ImVarAccess(iteratorVar)), t))))); // elem = next() - ImFunctionCall nextCall = ImFunctionCall(forIn, nextFuncIm, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL); + ImExpr nextCall = functionCall(forIn, nextFunc, JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), t); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(forIn, t, nextCall, nextReturn, loopVarType); @@ -180,8 +169,8 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { @Override public void visit(ImReturn imReturn) { super.visit(imReturn); - imReturn.replaceBy(ImHelper.statementExprVoid(JassIm.ImStmts(ImFunctionCall(forIn, t.getFuncFor(funcLink.getDef()), ImTypeArguments(), JassIm - .ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL), imReturn.copy()))); + imReturn.replaceBy(ImHelper.statementExprVoid(JassIm.ImStmts(functionCall(forIn, funcLink, JassIm + .ImExprs(JassIm.ImVarAccess(iteratorVar)), t), imReturn.copy()))); } }); @@ -190,8 +179,8 @@ public void visit(ImReturn imReturn) { result.add(ImLoop(forIn, imBody)); // close iterator after loop - closeFunc.ifPresent(nameLink -> result.add(ImFunctionCall(forIn, t.getFuncFor(nameLink.getDef()), ImTypeArguments(), JassIm.ImExprs(JassIm - .ImVarAccess(iteratorVar)), false, CallType.NORMAL))); + closeFunc.ifPresent(nameLink -> result.add(functionCall(forIn, nameLink, JassIm.ImExprs(JassIm + .ImVarAccess(iteratorVar)), t))); } @@ -199,6 +188,38 @@ public void visit(ImReturn imReturn) { return ImHelper.statementExprVoid(ImStmts(result)); } + private static ImExpr functionCall(Element trace, FuncLink funcLink, ImExprs args, ImTranslator tr) { + FunctionDefinition def = funcLink.getDef(); + ImTypeArguments typeParams = JassIm.ImTypeArguments(); + if (def instanceof AstElementWithTypeParameters) { + VariableBinding binding = funcLink.getVariableBinding(); + for (TypeParamDef tp : ((AstElementWithTypeParameters) def).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + WurstTypeBoundTypeParam t = binding.get(tp).get(); + typeParams.add(JassIm.ImTypeArgument(t.imTranslateType(tr))); + } + } + } + if (def.attrIsDynamicClassMember()) { + FuncDef funcDef = (FuncDef) def; + + // translate as method call + args = args.copyWithRefs(); + ImExpr receiver = args.remove(0); + return JassIm.ImMethodCall(trace, tr.getMethodFor(funcDef), + typeParams, receiver, args, false); + } + // otherwise it is a function call: + return JassIm.ImFunctionCall( + trace, + tr.getFuncFor(def), + typeParams, + args, + false, + CallType.NORMAL + ); + } + /** * Translate a for in vararg loop. Unlike the other for loops we don't need * an iterator etc. because the loop is unrolled in the VarargEliminator @@ -216,13 +237,13 @@ private static ImStmt case_StmtForVararg(StmtForIn s, ImTranslator t, ImFunction public static ImStmt translate(StmtForRangeUp s, ImTranslator t, ImFunction f) { return case_StmtForRange(t, f, s.getLoopVar(), s.getTo(), s.getStep(), s.getBody(), WurstOperator.PLUS, - WurstOperator.GREATER, s); + WurstOperator.GREATER, s); } public static ImStmt translate(StmtForRangeDown s, ImTranslator t, ImFunction f) { return case_StmtForRange(t, f, s.getLoopVar(), s.getTo(), s.getStep(), s.getBody(), - WurstOperator.MINUS, WurstOperator.LESS, s); + WurstOperator.MINUS, WurstOperator.LESS, s); } private static ImStmt case_StmtForRange(ImTranslator t, ImFunction f, LocalVarDef loopVar, @@ -263,7 +284,7 @@ private static ImExpr addCacheVariableSmart(ImTranslator t, ImFunction f, Listmap(e -> ImOperatorCall(WurstOperator.EQ, ImExprs(tempVar.copy(), e.imTranslateExpr(t, f)))) - .reduce((x, y) -> ImOperatorCall(WurstOperator.OR, ImExprs(x, y))) - .orElseGet(() -> JassIm.ImBoolVal(true)); + .stream() + .map(e -> ImOperatorCall(WurstOperator.EQ, ImExprs(tempVar.copy(), e.imTranslateExpr(t, f)))) + .reduce((x, y) -> ImOperatorCall(WurstOperator.OR, ImExprs(x, y))) + .orElseGet(() -> JassIm.ImBoolVal(true)); } public static ImStmt translate(EndFunctionStatement endFunctionStatement, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index 08799b7ca..0d90935d6 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -10,14 +10,14 @@ public class GenericsWithTypeclassesTests extends WurstScriptTest { public void identity() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " int x = identity(3)", - " string s = identity(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " int x = identity(3)", + " string s = identity(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", "endpackage" ); } @@ -26,18 +26,18 @@ public void identity() { public void identityTrans() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function identity1(A a) returns A", - " return a", - " function identity2(B a) returns B", - " return identity1(a)", - " function identity3(C a) returns C", - " return identity2(a)", - " init", - " int x = identity3(3)", - " string s = identity3(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", + " native testSuccess()", + " function identity1(A a) returns A", + " return a", + " function identity2(B a) returns B", + " return identity1(a)", + " function identity3(C a) returns C", + " return identity2(a)", + " init", + " int x = identity3(3)", + " string s = identity3(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", "endpackage" ); } @@ -46,16 +46,16 @@ public void identityTrans() { public void identityRec() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function identity(int i, A a) returns A", - " if i > 0", - " return identity(i - 1, a)", - " return a", - " init", - " int x = identity(5, 3)", - " string s = identity(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", + " native testSuccess()", + " function identity(int i, A a) returns A", + " if i > 0", + " return identity(i - 1, a)", + " return a", + " init", + " int x = identity(5, 3)", + " string s = identity(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", "endpackage" ); } @@ -65,17 +65,17 @@ public void identityRec() { public void identityRecTypeCreation() { testAssertErrorsLines(true, "some error message", "package test", - " native testSuccess()", - " class C", - " construct(T t)", - " function blub(int i, A a) returns int", - " if i <= 0", - " return 0", - " return 1 + blub>(i-1, new C(a))", - " init", - " int x = blub(5, 3)", - " if x == 5", - " testSuccess()", + " native testSuccess()", + " class C", + " construct(T t)", + " function blub(int i, A a) returns int", + " if i <= 0", + " return 0", + " return 1 + blub>(i-1, new C(a))", + " init", + " int x = blub(5, 3)", + " if x == 5", + " testSuccess()", "endpackage" ); } @@ -84,20 +84,20 @@ public void identityRecTypeCreation() { public void identityRecMut() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function identity1(int i, A a) returns A", - " if i > 0", - " return identity2(i - 1, a)", - " return a", - " function identity2(int i, A a) returns A", - " if i > 0", - " return identity1(i - 1, a)", - " return a", - " init", - " int x = identity1(5, 3)", - " string s = identity1(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", + " native testSuccess()", + " function identity1(int i, A a) returns A", + " if i > 0", + " return identity2(i - 1, a)", + " return a", + " function identity2(int i, A a) returns A", + " if i > 0", + " return identity1(i - 1, a)", + " return a", + " init", + " int x = identity1(5, 3)", + " string s = identity1(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", "endpackage" ); } @@ -106,16 +106,16 @@ public void identityRecMut() { public void extensionFunc() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function boolean.choice(A x, A y) returns A", - " if this", - " return x", - " return y", - " init", - " int x = true.choice(5, 3)", - " string s = false.choice(\"a\", \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", + " native testSuccess()", + " function boolean.choice(A x, A y) returns A", + " if this", + " return x", + " return y", + " init", + " int x = true.choice(5, 3)", + " string s = false.choice(\"a\", \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", "endpackage" ); } @@ -125,16 +125,16 @@ public void extensionFunc() { public void extensionFuncReceiver() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function A.choice(boolean b, A y) returns A", - " if b", - " return this", - " return y", - " init", - " int x = (5).choice(true, 3)", - " string s = \"a\".choice(false, \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", + " native testSuccess()", + " function A.choice(boolean b, A y) returns A", + " if b", + " return this", + " return y", + " init", + " int x = (5).choice(true, 3)", + " string s = \"a\".choice(false, \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", "endpackage" ); } @@ -145,14 +145,14 @@ public void genericsDispatch() { "package Test", "native testSuccess()", "class Cell", - " T o", + " T o", "init", - " Cell x = new Cell()", - " Cell y = new Cell()", - " x.o = 3", - " y.o = \"a\"", - " if x.o == 3 and y.o == \"a\"", - " testSuccess()" + " Cell x = new Cell()", + " Cell y = new Cell()", + " x.o = 3", + " y.o = \"a\"", + " if x.o == 3 and y.o == \"a\"", + " testSuccess()" ); } @@ -161,15 +161,15 @@ public void genericsDispatch() { public void identity2() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class C", - " function identity(A a) returns A", - " return a", - " init", - " C a = new C()", - " C b = identity(a)", - " if a == b", - " testSuccess()", + " native testSuccess()", + " class C", + " function identity(A a) returns A", + " return a", + " init", + " C a = new C()", + " C b = identity(a)", + " if a == b", + " testSuccess()", "endpackage" ); } @@ -179,28 +179,28 @@ public void identity2() { public void function() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class List", - " function iterator() returns Iterator", - " return new Iterator(this)", - " class Iterator", - " S t", - " construct(List t)", - " int x = 1", - " function hasNext() returns boolean", - " return true", - " function next() returns S", - " return t", - " class A", - " class B", - " class C", - " init", - " List a = new List()", -// " for B b in a", - " Iterator iterator = a.iterator()", - " while iterator.hasNext()", - " B b = iterator.next()", - " testSuccess()", + " native testSuccess()", + " class List", + " function iterator() returns Iterator", + " return new Iterator(this)", + " class Iterator", + " S t", + " construct(List t)", + " int x = 1", + " function hasNext() returns boolean", + " return true", + " function next() returns S", + " return t", + " class A", + " class B", + " class C", + " init", + " List a = new List()", +// " for B b in a", + " Iterator iterator = a.iterator()", + " while iterator.hasNext()", + " B b = iterator.next()", + " testSuccess()", "endpackage" ); } @@ -209,10 +209,10 @@ public void function() { public void testSubtypeGenericClass() { testAssertOkLines(false, "package test", - " class A", - " class B extends A", - " init", - " A x = new B", + " class A", + " class B extends A", + " init", + " A x = new B", "endpackage" ); } @@ -221,10 +221,10 @@ public void testSubtypeGenericClass() { public void testSubtypeGenericClass2() { testAssertOkLines(false, "package test", - " class A", - " class B extends A", - " function foo()", - " A x = new B", + " class A", + " class B extends A", + " function foo()", + " A x = new B", "endpackage" ); } @@ -233,10 +233,10 @@ public void testSubtypeGenericClass2() { public void testSubtypeGenericInterface() { testAssertOkLines(false, "package test", - " interface I", - " class B implements I", - " init", - " I x = new B", + " interface I", + " class B implements I", + " init", + " I x = new B", "endpackage" ); } @@ -245,13 +245,13 @@ public void testSubtypeGenericInterface() { public void identityFail1() { testAssertOkLines(true, "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " real x = identity(3.14)", - " if x == 3.14", - " testSuccess()", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " real x = identity(3.14)", + " if x == 3.14", + " testSuccess()", "endpackage" ); } @@ -260,12 +260,12 @@ public void identityFail1() { public void identityFail2() { testAssertErrorsLines(true, "Cannot assign C to real", "package test", - " function identity(A a) returns A", - " return a", - " class C", - " int y", - " init", - " real x = identity(new C())", + " function identity(A a) returns A", + " return a", + " class C", + " int y", + " init", + " real x = identity(new C())", "endpackage" ); } @@ -275,18 +275,18 @@ public void identityFail2() { public void cellExample() { testAssertErrorsLines(true, "Wrong parameter type", "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " class A", - " class B", - " init", - " Cell c = new Cell()", - " c.set(new B())", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " class A", + " class B", + " init", + " Cell c = new Cell()", + " c.set(new B())", "endpackage" ); } @@ -295,20 +295,20 @@ public void cellExample() { public void implicitConversions() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " if c.get() == bla(5, 3)", - " testSuccess()", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " if c.get() == bla(5, 3)", + " testSuccess()", "endpackage" ); } @@ -317,21 +317,21 @@ public void implicitConversions() { public void implicitConversions2() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " c.set(c.get())", - " if c.get() == bla(5, 3)", - " testSuccess()", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " c.set(c.get())", + " if c.get() == bla(5, 3)", + " testSuccess()", "endpackage" ); } @@ -340,25 +340,25 @@ public void implicitConversions2() { public void implicitConversions3() { testAssertOkLines(true, "package test", - " native testSuccess()", - " public interface FoldClosure", - " function apply(T t, Q q) returns Q", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " function fold(Q start, FoldClosure f) returns Q", - " return f.apply(elem, start)", + " native testSuccess()", + " public interface FoldClosure", + " function apply(T t, Q q) returns Q", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " function fold(Q start, FoldClosure f) returns Q", + " return f.apply(elem, start)", "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " let x = c.fold(2, (e, a) -> e.z + a)", - " if x == 7", - " testSuccess()", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " let x = c.fold(2, (e, a) -> e.z + a)", + " if x == 7", + " testSuccess()", "endpackage" ); } @@ -390,24 +390,24 @@ public void implicitConversions5() { // #490 "@extern native R2S(real r) returns string", "native println(string s)", "interface F", - " function apply(A a) returns R", + " function apply(A a) returns R", "class Cell", - " T elem", - " construct(T t)", - " this.elem = t", - " function get() returns T", - " return elem", - " function map(F f) returns Cell", - " return new Cell(f.apply(elem))", + " T elem", + " construct(T t)", + " this.elem = t", + " function get() returns T", + " return elem", + " function map(F f) returns Cell", + " return new Cell(f.apply(elem))", "function real.assertEquals(real expected)", - " if this == expected", - " testSuccess()", - " else", - " println(R2S(this))", + " if this == expected", + " testSuccess()", + " else", + " println(R2S(this))", "init", - " let a = new Cell(5)", - " let b = a.map(i -> i*10.)", - " b.get().assertEquals(50)" + " let a = new Cell(5)", + " let b = a.map(i -> i*10.)", + " b.get().assertEquals(50)" ); } @@ -415,20 +415,20 @@ public void implicitConversions5() { // #490 public void implicitConversionsFail() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(3,4))", - " if c.get() == bla(3,4)", - " testSuccess()", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(3,4))", + " if c.get() == bla(3,4)", + " testSuccess()", "endpackage" ); } @@ -439,16 +439,16 @@ public void implicitConversionsAssign() { testAssertOkLines(false, "type unit extends handle", "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " init", - " Cell c = new Cell()", - " Cell c2 = c", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " init", + " Cell c = new Cell()", + " Cell c2 = c", "endpackage" ); } @@ -460,7 +460,7 @@ public void nativeTypes() { "package Test", "class L", "init", - " L l = new L()" + " L l = new L()" ); } @@ -472,12 +472,12 @@ public void implicitConversionFailSimple() { // see bug #121 "package Test", "class List", - " function get() returns T", - " return 0 castTo T", + " function get() returns T", + " return 0 castTo T", "init", - " List fxs = new List()", - " let f = fxs.get()", + " List fxs = new List()", + " let f = fxs.get()", "endpackage"); } @@ -488,17 +488,17 @@ public void cast() { "package Test", "native testSuccess()", "class Cell", - " T o", + " T o", "class A", - " function foo() returns int", - " return 5", + " function foo() returns int", + " return 5", "class B extends A", - " override function foo() returns int", - " return 6", + " override function foo() returns int", + " return 6", "init", - " Cell c = new Cell()", - " c.o = new B()", - " B b = c.o castTo B" + " Cell c = new Cell()", + " c.o = new B()", + " B b = c.o castTo B" ); } @@ -508,18 +508,18 @@ public void genericsDispatch2() { "package Test", "native testSuccess()", "class Cell", - " T o", + " T o", "class A", - " function foo() returns int", - " return 5", + " function foo() returns int", + " return 5", "class B extends A", - " override function foo() returns int", - " return 6", + " override function foo() returns int", + " return 6", "init", - " Cell c = new Cell()", - " c.o = new B()", - " if c.o.foo() == 6", - " testSuccess()" + " Cell c = new Cell()", + " c.o = new B()", + " if c.o.foo() == 6", + " testSuccess()" ); } @@ -529,12 +529,12 @@ public void genericsSubstitute1() { "package Test", "native testSuccess()", "class A", - " function bla(T t)", + " function bla(T t)", "class B extends A", "class C", "init", - " let b = new B", - " b.bla(new C)" + " let b = new B", + " b.bla(new C)" ); } @@ -544,15 +544,15 @@ public void genericsSubstitute2() { "package Test", "native testSuccess()", "interface I", - " function bla(T t, S s)", - " skip", + " function bla(T t, S s)", + " skip", "class A implements I", "class B extends A", "class C", "class D", "init", - " let b = new B", - " b.bla(new D, new C)" + " let b = new B", + " b.bla(new D, new C)" ); } @@ -562,18 +562,18 @@ public void genericsSubstitute3() { "package Test", "native testSuccess()", "interface I", - " function bla(T t, S s)", - " skip", + " function bla(T t, S s)", + " skip", "interface J extends I", - " function foo()", - " skip", + " function foo()", + " skip", "class A implements J", "class B extends A", "class C", "class D", "init", - " let b = new B", - " b.bla(new D, new C)" + " let b = new B", + " b.bla(new D, new C)" ); } @@ -582,10 +582,10 @@ public void genericsSubstitute() { testAssertOkLines(false, "package Test", "class A", - " function bla(T a)", + " function bla(T a)", "class B extends A", - " function do()", - " bla(new MyType)", + " function do()", + " bla(new MyType)", "class MyType" ); @@ -596,10 +596,10 @@ public void genericsSubstitute_override() { testAssertOkLines(false, "package Test", "class A", - " function bla(T a)", + " function bla(T a)", "class B extends A", - " override function bla(MyType t)", - " skip", + " override function bla(MyType t)", + " skip", "class MyType" ); @@ -611,13 +611,13 @@ public void genericsSubstitute_override_interface() { testAssertOkLines(false, "package Test", "interface I", - " function bla(S s, T t)", + " function bla(S s, T t)", "interface J extends I", - " function foo(T t)", + " function foo(T t)", "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", "class MyType" ); @@ -628,13 +628,13 @@ public void genericsSubstitute_override_interface_fail() { testAssertErrorsLines(false, "Parameter int s should have type MyType to override function bla", "package Test", "interface I", - " function bla(S s, T t)", + " function bla(S s, T t)", "interface J extends I", - " function foo(T t)", + " function foo(T t)", "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", "class MyType" ); @@ -648,7 +648,7 @@ public void genericMethod1() { "class Blub", "function bla(Blub t)", "init", - " bla(new Blub)" + " bla(new Blub)" ); } @@ -658,9 +658,9 @@ public void genericExtensionMethod1() { "package Test", "class Blub", "function Blub.bla()", - " skip", + " skip", "init", - " new Blub.bla()" + " new Blub.bla()" ); } @@ -669,10 +669,10 @@ public void genericReturnOverride() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", "package Test", "interface I", - " function f() returns T", + " function f() returns T", "class C implements I", - " function f() returns T", - " return null" + " function f() returns T", + " return null" ); } @@ -681,10 +681,10 @@ public void genericReturnOverride2() { testAssertOkLines(false, "package Test", "interface I", - " function f(S t) returns S", + " function f(S t) returns S", "class C implements I", - " function f(T t) returns T", - " return t" + " function f(T t) returns T", + " return t" ); } @@ -693,9 +693,9 @@ public void genericRecursive() { testAssertOkLines(false, "package Test", "public class C", - " C x", - " function foo()", - " this.x.x = null" + " C x", + " function foo()", + " this.x.x = null" ); } @@ -704,10 +704,10 @@ public void genericRecursive2() { testAssertOkLines(false, "package Test", "public class C", - " C x", - " function foo()", - " C c = new C", - " c.x.x = null" + " C x", + " function foo()", + " C c = new C", + " c.x.x = null" ); } @@ -717,10 +717,10 @@ public void genericChain1() { "package Test", "class A", "public class C", - " K x", + " K x", "init", - " C>> c = null", - " c.x.x.x = new A" + " C>> c = null", + " c.x.x.x = new A" ); } @@ -730,10 +730,10 @@ public void genericChain1Err() { "package Test", "class A", "public class C", - " K x", + " K x", "init", - " C>> c = null", - " c.x.x = new A" + " C>> c = null", + " c.x.x = new A" ); } @@ -743,10 +743,10 @@ public void genericChain2() { "package Test", "class A", "public class C", - " C> x", + " C> x", "init", - " C c = null", - " c.x.x.x = new C>>>" + " C c = null", + " c.x.x.x = new C>>>" ); } @@ -756,10 +756,10 @@ public void genericChain2ErrA() { "package Test", "class A", "public class C", - " C> x", + " C> x", "init", - " C c = null", - " c.x.x.x = new C>>>>" + " C c = null", + " c.x.x.x = new C>>>>" ); } @@ -769,10 +769,10 @@ public void genericChain2ErrB() { "package Test", "class A", "public class C", - " C> x", + " C> x", "init", - " C c = null", - " c.x.x.x = new C>>" + " C c = null", + " c.x.x.x = new C>>" ); } @@ -782,14 +782,14 @@ public void implicitsWithClass() { "package test", "native testSuccess()", "interface Comparison", - " function leq(T t, T u) returns bool", + " function leq(T t, T u) returns bool", "class BoolComp implements Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", + " override function leq(bool a, bool b) returns bool", + " return not a or b", "Comparison bc = new BoolComp", "init", - " if bc.leq(false, true)", - " testSuccess()" + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -799,15 +799,15 @@ public void implicitsWithClass2() { "package test", "native testSuccess()", "class Comparison", - " function leq(T t, T u) returns bool", - " return true", + " function leq(T t, T u) returns bool", + " return true", "class BoolComp extends Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", + " override function leq(bool a, bool b) returns bool", + " return not a or b", "Comparison bc = new BoolComp", "init", - " if bc.leq(false, true)", - " testSuccess()" + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -817,16 +817,16 @@ public void implicitsWithClass3() { "package test", "native testSuccess()", "class Comparison", - " function leq(T t, T u) returns bool", - " return true", + " function leq(T t, T u) returns bool", + " return true", "class ComparisonX extends Comparison", "class BoolComp extends ComparisonX", - " override function leq(bool a, bool b) returns bool", - " return not a or b", + " override function leq(bool a, bool b) returns bool", + " return not a or b", "ComparisonX bc = new BoolComp", "init", - " if bc.leq(false, true)", - " testSuccess()" + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -836,11 +836,11 @@ public void implicitsWithClosures() { "package test", "native testSuccess()", "interface Comparison", - " function leq(T t, T u) returns bool", + " function leq(T t, T u) returns bool", "Comparison bc = (bool a, bool b) -> not a or b", "init", - " if bc.leq(false, true)", - " testSuccess()" + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -851,22 +851,53 @@ public void genericForIn() { "package test", "native testSuccess()", "class C", - " function iterator() returns Iterator", - " return new Iterator()", + " function iterator() returns Iterator", + " return new Iterator()", + "class Iterator", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", + " function close()", + " destroy this", + "init", + " let c = new C", + " for i in c", + " if i == 5", + " testSuccess()" + ); + } + + @Test + public void genericForIn2() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "class C", + " T x", + " construct(T x)", + " this.x = x", + " function iterator() returns Iterator", + " return new Iterator(x)", "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", - " function close()", - " destroy this", + " private int i = 0", + " T x", + " construct(T x)", + " this.x = x", + " function next() returns T", + " i = i + 1", + " return x", + " function hasNext() returns boolean", + " return i < 1", + " function close()", + " destroy this", "init", - " let c = new C", - " for i in c", - " if i == 5", - " testSuccess()" + " let c = new C(\"42\")", + " for i in c", + " if i == \"42\"", + " testSuccess()" ); } @@ -877,21 +908,21 @@ public void genericForFrom() { "package test", "native testSuccess()", "class C", - " function iterator() returns Iterator", - " return new Iterator()", + " function iterator() returns Iterator", + " return new Iterator()", "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", "init", - " let c = new C", - " let iter = c.iterator()", - " for i from iter", - " if i == 5", - " testSuccess()" + " let c = new C", + " let iter = c.iterator()", + " for i from iter", + " if i == 5", + " testSuccess()" ); } @@ -901,16 +932,16 @@ public void genericOverload() { "package test", "native testSuccess()", "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", "init", - " let c = new C(1)", - " c.foo(1)" + " let c = new C(1)", + " c.foo(1)" ); } @@ -920,18 +951,18 @@ public void genericOverload2() { "package test", "native testSuccess()", "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", - " function test()", - " let c = new C(1)", - " c.foo(1)", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", + " function test()", + " let c = new C(1)", + " c.foo(1)", "init", - " new C(1).test()" + " new C(1).test()" ); } @@ -941,13 +972,13 @@ public void inferType() { "package test", "native testSuccess()", "function id(int x) returns int", - " return x", + " return x", "class C", - " var x = id(4)", + " var x = id(4)", "init", - " let x= new C", - " if x.x == 4", - " testSuccess()" + " let x= new C", + " if x.x == 4", + " testSuccess()" ); } @@ -957,12 +988,12 @@ public void simpleFunctionCall() { "package test", "native testSuccess()", "class C", - " function foo() returns int", - " return 4", + " function foo() returns int", + " return 4", "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @@ -972,14 +1003,14 @@ public void simpleFunctionCall2() { "package test", "native testSuccess()", "class C", - " function foo() returns int", - " return bar()", - " function bar() returns int", - " return 4", + " function foo() returns int", + " return bar()", + " function bar() returns int", + " return 4", "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @@ -989,18 +1020,18 @@ public void genericFunctionOverload() { // #628 "package test", "native testSuccess()", "class LinkedList", - " T x", + " T x", "public function LinkedList.foo(string separator) returns string", - " this.foo(s -> s, separator) // Doesn't work", - " this.foo2(s -> s, separator) // Works", - " return separator", + " this.foo(s -> s, separator) // Doesn't work", + " this.foo2(s -> s, separator) // Works", + " return separator", "interface ToStringClosure", - " function apply(A a) returns A", + " function apply(A a) returns A", "public function LinkedList.foo(ToStringClosure cls, string separator)", "public function LinkedList.foo2(ToStringClosure cls, string separator)", "init", - " let x = new LinkedList", - " x.foo(\"a\")" + " let x = new LinkedList", + " x.foo(\"a\")" ); } @@ -1010,11 +1041,11 @@ public void extensionFunc2() { // #718 "package test", "native testSuccess()", "public function T.foo() returns T", - " return this", + " return this", "init", - " let x = \"hello\".foo()", - " if x == \"hello\"", - " testSuccess()" + " let x = \"hello\".foo()", + " if x == \"hello\"", + " testSuccess()" ); } @@ -1024,14 +1055,14 @@ public void strangeFoldl() { // #655 "package test", "native testSuccess()", "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return startValue", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return startValue", "interface FoldClosure", - " function run(X t, Y q) returns Y", + " function run(X t, Y q) returns Y", "init", - " let x = new LinkedList", - " x.foldl(0, (x, y) -> x + y)" + " let x = new LinkedList", + " x.foldl(0, (x, y) -> x + y)" ); } @@ -1045,19 +1076,19 @@ public void normalFoldlInfer() { // #657 "string array s", "int s_max = -1", "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return predicate.run(x, startValue)", - " function toString() returns string", - " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", - " return fold + \"]\"", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return predicate.run(x, startValue)", + " function toString() returns string", + " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", + " return fold + \"]\"", "interface FoldClosure", - " function run(T t, Q q) returns Q", + " function run(T t, Q q) returns Q", "init", - " let x = new LinkedList", - " x.x = 5", - " if x.toString() == \"[5,]\"", - " testSuccess()" + " let x = new LinkedList", + " x.x = 5", + " if x.toString() == \"[5,]\"", + " testSuccess()" ); } @@ -1067,13 +1098,13 @@ public void inheritField() { "package test", "native testSuccess()", "abstract class A", - " int someInt", + " int someInt", "class B extends A", - " construct()", - " someInt = 1", + " construct()", + " someInt = 1", "init", - " if new B().someInt == 1", - " testSuccess()" + " if new B().someInt == 1", + " testSuccess()" ); } @@ -1083,13 +1114,13 @@ public void inheritField2() { "package test", "native testSuccess()", "abstract class A", - " int someInt", + " int someInt", "class B extends A", - " construct()", - " someInt = 1", + " construct()", + " someInt = 1", "init", - " if new B().someInt == 1", - " testSuccess()" + " if new B().someInt == 1", + " testSuccess()" ); } @@ -1100,13 +1131,13 @@ public void inheritMethod() { "package test", "native testSuccess()", "abstract class A", - " function someInt()", - " testSuccess()", + " function someInt()", + " testSuccess()", "class B extends A", - " construct()", - " someInt()", + " construct()", + " someInt()", "init", - " new B()" + " new B()" ); } @@ -1116,10 +1147,10 @@ public void nullWithGeneric() { "package test", "native testSuccess()", "function foo(T t)", - " if t == null", - " testSuccess()", + " if t == null", + " testSuccess()", "init", - " foo(null)" + " foo(null)" ); } @@ -1128,9 +1159,9 @@ public void missingTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", "package test", "function foo() returns T", - " return null", + " return null", "init", - " let x = foo()" + " let x = foo()" ); } @@ -1139,11 +1170,11 @@ public void missingTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", "package test", "class C", - " function foo() returns T", - " return null", + " function foo() returns T", + " return null", "init", - " let c = new C", - " let x = c.foo()" + " let c = new C", + " let x = c.foo()" ); } @@ -1153,7 +1184,7 @@ public void missingTypeArgsConstructor() { "package test", "class C", "init", - " let c = new C" + " let c = new C" ); } @@ -1163,9 +1194,9 @@ public void tooManyTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", "package test", "function foo() returns T", - " return null", + " return null", "init", - " let x = foo()" + " let x = foo()" ); } @@ -1174,11 +1205,11 @@ public void tooManyTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", "package test", "class C", - " function foo() returns T", - " return null", + " function foo() returns T", + " return null", "init", - " let c = new C", - " let x = c.foo()" + " let c = new C", + " let x = c.foo()" ); } @@ -1188,7 +1219,7 @@ public void tooManyTypeArgsConstructor() { "package test", "class C", "init", - " let c = new C" + " let c = new C" ); } @@ -1199,15 +1230,15 @@ public void capturedType() { // #490 "native testSuccess()", "native println(string s)", "interface F", - " function apply(A a) returns R", + " function apply(A a) returns R", "function twice(F f) returns F", - " return x -> f.apply(f.apply(x))", // line 7 + " return x -> f.apply(f.apply(x))", // line 7 "init", - " F plus1 = x -> x + 1", // line 9 - " F plus2 = twice(plus1)", -// " F shout = twice((string s) -> s + \"!\")", // line 11 - " if plus2.apply(1) == 3", - " testSuccess()" + " F plus1 = x -> x + 1", // line 9 + " F plus2 = twice(plus1)", +// " F shout = twice((string s) -> s + \"!\")", // line 11 + " if plus2.apply(1) == 3", + " testSuccess()" ); } @@ -1218,16 +1249,16 @@ public void severalSubMethods() { // #490 "native testSuccess()", "native println(string s)", "class B", - " function id(T x) returns T", - " return x", + " function id(T x) returns T", + " return x", "class A extends B", - " override function id(Y y) returns Y", - " return y", + " override function id(Y y) returns Y", + " return y", "init", - " B a = new A", - " B b = new A", - " if a.id(4) == 4 and b.id(2) == 2", - " testSuccess()" + " B a = new A", + " B b = new A", + " if a.id(4) == 4 and b.id(2) == 2", + " testSuccess()" ); } @@ -1235,17 +1266,17 @@ public void severalSubMethods() { // #490 public void simpleCastTest() { testAssertOkLines(true, "package test", - " native testSuccess()", - " class A", - " class B extends A", - " int x", - " function get() returns A", - " let r = new B", - " r.x = 5", - " return r", - " init", - " if (get() castTo B).x == 5", - " testSuccess()", + " native testSuccess()", + " class A", + " class B extends A", + " int x", + " function get() returns A", + " let r = new B", + " r.x = 5", + " return r", + " init", + " if (get() castTo B).x == 5", + " testSuccess()", "endpackage" ); } @@ -1256,17 +1287,17 @@ public void abstractReturnT() { "package test", "native testSuccess()", "abstract class F", - " abstract function get() returns T", + " abstract function get() returns T", "class X extends F", - " T t", - " construct(T t)", - " this.t = t", - " override function get() returns T", - " return t", - "init", - " F x = new X(42)", - " if x.get() == 42", - " testSuccess()" + " T t", + " construct(T t)", + " this.t = t", + " override function get() returns T", + " return t", + "init", + " F x = new X(42)", + " if x.get() == 42", + " testSuccess()" ); } @@ -1313,7 +1344,7 @@ public void simpleTypeClass() { "native testSuccess()", "native testFail(string s)", "interface ToIndex", - " function toIndex(T x) returns int", // 5 + " function toIndex(T x) returns int", // 5 "class A", "implements ToIndex", " function toIndex(A x) returns int", @@ -1335,9 +1366,9 @@ public void twoTypeClasses() { "native testSuccess()", "native testFail(string s)", "interface Plus", - " function plus(T x, T y) returns T", + " function plus(T x, T y) returns T", "interface Times", - " function times(T x, T y) returns T", + " function times(T x, T y) returns T", "implements Plus", " function plus(int x, int y) returns int", " return x + y", @@ -1358,7 +1389,7 @@ public void forwardTypeClass() { "package test", "native testSuccess()", "interface ToIndex", - " function toIndex(T elem) returns int", + " function toIndex(T elem) returns int", "class A", "implements ToIndex", " function toIndex(A x) returns int", @@ -1381,9 +1412,9 @@ public void subTypeClass() { "native testSuccess()", "native testFail(string s)", "interface A", - " function x(T x) returns int", + " function x(T x) returns int", "interface B extends A", - " function y(T x) returns int", + " function y(T x) returns int", "class C", "implements B", " function x(C x) returns int", @@ -1406,9 +1437,9 @@ public void typeClassAmbiguous() { "native testSuccess()", "native testFail(string s)", "interface A", - " function x(T x) returns int", + " function x(T x) returns int", "interface B extends A", - " function y(T x) returns int", + " function y(T x) returns int", "class C", "implements A", " function x(C x) returns int", @@ -1435,9 +1466,9 @@ public void buildInstance() { "native testSuccess()", "native testFail(string s)", "interface A", - " function x(X x) returns int", + " function x(X x) returns int", "interface B", - " function y(Y y) returns int", + " function y(Y y) returns int", "class C", "implements A", " function x(C c) returns int", @@ -1462,7 +1493,7 @@ public void classParameterConstraint() { "native testFail(string s)", "@extern native S2I(string s) returns int", "interface ToInt", - " function toInt(X x) returns int", + " function toInt(X x) returns int", "class C", " int elem", " function set(T e)", @@ -1488,7 +1519,7 @@ public void classParameterConstraintBuild() { "native testFail(string s)", "@extern native S2I(string s) returns int", "interface ToInt", - " function toInt(X x) returns int", + " function toInt(X x) returns int", "class C", " int elem", " function set(T e)", @@ -1521,7 +1552,7 @@ public void subtypeConstraint1() { "native testFail(string s)", "@extern native S2I(string s) returns int", "interface ToInt", - " function toInt(X x) returns int", + " function toInt(X x) returns int", "class C", " int elem", " function set(T e)", @@ -1548,7 +1579,7 @@ public void subtypeConstraintDependent() { "native testFail(string s)", "@extern native S2I(string s) returns int", "interface ToInt", - " function toInt(X x) returns int", + " function toInt(X x) returns int", "class C", " int elem", " function set(T e)", @@ -1634,8 +1665,8 @@ public void missingFunction() { testAssertErrorsLines(false, "must implement the following functions", "package test", "interface Blub", - " function foo(T x) returns int", - " function bar(T x) returns int", + " function foo(T x) returns int", + " function bar(T x) returns int", "class A", "implements Blub", " function foo(A x) returns int", @@ -1649,9 +1680,9 @@ public void cyclicImplements() { testAssertErrorsLines(false, "Type A does not satisfy constraint Y: Apfel", "package test", "interface Apfel", - " function a(T x) returns int", + " function a(T x) returns int", "interface Birne", - " function b(T x) returns int", + " function b(T x) returns int", "class A", "implements Apfel for X: Birne", " function a(X t) returns int", @@ -1673,8 +1704,8 @@ public void anyRefClass() { "native testSuccess()", "native testFail(string s)", "interface AnyRef", - " function toIndex(T x) returns int", - " function fromIndex(int index) returns T", + " function toIndex(T x) returns int", + " function fromIndex(int index) returns T", "class A", "class Cell", " int x", @@ -1698,9 +1729,9 @@ public void toIndexFromIndexClass() { "native testSuccess()", "native testFail(string s)", "interface ToIndex", - " function toIndex(T x) returns int", + " function toIndex(T x) returns int", "interface FromIndex", - " function fromIndex(int index) returns T", + " function fromIndex(int index) returns T", "class A", "class Cell", " int x", @@ -1724,7 +1755,7 @@ public void defaultForClass() { "native testSuccess()", "native testFail(string s)", "interface Default", - " function defaultValue() returns T", + " function defaultValue() returns T", "class A", "function d() returns X", " return X.defaultValue()", @@ -1747,7 +1778,7 @@ public void overloadMiss() { "native testFail(string s)", "@extern native I2S(int i) returns string", "interface Show", - " function toString(T t) returns string", + " function toString(T t) returns string", "function T.toString() returns string", " return T.toString(this)", "function int.toString() returns string", @@ -1766,7 +1797,7 @@ public void overloadMiss2() { "native testFail(string s)", "@extern native I2S(int i) returns string", "interface Show", - " function toString(T t) returns string", + " function toString(T t) returns string", "function T.toString() returns string", " return T.toString(this)", "function int.toString() returns string", @@ -1789,9 +1820,9 @@ public void typeClassFindSupertypeInstance() { "native testFail(string s)", "interface ConvertIndex extends ToIndex, FromIndex", "interface ToIndex", - " function toIndex(T x) returns int", + " function toIndex(T x) returns int", "interface FromIndex", - " function fromIndex(int i) returns T", + " function fromIndex(int i) returns T", "class A", "implements ConvertIndex", " function toIndex(string x) returns int", @@ -1815,9 +1846,9 @@ public void typeClassExtendedInstance() { "native testFail(string s)", "interface ConvertIndex extends ToIndex, FromIndex", "interface ToIndex", - " function toIndex(T x) returns int", + " function toIndex(T x) returns int", "interface FromIndex", - " function fromIndex(int i) returns T", + " function fromIndex(int i) returns T", "class A", "implements ConvertIndex", " function toIndex(string x) returns int", @@ -1841,9 +1872,9 @@ public void dependentTypeClassSub() { "native testFail(string s)", "interface ConvertIndex extends ToIndex, FromIndex", "interface ToIndex", - " function toIndex(T x) returns int", + " function toIndex(T x) returns int", "interface FromIndex", - " function fromIndex(int i) returns T", + " function fromIndex(int i) returns T", "class A", "implements ConvertIndex", " function toIndex(string x) returns int", @@ -1866,7 +1897,7 @@ public void instanceViaPublicImport() { testAssertOkLines(true, "package a", "public interface ToIndex", - " function toIndex(T x) returns int", + " function toIndex(T x) returns int", "public implements ToIndex", " function toIndex(int x) returns int", " return x", @@ -1884,4 +1915,64 @@ public void instanceViaPublicImport() { " foo(42)" ); } + + + @Test + public void chainTypeClassesConvertIndex() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + " function toIndex(T x) returns int", + " function fromIndex(int i) returns T", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return bar(x)", + "function bar(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + + @Test + public void subInterface() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + " function toIndex(T x) returns int", + " function fromIndex(int i) returns T", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(ConvertIndex c, string x) returns int", + " return bar(c, x)", + "function bar(ToIndex c, string x) returns int", + " return c.toIndex(x)", + "init", + " let a = new A", + " if foo(a, \"x\") == 42", + " testSuccess()" + ); + } }