From ff3383c272888932463a267d8276191498f1b61c Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Wed, 9 Nov 2016 14:51:02 +0100
Subject: [PATCH] further cleanups in replacement and write variable nodes

---
 .../r/nodes/builtin/base/AllNames.java        |   3 +-
 .../truffle/r/nodes/builtin/base/Call.java    |   1 -
 .../oracle/truffle/r/nodes/RASTBuilder.java   |  97 ++++++----------
 .../r/nodes/access/BaseWriteVariableNode.java |   7 +-
 .../access/WriteSuperFrameVariableNode.java   | 107 ++++++++++--------
 .../nodes/access/WriteSuperVariableNode.java  |  96 ----------------
 .../r/nodes/access/WriteVariableNode.java     |  20 +---
 .../access/WriteVariableNodeSyntaxHelper.java |  53 ---------
 ...Node.java => WriteVariableSyntaxNode.java} |  70 ++++++------
 .../truffle/r/nodes/control/ForNode.java      |  18 +--
 .../control/ReplacementDispatchNode.java      |  85 ++++----------
 .../r/nodes/control/ReplacementNode.java      |  33 ++----
 12 files changed, 180 insertions(+), 410 deletions(-)
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
 rename com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/{WriteCurrentVariableNode.java => WriteVariableSyntaxNode.java} (53%)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
index 905fb5e0d0..053928da25 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
@@ -46,6 +46,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
 @RBuiltin(name = "all.names", kind = INTERNAL, parameterNames = {"expr", "functions", "max.names", "unique"}, behavior = PURE)
@@ -133,7 +134,7 @@ public abstract class AllNames extends RBuiltinNode {
 
         @Override
         protected Void visit(RSyntaxFunction element) {
-            accept(RSyntaxLookup.createDummyLookup(null, "function", true));
+            accept(RSyntaxLookup.createDummyLookup(RSyntaxNode.INTERNAL, "function", true));
             accept(element.getSyntaxBody());
             // functions do not recurse into the arguments
             return null;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
index 4944158701..b219b18a59 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
@@ -36,7 +36,6 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
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 c39d5e67c5..441422963f 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
@@ -41,7 +41,6 @@ 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.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;
@@ -54,7 +53,6 @@ import com.oracle.truffle.r.nodes.function.WrapDefaultArgumentNode;
 import com.oracle.truffle.r.nodes.function.signature.MissingNode;
 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;
@@ -62,8 +60,6 @@ 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;
 import com.oracle.truffle.r.runtime.nodes.RNode;
-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;
@@ -96,27 +92,28 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
     @Override
     public RSyntaxNode call(SourceSection source, RSyntaxNode lhs, List<Argument<RSyntaxNode>> args) {
         if (lhs instanceof RSyntaxLookup) {
-            String symbol = ((RSyntaxLookup) lhs).getIdentifier();
+            RSyntaxLookup lhsLookup = (RSyntaxLookup) lhs;
+            String symbol = lhsLookup.getIdentifier();
             if (args.size() == 0) {
                 switch (symbol) {
                     case "break":
-                        return new BreakNode(source, lhs);
+                        return new BreakNode(source, lhsLookup);
                     case "next":
-                        return new NextNode(source, lhs);
+                        return new NextNode(source, lhsLookup);
                 }
             } else if (args.size() == 1) {
                 switch (symbol) {
                     case "repeat":
-                        return new RepeatNode(source, lhs, args.get(0).value);
+                        return new RepeatNode(source, lhsLookup, args.get(0).value);
                     case "(":
                         return args.get(0).value;
                 }
             } else if (args.size() == 2) {
                 switch (symbol) {
                     case "while":
-                        return new WhileNode(source, lhs, args.get(0).value, args.get(1).value);
+                        return new WhileNode(source, lhsLookup, args.get(0).value, args.get(1).value);
                     case "if":
-                        return new IfNode(source, lhs, args.get(0).value, args.get(1).value, null);
+                        return new IfNode(source, lhsLookup, args.get(0).value, args.get(1).value, null);
                     case "=":
                     case "<-":
                     case ":=":
@@ -125,63 +122,38 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
                     case "->>":
                         boolean isSuper = "<<-".equals(symbol) || "->>".equals(symbol);
                         boolean switchArgs = "->".equals(symbol) || "->>".equals(symbol);
-                        RSyntaxLookup operator = (RSyntaxLookup) lhs;
                         // fix the operators while keeping the correct source sections
                         if ("->>".equals(symbol)) {
-                            operator = ReadVariableNode.createForcedFunctionLookup(lhs.getLazySourceSection(), "<<-");
+                            lhsLookup = ReadVariableNode.createForcedFunctionLookup(lhs.getLazySourceSection(), "<<-");
                         } else if ("->".equals(symbol)) {
-                            operator = ReadVariableNode.createForcedFunctionLookup(lhs.getLazySourceSection(), "<-");
+                            lhsLookup = ReadVariableNode.createForcedFunctionLookup(lhs.getLazySourceSection(), "<-");
                         }
-                        return createReplacement(source, operator, isSuper, args.get(switchArgs ? 1 : 0).value, args.get(switchArgs ? 0 : 1).value);
+                        // switch the args if needed
+                        RSyntaxNode lhsArg = args.get(switchArgs ? 1 : 0).value;
+                        RSyntaxNode rhsArg = args.get(switchArgs ? 0 : 1).value;
+                        return new ReplacementDispatchNode(source, lhsLookup, lhsArg, rhsArg, isSuper, context.getReplacementVarsStartIndex());
                 }
             } else if (args.size() == 3) {
                 switch (symbol) {
                     case "for":
                         if (args.get(0).value instanceof RSyntaxLookup) {
-                            String name = ((RSyntaxLookup) args.get(0).value).getIdentifier();
-                            WriteVariableNode cvar = WriteVariableNode.create(source, name, null, false);
-                            return new ForNode(source, lhs, cvar, args.get(1).value.asRNode(), args.get(2).value.asRNode());
+                            RSyntaxLookup var = (RSyntaxLookup) args.get(0).value;
+                            return new ForNode(source, lhsLookup, var, args.get(1).value.asRNode(), args.get(2).value.asRNode());
                         }
                         break;
                     case "if":
-                        return new IfNode(source, lhs, args.get(0).value, args.get(1).value, args.get(2).value);
+                        return new IfNode(source, lhsLookup, args.get(0).value, args.get(1).value, args.get(2).value);
                 }
             }
             switch (symbol) {
                 case "{":
-                    return new BlockNode(source, lhs, args.stream().map(n -> n.value.asRNode()).toArray(RNode[]::new));
+                    return new BlockNode(source, lhsLookup, args.stream().map(n -> n.value.asRNode()).toArray(RNode[]::new));
                 case "missing":
-                    return new MissingNode(source, lhs, createSignature(args), args.stream().map(a -> a.value).toArray(RSyntaxElement[]::new));
+                    return new MissingNode(source, lhsLookup, createSignature(args), args.stream().map(a -> a.value).toArray(RSyntaxElement[]::new));
             }
         }
 
-        ArgumentsSignature signature = createSignature(args);
-        RSyntaxNode[] nodes = args.stream().map(
-                        arg -> (arg.value == null && arg.name == null) ? ConstantNode.create(arg.source == null ? RSyntaxNode.SOURCE_UNAVAILABLE : arg.source, REmpty.instance) : arg.value).toArray(
-                                        RSyntaxNode[]::new);
-
-        return RCallSpecialNode.createCall(source, lhs.asRNode(), signature, nodes);
-    }
-
-    private RSyntaxNode createReplacement(SourceSection source, RSyntaxLookup operator, boolean isSuper, RSyntaxNode replacementLhs, RSyntaxNode replacementRhs) {
-        if (replacementLhs instanceof RSyntaxCall) {
-            return createReplacement(source, replacementLhs, replacementRhs, operator, isSuper);
-        } else {
-            String name;
-            if (replacementLhs instanceof RSyntaxLookup) {
-                name = ((RSyntaxLookup) replacementLhs).getIdentifier();
-            } else if (replacementLhs instanceof RSyntaxConstant) {
-                RSyntaxConstant c = (RSyntaxConstant) replacementLhs;
-                if (c.getValue() instanceof String) {
-                    name = (String) c.getValue();
-                } else {
-                    return new LHSError(source, operator, replacementLhs, replacementRhs);
-                }
-            } else {
-                throw RInternalError.unimplemented("unexpected lhs type: " + replacementLhs.getClass());
-            }
-            return (RSyntaxNode) WriteVariableNode.create(source, name, replacementRhs.asRNode(), isSuper);
-        }
+        return RCallSpecialNode.createCall(source, lhs.asRNode(), createSignature(args), createArguments(args));
     }
 
     private static ArgumentsSignature createSignature(List<Argument<RSyntaxNode>> args) {
@@ -190,6 +162,15 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         return signature;
     }
 
+    private static RSyntaxNode[] createArguments(List<Argument<RSyntaxNode>> args) {
+        RSyntaxNode[] nodes = new RSyntaxNode[args.size()];
+        for (int i = 0; i < nodes.length; i++) {
+            Argument<RSyntaxNode> arg = args.get(i);
+            nodes[i] = (arg.value == null && arg.name == null) ? ConstantNode.create(arg.source == null ? RSyntaxNode.SOURCE_UNAVAILABLE : arg.source, REmpty.instance) : arg.value;
+        }
+        return nodes;
+    }
+
     private static String getFunctionDescription(SourceSection source, Object assignedTo) {
         if (assignedTo instanceof String) {
             return (String) assignedTo;
@@ -201,10 +182,6 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         }
     }
 
-    private RSyntaxNode createReplacement(SourceSection source, RSyntaxNode lhs, RSyntaxNode rhs, RSyntaxElement operator, boolean isSuper) {
-        return new ReplacementDispatchNode(source, operator, lhs, rhs, isSuper, this.context.getReplacementVarsStartIndex());
-    }
-
     public static FastPathFactory createFunctionFastPath(RSyntaxElement body, ArgumentsSignature signature) {
         return EvaluatedArgumentsVisitor.process(body, signature);
     }
@@ -300,21 +277,19 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
     }
 
     @Override
-    public RSyntaxNode lookup(SourceSection sourceIn, String symbol, boolean functionLookup) {
+    public RSyntaxNode lookup(SourceSection source, String symbol, boolean functionLookup) {
+        assert source != null;
         if (constants != null && symbol.startsWith("C")) {
             Object object = constants.get(symbol);
             if (object != null) {
-                return ConstantNode.create(sourceIn, object);
+                return ConstantNode.create(source, object);
             }
         }
-        /*
-         * TODO Ideally, sourceIn != null always, however ReplacementNodes can cause this on the
-         * rewrite nodes.
-         */
-        SourceSection source = sourceIn == null ? RSyntaxNode.INTERNAL : sourceIn;
-        if (!functionLookup && getVariadicComponentIndex(symbol) != -1) {
-            int ind = getVariadicComponentIndex(symbol);
-            return new ReadVariadicComponentNode(source, ind > 0 ? ind - 1 : ind);
+        if (!functionLookup) {
+            int index = getVariadicComponentIndex(symbol);
+            if (index != -1) {
+                return new ReadVariadicComponentNode(source, index > 0 ? index - 1 : index);
+            }
         }
         return functionLookup ? ReadVariableNode.createForcedFunctionLookup(source, symbol) : ReadVariableNode.create(source, symbol, false);
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
index 98ed65cc09..52fa750d03 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
@@ -30,7 +30,6 @@ import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
@@ -122,17 +121,17 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
     }
 
     @SuppressWarnings("unused")
-    protected boolean isLogicalKind(VirtualFrame frame, FrameSlot frameSlot) {
+    protected boolean isLogicalKind(Frame frame, FrameSlot frameSlot) {
         return isKind(frameSlot, FrameSlotKind.Boolean);
     }
 
     @SuppressWarnings("unused")
-    protected boolean isIntegerKind(VirtualFrame frame, FrameSlot frameSlot) {
+    protected boolean isIntegerKind(Frame frame, FrameSlot frameSlot) {
         return isKind(frameSlot, FrameSlotKind.Int);
     }
 
     @SuppressWarnings("unused")
-    protected boolean isDoubleKind(VirtualFrame frame, FrameSlot frameSlot) {
+    protected boolean isDoubleKind(Frame frame, FrameSlot frameSlot) {
         return isKind(frameSlot, FrameSlotKind.Double);
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
index d5243f1b97..c3d06849fa 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
@@ -27,7 +27,6 @@ import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.find
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
-import com.oracle.truffle.api.dsl.NodeField;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
@@ -37,7 +36,7 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNodeFactory.UnresolvedWriteLocalFrameVariableNodeGen;
-import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
+import com.oracle.truffle.r.nodes.access.WriteSuperFrameVariableNodeFactory.ResolvedWriteSuperFrameVariableNodeGen;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -49,59 +48,75 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  *
  * The state starts out a "unresolved" and transforms to "resolved".
  */
-@SuppressWarnings("unused")
-@NodeChildren({@NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class), @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)})
-@NodeField(name = "mode", type = Mode.class)
-abstract class WriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHelper {
-    private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
-    private final BranchProfile invalidateProfile = BranchProfile.create();
-    private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile();
-
-    protected abstract FrameSlotNode getFrameSlotNode();
-
-    public abstract Mode getMode();
+abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
 
     static WriteVariableNode create(String name, RNode rhs, Mode mode) {
         return new UnresolvedWriteSuperFrameVariableNode(name, rhs, mode);
     }
 
-    @Specialization(guards = "isLogicalKind(frame, frameSlot)")
-    protected void doLogical(VirtualFrame frame, byte value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
-        FrameSlotChangeMonitor.setByteAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
-    }
+    protected abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame);
 
-    @Specialization(guards = "isIntegerKind(frame, frameSlot)")
-    protected void doInteger(VirtualFrame frame, int value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
-        FrameSlotChangeMonitor.setIntAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
+    @Override
+    public final Object execute(VirtualFrame frame) {
+        Object value = getRhs().execute(frame);
+        execute(frame, value);
+        return value;
     }
 
-    @Specialization(guards = "isDoubleKind(frame, frameSlot)")
-    protected void doDouble(VirtualFrame frame, double value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
-        FrameSlotChangeMonitor.setDoubleAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
-    }
+    @NodeChildren({@NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class), @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)})
+    protected abstract static class ResolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode {
+
+        private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
+        private final BranchProfile invalidateProfile = BranchProfile.create();
+        private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile();
+
+        private final Mode mode;
+
+        public ResolvedWriteSuperFrameVariableNode(Mode mode) {
+            this.mode = mode;
+        }
 
-    @Specialization
-    protected void doObject(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
-        MaterializedFrame profiledFrame = enclosingFrameProfile.profile(enclosingFrame);
-        Object newValue = shareObjectValue(profiledFrame, frameSlot, storedObjectProfile.profile(value), getMode(), true);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(profiledFrame, frameSlot, newValue, true, invalidateProfile);
+        protected abstract FrameSlotNode getFrameSlotNode();
+
+        @Specialization(guards = "isLogicalKind(enclosingFrame, frameSlot)")
+        protected void doLogical(byte value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
+            FrameSlotChangeMonitor.setByteAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
+        }
+
+        @Specialization(guards = "isIntegerKind(enclosingFrame, frameSlot)")
+        protected void doInteger(int value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
+            FrameSlotChangeMonitor.setIntAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
+        }
+
+        @Specialization(guards = "isDoubleKind(enclosingFrame, frameSlot)")
+        protected void doDouble(double value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
+            FrameSlotChangeMonitor.setDoubleAndInvalidate(enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
+        }
+
+        @Specialization
+        protected void doObject(Object value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
+            MaterializedFrame profiledFrame = enclosingFrameProfile.profile(enclosingFrame);
+            Object newValue = shareObjectValue(profiledFrame, frameSlot, storedObjectProfile.profile(value), mode, true);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(profiledFrame, frameSlot, newValue, true, invalidateProfile);
+        }
     }
 
-    private static class UnresolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHelper {
+    private static final class UnresolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode {
 
         @Child private RNode rhs;
-        private final String symbol;
-        private final BaseWriteVariableNode.Mode mode;
 
-        UnresolvedWriteSuperFrameVariableNode(String symbol, RNode rhs, BaseWriteVariableNode.Mode mode) {
+        private final String name;
+        private final Mode mode;
+
+        UnresolvedWriteSuperFrameVariableNode(String name, RNode rhs, Mode mode) {
             this.rhs = rhs;
-            this.symbol = symbol;
+            this.name = name;
             this.mode = mode;
         }
 
         @Override
         public String getName() {
-            return symbol;
+            return name;
         }
 
         @Override
@@ -112,21 +127,21 @@ abstract class WriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHe
         @Override
         public void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            if (getName().isEmpty()) {
+            if (name.isEmpty()) {
                 throw RError.error(RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
             }
-            final WriteSuperFrameVariableNodeHelper writeNode;
+            final WriteSuperFrameVariableNode writeNode;
             if (REnvironment.isGlobalEnvFrame(enclosingFrame)) {
                 /*
                  * we've reached the global scope, do unconditional write. if this is the first node
                  * in the chain, needs the rhs and enclosingFrame nodes
                  */
                 AccessEnclosingFrameNode enclosingFrameNode = RArguments.getEnclosingFrame(frame) == enclosingFrame ? new AccessEnclosingFrameNode() : null;
-                writeNode = WriteSuperFrameVariableNodeGen.create(getRhs(), enclosingFrameNode,
-                                FrameSlotNode.create(findOrAddFrameSlot(enclosingFrame.getFrameDescriptor(), symbol, FrameSlotKind.Illegal)), getName(), mode);
+                writeNode = ResolvedWriteSuperFrameVariableNodeGen.create(mode, rhs, enclosingFrameNode,
+                                FrameSlotNode.create(findOrAddFrameSlot(enclosingFrame.getFrameDescriptor(), name, FrameSlotKind.Illegal)), name);
             } else {
-                WriteSuperFrameVariableNode actualWriteNode = WriteSuperFrameVariableNodeGen.create(null, null, FrameSlotNode.create(symbol), this.getName(), mode);
-                writeNode = new WriteSuperFrameVariableConditionalNode(actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(symbol, null, mode), getRhs());
+                ResolvedWriteSuperFrameVariableNode actualWriteNode = ResolvedWriteSuperFrameVariableNodeGen.create(mode, null, null, FrameSlotNode.create(name), name);
+                writeNode = new WriteSuperFrameVariableConditionalNode(actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(name, null, mode), rhs);
             }
             replace(writeNode).execute(frame, value, enclosingFrame);
         }
@@ -139,22 +154,22 @@ abstract class WriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHe
                 execute(frame, value, enclosingFrame);
             } else {
                 // we're in global scope, do a local write instead
-                replace(UnresolvedWriteLocalFrameVariableNodeGen.create(getRhs(), symbol, mode)).execute(frame, value);
+                replace(UnresolvedWriteLocalFrameVariableNodeGen.create(rhs, name, mode)).execute(frame, value);
             }
         }
     }
 
-    public static class WriteSuperFrameVariableConditionalNode extends WriteSuperFrameVariableNodeHelper {
+    private static final class WriteSuperFrameVariableConditionalNode extends WriteSuperFrameVariableNode {
 
-        @Child private WriteSuperFrameVariableNode writeNode;
-        @Child private WriteSuperFrameVariableNodeHelper nextNode;
+        @Child private ResolvedWriteSuperFrameVariableNode writeNode;
+        @Child private WriteSuperFrameVariableNode nextNode;
         @Child private RNode rhs;
 
         private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile();
         private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile();
         private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile();
 
-        WriteSuperFrameVariableConditionalNode(WriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNodeHelper nextNode, RNode rhs) {
+        WriteSuperFrameVariableConditionalNode(ResolvedWriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNode nextNode, RNode rhs) {
             this.writeNode = writeNode;
             this.nextNode = nextNode;
             this.rhs = rhs;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
deleted file mode 100644
index dbbeb43fd0..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-package com.oracle.truffle.r.nodes.access;
-
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.NodeCost;
-import com.oracle.truffle.api.nodes.NodeInfo;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-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.RSyntaxElement;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-
-/**
- * The "syntax" variant corresponding to {@code x <<- y} in the source.
- *
- * Owing to type hierarchy restrictions (and lack of multiple (state) inheritance) this cannot
- * extend {@link RSourceSectionNode}, so we store the field in {@link WriteVariableNodeSyntaxHelper}
- * .
- */
-@NodeInfo(cost = NodeCost.NONE)
-public class WriteSuperVariableNode extends WriteVariableNodeSyntaxHelper implements RSyntaxNode, RSyntaxCall {
-
-    @Child private WriteVariableNode writeSuperFrameVariableNode;
-    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
-
-    protected WriteSuperVariableNode(SourceSection src, String name, RNode rhs) {
-        super(src);
-        writeSuperFrameVariableNode = WriteSuperFrameVariableNode.create(name, rhs, Mode.REGULAR);
-    }
-
-    static WriteSuperVariableNode create(SourceSection src, String name, RNode rhs) {
-        return new WriteSuperVariableNode(src, name, rhs);
-    }
-
-    @Override
-    public Object getName() {
-        return writeSuperFrameVariableNode.getName();
-    }
-
-    @Override
-    public RNode getRhs() {
-        return writeSuperFrameVariableNode.getRhs();
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        Object result = writeSuperFrameVariableNode.execute(frame);
-        visibility.execute(frame, false);
-        return result;
-    }
-
-    @Override
-    public void execute(VirtualFrame frame, Object value) {
-        writeSuperFrameVariableNode.execute(frame, value);
-    }
-
-    @Override
-    public RSyntaxElement getSyntaxLHS() {
-        return RSyntaxLookup.createDummyLookup(null, "<<-", true);
-    }
-
-    @Override
-    public RSyntaxElement[] getSyntaxArguments() {
-        return new RSyntaxElement[]{RSyntaxLookup.createDummyLookup(RSyntaxNode.INTERNAL, (String) getName(), false), getRhs().asRSyntaxNode()};
-    }
-
-    @Override
-    public ArgumentsSignature getSyntaxSignature() {
-        return ArgumentsSignature.empty(2);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
index 3f761e2308..d8bd5afed8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
@@ -23,18 +23,21 @@
 package com.oracle.truffle.r.nodes.access;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 
 /**
  * The base of the {@code WriteVariableNode} type hierarchy. There are several variants for
  * different situations and this class provides static methods to create these.
+ *
+ * The types in this hierarchy do not implement {@link RSyntaxElement} - use
+ * {@link WriteVariableSyntaxNode} instead.
  */
 public abstract class WriteVariableNode extends RNode {
-    public enum Mode {
 
+    public enum Mode {
         REGULAR,
         COPY,
         INVISIBLE
@@ -46,19 +49,6 @@ public abstract class WriteVariableNode extends RNode {
 
     public abstract void execute(VirtualFrame frame, Object value);
 
-    /**
-     * Variant for a variable that appears in the R language source.
-     *
-     * @param isSuper {@code true} if the write is {@code <<-}.
-     */
-    public static WriteVariableNode create(SourceSection src, String name, RNode rhs, boolean isSuper) {
-        if (isSuper) {
-            return WriteSuperVariableNode.create(src, name, rhs);
-        } else {
-            return WriteCurrentVariableNode.create(src, name, rhs);
-        }
-    }
-
     /**
      * Variant for saving function arguments, i.e. from {@link RArguments} into the frame.
      */
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
deleted file mode 100644
index a463662841..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-package com.oracle.truffle.r.nodes.access;
-
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-
-abstract class WriteVariableNodeSyntaxHelper extends WriteVariableNode implements RSyntaxNode {
-    @CompilationFinal private SourceSection sourceSectionR;
-
-    protected WriteVariableNodeSyntaxHelper(SourceSection sourceSection) {
-        assert sourceSection != null;
-        this.sourceSectionR = sourceSection;
-    }
-
-    @Override
-    public void setSourceSection(SourceSection sourceSection) {
-        this.sourceSectionR = sourceSection;
-    }
-
-    @Override
-    public SourceSection getLazySourceSection() {
-        return sourceSectionR;
-    }
-
-    @Override
-    public SourceSection getSourceSection() {
-        RDeparse.ensureSourceSection(this);
-        return sourceSectionR;
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableSyntaxNode.java
similarity index 53%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableSyntaxNode.java
index f3503c82e5..a442c0747a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableSyntaxNode.java
@@ -22,66 +22,62 @@
  */
 package com.oracle.truffle.r.nodes.access;
 
+import static com.oracle.truffle.api.nodes.NodeCost.NONE;
+
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
+import com.oracle.truffle.r.nodes.control.OperatorNode;
 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.RInternalError;
 import com.oracle.truffle.r.runtime.nodes.RNode;
-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;
 
-/**
- * The "syntax" variant corresponding to {@code x <- y} in the source.
- */
-@NodeInfo(cost = NodeCost.NONE)
-public class WriteCurrentVariableNode extends WriteVariableNodeSyntaxHelper implements RSyntaxNode, RSyntaxCall {
+@NodeInfo(cost = NONE)
+public final class WriteVariableSyntaxNode extends OperatorNode {
 
-    @Child private WriteLocalFrameVariableNode writeLocalFrameVariableNode;
+    @Child private WriteVariableNode write;
     @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
-    protected WriteCurrentVariableNode(SourceSection src, String name, RNode rhs) {
-        super(src);
-        writeLocalFrameVariableNode = WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR);
-    }
-
-    static WriteCurrentVariableNode create(SourceSection src, String name, RNode rhs) {
-        return new WriteCurrentVariableNode(src, name, rhs);
-    }
+    private final RSyntaxElement lhs;
 
-    @Override
-    public Object getName() {
-        return writeLocalFrameVariableNode.getName();
-    }
-
-    @Override
-    public RNode getRhs() {
-        return writeLocalFrameVariableNode.getRhs();
+    public WriteVariableSyntaxNode(SourceSection source, RSyntaxLookup operator, RSyntaxElement lhs, RNode rhs, boolean isSuper) {
+        super(source, operator);
+        this.lhs = lhs;
+        String name;
+        if (lhs instanceof RSyntaxLookup) {
+            name = ((RSyntaxLookup) lhs).getIdentifier();
+        } else if (lhs instanceof RSyntaxConstant) {
+            RSyntaxConstant c = (RSyntaxConstant) lhs;
+            if (c.getValue() instanceof String) {
+                name = (String) c.getValue();
+            } else {
+                // "this" needs to be initialized for error reporting to work
+                this.write = WriteVariableNode.createAnonymous("dummy", rhs, Mode.REGULAR, isSuper);
+                throw RError.error(this, RError.Message.INVALID_LHS, "do_set");
+            }
+        } else {
+            throw RInternalError.unimplemented("unexpected lhs type in replacement: " + lhs.getClass());
+        }
+        this.write = WriteVariableNode.createAnonymous(name, rhs, Mode.REGULAR, isSuper);
+        assert write != null;
     }
 
     @Override
     public Object execute(VirtualFrame frame) {
-        Object result = writeLocalFrameVariableNode.execute(frame);
+        Object result = write.execute(frame);
         visibility.execute(frame, false);
         return result;
     }
 
-    @Override
-    public void execute(VirtualFrame frame, Object value) {
-        writeLocalFrameVariableNode.execute(frame, value);
-    }
-
-    @Override
-    public RSyntaxElement getSyntaxLHS() {
-        return RSyntaxLookup.createDummyLookup(null, "<-", true);
-    }
-
     @Override
     public RSyntaxElement[] getSyntaxArguments() {
-        return new RSyntaxElement[]{RSyntaxLookup.createDummyLookup(RSyntaxNode.INTERNAL, (String) getName(), false), getRhs().asRSyntaxNode()};
+        return new RSyntaxElement[]{lhs, write.getRhs().asRSyntaxNode()};
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index 6e316b4d3d..a49df510c7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -53,8 +53,11 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
     @Child private LoopNode loopNode;
     @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
-    public ForNode(SourceSection src, RSyntaxElement operator, WriteVariableNode cvar, RNode range, RNode body) {
+    private final RSyntaxLookup var;
+
+    public ForNode(SourceSection src, RSyntaxLookup operator, RSyntaxLookup var, RNode range, RNode body) {
         super(src, operator);
+        this.var = var;
         String indexName = AnonymousFrameVariable.create("FOR_INDEX");
         String rangeName = AnonymousFrameVariable.create("FOR_RANGE");
         String lengthName = AnonymousFrameVariable.create("FOR_LENGTH");
@@ -62,7 +65,7 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
         this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, null, Mode.REGULAR);
         this.writeRangeNode = WriteVariableNode.createAnonymous(rangeName, range, Mode.REGULAR);
         this.writeLengthNode = WriteVariableNode.createAnonymous(lengthName, RLengthNodeGen.create(ReadVariableNode.create(rangeName)), Mode.REGULAR);
-        this.loopNode = Truffle.getRuntime().createLoopNode(new ForRepeatingNode(this, cvar, body, indexName, lengthName, rangeName));
+        this.loopNode = Truffle.getRuntime().createLoopNode(new ForRepeatingNode(this, var.getIdentifier(), body, indexName, lengthName, rangeName));
     }
 
     @Override
@@ -87,19 +90,17 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
         @Child private ReadVariableNode readIndexNode;
         @Child private ReadVariableNode readLengthNode;
         @Child private WriteVariableNode writeIndexNode;
-        @Child private RNode loadElement;
 
         private final ForNode forNode;
 
-        ForRepeatingNode(ForNode forNode, WriteVariableNode cvar, RNode body, String indexName, String lengthName, String rangeName) {
+        ForRepeatingNode(ForNode forNode, String var, RNode body, String indexName, String lengthName, String rangeName) {
             this.forNode = forNode;
-            this.writeElementNode = cvar;
+            this.writeElementNode = WriteVariableNode.createAnonymous(var, createIndexedLoad(indexName, rangeName), Mode.REGULAR, false);
             this.body = body;
 
             this.readIndexNode = ReadVariableNode.create(indexName);
             this.readLengthNode = ReadVariableNode.create(lengthName);
             this.writeIndexNode = WriteVariableNode.createAnonymous(indexName, null, Mode.REGULAR);
-            this.loadElement = createIndexedLoad(indexName, rangeName);
             // pre-initialize the profile so that loop exits to not deoptimize
             conditionProfile.profile(false);
         }
@@ -124,7 +125,7 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
             }
             try {
                 if (conditionProfile.profile(index <= length)) {
-                    writeElementNode.execute(frame, loadElement.execute(frame));
+                    writeElementNode.execute(frame);
                     body.execute(frame);
                     return true;
                 } else {
@@ -150,8 +151,7 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
     @Override
     public RSyntaxElement[] getSyntaxArguments() {
         ForRepeatingNode repeatingNode = (ForRepeatingNode) loopNode.getRepeatingNode();
-        return new RSyntaxElement[]{RSyntaxLookup.createDummyLookup(null, (String) repeatingNode.writeElementNode.getName(), false), writeRangeNode.getRhs().asRSyntaxNode(),
-                        repeatingNode.body.asRSyntaxNode()};
+        return new RSyntaxElement[]{var, writeRangeNode.getRhs().asRSyntaxNode(), repeatingNode.body.asRSyntaxNode()};
     }
 
     @Override
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
index d726f51153..689bacaeac 100644
--- 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
@@ -33,11 +33,13 @@ 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.WriteVariableNode;
+import com.oracle.truffle.r.nodes.access.WriteVariableSyntaxNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 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.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
@@ -52,18 +54,18 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  */
 public final class ReplacementDispatchNode extends OperatorNode {
 
-    @Child private ReplacementNode replacementNode;
+    // these are only @Child to make instrumentation work
+    @Child private RNode lhs;
+    @Child private RNode rhs;
 
-    private final RSyntaxNode lhs;
-    private final RSyntaxNode rhs;
     private final boolean isSuper;
     private final int tempNamesStartIndex;
 
-    public ReplacementDispatchNode(SourceSection src, RSyntaxElement operator, RSyntaxNode lhs, RSyntaxNode rhs, boolean isSuper, int tempNamesStartIndex) {
+    public ReplacementDispatchNode(SourceSection src, RSyntaxLookup operator, RSyntaxNode lhs, RSyntaxNode rhs, boolean isSuper, int tempNamesStartIndex) {
         super(src, operator);
         assert lhs != null && rhs != null;
-        this.lhs = lhs;
-        this.rhs = rhs;
+        this.lhs = lhs.asRNode();
+        this.rhs = rhs.asRNode();
         this.isSuper = isSuper;
         this.tempNamesStartIndex = tempNamesStartIndex;
     }
@@ -72,21 +74,14 @@ public final class ReplacementDispatchNode extends OperatorNode {
     public Object execute(VirtualFrame frame) {
         CompilerDirectives.transferToInterpreterAndInvalidate();
 
-        return replace(getReplacementNode()).execute(frame);
-    }
+        RNode replacement;
+        if (lhs instanceof RSyntaxCall) {
+            replacement = createReplacementNode();
+        } else {
+            replacement = new WriteVariableSyntaxNode(getLazySourceSection(), operator, lhs.asRSyntaxNode(), rhs, isSuper);
+        }
 
-    /**
-     * Support for syntax tree visitor.
-     */
-    public RSyntaxNode getLhs() {
-        return lhs;
-    }
-
-    /**
-     * Support for syntax tree visitor.
-     */
-    public RSyntaxNode getRhs() {
-        return rhs;
+        return replace(replacement).execute(frame);
     }
 
     @Override
@@ -96,15 +91,7 @@ public final class ReplacementDispatchNode extends OperatorNode {
 
     @Override
     public RSyntaxElement[] getSyntaxArguments() {
-        return new RSyntaxElement[]{lhs, rhs};
-    }
-
-    private ReplacementNode getReplacementNode() {
-        if (replacementNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            replacementNode = insert(createReplacementNode());
-        }
-        return replacementNode;
+        return new RSyntaxElement[]{lhs.asRSyntaxNode(), rhs.asRSyntaxNode()};
     }
 
     private ReplacementNode createReplacementNode() {
@@ -115,7 +102,7 @@ public final class ReplacementDispatchNode extends OperatorNode {
          * "a(...)" and "b(...)".
          */
         List<RSyntaxCall> calls = new ArrayList<>();
-        RSyntaxElement current = lhs;
+        RSyntaxElement current = lhs.asRSyntaxNode();
         while (!(current instanceof RSyntaxLookup)) {
             if (!(current instanceof RSyntaxCall)) {
                 if (current instanceof RSyntaxConstant && ((RSyntaxConstant) current).getValue() == RNull.instance) {
@@ -135,7 +122,7 @@ public final class ReplacementDispatchNode extends OperatorNode {
         }
         RSyntaxLookup variable = (RSyntaxLookup) current;
         ReadVariableNode varRead = createReplacementForVariableUsing(variable, isSuper);
-        return new ReplacementNode(getLazySourceSection(), operator, varRead, lhs, rhs.asRNode(), calls, "*rhs*" + tempNamesStartIndex, variable.getIdentifier(), isSuper, tempNamesStartIndex);
+        return new ReplacementNode(getLazySourceSection(), operator, varRead, lhs.asRSyntaxNode(), rhs, calls, "*rhs*" + tempNamesStartIndex, variable.getIdentifier(), isSuper, tempNamesStartIndex);
     }
 
     private static ReadVariableNode createReplacementForVariableUsing(RSyntaxLookup var, boolean isSuper) {
@@ -154,10 +141,10 @@ public final class ReplacementDispatchNode extends OperatorNode {
             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 (call.getSyntaxLHS() instanceof RSyntaxLookup) {
                 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) {
+                    if (args.length == 2 && args[0] instanceof RSyntaxLookup && args[1] instanceof RSyntaxLookup) {
                         return true;
                     }
                 }
@@ -179,36 +166,4 @@ public final class ReplacementDispatchNode extends OperatorNode {
         }
         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 OperatorNode {
-
-        private final RSyntaxElement lhs;
-        private final RSyntaxElement rhs;
-
-        public LHSError(SourceSection sourceSection, RSyntaxLookup operator, RSyntaxElement lhs, RSyntaxElement rhs) {
-            super(sourceSection, 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[] 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 9f24797fc4..2e3a23178b 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
@@ -27,14 +27,11 @@ import java.util.ArrayList;
 import java.util.List;
 
 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.CompilerDirectives;
 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.nodes.Node.Child;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode;
@@ -49,6 +46,7 @@ import com.oracle.truffle.r.runtime.builtins.RSpecialFactory.FullCallNeededExcep
 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;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder.CodeBuilderContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
@@ -106,10 +104,6 @@ final class ReplacementNode extends OperatorNode {
         return rhsValue;
     }
 
-    private ReplacementBase createReplacementNodeWithoutSpecials() {
-        return createReplacementNode(false);
-    }
-
     private ReplacementBase createReplacementNode(boolean useSpecials) {
         CompilerAsserts.neverPartOfCompilation();
         // Note: if specials are turned off in FastR, onlySpecials will never be true
@@ -187,14 +181,15 @@ final class ReplacementNode extends OperatorNode {
      * {@code newLhs}.
      */
     private static RNode createSpecialFunctionQuery(RSyntaxNode newLhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) {
+        RCodeBuilder<RSyntaxNode> builder = RContext.getASTBuilder();
         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);
+            argNodes[i] = i == 0 ? newLhs : builder.process(arguments[i], codeBuilderContext);
         }
 
-        return RCallSpecialNode.createCallInReplace(fun.getLazySourceSection(), process(fun.getSyntaxLHS(), codeBuilderContext).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode();
+        return RCallSpecialNode.createCallInReplace(fun.getLazySourceSection(), builder.process(fun.getSyntaxLHS(), codeBuilderContext).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode();
     }
 
     /**
@@ -203,6 +198,7 @@ final class ReplacementNode extends OperatorNode {
      * added to the arguments list.
      */
     private static RNode createFunctionUpdate(SourceSection source, RSyntaxNode newLhs, RSyntaxNode rhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) {
+        RCodeBuilder<RSyntaxNode> builder = RContext.getASTBuilder();
         RSyntaxElement[] arguments = fun.getSyntaxArguments();
 
         ArgumentsSignature signature = fun.getSyntaxSignature();
@@ -210,7 +206,7 @@ final class ReplacementNode extends OperatorNode {
         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);
+            argNodes[i] = i == 0 ? newLhs : builder.process(arguments[i], codeBuilderContext);
         }
         argNodes[argNodes.length - 1] = rhs;
         names[argNodes.length - 1] = "value";
@@ -225,27 +221,19 @@ final class ReplacementNode extends OperatorNode {
                 // to work properly
                 argNodes[0] = GetNonSharedNodeGen.create(argNodes[0].asRNode());
             }
-            newSyntaxLHS = lookup(lookupLHS.getLazySourceSection(), symbol + "<-", true);
+            newSyntaxLHS = builder.lookup(lookupLHS.getLazySourceSection(), 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].getLazySourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true);
+            newArgs[1] = builder.lookup(oldArgs[1].getLazySourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true);
             newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getLazySourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs);
         }
         return RCallSpecialNode.createCall(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes).asRNode();
     }
 
-    private static RSyntaxNode process(RSyntaxElement original, CodeBuilderContext codeBuilderContext) {
-        return RContext.getASTBuilder().process(original, codeBuilderContext);
-    }
-
-    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) {
@@ -295,13 +283,14 @@ final class ReplacementNode extends OperatorNode {
                 // OK with not calling any other update function and just update the value directly.
                 replaceCall.execute(frame);
             } catch (FullCallNeededException | RecursiveSpecialBailout e) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
 // String code = getReplacementNodeParent().source.getCode();
 // System.out.println("fallback to generic: " + code);
 // if (code.contains("season$previousSeasonalIndex.isOnAWeekday <-
 // previousSeasonalIndex.isOnAWeekday")) {
 // System.out.println("...");
 // }
-                replace(getReplacementNodeParent().createReplacementNodeWithoutSpecials()).execute(frame);
+                replace(getReplacementNodeParent().createReplacementNode(false)).execute(frame);
             }
         }
     }
-- 
GitLab