diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index 5e5e1c83c8ccae8f9d443739fd9bb5b15b5681d9..c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -604,7 +604,8 @@ public class ArgumentMatcher { } if (!matchedSuppliedArgs[suppliedIndex]) { String suppliedName = signature.getName(suppliedIndex); - if (suppliedName == null || suppliedName.isEmpty() || formalSignature.getName(formalIndex).isEmpty()) { + String formalName = formalSignature.getName(formalIndex); + if (suppliedName == null || suppliedName.isEmpty() || formalName == null || formalName.isEmpty()) { // unnamed parameter, match by position break; } @@ -732,6 +733,10 @@ public class ArgumentMatcher { /** * Searches for partial match of suppliedName inside formalNames and returns its (formal) index. + * If there are no varagrs and no match is found for given named actual argument, then this + * method raises "unused argument" error. However, if there are any null formal arguments, which + * may only happen in the case of builtins, then we let the argument matching proceed to + * positional matching. This situation may happen in S4 dispatch to a builtin, e.g. {@code $<-}. * * @return The position of the given suppliedName inside the formalNames. Throws errors if the * argument has been matched before @@ -739,6 +744,7 @@ public class ArgumentMatcher { private static <T> int findParameterPositionByPartialName(ArgumentsSignature formalsSignature, boolean[] formalsMatchedByExactName, String suppliedName, int[] resultPermutation, int suppliedIndex, boolean hasVarArgs, RBaseNode callingNode, int varArgIndex, IntFunction<String> errorString) { assert suppliedName != null && !suppliedName.isEmpty(); + boolean hasNullFormal = false; int found = MatchPermutation.UNMATCHED; for (int i = 0; i < formalsSignature.getLength(); i++) { if (formalsMatchedByExactName[i]) { @@ -747,6 +753,7 @@ public class ArgumentMatcher { } String formalName = formalsSignature.getName(i); + hasNullFormal |= formalName == null; if (formalName != null) { if (formalName.startsWith(suppliedName) && ((varArgIndex != ArgumentsSignature.NO_VARARG && i < varArgIndex) || varArgIndex == ArgumentsSignature.NO_VARARG)) { // partial-match only if the formal argument is positioned before ... @@ -760,7 +767,7 @@ public class ArgumentMatcher { } } } - if (found >= 0 || hasVarArgs) { + if (found >= 0 || hasVarArgs || hasNullFormal) { return found; } throw callingNode.error(RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex)); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 8bd5b6e68b9563df6f49186b63f669d347e3d5a5..ed3a0521e0794dc3f0509e3c8b4ecf8a73e5b374 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -80639,6 +80639,12 @@ Error in rnorm(s = 1, s = 1) : #{ f <- match.fun(length) ; f(c(1,2,3)) } [1] 3 +##com.oracle.truffle.r.test.functions.TestFunctions.testMatching# +#`$<-`(someNonesense = list(), anotherNonesense = 'foo', 42) +$foo +[1] 42 + + ##com.oracle.truffle.r.test.functions.TestFunctions.testMatching# #list(`...`=NULL); $... diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java index 00c2f892ebbf33e9aaafe888689c8a07691ddfee..753308f58bab44b4d5610cdac02aae83f1ffce54 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java @@ -259,6 +259,7 @@ public class TestFunctions extends TestBase { // however, two partial matches produce error, even if one is "longer" assertEval("{ foo <- function(xaaa, ...) list(xaa=xaaa, ...); foo(xa=4,xaa=5); }"); assertEval("list(`...`=NULL);"); + assertEval("`$<-`(someNonesense = list(), anotherNonesense = 'foo', 42)"); } @Test