diff --git a/.gitignore b/.gitignore
index 5d9988fc555a0ffe21e17fc12ad5b733bf6df86d..3eed6ca4779f8143dad9407775f4be957a28d9d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,7 +28,7 @@
 /com.oracle.truffle.r.native/include/linked
 /com.oracle.truffle.r.native/fficall/jni.done
 /com.oracle.truffle.r.native/fficall/jniboot.done
-
+/com.oracle.truffle.r.native.recommended/install.recommended
 /com.oracle.truffle.r.test.native/packages/copy_recommended
 /com.oracle.truffle.r.test.native/packages/recommended
 /com.oracle.truffle.r.test.native/packages/*/lib/*
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index 6e568a59be33fe0180fbcdf9d9144a7b885e4ddc..7589fcf8a876b1e29552a48df8e27e5fb4ddd724 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -429,8 +429,7 @@ final class REngine implements Engine, Engine.Timings {
             }
         }
         RArgsValuesAndNames reorderedArgs = CallMatcherGenericNode.reorderArguments(args, func,
-                        names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), false,
-                        RError.NO_CALLER);
+                        names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), RError.NO_CALLER);
         Object[] newArgs = reorderedArgs.getArguments();
         for (int i = 0; i < newArgs.length; i++) {
             Object arg = newArgs[i];
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 5267b65c3b23ac7727aa16c60915587ba23389b8..385068b962e9778e2a57a8c6c5b3b901ec02782b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -43,7 +43,6 @@ import com.oracle.truffle.r.nodes.RASTBuilder;
 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.WriteVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode;
@@ -54,7 +53,7 @@ import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
 import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
 import com.oracle.truffle.r.nodes.control.BlockNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
-import com.oracle.truffle.r.nodes.control.ReplacementNode;
+import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
@@ -442,22 +441,9 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
 
     @Override
     public String getCallerSource(RLanguage rl) {
-        RLanguage elem = rl;
-
-        /*
-         * This checks for the specific structure of replacements, to display the replacement
-         * instead of the "internal" form (with *tmp*, etc.) of the update call.
-         */
-
-        RSyntaxNode sn = (RSyntaxNode) rl.getRep();
-        Node parent = RASTUtils.unwrapParent(sn.asNode());
-        if (parent instanceof WriteVariableNode) {
-            WriteVariableNode wvn = (WriteVariableNode) parent;
-            if (wvn.getParent() instanceof ReplacementNode) {
-                elem = RDataFactory.createLanguage((RNode) wvn.getParent());
-            }
-        }
-
+        // This checks for the specific structure of replacements
+        RLanguage replacement = ReplacementDispatchNode.getRLanguage(rl);
+        RLanguage elem = replacement == null ? rl : replacement;
         String string = RDeparse.deparse(elem, RDeparse.DEFAULT_Cutoff, true, 0, -1);
         return string.split("\n")[0];
     }
