diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index f3119ba912260d9230d786fa75254fe960238358..987209e766adde27b169110c9a96055d26585c35 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -99,6 +99,8 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRPkgSource;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRPkgSourceNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfo;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfoNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssign;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssignNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTrace;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTraceNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfAttr;
@@ -375,6 +377,7 @@ public class BasePackage extends RBuiltinPackage {
         add(FastRProfAttr.class, FastRStatsFactory.FastRProfAttrNodeGen::create);
         add(FastRProfTypecounts.class, FastRStatsFactory.FastRProfTypecountsNodeGen::create);
         add(FastRProfFuncounts.class, FastRStatsFactory.FastRProfFuncountsNodeGen::create);
+        add(FastRSlotAssign.class, FastRSlotAssignNodeGen::create);
         add(FastRSyntaxTree.class, FastRSyntaxTreeNodeGen::create);
         add(FastRThrowIt.class, FastRThrowItNodeGen::create);
         add(FastRTrace.Trace.class, FastRTraceFactory.TraceNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
index 97584d822ee6a358ed5739eb6d9eee1ac067826b..684d9ce1e776ce18d07cfc59f4bf3415c079d7b4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
@@ -22,14 +22,12 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
-import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -38,105 +36,91 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+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;
 
 @RBuiltin(name = "@<-", kind = PRIMITIVE, parameterNames = {"", "", "value"}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class UpdateSlot extends RBuiltinNode {
 
-    private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("cl", "name", "valueClass");
-
-    @CompilationFinal private RFunction checkSlotAssignFunction;
-    @Child private ClassHierarchyNode objClassHierarchy;
-    @Child private ClassHierarchyNode valClassHierarchy;
     @Child private UpdateSlotNode updateSlotNode = com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen.create();
-    @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment");
-    @Child private CallRFunctionNode checkAtAssignmentCall;
-    private final ConditionProfile cached = ConditionProfile.createBinaryProfile();
 
     static {
         Casts casts = new Casts(UpdateSlot.class);
         casts.arg(0).asAttributable(true, true, true);
     }
 
-    protected String getName(Object nameObj) {
-        assert nameObj instanceof RPromise;
-        Object rep = ((RPromise) nameObj).getRep();
-        if (rep instanceof WrapArgumentNode) {
-            rep = ((WrapArgumentNode) rep).getOperand();
-        }
-        if (rep instanceof ConstantNode) {
-            Object val = ((ConstantNode) rep).getValue();
-            if (val instanceof String) {
-                return (String) val;
-            }
-            if (val instanceof RSymbol) {
-                return ((RSymbol) val).getName();
+    protected String getName(RPromise nameObj) {
+        Closure closure = nameObj.getClosure();
+        if (closure.asSymbol() != null) {
+            return closure.asSymbol();
+        } else if (closure.asStringConstant() != null) {
+            return closure.asStringConstant();
+        } else {
+            CompilerDirectives.transferToInterpreter();
+            RSyntaxElement element = closure.getExpr().asRSyntaxNode();
+            assert !(element instanceof RSyntaxLookup);
+            if (element instanceof RSyntaxConstant) {
+                throw error(RError.Message.SLOT_INVALID_TYPE, Predef.typeName().apply(((RSyntaxConstant) element).getValue()));
+            } else {
+                throw error(RError.Message.SLOT_INVALID_TYPE, "language");
             }
-        } else if (rep instanceof ReadVariableNode) {
-            return ((ReadVariableNode) rep).getIdentifier();
-        } else if (rep instanceof RCallNode) {
-            throw error(RError.Message.SLOT_INVALID_TYPE, "language");
         }
-        // TODO: this is not quite correct, but I wonder if we even reach here (can also be
-        // augmented on demand)
-        throw error(RError.Message.SLOT_INVALID_TYPE, nameObj.getClass().toString());
     }
 
-    private void checkSlotAssign(VirtualFrame frame, Object object, String name, Object value) {
-        // TODO: optimize using a mechanism similar to overrides?
-        if (checkSlotAssignFunction == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            checkSlotAssignFunction = (RFunction) checkAtAssignmentFind.execute(frame);
-        }
-        if (checkAtAssignmentCall == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget()));
-        }
-        if (objClassHierarchy == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
-        }
-        if (valClassHierarchy == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
-        }
-        RStringVector objClass = objClassHierarchy.execute(object);
-        RStringVector valClass = valClassHierarchy.execute(value);
-        RFunction currentFunction = (RFunction) checkAtAssignmentFind.execute(frame);
-        if (cached.profile(currentFunction == checkSlotAssignFunction)) {
-            // TODO: technically, someone could override checkAtAssignment function and access the
-            // caller, but it's rather unlikely
-            checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.create(frame, getOriginalCall()), null, new Object[]{objClass, name, valClass}, SIGNATURE,
-                            checkSlotAssignFunction.getEnclosingFrame(), null);
-        } else {
-            // slow path
-            RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, objClass, name, valClass);
-        }
-    }
+    public static final class CheckSlotAssignNode extends RBaseNode {
 
-    /*
-     * Motivation for cached version is that in the operator form (foo@bar<-baz), the name is an
-     * interned string which allows us to avoid longer lookup
-     */
-    @Specialization(guards = "sameName(nameObj, nameObjCached)")
-    protected Object updateSlotCached(VirtualFrame frame, Object object, @SuppressWarnings("unused") Object nameObj, Object value, @SuppressWarnings("unused") @Cached("nameObj") Object nameObjCached,
-                    @Cached("getName(nameObjCached)") String name) {
-        checkSlotAssign(frame, object, name, value);
-        return updateSlotNode.executeUpdate(object, name, value);
+        private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("cl", "name", "valueClass");
+
+        @CompilationFinal private RFunction checkSlotAssignFunction;
+        @Child private ClassHierarchyNode objClassHierarchy;
+        @Child private ClassHierarchyNode valClassHierarchy;
+        @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment");
+        @Child private CallRFunctionNode checkAtAssignmentCall;
+
+        private final ConditionProfile cached = ConditionProfile.createBinaryProfile();
+
+        public void execute(VirtualFrame frame, Object object, String name, Object value) {
+            if (checkSlotAssignFunction == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                checkSlotAssignFunction = (RFunction) checkAtAssignmentFind.execute(frame);
+            }
+            if (checkAtAssignmentCall == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget()));
+            }
+            if (objClassHierarchy == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
+            }
+            if (valClassHierarchy == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
+            }
+            RStringVector objClass = objClassHierarchy.execute(object);
+            RStringVector valClass = valClassHierarchy.execute(value);
+            RFunction currentFunction = (RFunction) checkAtAssignmentFind.execute(frame);
+            if (cached.profile(currentFunction == checkSlotAssignFunction)) {
+                // TODO: technically, someone could override checkAtAssignment function and access
+                // the caller, but it's rather unlikely
+                checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), null, new Object[]{objClass, name, valClass}, SIGNATURE,
+                                checkSlotAssignFunction.getEnclosingFrame(), null);
+            } else {
+                // slow path
+                RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.createInvalid(frame), null, objClass, name, valClass);
+            }
+        }
     }
 
