Skip to content
Snippets Groups Projects
Commit 9713a82c authored by stepan's avatar stepan
Browse files

Fix: formulae with '...' in them

parent e7fe0bef
No related branches found
No related tags found
No related merge requests found
Showing
with 120 additions and 46 deletions
......@@ -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));
}
}
......@@ -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);
......
......@@ -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;
}
......
......@@ -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);
}
......
......@@ -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() {
......
......@@ -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
......
......@@ -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;
}
}
......@@ -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
......@@ -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
......
......@@ -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); }");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment