diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java index b0d134c56567dd196efbba8cf438acf447a7ec76..1e9def93a9f8d2111667643ba6aa2fa8a76cefd7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java @@ -167,7 +167,7 @@ public abstract class RBuiltinPackage { ArgumentsSignature signature = ArgumentsSignature.get(parameterNames); putBuiltin(new RBuiltinFactory(annotation.name(), builtinMetaClass, builtinClass, annotation.visibility(), annotation.aliases(), annotation.kind(), signature, annotation.nonEvalArgs(), - annotation.splitCaller(), annotation.isFieldAccess(), + annotation.splitCaller(), annotation.isFieldAccess(), annotation.lookupVarArgs(), annotation.alwaysSplit(), annotation.dispatch(), annotation.genericName(), constructor, annotation.behavior(), specialCall)); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java index ff2110693bf99f7fb33cff160b0096eeb7931bb3..d32dc6beed428a3de4cbe24622049313be251266 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java @@ -53,7 +53,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * handled by an evaluated argument of type {@link RMissing}, although it appears as if the "model" * argument is missing, i.e. {@code ~ x} result in {@code `~`(x)}. */ -@RBuiltin(name = "~", kind = PRIMITIVE, parameterNames = {"x", "y"}, nonEvalArgs = {0, 1}, behavior = READS_FRAME) +@RBuiltin(name = "~", kind = PRIMITIVE, parameterNames = {"x", "y"}, nonEvalArgs = {0, 1}, lookupVarArgs = false, behavior = READS_FRAME) public abstract class Tilde extends RBuiltinNode.Arg2 { private static final RStringVector FORMULA_CLASS = RDataFactory.createStringVectorFromScalar(RRuntime.FORMULA_CLASS); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java index a5ab7e48165fe014f747db71c7cb7e0354e53eee..23238de07819281d5cd19dc01cc41a3958e1bb78 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java @@ -37,9 +37,10 @@ public final class RBuiltinFactory extends RBuiltinDescriptor { private final Supplier<RBuiltinNode> constructor; RBuiltinFactory(String name, Class<?> builtinMetaClass, Class<?> builtinNodeClass, RVisibility visibility, String[] aliases, RBuiltinKind kind, ArgumentsSignature signature, int[] nonEvalArgs, - boolean splitCaller, boolean isFieldAccess, + boolean splitCaller, boolean isFieldAccess, boolean lookupVarArgs, boolean alwaysSplit, RDispatch dispatch, String genericName, Supplier<RBuiltinNode> constructor, RBehavior behavior, RSpecialFactory specialCall) { - super(name, builtinMetaClass, builtinNodeClass, visibility, aliases, kind, signature, nonEvalArgs, splitCaller, isFieldAccess, alwaysSplit, dispatch, genericName, behavior, specialCall); + super(name, builtinMetaClass, builtinNodeClass, visibility, aliases, kind, signature, nonEvalArgs, splitCaller, isFieldAccess, lookupVarArgs, alwaysSplit, dispatch, genericName, behavior, + specialCall); this.constructor = constructor; } 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 ea6eb2d6213db291cecdbe9f216bd19f7a3483ed..f056c85eb1e3dee3e80f3aee5e482516355a0d19 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 @@ -153,17 +153,17 @@ public class ArgumentMatcher { public static Arguments<RNode> matchArguments(RRootNode target, CallArgumentsNode arguments, ArgumentsSignature varArgSignature, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode, boolean noOpt) { CompilerAsserts.neverPartOfCompilation(); - assert arguments.containsVarArgsSymbol() == (varArgSignature != null); + assert !RBuiltinDescriptor.lookupVarArgs(target.getBuiltin()) || arguments.containsVarArgsSymbol() == (varArgSignature != null); RNode[] argNodes; ArgumentsSignature signature; - if (!arguments.containsVarArgsSymbol()) { - argNodes = arguments.getArguments(); - signature = arguments.getSignature(); - } else { + if (arguments.containsVarArgsSymbol() && RBuiltinDescriptor.lookupVarArgs(target.getBuiltin())) { Arguments<RNode> suppliedArgs = arguments.unrollArguments(varArgSignature); argNodes = suppliedArgs.getArguments(); signature = suppliedArgs.getSignature(); + } else { + argNodes = arguments.getArguments(); + signature = arguments.getSignature(); } return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments.getClosureCache(), noOpt); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java index cbf2c45a488cfaa08eeffe7bd16e5f3fd8b06a30..3564e1c5c87fa88ae0a0daf5dc288860bfef200e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java @@ -216,24 +216,26 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS return Arguments.create(arguments, signature); } - private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame, boolean ignore) { - return ignore ? null : lookupVarArgs(frame); + private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame, boolean ignore, RBuiltinDescriptor builtin) { + return ignore ? null : lookupVarArgs(frame, builtin); } - private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame) { + private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame, RBuiltinDescriptor builtin) { if (explicitArgs != null) { return (RArgsValuesAndNames) explicitArgs.execute(frame); } if (lookupVarArgs == null) { return null; - } else { - Object varArgs = lookupVarArgs.execute(frame); - if (!(varArgs instanceof RArgsValuesAndNames)) { - CompilerDirectives.transferToInterpreter(); - throw RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT); - } - return (RArgsValuesAndNames) varArgs; } + if (builtin != null && !builtin.lookupVarArgs()) { + return null; + } + Object varArgs = lookupVarArgs.execute(frame); + if (!(varArgs instanceof RArgsValuesAndNames)) { + CompilerDirectives.transferToInterpreter(); + throw RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT); + } + return (RArgsValuesAndNames) varArgs; } protected FunctionDispatch createUninitializedCall() { @@ -255,8 +257,10 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS @Specialization(guards = "isDefaultDispatch(function)") public Object call(VirtualFrame frame, RFunction function, - @Cached("createUninitializedCall()") FunctionDispatch call) { - return call.execute(frame, function, lookupVarArgs(frame), null, null); + @Cached("createUninitializedCall()") FunctionDispatch call, + @Cached("createIdentityProfile()") ValueProfile builtinValueProfile) { + RBuiltinDescriptor builtin = builtinValueProfile.profile(function.getRBuiltin()); + return call.execute(frame, function, lookupVarArgs(frame, builtin), null, null); } @Specialization @@ -316,7 +320,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS } Object basicFun = getBasicFunction.execute(frame, builtin.getName()); if (basicFun != null) { - Object result = internalDispatchCall.execute(frame, (RFunction) basicFun, lookupVarArgs(frame, isFieldAccess), null, null); + Object result = internalDispatchCall.execute(frame, (RFunction) basicFun, lookupVarArgs(frame, isFieldAccess, builtin), null, null); if (result != RRuntime.DEFERRED_DEFAULT_MARKER) { return result; } @@ -340,7 +344,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (internalDispatchCall == null || internalDispatchCall.tempFrameSlot != slot) { createInternDispatchCall(isFieldAccess, slot); } - return internalDispatchCall.execute(frame, resultFunction, lookupVarArgs(frame, isFieldAccess), s3Args, null); + return internalDispatchCall.execute(frame, resultFunction, lookupVarArgs(frame, isFieldAccess, builtin), s3Args, null); } finally { TemporarySlotNode.cleanup(frame, dispatchObject, slot); } @@ -451,17 +455,16 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS @Cached("createUninitializedExplicitCall()") FunctionDispatch call, @Cached("create()") GetBaseEnvFrameNode getBaseEnvFrameNode) { - Object[] args = explicitArgs != null ? ((RArgsValuesAndNames) explicitArgs.execute(frame)).getArguments() : callArguments.evaluateFlattenObjects(frame, lookupVarArgs(frame)); - ArgumentsSignature argsSignature = explicitArgs != null ? ((RArgsValuesAndNames) explicitArgs.execute(frame)).getSignature() : callArguments.flattenNames(lookupVarArgs(frame)); + RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin()); + Object[] args = explicitArgs != null ? ((RArgsValuesAndNames) explicitArgs.execute(frame)).getArguments() : callArguments.evaluateFlattenObjects(frame, lookupVarArgs(frame, builtin)); + ArgumentsSignature argsSignature = explicitArgs != null ? ((RArgsValuesAndNames) explicitArgs.execute(frame)).getSignature() : callArguments.flattenNames(lookupVarArgs(frame, builtin)); if (emptyArgumentsProfile.profile(args.length == 0)) { // nothing to dispatch on, this is a valid situation, e.g. prod() == 1 return call.execute(frame, function, new RArgsValuesAndNames(args, argsSignature), null, null); } - RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin()); RDispatch dispatch = builtin.getDispatch(); - // max(na.rm=TRUE,arg1) dispatches to whatever is class of arg1 not taking the // named argument 'na.rm' into account. Note: signatures should be interned, identity // comparison is enough. Signature length > 0, because we dispatched on at least one arg @@ -594,7 +597,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS protected Object[] evaluateArgs(VirtualFrame frame) { return originalCall.explicitArgs != null ? ((RArgsValuesAndNames) originalCall.explicitArgs.execute(frame)).getArguments() - : arguments.evaluateFlattenObjects(frame, originalCall.lookupVarArgs(frame)); + : arguments.evaluateFlattenObjects(frame, originalCall.lookupVarArgs(frame, null)); } protected Foreign2R getForeign2RNode() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java index 1a4cc0d7091cdc10cb5230a33973182a8de13718..e46fcd12232951642758390cd5324bf34530a1ba 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java @@ -95,6 +95,14 @@ public @interface RBuiltin { */ boolean isFieldAccess() default false; + /** + * If {@code true} the runtime should lookup the '...' symbol in the caller frame to pass it to + * the builtin. The only example of {@code false} is builtin '~', where '...' among actual + * arguments is interpreted only on syntax level and should not case the actual lookup of '...' + * in the caller frame. + */ + boolean lookupVarArgs() default true; + /** * Indicates whether or not function containing a call of the form * <code>.Internal(name(...))</code> should trigger a split of the caller at its direct call diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java index 15dba041feaff3b0812c03f496dcf25d138a95e8..f9657258a6b1f35913a3ef2a4945ba2583b08780 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java @@ -48,6 +48,7 @@ public abstract class RBuiltinDescriptor { private final RDispatch dispatch; private final String genericName; private final RBehavior behavior; + private final boolean lookupVarArgs; private final RSpecialFactory specialCall; private final int primitiveMethodIndex; @@ -55,8 +56,9 @@ public abstract class RBuiltinDescriptor { private final boolean isFieldAccess; public RBuiltinDescriptor(String name, Class<?> builtinMetaClass, Class<?> builtinNodeClass, RVisibility visibility, String[] aliases, RBuiltinKind kind, ArgumentsSignature signature, - int[] nonEvalArgs, boolean splitCaller, boolean isFieldAccess, + int[] nonEvalArgs, boolean splitCaller, boolean isFieldAccess, boolean lookupVarArgs, boolean alwaysSplit, RDispatch dispatch, String genericName, RBehavior behavior, RSpecialFactory specialCall) { + this.lookupVarArgs = lookupVarArgs; this.specialCall = specialCall; this.name = Utils.intern(name); this.builtinMetaClass = builtinMetaClass; @@ -90,11 +92,11 @@ public abstract class RBuiltinDescriptor { } } - public String getName() { + public final String getName() { return name; } - public String getGenericName() { + public final String getGenericName() { if (genericName.isEmpty()) { return name; } else { @@ -102,65 +104,73 @@ public abstract class RBuiltinDescriptor { } } - public String[] getAliases() { + public final String[] getAliases() { return aliases; } - public RBuiltinKind getKind() { + public final RBuiltinKind getKind() { return kind; } - public ArgumentsSignature getSignature() { + public final ArgumentsSignature getSignature() { return signature; } - public boolean isAlwaysSplit() { + public final boolean isAlwaysSplit() { return alwaysSplit; } - public boolean isSplitCaller() { + public final boolean isSplitCaller() { return splitCaller; } - public RDispatch getDispatch() { + public final RDispatch getDispatch() { return dispatch; } - public boolean evaluatesArg(int index) { + public final boolean evaluatesArg(int index) { return evaluatesArgument[index]; } - public int getPrimMethodIndex() { + public final int getPrimMethodIndex() { return primitiveMethodIndex; } - public RVisibility getVisibility() { + public final RVisibility getVisibility() { return visibility; } - public Class<?> getBuiltinMetaClass() { + public final Class<?> getBuiltinMetaClass() { return builtinMetaClass; } - public Class<?> getBuiltinNodeClass() { + public final Class<?> getBuiltinNodeClass() { return builtinNodeClass; } - public RBehavior getBehavior() { + public final RBehavior getBehavior() { return behavior; } - public RSpecialFactory getSpecialCall() { + public final RSpecialFactory getSpecialCall() { return specialCall; } @Override - public String toString() { + public final String toString() { return "RBuiltinFactory [name=" + getName() + ", aliases=" + Arrays.toString(getAliases()) + ", kind=" + getKind() + ", siagnature=" + getSignature() + ", nonEvaledArgs=" + Arrays.toString(nonEvalArgs) + ", splitCaller=" + isSplitCaller() + ", dispatch=" + getDispatch() + ", behavior=" + getBehavior() + "]"; } - public boolean isFieldAccess() { + public final boolean isFieldAccess() { return isFieldAccess; } + + public final boolean lookupVarArgs() { + return lookupVarArgs; + } + + public static boolean lookupVarArgs(RBuiltinDescriptor builtin) { + return builtin == null || builtin.lookupVarArgs; + } } 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 8d6927ac6a65ab1fa583ad62ba0bd63b97067fe5..3159e3b8c8aba212dcfeee88b8dc2f1c454b0024 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 @@ -76618,6 +76618,10 @@ Error in tempfile(character()) : no 'pattern' #{ tempfile(integer()) } Error in tempfile(integer()) : invalid filename pattern +##com.oracle.truffle.r.test.builtins.TestBuiltin_tilde.testTildeDirect# +#... ~ 0 + x +... ~ 0 + x + ##com.oracle.truffle.r.test.builtins.TestBuiltin_tilde.testTildeDirect# #x ~ y + z x ~ y + z @@ -76714,6 +76718,14 @@ x q +##com.oracle.truffle.r.test.builtins.TestBuiltin_tilde.testTildeDirect# +#y ~ ... +y ~ ... + +##com.oracle.truffle.r.test.builtins.TestBuiltin_tilde.testTildeDirect# +#y ~ ... + 2 +y ~ ... + 2 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_tilde.testTildeDirect# #y ~ 0 + x y ~ 0 + x @@ -163760,6 +163772,36 @@ $variables list(cyl, hp, mpg, disp, drat, wt, qsec, vs, am, gear, carb) +##com.oracle.truffle.r.test.library.stats.TestFormulae.testFormulaeWithDotdotdot# +#terms.formula(... ~ w) +... ~ w +attr(,"variables") +list(..., w) +attr(,"factors") + w +... 0 +w 1 +attr(,"term.labels") +[1] "w" +attr(,"order") +[1] 1 +attr(,"intercept") +[1] 1 +attr(,"response") +[1] 1 +attr(,".Environment") +<environment: R_GlobalEnv> + +##com.oracle.truffle.r.test.library.stats.TestFormulae.testFormulaeWithDotdotdot# +#{ f <- function(w,...) model.frame(terms.formula(... ~ w)); f(1, a=3); } + ... w +1 3 1 + +##com.oracle.truffle.r.test.library.stats.TestFormulae.testFormulaeWithDotdotdot#Output.IgnoreErrorContext# +#{ f <- function(w,...) model.frame(terms.formula(... ~ w)); f(1, a=3, b=4); } +Error in model.frame.default(terms.formula(... ~ w)) : + number of variables != number of variable names + ##com.oracle.truffle.r.test.library.stats.TestFormulae.testModelFrame# #{y<-0:9;z<-1:10;k<-2:11;w<-3:12;m<-4:13;u<-5:14;v<-6:15;; model.frame(terms.formula(u~z*k+w*m)) } u z k w m diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tilde.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tilde.java index 54f100fe478b4f326840fb38a8b7240fe253b3c4..4501558f556f8e3f6b772ebf1d5d229213e6a9aa 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tilde.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tilde.java @@ -39,6 +39,9 @@ public class TestBuiltin_tilde extends TestBase { assertEval("~ x + y"); assertEval("x ~ y + z"); assertEval("y ~ 0 + x"); + assertEval("... ~ 0 + x"); + assertEval("y ~ ..."); + assertEval("y ~ ... + 2"); } @Test diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java index d3a56d60ae5847319716f77e6f77ae564a4bae7d..0cdcb84bc4ac9c01cc822681e8bb091dd6ab3274 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java @@ -120,4 +120,11 @@ public class TestFormulae extends TestBase { public void testModelFrameWithWeights() { assertEval("model.frame(formula = cyl ~ disp, data = mtcars[1:4,], weights = seq_len(nrow(mtcars[1:4,])))"); } + + @Test + public void testFormulaeWithDotdotdot() { + assertEval("terms.formula(... ~ w)"); + assertEval("{ f <- function(w,...) model.frame(terms.formula(... ~ w)); f(1, a=3); }"); + assertEval(Output.IgnoreErrorContext, "{ f <- function(w,...) model.frame(terms.formula(... ~ w)); f(1, a=3, b=4); }"); + } }