-    @Specialization(replaces = "updateSlotCached")
-    protected Object updateSlot(VirtualFrame frame, Object object, Object nameObj, Object value) {
+    @Specialization
+    protected Object updateSlot(VirtualFrame frame, Object object, RPromise nameObj, Object value,
+                    @Cached("new()") CheckSlotAssignNode check) {
         String name = getName(nameObj);
-        checkSlotAssign(frame, object, name, value);
+        check.execute(frame, object, name, value);
         return updateSlotNode.executeUpdate(object, name, value);
     }
-
-    protected boolean sameName(Object nameObj, Object nameObjCached) {
-        assert nameObj instanceof RPromise;
-        assert nameObjCached instanceof RPromise;
-        return ((RPromise) nameObj).getRep() == ((RPromise) nameObjCached).getRep();
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bf687e14fe56a99ad7892c2f024656429c40541
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, 2017, 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.builtin.fastr;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.RVisibility.ON;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
+import com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateAttr.InternStringNode;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateAttrNodeGen.InternStringNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateSlot.CheckSlotAssignNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RMissing;
+
+@RBuiltin(name = ".fastr.methods.slotassign", visibility = ON, kind = PRIMITIVE, parameterNames = {"object", "name", "check", "value"}, behavior = COMPLEX)
+public abstract class FastRSlotAssign extends RBuiltinNode {
+
+    static {
+        Casts casts = new Casts(FastRSlotAssign.class);
+        casts.arg("object").mustNotBeNull();
+        casts.arg("name").defaultError(Message.SLOT_INVALID_TYPE_OR_LEN).mustNotBeMissing().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+        casts.arg("check").mustNotBeMissing().asLogicalVector().findFirst().mustNotBeNA(Message.NA_UNEXP).map(toBoolean());
+        casts.arg("value").mustNotBeMissing();
+    }
+
+    @Child private InternStringNode intern = InternStringNodeGen.create();
+    @Child private UpdateSlotNode updateSlotNode = UpdateSlotNodeGen.create();
+
+    @Override
+    public Object[] getDefaultParameterValues() {
+        return new Object[]{RMissing.instance, RMissing.instance, RRuntime.LOGICAL_TRUE, RMissing.instance};
+    }
+
+    @Specialization(guards = "check")
+    public Object assign(VirtualFrame frame, Object object, String name, @SuppressWarnings("unused") boolean check, Object value,
+                    @Cached("new()") CheckSlotAssignNode checkNode) {
+        String interned = intern.execute(name);
+        checkNode.execute(frame, object, interned, value);
+        return updateSlotNode.executeUpdate(object, interned, value);
+    }
+
+    @Specialization(guards = "!check")
+    public Object assign(Object object, String name, @SuppressWarnings("unused") boolean check, Object value) {
+        String interned = intern.execute(name);
+        return updateSlotNode.executeUpdate(object, interned, value);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R
new file mode 100644
index 0000000000000000000000000000000000000000..99c947a62dbc8ce4e07359f7873abf9baab63558
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R
@@ -0,0 +1,26 @@
+# Copyright (c) 2017, 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.
+
+eval(expression({
+# this function is replaced with a primitive because it is expected to
+# modify its argument in-place, which can clash with argument refcount handling
+`slot<-` <- .fastr.methods.slotassign
+}), asNamespace("methods"))
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
index 4cd1ffa866ada9f3ed5cb3396c0ca55b103a22d6..59d8ab2c49c12060a5c96d63205779293f1f01e2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
@@ -15,7 +15,6 @@ package com.oracle.truffle.r.nodes.access;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -32,6 +31,8 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public abstract class UpdateSlotNode extends RBaseNode {
 
+    private static final String SET_DATA_PART = "setDataPart";
+
     public abstract Object executeUpdate(Object object, String name, Object value);
 
     protected SetAttributeNode createAttrUpdate() {
@@ -56,12 +57,9 @@ public abstract class UpdateSlotNode extends RBaseNode {
     protected Object updateSlotS4Data(RAttributable object, @SuppressWarnings("unused") String name, Object value) {
         // TODO: any way to cache it or use a mechanism similar to overrides?
         REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
-        String identifier = "setDataPart";
-        Object f = methodsNamespace.findFunction(identifier);
-        RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(identifier, f);
-        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), null, object,
-                        prepareValue(value),
-                        RRuntime.LOGICAL_TRUE);
+        Object f = methodsNamespace.findFunction(SET_DATA_PART);
+        RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(SET_DATA_PART, f);
+        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.createInvalid(null), null, object, prepareValue(value), RRuntime.LOGICAL_TRUE);
     }
 
     protected boolean isData(String name) {
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 a6bb49983999b7705e5efda6a6bb4a038a92e19e..13c607e358851a5b39313b9fc650e64b2d2aa620 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
@@ -80,8 +80,12 @@ abstract class ReplacementNode extends OperatorNode {
         }
     }
 
-    private static String getTargetTmpName(int tempNamesStartIndex) {
-        return "*tmp*" + tempNamesStartIndex;
+    private static String getTargetTemp(int index) {
+        return "*tmp*" + index;
+    }
+
+    private static String getRHSTemp(int index) {
+        return "*rhs*" + index;
     }
 
     private static boolean hasOnlySpecialCalls(List<RSyntaxCall> calls) {
@@ -133,11 +137,6 @@ abstract class ReplacementNode extends OperatorNode {
         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] = new GetNonSharedNode.GetNonSharedSyntaxNode(argNodes[0].asRNode());
-            }
             newSyntaxLHS = builder.lookup(lookupLHS.getLazySourceSection(), symbol + "<-", true);
         } else {
             // data types (and lengths) are verified in isNamespaceLookupCall
@@ -171,8 +170,8 @@ abstract class ReplacementNode extends OperatorNode {
             super(source, operator, lhs);
             this.rhs = rhs;
 
-            this.storeRhs = WriteVariableNode.createAnonymous("*rhs*" + tempNamesStartIndex, WriteVariableNode.Mode.INVISIBLE, rhs);
-            this.removeRhs = RemoveAndAnswerNode.create("*rhs*" + tempNamesStartIndex);
+            this.storeRhs = WriteVariableNode.createAnonymous(getRHSTemp(tempNamesStartIndex), WriteVariableNode.Mode.INVISIBLE, rhs);
+            this.removeRhs = RemoveAndAnswerNode.create(getRHSTemp(tempNamesStartIndex));
         }
 
         @Override
@@ -243,7 +242,7 @@ abstract class ReplacementNode extends OperatorNode {
                 extractFunc = createSpecialFunctionQuery(calls.get(i), extractFunc.asRSyntaxNode(), codeBuilderContext);
                 ((RCallSpecialNode) extractFunc).setPropagateFullCallNeededException();
             }
-            this.replaceCall = (RCallSpecialNode) createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create("*rhs*" + tempNamesStartIndex), calls.get(0), codeBuilderContext);
+            this.replaceCall = (RCallSpecialNode) createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create(getRHSTemp(tempNamesStartIndex)), calls.get(0), codeBuilderContext);
             this.replaceCall.setPropagateFullCallNeededException();
         }
 
@@ -341,10 +340,9 @@ abstract class ReplacementNode extends OperatorNode {
      */
     private static final class GenericReplacementNode extends ReplacementWithRhsNode {
 
-        @Child private WriteVariableNode targetTmpWrite;
         @Child private RemoveAndAnswerNode targetTmpRemove;
 
-        @Children private final RNode[] updates;
+        @Children private final WriteVariableNode[] updates;
 
         GenericReplacementNode(SourceSection source, RSyntaxLookup operator, RNode target, RSyntaxElement lhs, RNode rhs, List<RSyntaxCall> calls, String targetVarName, boolean isSuper,
                         int tempNamesStartIndex) {
@@ -353,8 +351,11 @@ abstract class ReplacementNode extends OperatorNode {
              * 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.
              */
-            List<RNode> instructions = new ArrayList<>();
+            List<WriteVariableNode> instructions = new ArrayList<>();
             CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + calls.size() + 1);
+
+            int targetIndex = tempNamesStartIndex;
+            instructions.add(WriteVariableNode.createAnonymous(getTargetTemp(targetIndex), WriteVariableNode.Mode.INVISIBLE, wrapForSlotUpdate(target, calls.get(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
@@ -362,35 +363,49 @@ abstract class ReplacementNode extends OperatorNode {
              * + 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 newFirstArg = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex));
-                RNode update = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext);
-                instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), WriteVariableNode.Mode.INVISIBLE, update));
+            for (int i = calls.size() - 1; i >= 1; i--) {
+                ReadVariableNode newFirstArg = ReadVariableNode.create(getTargetTemp(targetIndex));
+                RNode extract = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext);
+                instructions.add(WriteVariableNode.createAnonymous(getTargetTemp(++targetIndex), WriteVariableNode.Mode.INVISIBLE, wrapForSlotUpdate(extract, calls.get(i - 1))));
             }
             /*
              * 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}.
+             * intermediate results are stored to temporary variables *rhs*{index}.
              */
+            int replacementIndex = tempNamesStartIndex;
             for (int i = 0; i < calls.size(); i++) {
-                int tmpIndex = tempNamesStartIndex + calls.size() - i - 1;
-                String tmprName = i == 0 ? ("*rhs*" + tempNamesStartIndex) : ("*tmpr*" + (tempNamesStartIndex + i - 1));
-                RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext);
+                RNode update = createFunctionUpdate(source, ReadVariableNode.create(getTargetTemp(targetIndex--)), ReadVariableNode.create(getRHSTemp(replacementIndex)), calls.get(i),
+                                codeBuilderContext);
                 if (i < calls.size() - 1) {
-                    instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), WriteVariableNode.Mode.INVISIBLE, update));
+                    instructions.add(WriteVariableNode.createAnonymous(getRHSTemp(++replacementIndex), WriteVariableNode.Mode.INVISIBLE, update));
                 } else {
                     instructions.add(WriteVariableNode.createAnonymous(targetVarName, WriteVariableNode.Mode.REGULAR, update, isSuper));
                 }
             }
 
-            this.updates = instructions.toArray(new RNode[instructions.size()]);
-            this.targetTmpWrite = WriteVariableNode.createAnonymous(getTargetTmpName(tempNamesStartIndex), WriteVariableNode.Mode.INVISIBLE, target);
-            this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTmpName(tempNamesStartIndex));
+            this.updates = instructions.toArray(new WriteVariableNode[instructions.size()]);
+            this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTemp(tempNamesStartIndex));
+        }
+
+        /*
+         * This is complicated, but at this point seems like the only way to get setClass to work
+         * properly. the underlying problem is that slot<- and @<- modify shared values, whereas,
+         * e.g., [[<- does not.
+         */
+        private static RNode wrapForSlotUpdate(RNode target, RSyntaxCall call) {
+            RSyntaxElement syntaxLHS = call.getSyntaxLHS();
+            if (syntaxLHS instanceof RSyntaxLookup) {
+                String symbol = ((RSyntaxLookup) syntaxLHS).getIdentifier();
+                if ("slot".equals(symbol) || "@".equals(symbol)) {
+                    return new GetNonSharedNode.GetNonSharedSyntaxNode(target);
+                }
+            }
+            return target;
         }
 
         @Override
         @ExplodeLoop
         protected void executeReplacement(VirtualFrame frame) {
-            targetTmpWrite.execute(frame);
             for (RNode update : updates) {
                 update.execute(frame);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
index deef4720e12707573f72766e9ef1613cb097c997..9e70abd64665b293003018354688c1fc2cbe9285 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
@@ -27,8 +27,6 @@ import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 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.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RSharingAttributeStorage;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -36,7 +34,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public abstract class GetNonSharedNode extends Node {
 
-    public static final class GetNonSharedSyntaxNode extends RNode implements RSyntaxNode {
+    public static final class GetNonSharedSyntaxNode extends RNode {
 
         @Child private RNode delegate;
         @Child private GetNonSharedNode nonShared = GetNonSharedNodeGen.create();
@@ -54,21 +52,6 @@ public abstract class GetNonSharedNode extends Node {
         protected RSyntaxNode getRSyntaxNode() {
             return delegate.asRSyntaxNode();
         }
-
-        @Override
-        public void setSourceSection(SourceSection sourceSection) {
-            throw RInternalError.shouldNotReachHere();
-        }
-
-        @Override
-        public SourceSection getLazySourceSection() {
-            return RSyntaxNode.INTERNAL;
-        }
-
-        @Override
-        public SourceSection getSourceSection() {
-            return RSyntaxNode.INTERNAL;
-        }
     }
 
     public abstract Object execute(Object value);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index fa016d322eb30702abf9c64f4c33024ee8729108..6e8c5a12079d94ce95fab22b2ba509ba6cdf1054 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -750,6 +750,7 @@ public final class RError extends RuntimeException {
         SLOT_NON_S4("trying to get slot \"%s\" from an object (class \"%s\") that is not an S4 object "),
         SLOT_CANNOT_GET("cannot get a slot (\"%s\") from an object of type \"%s\""),
         SLOT_NONE("no slot of name \"%s\" for this object of class \"%s\""),
+        SLOT_INVALID_TYPE_OR_LEN("invalid type or length for slot name"),
         S4OBJECT_NX_ENVIRONMENT("S4 object does not extend class \"environment\""),
         NOT_A_SLOT("'%s' is not a slot in class ā€%sā€"),
         NS_ALREADY_REG("namespace already registered"),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
index 3e4b0807dd59742ab524841ad766da70654a62a2..24a4df04d697784f8f27accb67bd25cd15ab0fbd 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -39,8 +39,10 @@ import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 
 /**
@@ -445,14 +447,27 @@ public class RPromise implements RTypedValue {
 
         private final RBaseNode expr;
         private final String symbol;
+        private final String stringConstant;
 
         private Closure(RBaseNode expr) {
             this.expr = expr;
             if (expr.asRSyntaxNode() instanceof RSyntaxLookup) {
-                this.symbol = ((RSyntaxLookup) expr.asRSyntaxNode()).getIdentifier();
+                this.symbol = ((RSyntaxLookup) expr.asRSyntaxNode()).getIdentifier().intern();
             } else {
                 this.symbol = null;
             }
+            if (expr.asRSyntaxNode() instanceof RSyntaxConstant) {
+                Object constant = ((RSyntaxConstant) expr.asRSyntaxNode()).getValue();
+                if (constant instanceof String) {
+                    this.stringConstant = (String) constant;
+                } else if (constant instanceof RAbstractStringVector && ((RAbstractStringVector) constant).getLength() == 1) {
+                    this.stringConstant = ((RAbstractStringVector) constant).getDataAt(0);
+                } else {
+                    this.stringConstant = null;
+                }
+            } else {
+                this.stringConstant = null;
+            }
         }
 
         public static Closure create(RBaseNode expr) {
@@ -491,6 +506,10 @@ public class RPromise implements RTypedValue {
         public String asSymbol() {
             return symbol;
         }
+
+        public String asStringConstant() {
+            return stringConstant;
+        }
     }
 
     @Override