From 2d2d25510e3f76412c6c940fefa252d32ae60775 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Tue, 20 Jun 2017 17:50:24 +0200
Subject: [PATCH] Fix: do not raise "missing value" error when reading variable
 in inlined args

---
 .../access/variables/ReadVariableNode.java    | 13 +++++++---
 .../r/nodes/function/ArgumentMatcher.java     | 26 +++++++++++++++++--
 .../truffle/r/test/ExpectedTestOutput.test    |  4 +++
 .../r/test/builtins/TestBuiltin_seq.java      |  7 +++++
 4 files changed, 45 insertions(+), 5 deletions(-)

diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index dc171181ab..24665dff03 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -98,7 +98,10 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         // start the lookup in the enclosing frame
         Super,
         // whether a promise should be forced to check its type or not during lookup
-        ForcedTypeCheck;
+        ForcedTypeCheck,
+        // whether reads of RMissing should not throw error and just proceed, this is the case for
+        // inlined varargs, which should not show missing value error
+        SilentMissing
     }
 
     public static ReadVariableNode create(String name) {
@@ -113,6 +116,10 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         return new ReadVariableNode(RSyntaxNode.SOURCE_UNAVAILABLE, name, mode, ReadKind.Silent);
     }
 
+    public static ReadVariableNode createSilentMissing(String name, RType mode) {
+        return new ReadVariableNode(RSyntaxNode.SOURCE_UNAVAILABLE, name, mode, ReadKind.SilentMissing);
+    }
+
     public static ReadVariableNode createSuperLookup(SourceSection src, String name) {
         return new ReadVariableNode(src, name, RType.Any, ReadKind.Super);
     }
@@ -853,7 +860,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         if ((isNullProfile == null && obj == null) || (isNullProfile != null && isNullProfile.profile(obj == null))) {
             return false;
         }
-        if (obj == RMissing.instance) {
+        if (kind != ReadKind.SilentMissing && obj == RMissing.instance) {
             unexpectedMissingProfile.enter();
             throw RError.error(RError.SHOW_CALLER, RError.Message.ARGUMENT_MISSING, getIdentifier());
         }
@@ -893,7 +900,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         if (obj == null) {
             return false;
         }
-        if (obj == RMissing.instance) {
+        if (kind != ReadKind.SilentMissing && obj == RMissing.instance) {
             throw RError.error(RError.SHOW_CALLER, RError.Message.ARGUMENT_MISSING, getIdentifier());
         }
         if (mode == RType.Any) {
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 db25097a00..778adee698 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
@@ -38,6 +38,7 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -359,6 +360,7 @@ public class ArgumentMatcher {
             // Has varargs? Unfold!
             if (suppliedIndex == MatchPermutation.VARARGS) {
                 int varArgsLen = match.varargsPermutation.length;
+                boolean shouldInline = shouldInlineArgument(builtin, formalIndex, fastPath);
                 String[] newNames = new String[varArgsLen];
                 RNode[] newVarArgs = new RNode[varArgsLen];
                 int index = 0;
@@ -375,7 +377,7 @@ public class ArgumentMatcher {
                         }
                     }
                     newNames[index] = match.varargsSignature.getName(i);
-                    newVarArgs[index] = varArg;
+                    newVarArgs[index] = shouldInline ? updateInlinedArg(varArg) : varArg;
                     index++;
                 }
 
@@ -399,7 +401,7 @@ public class ArgumentMatcher {
                 }
 
                 ArgumentsSignature signature = ArgumentsSignature.get(newNames);
-                if (shouldInlineArgument(builtin, formalIndex, fastPath)) {
+                if (shouldInline) {
                     resArgs[formalIndex] = PromiseNode.createVarArgsInlined(newVarArgs, signature);
                 } else {
                     boolean forcedEager = fastPath != null && fastPath.forcedEagerPromise(formalIndex);
@@ -447,6 +449,26 @@ public class ArgumentMatcher {
         return builtin != null && builtin.evaluatesArg(formalIndex);
     }
 
+    /**
+     * Reads of the {@link RMissing} values must not be reported as error in inlined varargs. This
+     * method updates any wrapped ReadVariableNode to just return missing values without raising an
+     * error.
+     *
+     * @see com.oracle.truffle.r.nodes.function.PromiseNode.InlineVarArgsNode
+     */
+    private static RNode updateInlinedArg(RNode node) {
+        if (!(node instanceof WrapArgumentNode)) {
+            return node;
+        }
+        WrapArgumentNode wrapper = (WrapArgumentNode) node;
+        if (!(wrapper.getOperand() instanceof ReadVariableNode)) {
+            return node;
+        }
+        ReadVariableNode rvn = (ReadVariableNode) wrapper.getOperand();
+        ReadVariableNode newRvn = ReadVariableNode.createSilentMissing(rvn.getIdentifier(), rvn.getMode());
+        return WrapArgumentNode.create(newRvn, wrapper.getIndex());
+    }
+
     private static RNode wrapUnmatched(FormalArguments formals, RBuiltinDescriptor builtin, int formalIndex, boolean noOpt) {
         if (builtin != null && !builtin.evaluatesArg(formalIndex) && formals.getDefaultArgument(formalIndex) != null) {
             /*
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 4cfca94bd6..45e05ed3d3 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
@@ -61867,6 +61867,10 @@ integer(0)
 #seq(integer())
 integer(0)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_seq.testSeqArgMatching#
+#{ foo <- function(beg, end, by, len) seq(beg, end, by, length.out = len); foo(beg=1, by=1, len=10) }
+ [1]  1  2  3  4  5  6  7  8  9 10
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_seq.testSeqDispatch#
 #{ d <- as.Date(1, origin = "1970-01-01"); seq(d, by=1, length.out=4) }
 [1] "1970-01-02" "1970-01-03" "1970-01-04" "1970-01-05"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_seq.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_seq.java
index 493b127679..1475666adc 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_seq.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_seq.java
@@ -144,6 +144,13 @@ public class TestBuiltin_seq extends TestBase {
         assertEval(Output.MayIgnoreErrorContext, template("%0(1, 20, 3, length.out=10)", SEQFUNS));
     }
 
+    // argument matching corner-case appearing in seq (because it has a fast-path and varargs)
+    // taken and adapted from the scales package
+    @Test
+    public void testSeqArgMatching() {
+        assertEval("{ foo <- function(beg, end, by, len) seq(beg, end, by, length.out = len); foo(beg=1, by=1, len=10) }");
+    }
+
     // Old tests, undoubtedly partially overlapping
 
     @Test
-- 
GitLab