@@ -517,8 +503,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
 
     @Override
     public RSyntaxNode[] isReplacementNode(Node node) {
-        if (node instanceof ReplacementNode) {
-            ReplacementNode rn = (ReplacementNode) node;
+        if (node instanceof ReplacementDispatchNode) {
+            ReplacementDispatchNode rn = (ReplacementDispatchNode) node;
             return new RSyntaxNode[]{rn.getLhs(), rn.getRhs()};
         } else {
             return null;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
index d8e7acbe2183ac5c099d815e9a5709584b925488..1aff0091ef52ff496d09f462d7c2a1fd8db2e4dd 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
@@ -11,15 +11,22 @@
  */
 package com.oracle.truffle.r.library.stats;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.eq;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.RError.NO_CALLER;
+import static com.oracle.truffle.r.runtime.RError.SHOW_CALLER;
+
 import java.util.Arrays;
 
+import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -31,7 +38,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 /*
  * Logic derived from GNU-R, library/stats/src/cov.c
  */
-public final class Covcor extends RExternalBuiltinNode {
+public abstract class Covcor extends RExternalBuiltinNode.Arg4 {
 
     private final boolean isCor;
 
@@ -40,20 +47,21 @@ public final class Covcor extends RExternalBuiltinNode {
     }
 
     @Override
-    public Object call(RArgsValuesAndNames args) {
-        Object[] argValues = args.getArguments();
-        if (argValues[0] == RNull.instance) {
-            throw RError.error(this, RError.Message.IS_NULL, "x");
-        }
+    protected void createCasts(CastBuilder casts) {
+        casts.arg(0).mustNotBeNull(SHOW_CALLER, Message.IS_NULL, "x").asDoubleVector();
+        casts.arg(1).allowNull().asDoubleVector();
+        casts.arg(2).asIntegerVector().findFirst().mustBe(eq(4), this, Message.NYI, "covcor: other method than 4 not implemented.");
+        casts.arg(3).asLogicalVector().findFirst().map(toBoolean());
+    }
 
-        RAbstractDoubleVector x = castDouble(castVector(argValues[0]));
-        RAbstractDoubleVector y = argValues[1] == RNull.instance ? null : castDouble(castVector(argValues[1]));
-        int method = castInt(castVector(argValues[2]));
-        if (method != 4) {
-            throw RError.nyi(this, "method");
-        }
-        boolean iskendall = RRuntime.fromLogical(castLogical(castVector(argValues[3])));
-        return corcov(x.materialize(), y != null ? y.materialize() : null, method, iskendall, this);
+    @Specialization
+    public Object call(RAbstractDoubleVector x, @SuppressWarnings("unused") RNull y, int method, boolean iskendall) {
+        return corcov(x.materialize(), null, method, iskendall, this);
+    }
+
+    @Specialization
+    public Object call(RAbstractDoubleVector x, RAbstractDoubleVector y, int method, boolean iskendall) {
+        return corcov(x.materialize(), y.materialize(), method, iskendall, this);
     }
 
     private final NACheck check = NACheck.create();
@@ -730,9 +738,8 @@ public final class Covcor extends RExternalBuiltinNode {
         }
     }
 
-    private static void error(String string) {
-        // TODO should be an R error
-        throw new UnsupportedOperationException("error: " + string);
+    private static void error(String message) {
+        RError.error(NO_CALLER, Message.GENERIC, message);
     }
 
     private boolean checkNAs(double... xs) {
diff --git a/com.oracle.truffle.r.native.recommended/Makefile b/com.oracle.truffle.r.native.recommended/Makefile
index 0b26a47e96d57e4be0a80cc85d7b15b16ecc8efd..3d63321e40ed093c6ad02e5474a62e6c68106e3e 100644
--- a/com.oracle.truffle.r.native.recommended/Makefile
+++ b/com.oracle.truffle.r.native.recommended/Makefile
@@ -33,10 +33,10 @@ FASTR_R_HOME := $(abspath $(CURDIR)/..)
 NATIVE_PROJECT := $(subst native.recommended,native,$(CURDIR))
 R_VERSION := $(notdir $(wildcard $(NATIVE_PROJECT)/gnur/R-*))
 GNUR_HOME := $(NATIVE_PROJECT)/gnur/$(R_VERSION)
-GNUR_RECOMMENDED_TARS := $(wildcard $(GNUR_HOME)/src/library/Recommended/*.tgz)
+# order matters due to inter-package dependencies
+GNUR_RECOMMENDED_PKGNAMES := MASS boot class cluster codetools lattice nnet spatial Matrix survival KernSmooth foreign nlme rpart
+GNUR_RECOMMENDED_TARS := $(foreach pkg, $(GNUR_RECOMMENDED_PKGNAMES),$(GNUR_HOME)/src/library/Recommended/$(pkg).tgz)
 #$(info GNUR_RECOMMENDED_TARS=$(GNUR_RECOMMENDED_TARS))
-GNUR_RECOMMENDED_PKGNAMES := $(foreach tar,$(GNUR_RECOMMENDED_TARS),$(notdir $(basename $(tar))))
-#$(info GNUR_RECOMMENDED_PKGNAMES=$(GNUR_RECOMMENDED_PKGNAMES))
 
 all: install.recommended
 
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 06b2e17a7b9cd91938240e5a16400b54c68ef8d5..bc9a8097e0afd1b4b9d4f1119f307885cf0144fa 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
@@ -22,10 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.builtin;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -36,8 +32,8 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RSpecialFactory;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
@@ -93,7 +89,6 @@ public abstract class RBuiltinPackage {
 
     protected RBuiltinPackage(String name) {
         this.name = name;
-
         // Check for overriding R code
         ArrayList<Source> componentList = getRFiles(getName());
         if (componentList.size() > 0) {
@@ -111,22 +106,10 @@ public abstract class RBuiltinPackage {
      */
     public static ArrayList<Source> getRFiles(String pkgName) {
         ArrayList<Source> componentList = new ArrayList<>();
-        try {
-            InputStream is = ResourceHandlerFactory.getHandler().getResourceAsStream(RBuiltinPackage.class, pkgName + "/R");
-            if (is != null) {
-                try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
-                    String line;
-                    while ((line = r.readLine()) != null) {
-                        if (line.endsWith(".r") || line.endsWith(".R")) {
-                            final String rResource = pkgName + "/R/" + line.trim();
-                            Source content = Utils.getResourceAsSource(RBuiltinPackage.class, rResource);
-                            componentList.add(content);
-                        }
-                    }
-                }
-            }
-        } catch (IOException ex) {
-            Utils.rSuicide("error loading R code from " + pkgName + " : " + ex);
+        String[] rFileContents = ResourceHandlerFactory.getHandler().getRFiles(RBuiltinPackage.class, pkgName);
+        for (String rFileContent : rFileContents) {
+            Source content = RSource.fromTextInternal(rFileContent, RSource.Internal.R_IMPL);
+            componentList.add(content);
         }
         return componentList;
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
index 94669aeca126c7bc2bdba0e2c953aa716111eee4..91aa642cb77db7cde6343807f75c40d3cd2de3af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
@@ -53,6 +53,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public abstract class S3DispatchFunctions extends RBuiltinNode {
 
@@ -64,7 +65,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode {
 
     protected S3DispatchFunctions(boolean nextMethod) {
         methodLookup = S3FunctionLookupNode.create(true, nextMethod);
-        callMatcher = CallMatcherNode.create(nextMethod, false);
+        callMatcher = CallMatcherNode.create(false);
     }
 
     protected MaterializedFrame getCallerFrame(VirtualFrame frame) {
@@ -219,6 +220,9 @@ public abstract class S3DispatchFunctions extends RBuiltinNode {
         }
 
         protected static boolean isNotString(Object obj) {
+            // Note: if RAbstractStringVector becomes expected, then it must have length == 1, GnuR
+            // ignores character vectors longer than 1 as the "generic" argument of NextMethod
+            assert !(obj instanceof RAbstractStringVector) || ((RAbstractStringVector) obj).getLength() != 1 : "unexpected RAbstractStringVector with length != 1";
             return !(obj instanceof String);
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 76f8082bdc4eb0bc4ab028f23c6fe318ac9a0d42..2821716614aecf0854f0b63cd0fee013341867f0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -42,7 +42,7 @@ import com.oracle.truffle.r.library.methods.SlotFactory.R_setSlotNodeGen;
 import com.oracle.truffle.r.library.methods.SubstituteDirectNodeGen;
 import com.oracle.truffle.r.library.parallel.ParallelFunctionsFactory.MCIsChildNodeGen;
 import com.oracle.truffle.r.library.stats.CompleteCases;
-import com.oracle.truffle.r.library.stats.Covcor;
+import com.oracle.truffle.r.library.stats.CovcorNodeGen;
 import com.oracle.truffle.r.library.stats.Dbinom;
 import com.oracle.truffle.r.library.stats.GammaFunctionsFactory.QgammaNodeGen;
 import com.oracle.truffle.r.library.stats.Pbinom;
@@ -345,9 +345,9 @@ public class ForeignFunctions {
                 case "fft":
                     return FftNodeGen.create();
                 case "cov":
-                    return new Covcor(false);
+                    return CovcorNodeGen.create(false);
                 case "cor":
-                    return new Covcor(true);
+                    return CovcorNodeGen.create(true);
                 case "SplineCoef":
                     return SplineCoefNodeGen.create();
                 case "SplineEval":
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
index b7042dba728d69019cdbbdddff0ed9051dd10677..0aafef63839fe6e7fb309d335155efc1b8a8c9b5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
@@ -87,7 +87,7 @@ public abstract class AccessField extends RBuiltinNode {
         casts.arg(1).defaultError(Message.INVALID_SUBSCRIPT_TYPE, RType.Language.getName()).mustBe(stringValue()).asStringVector().findFirst();
     }
 
-    public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments) {
+    public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) {
         return signature.getNonNullCount() == 0 ? AccessFieldSpecialNodeGen.create(arguments) : null;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java
index 0151d99d910b40d7313c630e7684cc1c3a429a20..3a88c5ec748882bb8d2b9c2b75a58972e53c037f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java
@@ -123,7 +123,7 @@ abstract class SubscriptSpecial extends SubscriptSpecialBase {
     }
 }
 
-@RBuiltin(name = "[[", kind = PRIMITIVE, parameterNames = {"", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
+@RBuiltin(name = "[[", kind = PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Subscript extends RBuiltinNode {
 
     @RBuiltin(name = ".subset2", kind = PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"}, behavior = PURE)
@@ -131,7 +131,7 @@ public abstract class Subscript extends RBuiltinNode {
         // same implementation as "[[", with different dispatch
     }
 
-    public static RNode special(ArgumentsSignature signature, RNode[] arguments) {
+    public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) {
         return signature.getNonNullCount() == 0 && arguments.length == 2 ? SubscriptSpecialNodeGen.create(arguments) : null;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
index 1a9f76aff93e2c48a5fc300d204adf92b837fea9..09f0694fc7d050d6ce1b44fc01a32a4482b6754b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
@@ -88,8 +88,14 @@ public abstract class Subset extends RBuiltinNode {
         // same implementation as "[", with different dispatch
     }
 
-    public static RNode special(ArgumentsSignature signature, RNode[] arguments) {
-        return signature.getNonNullCount() == 0 && arguments.length == 2 ? SubsetSpecialNodeGen.create(arguments) : null;
+    public static RNode special(ArgumentsSignature signature, RNode[] arguments, boolean inReplacement) {
+        boolean correctSignature = signature.getNonNullCount() == 0 && arguments.length == 2;
+        if (!correctSignature) {
+            return null;
+        }
+        // Subset adds support for lists returning newly created list, which cannot work when used
+        // in replacement, because we need the reference to the existing (materialized) list element
+        return inReplacement ? SubscriptSpecialBaseNodeGen.create(arguments) : SubsetSpecialNodeGen.create(arguments);
     }
 
     @Child private ExtractVectorNode extractNode = ExtractVectorNode.create(ElementAccessMode.SUBSET, false);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
index 0d8cb22f53ff6da3aed269da0a809f3b061d5823..7a9928cbd661f3ee540bbac308e5f8190d38d11e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
@@ -82,7 +82,6 @@ abstract class UpdateFieldSpecial extends SpecialsUtils.ListFieldSpecialBase {
     }
 
     @Fallback
-    @SuppressWarnings("unused")
     public void doFallback(Object container, Object field, Object value) {
         throw RSpecialFactory.throwFullCallNeeded();
     }
@@ -109,7 +108,7 @@ public abstract class UpdateField extends RBuiltinNode {
         casts.arg(1).defaultError(Message.INVALID_SUBSCRIPT).mustBe(stringValue()).asStringVector().findFirst();
     }
 
-    public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments) {
+    public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) {
         return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateFieldSpecialNodeGen.create(arguments) : null;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java
index 9172b162b1c920dd68846c03d6566791804f71f7..4d17163f6d024c20abb2f8c7cba29e93e613a98f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java
@@ -74,50 +74,60 @@ abstract class UpdateSubscriptSpecial extends SubscriptSpecialCommon {
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"})
-    protected RIntVector access(RIntVector vector, int index, int value) {
+    protected RIntVector set(RIntVector vector, int index, int value) {
         return vector.updateDataAt(index - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"})
-    protected RDoubleVector access(RDoubleVector vector, int index, double value) {
+    protected RDoubleVector set(RDoubleVector vector, int index, double value) {
         return vector.updateDataAt(index - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"})
-    protected RStringVector access(RStringVector vector, int index, String value) {
+    protected RStringVector set(RStringVector vector, int index, String value) {
         return vector.updateDataAt(index - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(list)", "!list.isShared()", "isValidIndex(list, index)", "isSingleElement(value)"})
-    protected static Object access(RList list, int index, Object value) {
+    protected static Object set(RList list, int index, Object value) {
         list.setDataAt(list.getInternalStore(), index - 1, value);
         return list;
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"})
-    protected RIntVector accessDoubleIndex(RIntVector vector, double index, int value) {
+    protected RIntVector setDoubleIndex(RIntVector vector, double index, int value) {
+        return vector.updateDataAt(toIndex(index) - 1, value, naCheck);
+    }
+
+    @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"})
+    protected RDoubleVector setDoubleIntIndexIntValue(RDoubleVector vector, int index, int value) {
+        return vector.updateDataAt(toIndex(index) - 1, value, naCheck);
+    }
+
+    @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"})
+    protected RDoubleVector setDoubleIndexIntValue(RDoubleVector vector, double index, int value) {
         return vector.updateDataAt(toIndex(index) - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"})
-    protected RDoubleVector accessDoubleIndex(RDoubleVector vector, double index, double value) {
+    protected RDoubleVector setDoubleIndex(RDoubleVector vector, double index, double value) {
         return vector.updateDataAt(toIndex(index) - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"})
-    protected RStringVector accessDoubleIndex(RStringVector vector, double index, String value) {
+    protected RStringVector setDoubleIndex(RStringVector vector, double index, String value) {
         return vector.updateDataAt(toIndex(index) - 1, value, naCheck);
     }
 
     @Specialization(guards = {"simple(list)", "!list.isShared()", "isValidDoubleIndex(list, index)", "isSingleElement(value)"})
-    protected Object accessDoubleIndex(RList list, double index, Object value) {
+    protected Object setDoubleIndex(RList list, double index, Object value) {
         list.setDataAt(list.getInternalStore(), toIndex(index) - 1, value);
         return list;
     }
 
     @SuppressWarnings("unused")
     @Fallback
-    protected static Object access(Object vector, Object index, Object value) {
+    protected static Object setFallback(Object vector, Object index, Object value) {
         throw RSpecialFactory.throwFullCallNeeded();
     }
 }
@@ -129,7 +139,7 @@ public abstract class UpdateSubscript extends RBuiltinNode {
 
     private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile();
 
-    public static RNode special(ArgumentsSignature signature, RNode[] arguments) {
+    public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) {
         return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateSubscriptSpecialNodeGen.create(arguments) : null;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java
index a876a55c0ed452d0fd1fb0e9ad4475e51b382bd9..5c92d85bf3ca0df6b123e20f9cec2b15573b631a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java
@@ -60,7 +60,7 @@ public abstract class UpdateSubset extends RBuiltinNode {
     @Child private ReplaceVectorNode replaceNode = ReplaceVectorNode.create(ElementAccessMode.SUBSET, false);
     private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile();
 
-    public static RNode special(ArgumentsSignature signature, RNode[] arguments) {
+    public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) {
         return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateSubsetSpecialNodeGen.create(arguments) : null;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
index 6bf78693713bc14e41e9f5d82229a6155e261e4e..630d3da88adc1d0578fdf2807ca06fcc3ac7a772 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -41,24 +40,24 @@ import com.oracle.truffle.r.nodes.control.ForNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
 import com.oracle.truffle.r.nodes.control.NextNode;
 import com.oracle.truffle.r.nodes.control.RepeatNode;
-import com.oracle.truffle.r.nodes.control.ReplacementNode;
+import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode;
+import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode.LHSError;
 import com.oracle.truffle.r.nodes.control.WhileNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
 import com.oracle.truffle.r.nodes.function.PostProcessArgumentsNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
 import com.oracle.truffle.r.nodes.function.WrapDefaultArgumentNode;
 import com.oracle.truffle.r.nodes.function.signature.MissingNode;
-import com.oracle.truffle.r.nodes.unary.GetNonSharedNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.EvaluatedArgumentsVisitor;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
@@ -79,6 +78,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
 
     private final Map<String, Object> constants;
+    private CodeBuilderContext context = CodeBuilderContext.DEFAULT;
 
     public RASTBuilder() {
         this.constants = null;
@@ -88,6 +88,10 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         this.constants = constants;
     }
 
+    private RCallNode unused() {
+        return null; // we need reference to RCallNode, otherwise it won't compile, compilation bug?
+    }
+
     @Override
     public RSyntaxNode call(SourceSection source, RSyntaxNode lhs, List<Argument<RSyntaxNode>> args) {
         if (lhs instanceof RSyntaxLookup) {
@@ -164,7 +168,7 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
                 if (c.getValue() instanceof String) {
                     name = (String) c.getValue();
                 } else {
-                    return new ReplacementNode.LHSError(source, operator, replacementLhs, replacementRhs, false);
+                    return new LHSError(source, operator, replacementLhs, replacementRhs);
                 }
             } else {
                 throw RInternalError.unimplemented("unexpected lhs type: " + replacementLhs.getClass());
@@ -190,162 +194,8 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         }
     }
 
-    /**
-     * Creates a call that looks like {@code fun} but has the first argument replaced with
-     * {@code newLhs}.
-     */
-    private RNode createSpecialFunctionQuery(RSyntaxNode newLhs, RSyntaxCall fun) {
-        RSyntaxElement[] arguments = fun.getSyntaxArguments();
-
-        RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length];
-        for (int i = 0; i < arguments.length; i++) {
-            argNodes[i] = i == 0 ? newLhs : process(arguments[i]);
-        }
-
-        return RCallSpecialNode.createCall(fun.getSourceSection(), process(fun.getSyntaxLHS()).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode();
-    }
-
-    /**
-     * Creates a call that looks like {@code fun}, but has its first argument replaced with
-     * {@code newLhs}, its target turned into an update function ("foo<-"), with the given value
-     * added to the arguments list.
-     */
-    private RNode createFunctionUpdate(SourceSection source, RSyntaxNode newLhs, RSyntaxNode rhs, RSyntaxCall fun) {
-        RSyntaxElement[] arguments = fun.getSyntaxArguments();
-
-        ArgumentsSignature signature = fun.getSyntaxSignature();
-        RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length + 1];
-        String[] names = new String[argNodes.length];
-        for (int i = 0; i < arguments.length; i++) {
-            names[i] = signature.getName(i);
-            argNodes[i] = i == 0 ? newLhs : process(arguments[i]);
-        }
-        argNodes[argNodes.length - 1] = rhs;
-        names[argNodes.length - 1] = "value";
-
-        RSyntaxElement syntaxLHS = fun.getSyntaxLHS();
-        RSyntaxNode newSyntaxLHS;
-        if (syntaxLHS instanceof RSyntaxLookup) {
-            RSyntaxLookup lookupLHS = (RSyntaxLookup) syntaxLHS;
-            String symbol = lookupLHS.getIdentifier();
-            if ("slot".equals(symbol) || "@".equals(symbol)) {
-                // this is pretty gross, but at this point seems like the only way to get setClass
-                // to work properly
-                argNodes[0] = GetNonSharedNodeGen.create(argNodes[0].asRNode());
-            }
-            newSyntaxLHS = lookup(lookupLHS.getSourceSection(), symbol + "<-", true);
-        } else {
-            // data types (and lengths) are verified in isNamespaceLookupCall
-            RSyntaxCall callLHS = (RSyntaxCall) syntaxLHS;
-            RSyntaxElement[] oldArgs = callLHS.getSyntaxArguments();
-            RSyntaxNode[] newArgs = new RSyntaxNode[2];
-            newArgs[0] = (RSyntaxNode) oldArgs[0];
-            newArgs[1] = lookup(oldArgs[1].getSourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true);
-            newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getSourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs);
-        }
-        return RCallSpecialNode.createCall(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes).asRNode();
-    }
-
-    /*
-     * Determines if syntax call is of the form foo::bar
-     */
-    private static boolean isNamespaceLookupCall(RSyntaxElement e) {
-        if (e instanceof RSyntaxCall) {
-            RSyntaxCall call = (RSyntaxCall) e;
-            // check for syntax nodes as this will be required to recreate a call during
-            // replacement form construction in createFunctionUpdate
-            if (call.getSyntaxLHS() instanceof RSyntaxLookup && call.getSyntaxLHS() instanceof RSyntaxNode) {
-                if (((RSyntaxLookup) call.getSyntaxLHS()).getIdentifier().equals("::")) {
-                    RSyntaxElement[] args = call.getSyntaxArguments();
-                    if (args.length == 2 && args[0] instanceof RSyntaxLookup && args[0] instanceof RSyntaxNode && args[1] instanceof RSyntaxLookup && args[1] instanceof RSyntaxNode) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    // used to create unique temp names for nested replacements
-    private int tempNamesCount;
-
-    /**
-     * This method builds the sequence of calls needed to represent a replacement.<br/>
-     * For example, the replacement {@code a(b(c(x),i),j) <- z} should be decomposed into the
-     * following statements:
-     *
-     * <pre>
-     * t3 <- x
-     * t2 <- c(t3)
-     * t1 <- b(t2,i)
-     * tt0 <- z
-     * tt1 <- `a<-`(t1, j, tt0) // b(...) with a replaced
-     * tt2 <- `b<-`(t2, i, tt1) // a(...) with b replaced
-     * x <- `c<-`(t3, tt2) // x with c replaced
-     * </pre>
-     */
     private RSyntaxNode createReplacement(SourceSection source, RSyntaxNode lhs, RSyntaxNode rhs, String operator, boolean isSuper) {
-        /*
-         * Collect all the function calls in this replacement. For "a(b(x)) <- z", this would be
-         * "a(...)" and "b(...)".
-         */
-        List<RSyntaxCall> calls = new ArrayList<>();
-        RSyntaxElement current = lhs;
-        while (!(current instanceof RSyntaxLookup)) {
-            if (!(current instanceof RSyntaxCall)) {
-                return new ReplacementNode.LHSError(source, operator, lhs, rhs, current instanceof RSyntaxConstant && ((RSyntaxConstant) current).getValue() == RNull.instance);
-            }
-            RSyntaxCall call = (RSyntaxCall) current;
-            calls.add(call);
-
-            RSyntaxElement syntaxLHS = call.getSyntaxLHS();
-            if (call.getSyntaxArguments().length == 0 || !(syntaxLHS instanceof RSyntaxLookup || isNamespaceLookupCall(syntaxLHS))) {
-                return new ReplacementNode.LHSError(source, operator, lhs, rhs, true);
-            }
-            current = call.getSyntaxArguments()[0];
-        }
-        RSyntaxLookup variable = (RSyntaxLookup) current;
-
-        List<RNode> instructions = new ArrayList<>();
-        int tempNamesIndex = tempNamesCount;
-        tempNamesCount += calls.size() + 1;
-
-        /*
-         * Create the calls that extract inner components - only needed for complex replacements
-         * like "a(b(x)) <- z" (where we would extract "b(x)").
-         */
-        for (int i = calls.size() - 1; i >= 1; i--) {
-            ReadVariableNode newLhs = ReadVariableNode.create("*tmp*" + (tempNamesIndex + i + 1));
-            RNode update = createSpecialFunctionQuery(newLhs, calls.get(i));
-            instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesIndex + i), update, WriteVariableNode.Mode.INVISIBLE));
-        }
-        /*
-         * Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`.
-         */
-        for (int i = 0; i < calls.size(); i++) {
-            RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + (tempNamesIndex + i + 1)), ReadVariableNode.create("*tmpr*" + (tempNamesIndex + i - 1)),
-                            calls.get(i));
-            if (i < calls.size() - 1) {
-                instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesIndex + i), update, WriteVariableNode.Mode.INVISIBLE));
-            } else {
-                instructions.add(WriteVariableNode.createAnonymous(variable.getIdentifier(), update, WriteVariableNode.Mode.REGULAR, isSuper));
-            }
-        }
-
-        ReadVariableNode variableValue = createReplacementForVariableUsing(variable, isSuper);
-        ReplacementNode newReplacementNode = new ReplacementNode(source, operator, lhs, rhs, "*tmpr*" + (tempNamesIndex - 1),
-                        variableValue, "*tmp*" + (tempNamesIndex + calls.size()), instructions);
-
-        tempNamesCount -= calls.size() + 1;
-        return newReplacementNode;
-    }
-
-    private static ReadVariableNode createReplacementForVariableUsing(RSyntaxLookup var, boolean isSuper) {
-        if (isSuper) {
-            return ReadVariableNode.createSuperLookup(var.getSourceSection(), var.getIdentifier());
-        } else {
-            return ReadVariableNode.create(var.getSourceSection(), var.getIdentifier(), true);
-        }
+        return new ReplacementDispatchNode(source, operator, lhs, rhs, isSuper, this.context.getReplacementVarsStartIndex());
     }
 
     public static FastPathFactory createFunctionFastPath(RSyntaxElement body, ArgumentsSignature signature) {
@@ -411,6 +261,16 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         return Truffle.getRuntime().createCallTarget(rootNode);
     }
 
+    @Override
+    public void setContext(CodeBuilderContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public CodeBuilderContext getContext() {
+        return context;
+    }
+
     @Override
     public RSyntaxNode constant(SourceSection source, Object value) {
         if (value instanceof String && !RRuntime.isNA((String) value)) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java
index 5324ee862791188fe3812ab83b1b57ff2ff1ec9e..d65a911c9c9c4623a8c69d4098f968626840fc02 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java
@@ -58,10 +58,11 @@ public abstract class BinaryArithmeticSpecial extends RNode {
         final boolean handleNA = !(opFactory == BinaryArithmetic.POW || opFactory == BinaryArithmetic.MOD);
         boolean handleIntegers = !(opFactory == BinaryArithmetic.POW || opFactory == BinaryArithmetic.DIV);
         if (handleIntegers) {
-            return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2
+            return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2
                             ? IntegerBinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments) : null;
         } else {
-            return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments)
+            return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2
+                            ? BinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments)
                             : null;
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java
index 5aeba584433b2a661fa11aadff1a7725351359c8..1a93af53656d1dc6c0d57ab6e5dd3892d7492a18 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java
@@ -48,7 +48,7 @@ public abstract class BinaryBooleanSpecial extends RNode {
     }
 
     public static RSpecialFactory createSpecialFactory(final BooleanOperationFactory opFactory) {
-        return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryBooleanSpecialNodeGen.create(opFactory.createOperation(), arguments) : null;
+        return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryBooleanSpecialNodeGen.create(opFactory.createOperation(), arguments) : null;
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java
index 6099c04dfecf655d59ff6d3a77b9e66864e4ba08..78914ee9313f30da5b6375abe5aac790555c4695 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java
@@ -45,7 +45,7 @@ public final class RInternalCodeBuiltinNode extends RExternalBuiltinNode {
     private final Source code;
     private final String functionName;
 
-    @Child private CallMatcherNode call = CallMatcherNode.create(false, true);
+    @Child private CallMatcherNode call = CallMatcherNode.create(true);
     @CompilationFinal private RFunction function;
 
     public RInternalCodeBuiltinNode(RContext context, String basePackage, Source code, String functionName) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..24a8116b92f459a8bbdb39b0b06d458b28795f4e
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.nodes.control;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.RASTUtils;
+import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode;
+import com.oracle.truffle.r.nodes.access.WriteVariableNode;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+/**
+ * Represents a replacement consisting of execution of the RHS, call to the actual replacement
+ * sequence and removal of RHS returning the RHS value to the caller. The actual replacement is
+ * created lazily. Moreover, we use 'special' fast-path version of replacement where possible with
+ * fallback to generic implementation.
+ */
+public final class ReplacementDispatchNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
+
+    @Child private ReplacementNode replacementNode;
+
+    @Child private WriteVariableNode storeRhs;
+    @Child private RemoveAndAnswerNode removeRhs;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
+    private final String operator;
+    private final RSyntaxNode lhs;
+    private final boolean isSuper;
+    private final String rhsName;
+    private final int tempNamesStartIndex;
+
+    public ReplacementDispatchNode(SourceSection src, String operator, RSyntaxNode lhs, RSyntaxNode rhs, boolean isSuper, int tempNamesStartIndex) {
+        super(src);
+        assert "<-".equals(operator) || "<<-".equals(operator) || "=".equals(operator);
+        assert lhs != null && rhs != null;
+        rhsName = "*rhs*" + tempNamesStartIndex;
+        storeRhs = WriteVariableNode.createAnonymous(rhsName, rhs.asRNode(), WriteVariableNode.Mode.INVISIBLE);
+        removeRhs = RemoveAndAnswerNode.create(rhsName);
+        this.operator = operator;
+        this.lhs = lhs;
+        this.isSuper = isSuper;
+        this.tempNamesStartIndex = tempNamesStartIndex;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        storeRhs.execute(frame);
+        getReplacementNode().execute(frame);
+        Object result = removeRhs.execute(frame);
+        visibility.execute(frame, false);
+        return result;
+    }
+
+    /**
+     * Support for syntax tree visitor.
+     */
+    public RSyntaxNode getLhs() {
+        return lhs;
+    }
+
+    /**
+     * Support for syntax tree visitor.
+     */
+    public RSyntaxNode getRhs() {
+        return storeRhs.getRhs().asRSyntaxNode();
+    }
+
+    @Override
+    public RSyntaxElement getSyntaxLHS() {
+        return RSyntaxLookup.createDummyLookup(null, operator, true);
+    }
+
+    @Override
+    public ArgumentsSignature getSyntaxSignature() {
+        return ArgumentsSignature.empty(2);
+    }
+
+    @Override
+    public RSyntaxElement[] getSyntaxArguments() {
+        return new RSyntaxElement[]{lhs, storeRhs.getRhs().asRSyntaxNode()};
+    }
+
+    private ReplacementNode getReplacementNode() {
+        if (replacementNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            replacementNode = insert(createReplacementNode());
+        }
+        return replacementNode;
+    }
+
+    private ReplacementNode createReplacementNode() {
+        CompilerAsserts.neverPartOfCompilation();
+
+        /*
+         * Collect all the function calls in this replacement. For "a(b(x)) <- z", this would be
+         * "a(...)" and "b(...)".
+         */
+        List<RSyntaxCall> calls = new ArrayList<>();
+        RSyntaxElement current = lhs;
+        while (!(current instanceof RSyntaxLookup)) {
+            if (!(current instanceof RSyntaxCall)) {
+                if (current instanceof RSyntaxConstant && ((RSyntaxConstant) current).getValue() == RNull.instance) {
+                    throw RError.error(this, RError.Message.INVALID_NULL_LHS);
+                } else {
+                    throw RError.error(this, RError.Message.NON_LANG_ASSIGNMENT_TARGET);
+                }
+            }
+            RSyntaxCall call = (RSyntaxCall) current;
+            calls.add(call);
+
+            RSyntaxElement syntaxLHS = call.getSyntaxLHS();
+            if (call.getSyntaxArguments().length == 0 || !(syntaxLHS instanceof RSyntaxLookup || isNamespaceLookupCall(syntaxLHS))) {
+                throw RError.error(this, RError.Message.INVALID_NULL_LHS);
+            }
+            current = call.getSyntaxArguments()[0];
+        }
+        RSyntaxLookup variable = (RSyntaxLookup) current;
+        ReadVariableNode varRead = createReplacementForVariableUsing(variable, isSuper);
+        return ReplacementNodeGen.create(getSourceSection(), calls, rhsName, variable.getIdentifier(), isSuper, tempNamesStartIndex, varRead);
+    }
+
+    private static ReadVariableNode createReplacementForVariableUsing(RSyntaxLookup var, boolean isSuper) {
+        if (isSuper) {
+            return ReadVariableNode.createSuperLookup(var.getSourceSection(), var.getIdentifier());
+        } else {
+            return ReadVariableNode.create(var.getSourceSection(), var.getIdentifier(), true);
+        }
+    }
+
+    /*
+     * Determines if syntax call is of the form foo::bar
+     */
+    private static boolean isNamespaceLookupCall(RSyntaxElement e) {
+        if (e instanceof RSyntaxCall) {
+            RSyntaxCall call = (RSyntaxCall) e;
+            // check for syntax nodes as this will be required to recreate a call during
+            // replacement form construction in createFunctionUpdate
+            if (call.getSyntaxLHS() instanceof RSyntaxLookup && call.getSyntaxLHS() instanceof RSyntaxNode) {
+                if (((RSyntaxLookup) call.getSyntaxLHS()).getIdentifier().equals("::")) {
+                    RSyntaxElement[] args = call.getSyntaxArguments();
+                    if (args.length == 2 && args[0] instanceof RSyntaxLookup && args[0] instanceof RSyntaxNode && args[1] instanceof RSyntaxLookup && args[1] instanceof RSyntaxNode) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /*
+     * Encapsulates check for the specific structure of replacements, to display the replacement
+     * instead of the "internal" form (with *tmp*, etc.) of the update call.
+     */
+    public static RLanguage getRLanguage(RLanguage language) {
+        RSyntaxNode sn = (RSyntaxNode) language.getRep();
+        Node parent = RASTUtils.unwrapParent(sn.asNode());
+        if (parent instanceof WriteVariableNode) {
+            WriteVariableNode wvn = (WriteVariableNode) parent;
+            return ReplacementNode.getLanguage(wvn);
+        }
+        return null;
+    }
+
+    /**
+     * Used by the parser for assignments that miss a left hand side. This node will raise an error
+     * once executed.
+     */
+    public static final class LHSError extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
+
+        private final String operator;
+        private final RSyntaxElement lhs;
+        private final RSyntaxElement rhs;
+
+        public LHSError(SourceSection sourceSection, String operator, RSyntaxElement lhs, RSyntaxElement rhs) {
+            super(sourceSection);
+            this.operator = operator;
+            this.lhs = lhs;
+            this.rhs = rhs;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            CompilerDirectives.transferToInterpreter();
+            throw RError.error(this, RError.Message.INVALID_LHS, "do_set");
+        }
+
+        @Override
+        public RSyntaxElement getSyntaxLHS() {
+            return RSyntaxLookup.createDummyLookup(null, operator, true);
+        }
+
+        @Override
+        public RSyntaxElement[] getSyntaxArguments() {
+            return new RSyntaxElement[]{lhs, rhs};
+        }
+
+        @Override
+        public ArgumentsSignature getSyntaxSignature() {
+            return ArgumentsSignature.empty(2);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
index 9c83ce14cdf3f5fdcd8cccf933df560c8b30490c..d4c2e4381e467e8aa18fb638aa443c1a178f957d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,153 +20,300 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 package com.oracle.truffle.r.nodes.control;
 
+import java.util.ArrayList;
 import java.util.List;
 
-import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.NodeChild;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
-import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
+import com.oracle.truffle.r.nodes.function.RCallSpecialNode.RecursiveSpecialBailout;
+import com.oracle.truffle.r.nodes.unary.GetNonSharedNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RSpecialFactory.FullCallNeededException;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.nodes.RCodeBuilder.CodeBuilderContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-/**
- * Holds the sequence of nodes created for R's replacement assignment. Allows custom deparse and
- * debug handling.
- *
- */
-public final class ReplacementNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
+@NodeChild(value = "target")
+@TypeSystemReference(EmptyTypeSystemFlatLayout.class)
+abstract class ReplacementNode extends RNode {
+
+    private final int tempNamesStartIndex;
+    private final SourceSection source;
+    private final List<RSyntaxCall> calls;
+    private final String targetVarName;
+    private final String rhsVarName;
+    private final boolean isSuper;
+
+    ReplacementNode(SourceSection source, List<RSyntaxCall> calls, String rhsVarName, String targetVarName, boolean isSuper, int tempNamesStartIndex) {
+        this.source = source;
+        this.calls = calls;
+        this.rhsVarName = rhsVarName;
+        this.targetVarName = targetVarName;
+        this.isSuper = isSuper;
+        this.tempNamesStartIndex = tempNamesStartIndex;
+    }
+
+    @Specialization
+    protected Object doRObject(VirtualFrame frame, Object target,
+                    @Cached("createTargetTmpWrite()") WriteVariableNode targetTmpWrite,
+                    @Cached("createTargetTmpRemove()") RemoveAndAnswerNode targetTmpRemove,
+                    @Cached("createTargetWrite()") WriteVariableNode targetWrite,
+                    @Cached("createReplacementNode()") ReplacementBase replacement) {
+        targetTmpWrite.execute(frame, target);
+        replacement.execute(frame);
+        targetWrite.execute(frame, targetTmpRemove.execute(frame));
+        return null;
+    }
+
+    protected final WriteVariableNode createTargetTmpWrite() {
+        return WriteVariableNode.createAnonymous(getTargetTmpName(), null, WriteVariableNode.Mode.INVISIBLE);
+    }
+
+    protected final WriteVariableNode createTargetWrite() {
+        return WriteVariableNode.createAnonymous(targetVarName, null, WriteVariableNode.Mode.INVISIBLE, isSuper);
+    }
+
+    protected final RemoveAndAnswerNode createTargetTmpRemove() {
+        return RemoveAndAnswerNode.create(getTargetTmpName());
+    }
+
+    protected final ReplacementBase createReplacementNode() {
+        return createReplacementNode(true);
+    }
+
+    private RNode createReplacementNodeWithoutSpecials() {
+        return createReplacementNode(false);
+    }
+
+    private ReplacementBase createReplacementNode(boolean useSpecials) {
+        CompilerAsserts.neverPartOfCompilation();
+        // Note: if specials are turned off in FastR, onlySpecials will never be true
+        boolean createSpecial = hasOnlySpecialCalls() && useSpecials;
+        return createSpecial ? createSpecialReplacement(source, calls) : createGenericReplacement(source, calls);
+    }
+
+    private String getTargetTmpName() {
+        return "*tmp*" + tempNamesStartIndex;
+    }
+
+    private boolean hasOnlySpecialCalls() {
+        for (int i = 0; i < calls.size(); i++) {
+            if (!(calls.get(i) instanceof RCallSpecialNode)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
     /**
-     * This is just the left hand side of the assignment and only used when looking at the original
-     * structure of this replacement.
+     * Creates a replacement that consists only of {@link RCallSpecialNode} calls.
      */
-    private final RSyntaxNode syntaxLhs;
-    private final String operator;
+    private SpecialReplacementNode createSpecialReplacement(SourceSection source, List<RSyntaxCall> calls) {
+        CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + 2);
+        RNode extractFunc = ReadVariableNode.create(getTargetTmpName());
+        for (int i = calls.size() - 1; i >= 1; i--) {
+            extractFunc = createSpecialFunctionQuery(extractFunc.asRSyntaxNode(), calls.get(i), codeBuilderContext);
+        }
+        RNode updateFunc = createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create(rhsVarName), calls.get(0), codeBuilderContext);
+        assert updateFunc instanceof RCallSpecialNode : "should be only specials";
+        return new SpecialReplacementNode((RCallSpecialNode) updateFunc);
+    }
 
     /**
-     * The original right hand side in the source can be found by {@code storeRhs.getRhs()}.
+     * When there are more than two function calls in LHS, then we save some function calls by
+     * saving the intermediate results into temporary variables and reusing them.
      */
-    @Child private WriteVariableNode storeRhs;
-    @Child private WriteVariableNode storeValue;
-    @Children private final RNode[] updates;
-    @Child private RemoveAndAnswerNode removeTemp;
-    @Child private RemoveAndAnswerNode removeRhs;
-    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
-
-    public ReplacementNode(SourceSection src, String operator, RSyntaxNode syntaxLhs, RSyntaxNode rhs, String rhsSymbol, RNode v, String tmpSymbol, List<RNode> updates) {
-        super(src);
-        assert "<-".equals(operator) || "<<-".equals(operator) || "=".equals(operator);
-        this.operator = operator;
-        this.syntaxLhs = syntaxLhs;
-        this.storeRhs = WriteVariableNode.createAnonymous(rhsSymbol, rhs.asRNode(), WriteVariableNode.Mode.INVISIBLE);
-        this.storeValue = WriteVariableNode.createAnonymous(tmpSymbol, v, WriteVariableNode.Mode.INVISIBLE);
-        this.updates = updates.toArray(new RNode[updates.size()]);
-        // remove var and rhs, returning rhs' value
-        this.removeTemp = RemoveAndAnswerNode.create(tmpSymbol);
-        this.removeRhs = RemoveAndAnswerNode.create(rhsSymbol);
+    private GenericReplacementNode createGenericReplacement(SourceSection source, List<RSyntaxCall> calls) {
+        List<RNode> instructions = new ArrayList<>();
+        CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + calls.size() + 1);
+
+        /*
+         * Create the calls that extract inner components - only needed for complex replacements
+         * like "a(b(x)) <- z" (where we would extract "b(x)"). The extracted values are saved into
+         * temporary variables *tmp*{index} indexed from tempNamesIndex to (tempNamesIndex +
+         * calls.size()-1), the first such temporary variable holds the "target" of the replacement,
+         * 'x' in our example (the assignment from 'x' is not done in this loop).
+         */
+        for (int i = calls.size() - 1, tmpIndex = 0; i >= 1; i--, tmpIndex++) {
+            ReadVariableNode newLhs = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex));
+            RNode update = createSpecialFunctionQuery(newLhs, calls.get(i), codeBuilderContext);
+            instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), update, WriteVariableNode.Mode.INVISIBLE));
+        }
+        /*
+         * Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`, the
+         * intermediate results are stored to temporary variables *tmpr*{index}.
+         */
+        for (int i = 0; i < calls.size(); i++) {
+            int tmpIndex = tempNamesStartIndex + calls.size() - i - 1;
+            String tmprName = i == 0 ? rhsVarName : "*tmpr*" + (tempNamesStartIndex + i - 1);
+            RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext);
+            if (i < calls.size() - 1) {
+                instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), update, WriteVariableNode.Mode.INVISIBLE));
+            } else {
+                instructions.add(WriteVariableNode.createAnonymous(getTargetTmpName(), update, WriteVariableNode.Mode.REGULAR));
+            }
+        }
+
+        GenericReplacementNode newReplacementNode = new GenericReplacementNode(instructions);
+        return newReplacementNode;
     }
 
     /**
-     * Support for syntax tree visitor.
+     * Creates a call that looks like {@code fun} but has the first argument replaced with
+     * {@code newLhs}.
      */
-    public RSyntaxNode getLhs() {
-        return syntaxLhs;
+    private static RNode createSpecialFunctionQuery(RSyntaxNode newLhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) {
+        RSyntaxElement[] arguments = fun.getSyntaxArguments();
+
+        RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length];
+        for (int i = 0; i < arguments.length; i++) {
+            argNodes[i] = i == 0 ? newLhs : process(arguments[i], codeBuilderContext);
+        }
+
+        return RCallSpecialNode.createCallInReplace(fun.getSourceSection(), process(fun.getSyntaxLHS(), codeBuilderContext).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode();
     }
 
     /**
-     * Support for syntax tree visitor.
+     * Creates a call that looks like {@code fun}, but has its first argument replaced with
+     * {@code newLhs}, its target turned into an update function ("foo<-"), with the given value
+     * added to the arguments list.
      */
-    public RSyntaxNode getRhs() {
-        return storeRhs.getRhs().asRSyntaxNode();
-    }
+    private static RNode createFunctionUpdate(SourceSection source, RSyntaxNode newLhs, RSyntaxNode rhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) {
+        RSyntaxElement[] arguments = fun.getSyntaxArguments();
 
-    @Override
-    @ExplodeLoop
-    public Object execute(VirtualFrame frame) {
-        storeRhs.execute(frame);
-        storeValue.execute(frame);
-        for (RNode update : updates) {
-            update.execute(frame);
+        ArgumentsSignature signature = fun.getSyntaxSignature();
+        RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length + 1];
+        String[] names = new String[argNodes.length];
+        for (int i = 0; i < arguments.length; i++) {
+            names[i] = signature.getName(i);
+            argNodes[i] = i == 0 ? newLhs : process(arguments[i], codeBuilderContext);
         }
-        removeTemp.execute(frame);
-        try {
-            return removeRhs.execute(frame);
-        } finally {
-            visibility.execute(frame, false);
+        argNodes[argNodes.length - 1] = rhs;
+        names[argNodes.length - 1] = "value";
+
+        RSyntaxElement syntaxLHS = fun.getSyntaxLHS();
+        RSyntaxNode newSyntaxLHS;
+        if (syntaxLHS instanceof RSyntaxLookup) {
+            RSyntaxLookup lookupLHS = (RSyntaxLookup) syntaxLHS;
+            String symbol = lookupLHS.getIdentifier();
+            if ("slot".equals(symbol) || "@".equals(symbol)) {
+                // this is pretty gross, but at this point seems like the only way to get setClass
+                // to work properly
+                argNodes[0] = GetNonSharedNodeGen.create(argNodes[0].asRNode());
+            }
+            newSyntaxLHS = lookup(lookupLHS.getSourceSection(), symbol + "<-", true);
+        } else {
+            // data types (and lengths) are verified in isNamespaceLookupCall
+            RSyntaxCall callLHS = (RSyntaxCall) syntaxLHS;
+            RSyntaxElement[] oldArgs = callLHS.getSyntaxArguments();
+            RSyntaxNode[] newArgs = new RSyntaxNode[2];
+            newArgs[0] = (RSyntaxNode) oldArgs[0];
+            newArgs[1] = lookup(oldArgs[1].getSourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true);
+            newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getSourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs);
         }
+        return RCallSpecialNode.createCall(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes).asRNode();
     }
 
-    @Override
-    public RSyntaxElement getSyntaxLHS() {
-        return RSyntaxLookup.createDummyLookup(null, operator, true);
+    private static RSyntaxNode process(RSyntaxElement original, CodeBuilderContext codeBuilderContext) {
+        return RContext.getASTBuilder().process(original, codeBuilderContext);
     }
 
-    @Override
-    public RSyntaxElement[] getSyntaxArguments() {
-        return new RSyntaxElement[]{syntaxLhs, storeRhs.getRhs().asRSyntaxNode()};
+    private static RSyntaxNode lookup(SourceSection source, String symbol, boolean functionLookup) {
+        return RContext.getASTBuilder().lookup(source, symbol, functionLookup);
+    }
+
+    static RLanguage getLanguage(WriteVariableNode wvn) {
+        Node parent = wvn.getParent();
+        if (parent instanceof ReplacementBase) {
+            Node replacementBlock = ((ReplacementBase) parent).getReplacementNodeParent().getParent();
+            assert replacementBlock instanceof ReplacementDispatchNode;
+            return RDataFactory.createLanguage((RNode) replacementBlock);
+        }
+        return null;
     }
 
-    @Override
-    public ArgumentsSignature getSyntaxSignature() {
-        return ArgumentsSignature.empty(2);
+    /**
+     * Base class for nodes implementing the actual replacement.
+     */
+    protected abstract static class ReplacementBase extends RNode {
+        protected final ReplacementNode getReplacementNodeParent() {
+            // Note: new DSL puts another node in between ReplacementBase instance and
+            // ReplacementNode, to be flexible we traverse the parents until we reach it
+            Node current = this;
+            do {
+                current = current.getParent();
+            } while (!(current instanceof ReplacementNode));
+            return (ReplacementNode) current;
+        }
     }
 
     /**
-     * Used by the parser for assignments that miss a left hand side. This node will raise an error
-     * once executed.
+     * Replacement that is made of only special calls, if one of the special calls falls back to
+     * full version, the replacement also falls back to {@link ReplacementNode}.
      */
-    public static final class LHSError extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
-
-        private final String operator;
-        private final RSyntaxElement lhs;
-        private final RSyntaxElement rhs;
-        private final boolean nullError;
-
-        public LHSError(SourceSection sourceSection, String operator, RSyntaxElement lhs, RSyntaxElement rhs, boolean nullError) {
-            super(sourceSection);
-            this.operator = operator;
-            this.lhs = lhs;
-            this.rhs = rhs;
-            this.nullError = nullError;
+    private static final class SpecialReplacementNode extends ReplacementBase {
+
+        @Child private RCallSpecialNode replaceCall;
+
+        SpecialReplacementNode(RCallSpecialNode replaceCall) {
+            this.replaceCall = replaceCall;
+            this.replaceCall.setPropagateFullCallNeededException(true);
         }
 
         @Override
         public Object execute(VirtualFrame frame) {
-            CompilerDirectives.transferToInterpreter();
-            if (nullError) {
-                throw RError.error(this, RError.Message.INVALID_NULL_LHS);
-            } else if (lhs instanceof RSyntaxConstant) {
-                throw RError.error(this, RError.Message.INVALID_LHS, "do_set");
-            } else {
-                throw RError.error(this, RError.Message.NON_LANG_ASSIGNMENT_TARGET);
+            try {
+                // Note: the very last call is the actual assignment, e.g. [[<-, if this call's
+                // argument is shared, it bails out. Moreover, if that call's argument is not
+                // shared, it could not be extracted from a shared container (list), so we should be
+                // OK with not calling any other update function and just update the value directly.
+                replaceCall.execute(frame);
+            } catch (FullCallNeededException | RecursiveSpecialBailout e) {
+                RNode newReplacement = getReplacementNodeParent().createReplacementNodeWithoutSpecials();
+                return replace(newReplacement).execute(frame);
             }
+            return null;
         }
+    }
 
-        @Override
-        public RSyntaxElement getSyntaxLHS() {
-            return RSyntaxLookup.createDummyLookup(null, operator, true);
-        }
+    /**
+     * Holds the sequence of nodes created for R's replacement assignment.
+     */
+    private static final class GenericReplacementNode extends ReplacementBase {
+        @Children private final RNode[] updates;
 
-        @Override
-        public RSyntaxElement[] getSyntaxArguments() {
-            return new RSyntaxElement[]{lhs, rhs};
+        GenericReplacementNode(List<RNode> updates) {
+            this.updates = updates.toArray(new RNode[updates.size()]);
         }
 
         @Override
-        public ArgumentsSignature getSyntaxSignature() {
-            return ArgumentsSignature.empty(2);
+        @ExplodeLoop
+        public Object execute(VirtualFrame frame) {
+            for (RNode update : updates) {
+                update.execute(frame);
+            }
+            return null;
         }
     }
 }
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 a56caa191cadd1e6ac3cde0064ab64bd3d226668..177b16a70499c1366fcb1fef5c3640acbb72a669 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
@@ -42,13 +42,13 @@ import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
-import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -67,7 +67,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * {@link #matchArguments(RRootNode, CallArgumentsNode, ArgumentsSignature, S3DefaultArguments, RBaseNode, boolean)}
  * . The other match functions are used for special cases, where builtins make it necessary to
  * re-match parameters, e.g.:
- * {@link #matchArgumentsEvaluated(RRootNode, RArgsValuesAndNames, S3DefaultArguments, boolean, RBaseNode)}
+ * {@link #matchArgumentsEvaluated(RRootNode, RArgsValuesAndNames, S3DefaultArguments, RBaseNode)}
  * for 'UseMethod'.
  * </p>
  *
@@ -157,9 +157,9 @@ public class ArgumentMatcher {
         return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt);
     }
 
-    public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, boolean forNextMethod, RBuiltinDescriptor builtin) {
+    public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) {
         CompilerAsserts.neverPartOfCompilation();
-        return permuteArguments(supplied, formal, callingNode, forNextMethod, index -> false, index -> supplied.getName(index) == null ? "" : supplied.getName(index), builtin);
+        return permuteArguments(supplied, formal, callingNode, index -> false, index -> supplied.getName(index) == null ? "" : supplied.getName(index), builtin);
     }
 
     public static ArgumentsSignature getFunctionSignature(RFunction function) {
@@ -219,15 +219,13 @@ public class ArgumentMatcher {
      *            taken from the stack)
      * @param s3DefaultArguments default values carried over from S3 group dispatch method (e.g.
      *            from max to Summary.factor). {@code null} if there are no such arguments.
-     * @param forNextMethod matching when evaluating NextMethod
      * @param callingNode The {@link Node} invoking the match
      * @return A Fresh {@link RArgsValuesAndNames} containing the arguments rearranged and stuffed
      *         with default values (in the form of {@link RPromise}s where needed)
      */
-    public static RArgsValuesAndNames matchArgumentsEvaluated(RRootNode target, RArgsValuesAndNames evaluatedArgs, S3DefaultArguments s3DefaultArguments, boolean forNextMethod,
-                    RBaseNode callingNode) {
+    public static RArgsValuesAndNames matchArgumentsEvaluated(RRootNode target, RArgsValuesAndNames evaluatedArgs, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode) {
         FormalArguments formals = target.getFormalArguments();
-        MatchPermutation match = permuteArguments(evaluatedArgs.getSignature(), formals.getSignature(), callingNode, forNextMethod, index -> {
+        MatchPermutation match = permuteArguments(evaluatedArgs.getSignature(), formals.getSignature(), callingNode, index -> {
             throw RInternalError.unimplemented("S3Dispatch should not have arg length mismatch");
         }, index -> evaluatedArgs.getSignature().getName(index), null);
 
@@ -310,7 +308,7 @@ public class ArgumentMatcher {
         FormalArguments formals = target.getFormalArguments();
 
         // Rearrange arguments
-        MatchPermutation match = permuteArguments(suppliedSignature, formals.getSignature(), callingNode, false,
+        MatchPermutation match = permuteArguments(suppliedSignature, formals.getSignature(), callingNode,
                         index -> RASTUtils.isLookup(suppliedArgs[index], ArgumentsSignature.VARARG_NAME), index -> getErrorForArgument(suppliedArgs, suppliedSignature, index),
                         target.getBuiltin());
 
@@ -491,12 +489,11 @@ public class ArgumentMatcher {
      * @param signature The signature (==names) of the supplied arguments
      * @param formalSignature The signature (==names) of the formal arguments
      * @param callingNode The {@link Node} invoking the match
-     * @param forNextMethod matching when evaluating NextMethod
      * @param builtin builtin function descriptor (or null if not a builtin)
      * @return An array of type <T> with the supplied arguments in the correct order
      */
     @TruffleBoundary
-    private static MatchPermutation permuteArguments(ArgumentsSignature signature, ArgumentsSignature formalSignature, RBaseNode callingNode, boolean forNextMethod, IntPredicate isVarSuppliedVarargs,
+    private static MatchPermutation permuteArguments(ArgumentsSignature signature, ArgumentsSignature formalSignature, RBaseNode callingNode, IntPredicate isVarSuppliedVarargs,
                     IntFunction<String> errorString, RBuiltinDescriptor builtin) {
         // assert Arrays.stream(suppliedNames).allMatch(name -> name == null || !name.isEmpty());
 
@@ -517,8 +514,7 @@ public class ArgumentMatcher {
             }
 
             // Search for argument name inside formal arguments
-            int formalIndex = findParameterPosition(formalSignature, signature.getName(suppliedIndex), resultPermutation, suppliedIndex, hasVarArgs, callingNode, varArgIndex, forNextMethod,
-                            errorString, builtin);
+            int formalIndex = findParameterPosition(formalSignature, signature.getName(suppliedIndex), resultPermutation, suppliedIndex, hasVarArgs, callingNode, varArgIndex, errorString, builtin);
             if (formalIndex != MatchPermutation.UNMATCHED) {
                 resultPermutation[formalIndex] = suppliedIndex;
                 resultSignature[formalIndex] = signature.getName(suppliedIndex);
@@ -541,10 +537,6 @@ public class ArgumentMatcher {
                         break outer;
                     }
                     if (!matchedSuppliedArgs[suppliedIndex]) {
-                        if (forNextMethod) {
-                            // for NextMethod, unused parameters are matched even when named
-                            break;
-                        }
                         if (signature.getName(suppliedIndex) == null || signature.getName(suppliedIndex).isEmpty()) {
                             // unnamed parameter, match by position
                             break;
@@ -638,7 +630,7 @@ public class ArgumentMatcher {
      *         argument has been matched before
      */
     private static <T> int findParameterPosition(ArgumentsSignature formalsSignature, String suppliedName, int[] resultPermutation, int suppliedIndex, boolean hasVarArgs, RBaseNode callingNode,
-                    int varArgIndex, boolean forNextMethod, IntFunction<String> errorString, RBuiltinDescriptor builtin) {
+                    int varArgIndex, IntFunction<String> errorString, RBuiltinDescriptor builtin) {
         int found = MatchPermutation.UNMATCHED;
         for (int i = 0; i < formalsSignature.getLength(); i++) {
             String formalName = formalsSignature.getName(i);
@@ -672,7 +664,7 @@ public class ArgumentMatcher {
                 }
             }
         }
-        if (found >= 0 || hasVarArgs || forNextMethod) {
+        if (found >= 0 || hasVarArgs) {
             return found;
         }
         throw RError.error(callingNode, RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex));
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index 6e4b72a729c466500034a71502520b95483f5c58..9454d1b46b985707c8c836d1c714a9b1b86936e3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -27,10 +27,10 @@ import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
+import com.oracle.truffle.r.nodes.function.signature.VarArgsHelper;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.ArgumentsSignature.VarArgsInfo;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -46,7 +46,6 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public abstract class CallMatcherNode extends RBaseNode {
 
-    protected final boolean forNextMethod;
     protected final boolean argsAreEvaluated;
 
     @Child private PromiseHelperNode promiseHelper;
@@ -54,15 +53,14 @@ public abstract class CallMatcherNode extends RBaseNode {
     protected final ConditionProfile missingArgProfile = ConditionProfile.createBinaryProfile();
     protected final ConditionProfile emptyArgProfile = ConditionProfile.createBinaryProfile();
 
-    private CallMatcherNode(boolean forNextMethod, boolean argsAreEvaluated) {
-        this.forNextMethod = forNextMethod;
+    private CallMatcherNode(boolean argsAreEvaluated) {
         this.argsAreEvaluated = argsAreEvaluated;
     }
 
     private static final int MAX_CACHE_DEPTH = 3;
 
-    public static CallMatcherNode create(boolean forNextMethod, boolean argsAreEvaluated) {
-        return new CallMatcherUninitializedNode(forNextMethod, argsAreEvaluated);
+    public static CallMatcherNode create(boolean argsAreEvaluated) {
+        return new CallMatcherUninitializedNode(argsAreEvaluated);
     }
 
     public abstract Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs);
@@ -85,15 +83,15 @@ public abstract class CallMatcherNode extends RBaseNode {
         int argCount = suppliedArguments.length;
 
         // extract vararg signatures from the arguments
-        VarArgsInfo varArgsInfo = suppliedSignature.getVarArgsInfo(suppliedArguments);
+        VarArgsHelper varArgsInfo = VarArgsHelper.create(suppliedSignature, suppliedArguments);
         int argListSize = varArgsInfo.getArgListSize();
 
         // see flattenIndexes for the interpretation of the values
         long[] preparePermutation;
         ArgumentsSignature resultSignature;
         if (varArgsInfo.hasVarArgs()) {
-            resultSignature = suppliedSignature.flattenNames(varArgsInfo);
-            preparePermutation = suppliedSignature.flattenIndexes(varArgsInfo);
+            resultSignature = varArgsInfo.flattenNames(suppliedSignature);
+            preparePermutation = varArgsInfo.flattenIndexes(suppliedSignature);
         } else {
             preparePermutation = new long[argListSize];
             String[] newSuppliedSignature = new String[argListSize];
@@ -110,9 +108,9 @@ public abstract class CallMatcherNode extends RBaseNode {
 
         assert resultSignature != null;
         ArgumentsSignature formalSignature = ArgumentMatcher.getFunctionSignature(function);
-        MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, this, forNextMethod, function.getRBuiltin());
+        MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, this, function.getRBuiltin());
 
-        return new CallMatcherCachedNode(suppliedSignature, varArgsInfo.getVarArgsSignatures(), function, preparePermutation, permutation, forNextMethod, argsAreEvaluated, next);
+        return new CallMatcherCachedNode(suppliedSignature, varArgsInfo.getVarArgsSignatures(), function, preparePermutation, permutation, argsAreEvaluated, next);
     }
 
     protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args, int varArgIndex) {
@@ -153,8 +151,8 @@ public abstract class CallMatcherNode extends RBaseNode {
 
     @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class CallMatcherUninitializedNode extends CallMatcherNode {
-        CallMatcherUninitializedNode(boolean forNextMethod, boolean argsAreEvaluated) {
-            super(forNextMethod, argsAreEvaluated);
+        CallMatcherUninitializedNode(boolean argsAreEvaluated) {
+            super(argsAreEvaluated);
         }
 
         private int depth;
@@ -163,7 +161,7 @@ public abstract class CallMatcherNode extends RBaseNode {
         public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             if (++depth > MAX_CACHE_DEPTH) {
-                return replace(new CallMatcherGenericNode(forNextMethod, argsAreEvaluated)).execute(frame, suppliedSignature, suppliedArguments, function, functionName, dispatchArgs);
+                return replace(new CallMatcherGenericNode(argsAreEvaluated)).execute(frame, suppliedSignature, suppliedArguments, function, functionName, dispatchArgs);
             } else {
                 CallMatcherCachedNode cachedNode = replace(specialize(suppliedSignature, suppliedArguments, function, this));
                 // for splitting if necessary
@@ -193,7 +191,7 @@ public abstract class CallMatcherNode extends RBaseNode {
         private final ArgumentsSignature[] cachedVarArgSignatures;
         private final RFunction cachedFunction;
         /**
-         * {@link ArgumentsSignature#flattenNames(VarArgsInfo)} for the interpretation of the
+         * {@link VarArgsHelper#flattenNames(ArgumentsSignature)} for the interpretation of the
          * values.
          */
         @CompilationFinal private final long[] preparePermutation;
@@ -201,8 +199,8 @@ public abstract class CallMatcherNode extends RBaseNode {
         private final FormalArguments formals;
 
         CallMatcherCachedNode(ArgumentsSignature suppliedSignature, ArgumentsSignature[] varArgSignatures, RFunction function, long[] preparePermutation, MatchPermutation permutation,
-                        boolean forNextMethod, boolean argsAreEvaluated, CallMatcherNode next) {
-            super(forNextMethod, argsAreEvaluated);
+                        boolean argsAreEvaluated, CallMatcherNode next) {
+            super(argsAreEvaluated);
             this.cachedSuppliedSignature = suppliedSignature;
             this.cachedVarArgSignatures = varArgSignatures;
             this.cachedFunction = function;
@@ -300,9 +298,9 @@ public abstract class CallMatcherNode extends RBaseNode {
             Object[] values = new Object[preparePermutation.length];
             for (int i = 0; i < values.length; i++) {
                 long source = preparePermutation[i];
-                if (ArgumentsSignature.isVarArgsIndex(source)) {
-                    int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source);
-                    int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source);
+                if (VarArgsHelper.isVarArgsIndex(source)) {
+                    int varArgsIdx = VarArgsHelper.extractVarArgsIndex(source);
+                    int argsIdx = VarArgsHelper.extractVarArgsArgumentIndex(source);
                     RArgsValuesAndNames varargs = (RArgsValuesAndNames) arguments[varArgsIdx];
                     values[i] = varargs.getArguments()[argsIdx];
                 } else {
@@ -315,8 +313,8 @@ public abstract class CallMatcherNode extends RBaseNode {
 
     public static final class CallMatcherGenericNode extends CallMatcherNode {
 
-        CallMatcherGenericNode(boolean forNextMethod, boolean argsAreEvaluated) {
-            super(forNextMethod, argsAreEvaluated);
+        CallMatcherGenericNode(boolean argsAreEvaluated) {
+            super(argsAreEvaluated);
         }
 
         @Child private CallRFunctionCachedNode call = CallRFunctionCachedNodeGen.create(0);
@@ -324,7 +322,7 @@ public abstract class CallMatcherNode extends RBaseNode {
 
         @Override
         public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) {
-            RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, forNextMethod, this);
+            RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, this);
             evaluatePromises(frame, function, reorderedArgs.getArguments(), reorderedArgs.getSignature().getVarArgIndex());
 
             RCaller parent = RArguments.getCall(frame).getParent();
@@ -351,7 +349,7 @@ public abstract class CallMatcherNode extends RBaseNode {
         }
 
         @TruffleBoundary
-        public static RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, boolean forNextMethod, RBaseNode callingNode) {
+        public static RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, RBaseNode callingNode) {
             assert paramSignature.getLength() == args.length;
 
             int argCount = args.length;
@@ -412,7 +410,7 @@ public abstract class CallMatcherNode extends RBaseNode {
             RArgsValuesAndNames evaledArgs = new RArgsValuesAndNames(argValues, signature);
 
             // ...to match them against the chosen function's formal arguments
-            RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, forNextMethod, callingNode);
+            RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, callingNode);
             return evaluated;
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
index 521cc42bc3a68ad0891f1b68c74e0dfc8f8f2a5e..ebdf2442aca958996f449690d8aefef563ca74f2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
@@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RSpecialFactory;
+import com.oracle.truffle.r.runtime.builtins.RSpecialFactory.FullCallNeededException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -121,6 +122,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
      * index into the parent arguments array, otherwise {@link #NO_RECURSIVE_ARGUMENT_INDEX}.
      */
     private int argumentIndex = NO_RECURSIVE_ARGUMENT_INDEX;
+    private boolean propagateFullCallNeededException;
 
     private RCallSpecialNode(SourceSection sourceSection, RNode functionNode, RFunction expectedFunction, RSyntaxNode[] arguments, ArgumentsSignature signature, RNode special) {
         this.sourceSectionR = sourceSection;
@@ -131,10 +133,18 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
         this.signature = signature;
     }
 
+    public static RSyntaxNode createCallInReplace(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) {
+        return createCall(sourceSection, functionNode, signature, arguments, true);
+    }
+
     public static RSyntaxNode createCall(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) {
+        return createCall(sourceSection, functionNode, signature, arguments, false);
+    }
+
+    private static RSyntaxNode createCall(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments, boolean inReplace) {
         RCallSpecialNode special = null;
         if (useSpecials) {
-            special = tryCreate(sourceSection, functionNode, signature, arguments);
+            special = tryCreate(sourceSection, functionNode, signature, arguments, inReplace);
         }
         if (special != null) {
             if (sourceSection == RSyntaxNode.EAGER_DEPARSE) {
@@ -146,7 +156,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
         }
     }
 
-    private static RCallSpecialNode tryCreate(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) {
+    private static RCallSpecialNode tryCreate(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments, boolean inReplace) {
         RSyntaxNode syntaxFunction = functionNode.asRSyntaxNode();
         if (!(syntaxFunction instanceof RSyntaxLookup)) {
             // LHS is not a simple lookup -> bail out
@@ -181,7 +191,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
                 localArguments[i] = arguments[i].asRNode();
             }
         }
-        RNode special = specialCall.create(signature, localArguments);
+        RNode special = specialCall.create(signature, localArguments, inReplace);
         if (special == null) {
             // the factory refused to create a special call -> bail out
             return null;
@@ -236,7 +246,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
         // Note: we need to check the parent's parent, because it might have been rewritten by
         // bailout of some of its other arguments. If parent is special node, then its parent must
         // be RCallSpecialNode
-        return argumentIndex != NO_RECURSIVE_ARGUMENT_INDEX && getParent() != null && getParent().getParent() instanceof RCallSpecialNode;
+        return propagateFullCallNeededException || (argumentIndex != NO_RECURSIVE_ARGUMENT_INDEX && getParent() != null && getParent().getParent() instanceof RCallSpecialNode);
     }
 
     private RCallNode getRCallNode(RSyntaxNode[] newArguments) {
@@ -251,6 +261,14 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
         argumentIndex = index;
     }
 
+    /**
+     * If set to {@code true} the special call will raise {@link FullCallNeededException} even when
+     * the parent is a special call.
+     */
+    public void setPropagateFullCallNeededException(boolean flag) {
+        propagateFullCallNeededException = flag;
+    }
+
     @Override
     public Object execute(VirtualFrame frame) {
         return execute(frame, functionNode.execute(frame));
@@ -273,7 +291,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
     }
 
     @SuppressWarnings("serial")
-    private static final class RecursiveSpecialBailout extends RuntimeException {
+    public static final class RecursiveSpecialBailout extends RuntimeException {
         public final int argumentIndex;
 
         RecursiveSpecialBailout(int argumentIndex) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
index af7f0639df7fd2f1dc27484dbcd0017a644bce79..d16b817a367f6b68725a7aefcb16ba29ef59aa49 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
@@ -27,6 +27,7 @@ import java.util.function.Supplier;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.signature.VarArgsHelper;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -133,12 +134,12 @@ public final class RCallerHelper {
                     String[] names = new String[preparePermutation.length];
                     for (int i = 0; i < values.length; i++) {
                         long source = preparePermutation[i];
-                        if (!ArgumentsSignature.isVarArgsIndex(source)) {
+                        if (!VarArgsHelper.isVarArgsIndex(source)) {
                             values[i] = suppliedArguments[(int) source];
                             names[i] = suppliedSignature.getName((int) source);
                         } else {
-                            int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source);
-                            int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source);
+                            int varArgsIdx = VarArgsHelper.extractVarArgsIndex(source);
+                            int argsIdx = VarArgsHelper.extractVarArgsArgumentIndex(source);
                             RArgsValuesAndNames varargs = (RArgsValuesAndNames) suppliedArguments[varArgsIdx];
                             values[i] = varargs.getArguments()[argsIdx];
                             names[i] = varargs.getSignature().getName(argsIdx);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
index 284b6b0c0825045733416846ed73b1ab1641f761..739931976e6def1f5933a27c1c92806339e413c4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
@@ -24,7 +24,7 @@ public final class UseMethodInternalNode extends RNode {
 
     @Child private ClassHierarchyNode classHierarchyNode = ClassHierarchyNodeGen.create(true, true);
     @Child private S3FunctionLookupNode lookup = S3FunctionLookupNode.create(false, false);
-    @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false, false);
+    @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false);
     @Child private PreProcessArgumentsNode argPreProcess;
 
     private final String generic;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
index ee7c56dace3c2fb6fc13e9ff516535ec2344587d..60218f9968d1610f1a04a6f303e88eb16bbd1e53 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
@@ -183,7 +183,7 @@ public abstract class PrepareArguments extends Node {
         CachedExplicitPrepareArguments(PrepareArguments next, RRootNode target, RCallNode call, ArgumentsSignature explicitArgSignature) {
             this.next = next;
             formals = target.getFormalArguments();
-            permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, false, target.getBuiltin());
+            permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, target.getBuiltin());
             cachedExplicitArgSignature = explicitArgSignature;
         }
 
@@ -208,7 +208,7 @@ public abstract class PrepareArguments extends Node {
         public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
             CompilerDirectives.transferToInterpreter();
             // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/
-            return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, false, RError.ROOTNODE);
+            return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, RError.ROOTNODE);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java
index 2c604132b4fb7bd50d6fc309aec5410761cd6a58..b360a6e5aa9cee0c359b52901ed7dae3ab61e7c9 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.ArgumentsSignature.VarArgsInfo;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
@@ -76,7 +75,7 @@ public abstract class CombineSignaturesNode extends RBaseNode {
                     @Cached("createBinaryProfile()") ConditionProfile noVarArgsProfile) {
         Object[] flatLeftValues = leftValues;
         if (noVarArgsProfile.profile(resultCached.varArgsInfo.hasVarArgs())) {
-            flatLeftValues = left.flattenValues(leftValues, resultCached.varArgsInfo);
+            flatLeftValues = resultCached.varArgsInfo.flattenValues(left, leftValues);
         }
         return new RArgsValuesAndNames(resultCached.getValues(flatLeftValues, rightValues, shufflingProfile), resultCached.signature);
     }
@@ -84,10 +83,10 @@ public abstract class CombineSignaturesNode extends RBaseNode {
     @TruffleBoundary
     protected CombineResult combine(ArgumentsSignature leftOriginal, Object[] leftValues, ArgumentsSignature right) {
         // flatten any varargs, note there should not be any in right
-        VarArgsInfo varArgsInfo = leftOriginal.getVarArgsInfo(leftValues);
+        VarArgsHelper varArgsInfo = VarArgsHelper.create(leftOriginal, leftValues);
         ArgumentsSignature left = leftOriginal;
         if (varArgsInfo.hasVarArgs()) {
-            left = leftOriginal.flattenNames(varArgsInfo);
+            left = varArgsInfo.flattenNames(leftOriginal);
         }
 
         // calculate the size of the resulting signature - some values in left are overridden by the
@@ -157,9 +156,9 @@ public abstract class CombineSignaturesNode extends RBaseNode {
          */
         private final int[] valuesIndexes;
 
-        private final VarArgsInfo varArgsInfo;
+        private final VarArgsHelper varArgsInfo;
 
-        CombineResult(ArgumentsSignature signature, int[] valuesIndexes, VarArgsInfo varArgsInfo) {
+        CombineResult(ArgumentsSignature signature, int[] valuesIndexes, VarArgsHelper varArgsInfo) {
             this.varArgsInfo = varArgsInfo;
             assert valuesIndexes == null || signature.getLength() == valuesIndexes.length;
             this.signature = signature;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0b110efecb3470bd4b48d404f831aa15b605cfd
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.nodes.function.signature;
+
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+
+/**
+ * Represents the information about varargs contained in {@link ArgumentsSignature} and its argument
+ * values and provides helper functions related to flattening the varargs.
+ */
+public final class VarArgsHelper {
+
+    /**
+     * Array of the same size as the original signature with {@code null} in places where there are
+     * regular arguments and with {@link ArgumentsSignature} instances under the same indexes of
+     * their corresponding {@link RArgsValuesAndNames}.
+     */
+    private final ArgumentsSignature[] varArgs;
+
+    /**
+     * The total number of arguments including those in varargs, and excluding unmatched ones.
+     */
+    private final int argListSize;
+
+    private VarArgsHelper(ArgumentsSignature[] varArgs, int argListSize) {
+        this.varArgs = varArgs;
+        this.argListSize = argListSize;
+    }
+
+    public static VarArgsHelper create(ArgumentsSignature signature, Object[] suppliedArguments) {
+        assert signature.getLength() == suppliedArguments.length;
+        ArgumentsSignature[] varArgs = null;
+        int argListSize = signature.getLength();
+        for (int i = 0; i < suppliedArguments.length; i++) {
+            Object arg = suppliedArguments[i];
+            if (arg instanceof RArgsValuesAndNames) {
+                if (varArgs == null) {
+                    varArgs = new ArgumentsSignature[suppliedArguments.length];
+                }
+                varArgs[i] = ((RArgsValuesAndNames) arg).getSignature();
+                argListSize += ((RArgsValuesAndNames) arg).getLength() - 1;
+            } else if (signature.isUnmatched(i)) {
+                argListSize--;
+            }
+        }
+        return new VarArgsHelper(varArgs, argListSize);
+    }
+
+    public boolean hasVarArgs() {
+        return varArgs != null;
+    }
+
+    public ArgumentsSignature[] getVarArgsSignatures() {
+        return varArgs;
+    }
+
+    public int getArgListSize() {
+        return argListSize;
+    }
+
+    /**
+     * Returns an array where each index is either index into the variables array (positive number)
+     * or it is a packed representation of two indices: one into the variables array pointing to
+     * varargs instance and the other is index into this varargs' arguments array. Use static
+     * methods {@link #isVarArgsIndex(long)}, {@link #extractVarArgsArgumentIndex(long)} and
+     * {@link #extractVarArgsArgumentIndex(long)} to access the data packed in the {@code long}
+     * value. This method also removes arguments that are marked as 'unmatched' in the signature.
+     *
+     * Note: where {@link #hasVarArgs()} returns {@code false}, then the flattening may not be
+     * necessary. This optimization is left to the caller.
+     */
+    public long[] flattenIndexes(ArgumentsSignature signature) {
+        long[] preparePermutation = new long[argListSize];
+        int index = 0;
+        for (int i = 0; i < varArgs.length; i++) {
+            ArgumentsSignature varArgSignature = varArgs[i];
+            if (varArgSignature != null) {
+                for (int j = 0; j < varArgSignature.getLength(); j++) {
+                    preparePermutation[index++] = -((((long) i) << 32) + j) - 1;
+                }
+            } else if (!signature.isUnmatched(i)) {
+                preparePermutation[index++] = i;
+            }
+        }
+        return preparePermutation;
+    }
+
+    /** @see #flattenIndexes(ArgumentsSignature) */
+    public Object[] flattenValues(ArgumentsSignature signature, Object[] values) {
+        Object[] result = new Object[argListSize];
+        int resultIdx = 0;
+        for (int valuesIdx = 0; valuesIdx < values.length; valuesIdx++) {
+            if (varArgs[valuesIdx] != null) {
+                assert values[valuesIdx] instanceof RArgsValuesAndNames;
+                assert ((RArgsValuesAndNames) values[valuesIdx]).getSignature() == varArgs[valuesIdx];
+                RArgsValuesAndNames varArgs = (RArgsValuesAndNames) values[valuesIdx];
+                for (int i = 0; i < varArgs.getLength(); i++) {
+                    result[resultIdx++] = varArgs.getArgument(i);
+                }
+            } else if (!signature.isUnmatched(valuesIdx)) {
+                result[resultIdx++] = values[valuesIdx];
+            }
+        }
+        return result;
+    }
+
+    /** @see #flattenIndexes(ArgumentsSignature) */
+    public ArgumentsSignature flattenNames(ArgumentsSignature signature) {
+        String[] argNames = new String[argListSize];
+        int index = 0;
+        for (int i = 0; i < varArgs.length; i++) {
+            ArgumentsSignature varArgSignature = varArgs[i];
+            if (varArgSignature != null) {
+                for (int j = 0; j < varArgSignature.getLength(); j++) {
+                    argNames[index++] = varArgSignature.getName(j);
+                }
+            } else if (!signature.isUnmatched(i)) {
+                argNames[index++] = signature.getName(i);
+            }
+        }
+        return ArgumentsSignature.get(argNames);
+    }
+
+    /*
+     * Utility functions for interpreting the result of flattenIndexes.
+     */
+
+    public static boolean isVarArgsIndex(long idx) {
+        return idx < 0;
+    }
+
+    public static int extractVarArgsIndex(long idx) {
+        return (int) ((-idx - 1) >> 32);
+    }
+
+    public static int extractVarArgsArgumentIndex(long idx) {
+        return (int) (-idx - 1);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
index a486be7101ff3eb27da17ed9a59ad0f3c320ef5c..4619df7873767cf57eb2f9b2b0a24c3512e4f14e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
@@ -42,7 +42,7 @@ final class ExecuteMethod extends RBaseNode {
         if (collectArgs == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             collectArgs = insert(CollectArgumentsNodeGen.create());
-            callMatcher = insert(CallMatcherNode.create(false, false));
+            callMatcher = insert(CallMatcherNode.create(false));
         }
 
         FormalArguments formals = ((RRootNode) fdef.getRootNode()).getFormalArguments();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
index 1edbe70707699fc90a97203aaa9cdf50f27b17d7..20acad69f07ff206c3be83053d1d928b41138016 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
@@ -30,7 +30,6 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 
 public final class ArgumentsSignature implements Iterable<String> {
 
@@ -193,136 +192,4 @@ public final class ArgumentsSignature implements Iterable<String> {
     public String toString() {
         return "Signature " + Arrays.toString(names);
     }
-
-    /*
-     * Utility functions
-     */
-
-    public static boolean isVarArgsIndex(long idx) {
-        return idx < 0;
-    }
-
-    public static int extractVarArgsIndex(long idx) {
-        return (int) ((-idx - 1) >> 32);
-    }
-
-    public static int extractVarArgsArgumentIndex(long idx) {
-        return (int) (-idx - 1);
-    }
-
-    /**
-     * Creates instance of {@link VarArgsInfo} for this signature and supplied arguments values.
-     * This object is required by other utility methods handling varargs.
-     */
-    public VarArgsInfo getVarArgsInfo(Object[] suppliedArguments) {
-        ArgumentsSignature[] varArgs = null;
-        int argListSize = getLength();
-        for (int i = 0; i < suppliedArguments.length; i++) {
-            Object arg = suppliedArguments[i];
-            if (arg instanceof RArgsValuesAndNames) {
-                if (varArgs == null) {
-                    varArgs = new ArgumentsSignature[suppliedArguments.length];
-                }
-                varArgs[i] = ((RArgsValuesAndNames) arg).getSignature();
-                argListSize += ((RArgsValuesAndNames) arg).getLength() - 1;
-            } else if (isUnmatched(i)) {
-                argListSize--;
-            }
-        }
-        return new VarArgsInfo(varArgs, argListSize);
-    }
-
-    /**
-     * Returns an array where each index is either index into the variables array (positive number)
-     * or it is a packed representation of two indices: one into the variables array pointing to
-     * varargs instance and the other is index into this varargs' arguments array. Use static
-     * methods {@link #isVarArgsIndex(long)}, {@link #extractVarArgsArgumentIndex(long)} and
-     * {@link #extractVarArgsArgumentIndex(long)} to access the data packed in the {@code long}
-     * value. This method also removes arguments that are marked as 'unmatched' in the signature.
-     *
-     * Note: where {@link VarArgsInfo#hasVarArgs()} returns {@code false}, then the flattening may
-     * not be necessary. This optimization is left to the caller.
-     */
-    public long[] flattenIndexes(VarArgsInfo varArgsInfo) {
-        long[] preparePermutation = new long[varArgsInfo.argListSize];
-        int index = 0;
-        for (int i = 0; i < varArgsInfo.varArgs.length; i++) {
-            ArgumentsSignature varArgSignature = varArgsInfo.varArgs[i];
-            if (varArgSignature != null) {
-                for (int j = 0; j < varArgSignature.getLength(); j++) {
-                    preparePermutation[index++] = -((((long) i) << 32) + j) - 1;
-                }
-            } else if (!isUnmatched(i)) {
-                preparePermutation[index++] = i;
-            }
-        }
-        return preparePermutation;
-    }
-
-    /** @see #flattenIndexes(VarArgsInfo varArgsInfo) */
-    public Object[] flattenValues(Object[] values, VarArgsInfo varArgsInfo) {
-        Object[] result = new Object[varArgsInfo.argListSize];
-        int resultIdx = 0;
-        for (int valuesIdx = 0; valuesIdx < values.length; valuesIdx++) {
-            if (varArgsInfo.varArgs[valuesIdx] != null) {
-                assert values[valuesIdx] instanceof RArgsValuesAndNames;
-                assert ((RArgsValuesAndNames) values[valuesIdx]).getSignature() == varArgsInfo.varArgs[valuesIdx];
-                RArgsValuesAndNames varArgs = (RArgsValuesAndNames) values[valuesIdx];
-                for (int i = 0; i < varArgs.getLength(); i++) {
-                    result[resultIdx++] = varArgs.getArgument(i);
-                }
-            } else if (!isUnmatched(valuesIdx)) {
-                result[resultIdx++] = values[valuesIdx];
-            }
-        }
-        return result;
-    }
-
-    /** @see #flattenIndexes(VarArgsInfo varArgsInfo) */
-    public ArgumentsSignature flattenNames(VarArgsInfo varArgsInfo) {
-        String[] argNames = new String[varArgsInfo.argListSize];
-        int index = 0;
-        for (int i = 0; i < varArgsInfo.varArgs.length; i++) {
-            ArgumentsSignature varArgSignature = varArgsInfo.varArgs[i];
-            if (varArgSignature != null) {
-                for (int j = 0; j < varArgSignature.getLength(); j++) {
-                    argNames[index++] = varArgSignature.getName(j);
-                }
-            } else if (!isUnmatched(i)) {
-                argNames[index++] = getName(i);
-            }
-        }
-        return ArgumentsSignature.get(argNames);
-    }
-
-    public static final class VarArgsInfo {
-        /**
-         * Array of the same size as the original signature with {@code null} in places where there
-         * are regular arguments and with {@link ArgumentsSignature} instances under the same
-         * indexes of their corresponding {@link RArgsValuesAndNames}.
-         */
-        private final ArgumentsSignature[] varArgs;
-
-        /**
-         * The total number of arguments including those in varargs, and excluding unmatched ones.
-         */
-        private final int argListSize;
-
-        private VarArgsInfo(ArgumentsSignature[] varArgs, int argListSize) {
-            this.varArgs = varArgs;
-            this.argListSize = argListSize;
-        }
-
-        public boolean hasVarArgs() {
-            return varArgs != null;
-        }
-
-        public ArgumentsSignature[] getVarArgsSignatures() {
-            return varArgs;
-        }
-
-        public int getArgListSize() {
-            return argListSize;
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java
index 98185edaafddc48b1194ac3b0b60e38cbe8eb7ea..0f90524e98b782cbc2fed6760582fa4c96e4a0e7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java
@@ -22,8 +22,19 @@
  */
 package com.oracle.truffle.r.runtime;
 
+import java.io.BufferedReader;
+import java.io.File;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
 import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory.Handler;
 
@@ -46,4 +57,59 @@ class DefaultResourceHandlerFactory extends ResourceHandlerFactory implements Ha
     protected Handler newHandler() {
         return this;
     }
+
+    @Override
+    public String[] getRFiles(Class<?> accessor, String pkgName) {
+        CodeSource source = accessor.getProtectionDomain().getCodeSource();
+        ArrayList<String> list = new ArrayList<>();
+        try {
+            URL url = source.getLocation();
+            Path sourcePath = Paths.get(url.toURI().getPath());
+            File sourceFile = sourcePath.toFile();
+            if (sourceFile.isDirectory()) {
+                InputStream is = accessor.getResourceAsStream(pkgName + "/R");
+                if (is != null) {
+                    try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
+                        String line;
+                        while ((line = r.readLine()) != null) {
+                            if (line.endsWith(".r") || line.endsWith(".R")) {
+                                final String rResource = pkgName + "/R/" + line.trim();
+                                list.add(Utils.getResourceAsString(accessor, rResource, true));
+                            }
+                        }
+                    }
+                }
+            } else {
+                JarFile fastrJar = new JarFile(sourceFile);
+                Enumeration<JarEntry> iter = fastrJar.entries();
+                while (iter.hasMoreElements()) {
+                    JarEntry entry = iter.nextElement();
+                    String name = entry.getName();
+                    if (name.endsWith(".R") || name.endsWith(".r")) {
+                        Path p = Paths.get(name);
+                        String entryPkg = p.getName(p.getNameCount() - 3).getFileName().toString();
+                        String entryParent = p.getName(p.getNameCount() - 2).getFileName().toString();
+                        if (entryParent.equals("R") && entryPkg.equals(pkgName)) {
+                            int size = (int) entry.getSize();
+                            byte[] buf = new byte[size];
+                            InputStream is = fastrJar.getInputStream(entry);
+                            int totalRead = 0;
+                            int n;
+                            while ((n = is.read(buf, totalRead, buf.length - totalRead)) > 0) {
+                                totalRead += n;
+                            }
+                            list.add(new String(buf));
+                        }
+                    }
+                }
+            }
+            String[] result = new String[list.size()];
+            list.toArray(result);
+            return result;
+        } catch (Exception ex) {
+            Utils.rSuicide(ex.getMessage());
+            return null;
+        }
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java
index 0fddff7ddb5511d204ab70cce66397c540d59db1..b20589fd8459804332cbe644af1209bdca750a7c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java
@@ -44,6 +44,12 @@ public abstract class ResourceHandlerFactory {
          * See {@link java.lang.Class#getResourceAsStream(String)}.
          */
         InputStream getResourceAsStream(Class<?> accessor, String name);
+
+        /**
+         * Return the contents of all "R" files (ending with ".r" or ".R") relative to
+         * {@code accessor} and {@code pkgname/R}. I.e. essentially a directory search.
+         */
+        String[] getRFiles(Class<?> accessor, String pkgName);
     }
 
     static {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java
index da9ce251356d3592525d6c44565c51bdc0f1b859..d078aab5d1c6f57154a261b42cea87132f4bfbc9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java
@@ -41,8 +41,13 @@ public interface RSpecialFactory {
     /**
      * Returns a 'special node' if the given arguments with their signature can be handled by it. If
      * if returns {@code null}, the full blown built-in node will be created.
+     *
+     * @param inReplacement whether a non-replacement call is part of replacement sequence, but not
+     *            whether the call itself is a replacement (i.e. with arrow '<-'). Example: f(g(x))
+     *            <- val, the call to g(x) will be constructed with {@code inReplacement == true},
+     *            but the call to `g<-(...)` won't.
      */
-    RNode create(ArgumentsSignature argumentsSignature, RNode[] arguments);
+    RNode create(ArgumentsSignature argumentsSignature, RNode[] arguments, boolean inReplacement);
 
     @SuppressWarnings("serial")
     final class FullCallNeededException extends RuntimeException {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
index c04bf7676a094d05b55ce87eca8d32f5e11e6ee8..68a6831e22872a16a63fddd7ee51a3f1b8df4d63 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
@@ -48,6 +48,26 @@ public interface RCodeBuilder<T> {
         }
     }
 
+    final class CodeBuilderContext {
+        public static CodeBuilderContext DEFAULT = new CodeBuilderContext(0);
+
+        private final int replacementVarsStartIndex;
+
+        public CodeBuilderContext(int replacementVarsStartIndex) {
+            this.replacementVarsStartIndex = replacementVarsStartIndex;
+        }
+
+        /**
+         * Used to initialize {@code ReplacementBlockNode}. When we are processing a replacement AST
+         * that is within another replacement, example {@code x[x[1]<-2]<-3}, we set this value so
+         * that newly created replacements within the original replacement have different temporary
+         * variable names.
+         */
+        public int getReplacementVarsStartIndex() {
+            return replacementVarsStartIndex;
+        }
+    }
+
     /**
      * Creates a function call argument.
      */
@@ -98,6 +118,10 @@ public interface RCodeBuilder<T> {
      */
     RootCallTarget rootFunction(SourceSection source, List<Argument<T>> arguments, T body, String name);
 
+    void setContext(CodeBuilderContext context);
+
+    CodeBuilderContext getContext();
+
     /**
      * This method returns a newly created AST fragment for the given original element. This
      * functionality can be used to quickly create new AST snippets for existing code.
@@ -137,6 +161,15 @@ public interface RCodeBuilder<T> {
         }.accept(original);
     }
 
+    /** @see #process(RSyntaxElement) */
+    default T process(RSyntaxElement original, CodeBuilderContext context) {
+        CodeBuilderContext saved = getContext();
+        setContext(context);
+        T result = process(original);
+        setContext(saved);
+        return result;
+    }
+
     /**
      * Helper function: create a call with no arguments.
      */
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 92c540ced4e357138a097bae61e42c3771bf6656..872652954e2863f1c016e2ffffbaf8ebebd4cfa8 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
@@ -57620,6 +57620,335 @@ g.default args:
 NULL
 
 
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R") }
+called foo.default with  42
+with '' as class
+called foo.default with  42
+with classes baz and bar:
+called foo.baz with  42
+called foo.bar with  42
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R") }
+called foo.baz with  42
+list()
+called foo.bar with  42
+[[1]]
+[1] 3
+
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R") }
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "baz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named$f
+[1] "named-from-baz"
+
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named$f
+[1] "named-from-baz"
+
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazzz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named[[2]]
+[1] "matched-positionally?"
+
+$named$f
+[1] "named-from-baz"
+
+
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R") }
+foo.baz
+foo.bar with:
+evaluated  b-from-caller
+$x
+[1] 42
+attr(,"class")
+[1] "baz" "bar"
+
+$a
+[1] "a-from-baz"
+
+$b
+[1] "b-from-caller"
+
+$named
+list()
+
+foo.bar with:
+evaluated  b-from-caller
+$x
+[1] 42
+attr(,"class")
+[1] "bazz" "bar"
+
+$a
+[1] "a-from-bazz"
+
+$b
+[1] "b-from-caller"
+
+$named
+list()
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazzz" "bar"
+
+$a
+[1] "a-from-bazz"
+
+$b
+[1] "b-from-caller"
+
+$named
+list()
+
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R") }
+foo.baz
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "baz" "bar"
+
+$a
+[1] "positional-from-caller"
+
+$b
+[1] "positional-explicit"
+
+$mynamed
+[1] "named-explicit"
+
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#Ignored.Unknown#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R") }
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "baz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named$f
+[1] "named-from-baz"
+
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named$f
+[1] "named-from-baz"
+
+
+foo.bar with:
+evaluated  matched-positionally?
+$x
+[1] 42
+attr(,"class")
+[1] "bazzz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "explicit-from-baz"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+$named[[2]]
+[1] "matched-positionally?"
+
+$named$f
+[1] "named-from-baz"
+
+
+
+##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R") }
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "baz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "def-bar"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "def-bar"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+
+foo.bar with:
+$x
+[1] 42
+attr(,"class")
+[1] "bazzz" "bar"
+
+$a
+[1] "caller-a"
+
+$b
+[1] "caller-b"
+
+$c
+[1] "def-bar"
+
+$cc
+[1] "caller-d"
+
+$named
+$named$e
+[1] "caller-e"
+
+
+
 ##com.oracle.truffle.r.test.functions.TestS3Dispatch.testComplexGroupDispatch#
 #{x<--7+2i;class(x)<-"foo";Complex.foo<-function(z){1;};Im(x);}
 [1] 1
@@ -65132,10 +65461,34 @@ $z
 [1] 1 4 8
 
 
+##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate#
+#{ l <- list(42); l[1][1] <- 7; l }
+[[1]]
+[1] 7
+
+
 ##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate#
 #{ l <- list(c(1,2,3),c(4,5,6)) ; l[[1]] <- c(7,8,9) ; l[[1]] }
 [1] 7 8 9
 
+##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate#
+#{ l <- list(c(42)); idx <- TRUE; l[idx] <- list(c(1,2,3)); l }
+[[1]]
+[1] 1 2 3
+
+
+##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate#
+#{ l <- list(c(42)); l[1][1] <- 7; l }
+[[1]]
+[1] 7
+
+
+##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate#
+#{ l <- list(c(42, 43)); l[[1]][1] <- 7; l }
+[[1]]
+[1]  7 43
+
+
 ##com.oracle.truffle.r.test.library.base.TestSimpleLoop.testDynamic#
 #{ l <- quote({x <- 0 ; for(i in 1:10) { x <- x + i } ; x}) ; f <- function() { eval(l) } ; x <<- 10 ; f() }
 [1] 55
@@ -109924,6 +110277,26 @@ attr(,"is.truffle.object")
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { TRUE } else { { x<-rep(1, 100); xi1<-.fastr.identity(x); f<-function(x) { y<-x; y }; f(x); x[1]<-7; xi2<-.fastr.identity(x); xi1 == xi2 } }
 [1] TRUE
 
+##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcor#
+#.Call(stats:::C_cov, 1:5, 1:5, 4, FALSE)
+[1] 2.5
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcor#
+#.Call(stats:::C_cov, 1:5, c(1,5,1,5,10), 4, FALSE)
+[1] 4.5
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts#
+#.Call(stats:::C_cov, 1:3, 1:5, 4, FALSE)
+Error: incompatible dimensions
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts#
+#.Call(stats:::C_cov, NULL, 1:5, 4, FALSE)
+Error: 'x' is NULL
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts#
+#.Call(stats:::C_cov, c('1','2','3','4','5'), 1:5, 4, FALSE)
+[1] 2.5
+
 ##com.oracle.truffle.r.test.library.stats.TestFitting.testLm#Output.IgnoreWhitespace#
 #y <- c(26.55, 37.21, 57.28, 90.82, 20.16, 89.838, 94.46, 20.5, 17.6, 68.7, 38.41, 76.9, 49.7, 71, 99.19, 16); x <- c(26.55, 37.21, 57.28, 90.82, 20.16, 89.838, 94.46, 20.5, 17.6, 68.7, 38.41, 76.9, 49.7, 71, 99.19, 16); res <- lm(y~x); print(res$coefficients);print(res$fitted.values);print(res$xlevels);print(res$residuals);print(res$assign);print(res$effects);print(res$qr$qr);print(res$rank);print(res$model);
   (Intercept)             x
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java
index c3e3ee014015f9dca26c83386d2265b0c686bd61..69a51571c41f298b060439b7061e463ee4d54dc4 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java
@@ -83,6 +83,8 @@ public class TestRBase extends TestBase {
                             testTrait = Output.IgnoreErrorContext;
                         } else if (l.contains("IgnoreWarningContext")) {
                             testTrait = Output.IgnoreWarningContext;
+                        } else if (l.contains("Ignored")) {
+                            testTrait = Ignored.Unknown;
                         }
                     }
                 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R
index 0d8b326a796201ba6725be2f369da52e4e786c6f..36069c8bce2974bcdd794df5f161d7aee1daba8a 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R
@@ -1,3 +1,24 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
 g.default <- function(y,...) { cat('g.default args:\n'); print(list(if(missing(y)) NULL else y,...)); }
 g.c <- function(x,...) { cat('g.c args:\n'); print(list(if(missing(x)) NULL else x,...)); }
 g <- function(x,...) { cat('dispatch\n'); UseMethod('g') }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R
new file mode 100644
index 0000000000000000000000000000000000000000..040d76962e143d55c3fab1641b42c40ada6c5dc0
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R
@@ -0,0 +1,36 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+foo <- function(x) UseMethod("foo")
+foo.default <- function(x) cat("called foo.default with ", x, "\n")
+foo.bar <- function(x) cat("called foo.bar with ", x, "\n")
+foo.baz <- function(x) { cat("called foo.baz with ", x, "\n"); NextMethod(unclass(x)); }
+
+val <- 42
+foo(val)
+
+cat("with '' as class\n")
+class(val) <- c('')
+foo(val)
+
+cat("with classes baz and bar:\n")
+class(val) <- c('baz', 'bar')
+foo(val)
\ No newline at end of file
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R
new file mode 100644
index 0000000000000000000000000000000000000000..d5bc3014322c8ae183cbf5063b35c56085f3379b
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R
@@ -0,0 +1,32 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+foo <- function(x, ...) UseMethod("foo")
+foo.default <- function(x, ...) { cat("called foo.default with ", x, "\n"); print(list(...)); }
+foo.bar <- function(x, ...) { cat("called foo.bar with ", x, "\n"); print(list(...)); }
+
+# in foo.baz NextMethod gets additional argument "3" that should be passed to foo.bar.
+# However, the first argument: vector of characters, is ignored
+foo.baz <- function(x, ...) { cat("called foo.baz with ", x, "\n"); print(list(...)); NextMethod(c('something', 'foo'), x, 3); }
+
+val <- 42
+class(val) <- c('baz', 'bar')
+foo(val)
\ No newline at end of file
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R
new file mode 100644
index 0000000000000000000000000000000000000000..21e0f894291d9d04e741bc14b50a080a62fc78ae
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R
@@ -0,0 +1,41 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+# tests the scenario when NextMethod has some additional arguments.
+# Note: it seems that those additional arguments are taken into account only
+# if named, otherwise the value is ignored (see 'matched-positionally?')
+# see also nextMethodArgsMatching.R
+
+withClass <- function(x, cls) { class(x) <- cls; x }
+
+foo <- function(x, ...) UseMethod("foo")
+foo.bar <- function(x, a, b, cc, c='def-bar', ...) {
+    cat("foo.bar with: \n");
+    print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...)))
+}
+
+foo.baz <-   function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x, 'matched-positionally?', c='explicit-from-baz', f='named-from-baz')
+foo.bazz <-  function(x, d, a=1, c='def-baz', ...) NextMethod(c='explicit-from-baz', f='named-from-baz')
+foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo', x, 'matched-positionally?', c='explicit-from-baz', f='named-from-baz')
+
+foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R
new file mode 100644
index 0000000000000000000000000000000000000000..0b2523a798dca0b1d41334494aff07b5d929fd74
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R
@@ -0,0 +1,40 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+# tests the execution of promises when NextMethod has some additional arguments
+# that may override the original arguments. See also nextMethodArgsMatching.R
+
+withClass <- function(x, cls) { class(x) <- cls; x }
+side <- function(x) { cat("evaluated ", x, "\n"); x }
+
+foo <- function(x, ...) UseMethod("foo")
+foo.bar <- function(x, a, b, ...) {
+    cat("foo.bar with: \n");
+    print(list(x=x, a=a, b=b, named=list(...)))
+}
+
+foo.baz <-   function(x, ...) { cat("foo.baz\n"); NextMethod(x, a='a-from-baz') }
+foo.bazz <-  function(x, a, b, ...) NextMethod(x, a='a-from-bazz')
+foo.bazzz <-  function(x, a='a-default', b, ...) NextMethod(x, a='a-from-bazz')
+
+foo(withClass(42, c('baz', 'bar')), a=side('a-from-caller'), b=side('b-from-caller'))
+foo(withClass(42, c('bazz', 'bar')), a=side('a-from-caller'), b=side('b-from-caller'))
+foo(withClass(42, c('bazzz', 'bar')), b='b-from-caller')
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R
new file mode 100644
index 0000000000000000000000000000000000000000..da15897fab5c29fa350ddbb876c6b94edfe44876
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R
@@ -0,0 +1,34 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+# Tests the scenario when NextMethod has some additional arguments.
+# Tests the overriding of arguments passed to S3 dispatch method (foo) by explicit arguments given to NextMethod.
+
+withClass <- function(x, cls) { class(x) <- cls; x }
+
+foo <- function(x, ...) UseMethod("foo")
+foo.bar <- function(x, a, b, mynamed) {
+    cat("foo.bar with: \n");
+    print(list(x=x, a=a, b=b, mynamed=mynamed))
+}
+foo.baz <- function(x, ...) { cat("foo.baz\n"); NextMethod('foo', x, 'positional-explicit', mynamed='named-explicit') }
+
+foo(withClass(42, c('baz', 'bar')), 'positional-from-caller', mynamed='named-from-caller')
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R
new file mode 100644
index 0000000000000000000000000000000000000000..adb4873d768596701b92afc80042515629442ba1
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R
@@ -0,0 +1,47 @@
+# Ignored
+# Note: once this works, we can remove nextMethodAdditionalArgs2.R,
+# which is the same test without promises evaluation check
+
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+# tests the scenario when NextMethod has some additional arguments.
+# Note: it seems that those additional arguments are taken into account only
+# if named, otherwise the value is ignored (see 'matched-positionally?')
+# see also nextMethodArgsMatching.R
+
+withClass <- function(x, cls) { class(x) <- cls; x }
+
+side <- function(x) { cat("evaluated ", x, "\n"); x }
+
+foo <- function(x, ...) UseMethod("foo")
+foo.bar <- function(x, a, b, cc, c='def-bar', ...) {
+    cat("foo.bar with: \n");
+    print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...)))
+}
+
+foo.baz <-   function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x, side('matched-positionally?'), c='explicit-from-baz', f='named-from-baz')
+foo.bazz <-  function(x, d, a=1, c='def-baz', ...) NextMethod(c='explicit-from-baz', f='named-from-baz')
+foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo', x, side('matched-positionally?'), c='explicit-from-baz', f='named-from-baz')
+
+foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R
new file mode 100644
index 0000000000000000000000000000000000000000..bb2b5db25f938199f5c44f2fafa009a5dac09dff
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R
@@ -0,0 +1,47 @@
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+# tests that the signature of the call of the S3 dispatch function is matched to
+# the signature of the target of NextMethod. Note that default value of 'c' in
+# foo.baz is ignored and 'cc' in foo.bar is matched positionally with 'caller-d'.
+
+withClass <- function(x, cls) { class(x) <- cls; x }
+
+# a - matched by name param
+# b - unmatched named param, but matched named in foo.bar
+# c - default param (no value given), default (no value given) in foo.bar
+# cc - default param (no value given), not default default in foo.bar
+# d - matched positional param
+# e - unmatched named param, stays unmatched named in foo.bar
+
+foo <- function(x, ...) UseMethod("foo")
+foo.bar <- function(x, a, b, cc, c='def-bar', ...) {
+    cat("foo.bar with: \n");
+    print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...)))
+}
+
+foo.baz <-   function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x)
+foo.bazz <-  function(x, d, a=1, c='def-baz', ...) NextMethod()
+foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo')
+
+foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
+foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e')
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java
index 8529d785457ec4aa1c7bf389d1606eead86c753e..cfcffb27f3dbbe436ce32644f678d54bc4fb0885 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java
@@ -73,6 +73,10 @@ public class TestSimpleLists extends TestBase {
     @Test
     public void testListUpdate() {
         assertEval("{ l <- list(c(1,2,3),c(4,5,6)) ; l[[1]] <- c(7,8,9) ; l[[1]] }");
+        assertEval("{ l <- list(42); l[1][1] <- 7; l }");
+        assertEval("{ l <- list(c(42)); l[1][1] <- 7; l }");
+        assertEval("{ l <- list(c(42, 43)); l[[1]][1] <- 7; l }");
+        assertEval("{ l <- list(c(42)); idx <- TRUE; l[idx] <- list(c(1,2,3)); l }");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f548a2355f67085dc227c71caed04103bece6e7
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.test.library.stats;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestExternal_covcor extends TestBase {
+    @Test
+    public void testCovcor() {
+        assertEval(".Call(stats:::C_cov, 1:5, 1:5, 4, FALSE)");
+        assertEval(".Call(stats:::C_cov, 1:5, c(1,5,1,5,10), 4, FALSE)");
+    }
+
+    @Test
+    public void testCovcorArgsCasts() {
+        assertEval(".Call(stats:::C_cov, c('1','2','3','4','5'), 1:5, 4, FALSE)");
+        assertEval(".Call(stats:::C_cov, NULL, 1:5, 4, FALSE)");
+        assertEval(".Call(stats:::C_cov, 1:3, 1:5, 4, FALSE)");
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java
deleted file mode 100644
index 8d03d05af1ed778b56a9e4a412d0252eef883ae2..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.test.tools.cmpr;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.oracle.truffle.r.runtime.ResourceHandlerFactory;
-import com.oracle.truffle.r.runtime.Utils;
-
-/**
- * Compare the FastR versions of .R files in the standard packages against GnuR. Removes all
- * formatting to perform the check, replacing all whitespace (including newlines) with exactly one
- * space.
- * <p>
- * Usage:
- *
- * <pre>
- * --gnurhome path --package pkg | --files path1 path2
- * </pre>
- *
- * {@code gnurhome} is the path to the GnuR distribution. {@cpde package} gibes the package to
- * compare,e.g. {@code base}. The second form just compares the two files.
- */
-public class CompareLibR {
-
-    private static class FileContent {
-        Path path;
-        String content;
-        String flattened;
-
-        FileContent(Path path, String content) {
-            this.path = path;
-            this.content = content;
-        }
-
-        @Override
-        public String toString() {
-            return path.toString();
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        // Checkstyle: stop system print check
-        String gnurHome = null;
-        String pkg = null;
-        String path1 = null;
-        String path2 = null;
-        boolean printPaths = false;
-        String diffApp = "diff";
-        int i = 0;
-        while (i < args.length) {
-            String arg = args[i];
-            switch (arg) {
-                case "--gnurhome":
-                    i++;
-                    gnurHome = args[i];
-                    break;
-                case "--package":
-                    i++;
-                    pkg = args[i];
-                    break;
-                case "--files":
-                    if (args.length == 3) {
-                        i++;
-                        path1 = args[i];
-                        i++;
-                        path2 = args[i];
-                    } else {
-                        usage();
-                    }
-                    break;
-                case "--paths":
-                    printPaths = true;
-                    break;
-
-                case "--diffapp":
-                    i++;
-                    diffApp = args[i];
-                    break;
-                default:
-                    usage();
-            }
-            i++;
-        }
-
-        if (gnurHome == null && path1 == null) {
-            usage();
-        }
-
-        if (path1 != null) {
-            compareFiles(path1, path2);
-        } else {
-
-            Map<String, FileContent> fastRFiles = getFastR(pkg);
-            Map<String, FileContent> gnuRFiles = getGnuR(gnurHome, pkg, fastRFiles);
-            deformat(gnuRFiles);
-            deformat(fastRFiles);
-            for (Map.Entry<String, FileContent> entry : fastRFiles.entrySet()) {
-                FileContent fastR = entry.getValue();
-                String fileName = entry.getKey();
-                FileContent gnuR = gnuRFiles.get(fileName);
-                if (gnuR == null) {
-                    System.out.println("FastR has file: " + fileName + " not found in GnuR");
-                } else {
-                    if (!fastR.flattened.equals(gnuR.flattened)) {
-                        if (printPaths) {
-                            System.out.printf("%s %s %s%n", diffApp, gnuR.toString(), replaceBin(fastR.toString()));
-                        } else {
-                            System.out.println(fileName + " differs");
-                        }
-                    } else {
-                        System.out.println(fileName + " is identical (modulo formatting)");
-                    }
-                }
-            }
-        }
-    }
-
-    private static String deformat(String s) {
-        return s.replaceAll("\\s+", " ");
-    }
-
-    private static void deformat(Map<String, FileContent> map) {
-        for (Map.Entry<String, FileContent> entry : map.entrySet()) {
-            FileContent fc = entry.getValue();
-            fc.flattened = deformat(fc.content);
-        }
-    }
-
-    private static Map<String, FileContent> getGnuR(String gnurHome, String lib, Map<String, FileContent> filter) throws IOException {
-        FileSystem fs = FileSystems.getDefault();
-        Path baseR = fs.getPath(lib, "R");
-        Path library = fs.getPath(gnurHome, "src", "library");
-        baseR = library.resolve(baseR);
-        Map<String, FileContent> result = new HashMap<>();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(baseR)) {
-            for (Path entry : stream) {
-                String entryName = entry.getFileName().toString();
-                if (entryName.endsWith(".R") && (filter.get(entryName) != null)) {
-                    File file = entry.toFile();
-                    result.put(entryName, new FileContent(entry, readFileContent(file)));
-                }
-            }
-        }
-        return result;
-    }
-
-    private static String toFirstUpper(String input) {
-        return input.substring(0, 1).toUpperCase() + input.substring(1);
-    }
-
-    private static Map<String, FileContent> getFastR(String lib) throws Exception {
-        Class<?> klass = Class.forName("com.oracle.truffle.r.nodes.builtin." + lib + "." + toFirstUpper(lib) + "Package");
-        InputStream is = ResourceHandlerFactory.getHandler().getResourceAsStream(klass, "R");
-        Map<String, FileContent> result = new HashMap<>();
-        if (is == null) {
-            return result;
-        }
-        try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
-            String line;
-            while ((line = r.readLine()) != null) {
-                if (line.endsWith(".r") || line.endsWith(".R")) {
-                    String fileName = line.trim();
-                    final String rResource = "R/" + fileName;
-                    URL url = klass.getResource(rResource);
-                    String content = Utils.getResourceAsString(klass, rResource, true);
-                    result.put(fileName, new FileContent(FileSystems.getDefault().getPath(url.getPath()), content));
-                }
-            }
-        }
-        return result;
-    }
-
-    private static String replaceBin(String s) {
-        return s.replace("/bin/", "/src/");
-    }
-
-    private static String readFileContent(File file) throws IOException {
-        byte[] buf = new byte[(int) file.length()];
-        try (BufferedInputStream bs = new BufferedInputStream(new FileInputStream(file))) {
-            bs.read(buf);
-        }
-        return new String(buf);
-    }
-
-    private static void writeFile(File file, String s) throws IOException {
-        try (BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(file))) {
-            bs.write(s.getBytes());
-        }
-    }
-
-    private static void compareFiles(String path1, String path2) throws IOException {
-        String c1 = deformat(readFileContent(new File(path1)));
-        String c2 = deformat(readFileContent(new File(path2)));
-        if (c1.equals(c2)) {
-            System.out.println("files are identical (modulo formatting)");
-        } else {
-            System.out.println("files differ");
-            writeFile(new File(path1 + ".deformat"), c1);
-            writeFile(new File(path2 + ".deformat"), c2);
-        }
-    }
-
-    private static void usage() {
-        // Checkstyle: stop system print check
-        System.err.println("usage: --gnurhome path --package pkg | --files path1 path2");
-        System.exit(1);
-    }
-}
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 55843f099afd0f4dc8bf7c64fbcac7a7d3caa15f..eb145f15c27e02aedfa50ffe523d45476ad36c99 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -27,7 +27,7 @@ import mx
 import mx_gate
 import mx_fastr_pkgs
 import mx_fastr_dists
-from mx_fastr_dists import FastRNativeProject, FastRTestNativeProject, FastRReleaseProject #pylint: disable=unused-import
+from mx_fastr_dists import FastRNativeProject, FastRTestNativeProject, FastRReleaseProject, FastRNativeRecommendedProject #pylint: disable=unused-import
 import mx_copylib
 import mx_fastr_mkgramrd
 
@@ -90,15 +90,9 @@ def do_run_r(args, command, extraVmArgs=None, jdk=None, **kwargs):
     if not jdk:
         jdk = get_default_jdk()
 
-    vmArgs = ['-cp', mx.classpath(jdk=jdk)]
+    vmArgs = ['-cp', mx.classpath('FASTR', jdk=jdk)]
 
-    if 'nocompile' in kwargs:
-        nocompile = True
-        del kwargs['nocompile']
-    else:
-        nocompile = False
-
-    vmArgs += set_graal_options(nocompile)
+    vmArgs += set_graal_options()
 
     if extraVmArgs is None or not '-da' in extraVmArgs:
         # unless explicitly disabled we enable assertion checking
@@ -113,7 +107,7 @@ def do_run_r(args, command, extraVmArgs=None, jdk=None, **kwargs):
     return mx.run_java(vmArgs + args, jdk=jdk, **kwargs)
 
 def r_classpath(args):
-    print mx.classpath(jdk=mx.get_jdk())
+    print mx.classpath('FASTR', jdk=mx.get_jdk())
 
 def _sanitize_vmArgs(jdk, vmArgs):
     '''
@@ -137,11 +131,12 @@ def _sanitize_vmArgs(jdk, vmArgs):
         i = i + 1
     return xargs
 
-def set_graal_options(nocompile=False):
+def set_graal_options():
+    '''
+    If Graal is enabled, set some options specific to FastR
+    '''
     if _mx_graal:
         result = ['-Dgraal.InliningDepthError=500', '-Dgraal.EscapeAnalysisIterations=3', '-XX:JVMCINMethodSizeLimit=1000000']
-        if nocompile:
-            result += ['-Dgraal.TruffleCompilationThreshold=100000']
         return result
     else:
         return []
@@ -345,8 +340,6 @@ def _junit_r_harness(args, vmArgs, jdk, junitArgs):
     # no point in printing errors to file when running tests (that contain errors on purpose)
     vmArgs += ['-DR:-PrintErrorStacktracesToFile']
 
-    vmArgs += set_graal_options(nocompile=True)
-
     setREnvironment()
 
     return mx.run_java(vmArgs + junitArgs, nonZeroIsFatal=False, jdk=jdk)
@@ -511,27 +504,6 @@ def rbdiag(args):
 
     mx.run_java(['-cp', cp, 'com.oracle.truffle.r.nodes.test.RBuiltinDiagnostics'] + args)
 
-def rcmplib(args):
-    '''compare FastR library R sources against GnuR'''
-    parser = ArgumentParser(prog='mx rcmplib')
-    parser.add_argument('--gnurhome', action='store', help='path to GnuR sources', required=True)
-    parser.add_argument('--package', action='store', help='package to check', default="base")
-    parser.add_argument('--paths', action='store_true', help='print full paths of files that differ')
-    parser.add_argument('--diffapp', action='store', help='diff application', default="diff")
-    args = parser.parse_args(args)
-    cmpArgs = []
-    cmpArgs.append("--gnurhome")
-    cmpArgs.append(args.gnurhome)
-    cmpArgs.append("--package")
-    cmpArgs.append(args.package)
-    if args.paths:
-        cmpArgs.append("--paths")
-        cmpArgs.append("--diffapp")
-        cmpArgs.append(args.diffapp)
-
-    cp = mx.classpath([pcp.name for pcp in mx.projects_opt_limit_to_suites()])
-    mx.run_java(['-cp', cp, 'com.oracle.truffle.r.test.tools.cmpr.CompareLibR'] + cmpArgs)
-
 def _gnur_path():
     np = mx.project('com.oracle.truffle.r.native')
     return join(np.dir, 'gnur', r_version(), 'bin')
@@ -570,7 +542,6 @@ _commands = {
     'unittest' : [unittest, ['options']],
     'rbcheck' : [rbcheck, '--filter [gnur-only,fastr-only,both,both-diff]'],
     'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error] [--maxSweeps=N] [--outMaxLev=N]'],
-    'rcmplib' : [rcmplib, ['options']],
     'rrepl' : [rrepl, '[options]'],
     'rembed' : [rembed, '[options]'],
     'r-cp' : [r_classpath, '[options]'],
diff --git a/mx.fastr/mx_fastr_dists.py b/mx.fastr/mx_fastr_dists.py
index 39961a6f024e63d1f9f7d17744e708655efc5abd..3892b38573639e87efb45c1d060868e6b15934f2 100644
--- a/mx.fastr/mx_fastr_dists.py
+++ b/mx.fastr/mx_fastr_dists.py
@@ -228,8 +228,9 @@ class ReleaseBuildTask(mx.NativeBuildTask):
             if isinstance(dep, mx.JARDistribution):
                 shutil.copy(join(dep.suite.dir, dep.path), jars_dir)
             elif isinstance(dep, mx.Library):
-                jar_name = dep.name.lower() + '.jar'
-                shutil.copyfile(join(dep.suite.dir, dep.path), join(jars_dir, jar_name))
+                if not dep.name.lower() == 'jdk_tools':
+                    jar_name = dep.name.lower() + '.jar'
+                    shutil.copyfile(join(dep.suite.dir, dep.path), join(jars_dir, jar_name))
             elif isinstance(dep, mx.JavaProject):
                 if 'com.oracle.truffle.r' in dep.name:
                     classfiles_dir = dep.output_dir()
@@ -263,6 +264,30 @@ class ReleaseBuildTask(mx.NativeBuildTask):
         rscript_launcher = join(self.subject.dir, 'src', 'Rscript_launcher')
         self._template(rscript_launcher, join(bin_dir, 'Rscript'), template_dict)
 
+class FastRNativeRecommendedProject(mx.NativeProject):
+    '''
+    This finesses an ordering problem on installing the recommended R packages.
+    These must be installed by FastR using bin/R CMD INSTALL. That will invoke a
+    nested 'mx R' invocation which requires the FASTR distribution to be available.
+    However, this dependency cannt be specified in the suite.py file so we achieve
+    it here by ensuring that it is built prior to the native.recommended project.
+    '''
+    def __init__(self, suite, name, deps, workingSets, theLicense, **args):
+        mx.NativeProject.__init__(self, suite, name, None, [], deps, workingSets, None, None, join(suite.dir, name), theLicense)
+
+    def getBuildTask(self, args):
+        return NativeRecommendedBuildTask(self, args)
+
+class NativeRecommendedBuildTask(mx.NativeBuildTask):
+    def __init__(self, project, args):
+        mx.NativeBuildTask.__init__(self, args, project)
+
+    def build(self):
+        # must archive FASTR before build so that nested mx R CMD INSTALL can execute
+        mx.archive(['@FASTR'])
+        mx.NativeBuildTask.build(self)
+
+
 class FastRArchiveParticipant:
     def __init__(self, dist):
         self.dist = dist
@@ -292,7 +317,6 @@ class FastRArchiveParticipant:
             include_dir = join(self.release_project.dir, 'include')
             shutil.rmtree(include_dir)
 
-
 def mx_post_parse_cmd_line(opts):
     for dist in mx_fastr._fastr_suite.dists:
         dist.set_archiveparticipant(FastRArchiveParticipant(dist))
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index d83edbd3277c23b04c13aa0f967045acc91b452f..63ef682bce091569d03d3224022cd192b0b5428a 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -261,22 +261,19 @@ suite = {
 
     "com.oracle.truffle.r.release" : {
       "sourceDirs" : ["src"],
-      "dependencies" : ["com.oracle.truffle.r.engine", "com.oracle.truffle.r.runtime.ffi", "com.oracle.truffle.r.native"],
+      "dependencies" : ["com.oracle.truffle.r.native.recommended"],
       "class" : "FastRReleaseProject",
       "output" : "com.oracle.truffle.r.release"
     },
 
     "com.oracle.truffle.r.native.recommended" : {
-      "sourceDirs" : [],
-      # these dependencies ensure that all distributions are built
-      # before the nested mx that does the CMD INSTALL runs
       "dependencies" : [
-        "com.oracle.truffle.r.release",
-        "com.oracle.truffle.r.test",
-        "com.oracle.truffle.r.test.native"
+        "com.oracle.truffle.r.native",
+        "com.oracle.truffle.r.engine",
+        "com.oracle.truffle.r.runtime.ffi"
       ],
+      "class" : "FastRNativeRecommendedProject",
       "native" : "true",
-      "output" : "com.oracle.truffle.r.native.recommended",
       "workingSets" : "FastR",
     },