diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/OverloadingResolver.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/OverloadingResolver.java index 47c298603..ea8f5ce4c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/OverloadingResolver.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/OverloadingResolver.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeTypeParam; +import de.peeeq.wurstscript.types.WurstTypeVararg; import de.peeeq.wurstscript.utils.NotNullList; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -23,6 +24,27 @@ public abstract class OverloadingResolver { abstract void handleError(List hints); + boolean isVararg(F f) { + int paramCount = getParameterCount(f); + return paramCount > 0 && getParameterType(f, paramCount - 1) instanceof WurstTypeVararg; + } + + int getMinParameterCount(F f) { + return isVararg(f) ? getParameterCount(f) - 1 : getParameterCount(f); + } + + boolean hasValidParameterCount(F f, C caller) { + int argCount = getArgumentCount(caller); + return argCount >= getMinParameterCount(f) && (isVararg(f) || argCount <= getParameterCount(f)); + } + + WurstType getParameterTypeForArg(F f, int i) { + if (isVararg(f) && i >= getParameterCount(f) - 1) { + return ((WurstTypeVararg) getParameterType(f, getParameterCount(f) - 1)).getBaseType(); + } + return getParameterType(f, i); + } + Optional resolve(Iterable alternativeFunctions, C caller) { int size = Utils.size(alternativeFunctions); if (size == 0) { @@ -35,13 +57,18 @@ Optional resolve(Iterable alternativeFunctions, C caller) { Map numMatches = new HashMap<>(); for (F f : alternativeFunctions) { + if (!hasValidParameterCount(f, caller)) { + numMatches.put(f, -1); + continue; + } int matches = 0; - for (int i = 0; i < getArgumentCount(caller) && i < getParameterCount(f); i++) { + for (int i = 0; i < getArgumentCount(caller); i++) { + WurstType expectedParamType = getParameterTypeForArg(f, i); if (getArgumentType(caller, i) instanceof WurstTypeTypeParam - && getParameterType(f, i) instanceof WurstTypeTypeParam) { + && expectedParamType instanceof WurstTypeTypeParam) { // should be ok! - } else if (!getArgumentType(caller, i).isSubtypeOf(getParameterType(f, i), f)) { - hints.add("Expected " + getParameterType(f, i) + } else if (!getArgumentType(caller, i).isSubtypeOf(expectedParamType, f)) { + hints.add("Expected " + expectedParamType + " as parameter " + i + " ,but found " + getArgumentType(caller, i) + "."); continue; } @@ -67,7 +94,7 @@ && getParameterType(f, i) instanceof WurstTypeTypeParam) { List rightNumberOfParams = new ArrayList<>(); for (F f1 : funcs) { - if (getParameterCount(f1) == getArgumentCount(caller)) { + if (hasValidParameterCount(f1, caller)) { rightNumberOfParams.add(f1); } } @@ -108,7 +135,7 @@ int getParameterCount(ConstructorDef f) { @Override WurstType getParameterType(ConstructorDef f, int i) { - return f.getParameters().get(i).getTyp().attrTyp().dynamic(); + return f.getParameters().get(i).attrTyp(); } @Override @@ -138,7 +165,7 @@ int getParameterCount(ConstructorDef f) { @Override WurstType getParameterType(ConstructorDef f, int i) { - return f.getParameters().get(i).getTyp().attrTyp().dynamic(); + return f.getParameters().get(i).attrTyp(); } @Override 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 a8aa0c3a2..ba3eb07b0 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 @@ -54,7 +54,10 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab try { // --- varargs rewrite --- - if (f.hasFlag(FunctionFlagEnum.IS_VARARG)) { + if (f.hasFlag(FunctionFlagEnum.IS_VARARG) + && !(args.length == f.getParameters().size() + && args.length > 0 + && args[args.length - 1] instanceof VarargArray)) { ILconst[] newArgs = new ILconst[f.getParameters().size()]; if (newArgs.length - 1 >= 0) { System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1); 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 e9e1b0a4e..f825187f7 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 @@ -1400,8 +1400,12 @@ public ImFunction getConstructFunc(ConstructorDef constr) { for (WParameter p : constr.getParameters()) { params.add(getVarFor(p)); } - - f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags()); + List constructorFlags = flags(); + if (!constr.getParameters().isEmpty() + && constr.getParameters().get(constr.getParameters().size() - 1).attrIsVararg()) { + constructorFlags.add(IS_VARARG); + } + f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), constructorFlags); addFunction(f, constr); constructorFuncs.put(constr, f); } @@ -1433,8 +1437,12 @@ public ImFunction getConstructNewFunc(ConstructorDef constr) { ImFunction f = constrNewFuncs.get(constr); if (f == null) { String name = "new_" + constr.attrNearestClassDef().getName(); - - f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); + List constructorFlags = flags(); + if (!constr.getParameters().isEmpty() + && constr.getParameters().get(constr.getParameters().size() - 1).attrIsVararg()) { + constructorFlags.add(IS_VARARG); + } + f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), constructorFlags); addFunction(f, constr); constrNewFuncs.put(constr, f); } 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 8105bddbe..7facfae8b 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 @@ -1419,7 +1419,13 @@ private boolean isValidVarnameStart(String varName) { private void visit(WParameter p) { checkVarName(p, false); if (p.attrIsVararg()) { - if (p.attrNearestFuncDef().getParameters().size() != 1) { + Element owner = p.getParent().getParent(); + if (owner instanceof ConstructorDef) { + WParameters params = ((ConstructorDef) owner).getParameters(); + if (params.get(params.size() - 1) != p) { + p.addError("Vararg parameter in constructors must be last"); + } + } else if (p.attrNearestFuncDef().getParameters().size() != 1) { p.addError("Vararg functions may only have one parameter"); } } @@ -1769,17 +1775,24 @@ private void checkParams(Element where, String preMsg, List args, Function @Deprecated private void checkParams(Element where, String preMsg, List args, List parameterTypes) { - if (args.size() > parameterTypes.size()) { - where.addError(preMsg + "Too many parameters."); - - } else if (args.size() < parameterTypes.size()) { + boolean isVararg = !parameterTypes.isEmpty() && Utils.getLast(parameterTypes) instanceof WurstTypeVararg; + int minParameters = isVararg ? parameterTypes.size() - 1 : parameterTypes.size(); + if (args.size() < minParameters) { where.addError(preMsg + "Missing parameters."); + } else if (!isVararg && args.size() > parameterTypes.size()) { + where.addError(preMsg + "Too many parameters."); } else { + WurstType varargBaseType = isVararg + ? ((WurstTypeVararg) Utils.getLast(parameterTypes)).getBaseType() + : null; for (int i = 0; i < args.size(); i++) { - WurstType actual = args.get(i).attrTyp(); - WurstType expected = parameterTypes.get(i); - // if (expected instanceof AstElementWithTypeArgs) + WurstType expected; + if (isVararg && i >= parameterTypes.size() - 1) { + expected = varargBaseType; + } else { + expected = parameterTypes.get(i); + } if (!actual.isSubtypeOf(expected, where)) { args.get(i).addError( preMsg + "Expected " + expected + " as parameter " + (i + 1) + " but found " + actual); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/VarargTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/VarargTests.java index 224d1c73d..f74a0c48f 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/VarargTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/VarargTests.java @@ -303,5 +303,42 @@ public void varargOverride() { } + @Test + public void varargConstructor() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "class A", + " int value = 0", + " construct(int i, vararg string strings)", + " value = i", + " for s in strings", + " value++", + "init", + " let a = new A(2, \"x\", \"y\", \"z\")", + " if a.value == 5", + " testSuccess()"); + } + + @Test + public void varargConstructorSuperCall() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "class A", + " int sum = 0", + " construct(int i, vararg int xs)", + " sum = i", + " for x in xs", + " sum += x", + "class B extends A", + " construct()", + " super(1,2,3,4)", + "init", + " let b = new B()", + " if b.sum == 10", + " testSuccess()"); + } + }