diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index c1c66e7f6d09059fe7f3a70fcc8f35dc0e766857..972ed1ade43a577e5cdfd9f91ca678b2519c8708 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -51,7 +51,6 @@ import com.oracle.truffle.r.nodes.function.RCallerHelper;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode;
 import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
-import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -146,7 +145,7 @@ public class GetFunctions {
 
     public static final class S4ToEnvNode extends CastNode {
 
-        @Child private GetS4DataSlot getS4Data = GetS4DataSlotNodeGen.create(RType.Environment);
+        @Child private GetS4DataSlot getS4Data = GetS4DataSlot.create(RType.Environment);
 
         @Override
         public Object execute(Object obj) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
index 903947939fed214361eb750ec927e7f480a2c503..4ab4126616010c6acaae4d804942752ec14601e6 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -39,10 +39,8 @@ import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNa
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.profile.AlwaysOnBranchProfile;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -52,8 +50,6 @@ import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RLogical;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RString;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
@@ -61,7 +57,6 @@ import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 final class CachedExtractVectorNode extends CachedVectorNode {
@@ -69,7 +64,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     private static final boolean DEFAULT_EXACT = true;
     private static final boolean DEFAULT_DROP_DIMENSION = true;
 
-    private final Class<? extends RTypedValue> targetClass;
+    private final Class<? extends RAbstractContainer> targetClass;
     private final Class<? extends RTypedValue> exactClass;
     private final Class<? extends RTypedValue> dropDimensionsClass;
     private final boolean exact;
@@ -90,10 +85,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
 
     @Child private ExtractDimNamesNode extractDimNames;
 
-    @Child private ExtractS4ObjectNode extractS4ObjectNode;
-
     private final ConditionProfile resultHasDimensions = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile promiseInEnvironment = ConditionProfile.createBinaryProfile();
 
     /**
      * Profile if any metadata was applied at any point in time. This is useful extract primitive
@@ -101,8 +93,9 @@ final class CachedExtractVectorNode extends CachedVectorNode {
      */
     private final AlwaysOnBranchProfile metadataApplied = AlwaysOnBranchProfile.create();
 
-    CachedExtractVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue exact, RTypedValue dropDimensions, boolean recursive) {
+    CachedExtractVectorNode(ElementAccessMode mode, RAbstractContainer vector, Object[] positions, RTypedValue exact, RTypedValue dropDimensions, boolean recursive) {
         super(mode, vector, positions, recursive);
+        assert vectorType != RType.Null && vectorType != RType.Environment;
         this.targetClass = vector.getClass();
         this.exactClass = exact.getClass();
         this.dropDimensionsClass = dropDimensions.getClass();
@@ -112,9 +105,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         this.exact = logicalAsBoolean(exact, DEFAULT_EXACT);
         this.dropDimensions = logicalAsBoolean(dropDimensions, DEFAULT_DROP_DIMENSION);
         this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, this.exact, false, recursive);
-        if (error == null && vectorType != RType.Null && vectorType != RType.Environment && vectorType != RType.S4Object) {
-            this.writeVectorNode = WriteIndexedVectorNode.create(vectorType, convertedPositions.length, true, false, false, false);
-        }
+        this.writeVectorNode = WriteIndexedVectorNode.create(vectorType, convertedPositions.length, true, false, false);
     }
 
     public boolean isSupported(Object target, Object[] positions, Object exactValue, Object dropDimensionsValue) {
@@ -130,36 +121,10 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     private final ConditionProfile extractedLengthGTZeroProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile oneDimensionProfile = ConditionProfile.createBinaryProfile();
 
-    public Object apply(Object originalVector, Object[] originalPositions, PositionProfile[] originalProfiles, Object originalExact, Object originalDropDimensions) {
-        if (error != null) {
-            CompilerDirectives.transferToInterpreter();
-            error.run();
-        }
+    public Object apply(RAbstractContainer originalVector, Object[] originalPositions, PositionProfile[] originalProfiles, Object originalExact, Object originalDropDimensions) {
         final Object[] positions = filterPositions(originalPositions);
-
         assert isSupported(originalVector, positions, originalExact, originalDropDimensions);
-
-        final RTypedValue castVector = targetClass.cast(originalVector);
-        final RAbstractContainer vector;
-        switch (vectorType) {
-            case Null:
-                return RNull.instance;
-            case Environment:
-                /*
-                 * TODO (chumer) the environment case cannot be applied to the default extract
-                 * method as it does not implement RAbstractContainer. This should be harmonized
-                 * later.
-                 */
-                return doEnvironment((REnvironment) castVector, positions);
-            case S4Object:
-                return doS4Object((RS4Object) castVector, positions);
-            case Integer:
-                vector = (RAbstractContainer) castVector;
-                break;
-            default:
-                vector = (RAbstractContainer) castVector;
-                break;
-        }
+        RAbstractContainer vector = targetClass.cast(originalVector);
 
         int vectorLength = vectorLengthProfile.profile(vector.getLength());
 
@@ -178,11 +143,8 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         }
 
         int extractedVectorLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles);
-        final RVector<?> extractedVector;
+        RVector<?> extractedVector;
         switch (vectorType) {
-            case Expression:
-                extractedVector = RType.Expression.create(extractedVectorLength, false);
-                break;
             case Language:
             case PairList:
                 extractedVector = RType.List.create(extractedVectorLength, false);
@@ -194,9 +156,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
 
         if (mode.isSubset()) {
             if (extractedLengthGTZeroProfile.profile(extractedVectorLength > 0)) {
-                writeVectorNode.enableValueNACheck(vector);
-                writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions);
-                extractedVector.setComplete(writeVectorNode.neverSeenNAInValue());
+                writeVectorNode.execute(extractedVector, positions, vector, dimensions);
                 RBaseNode.reportWork(this, extractedVectorLength);
             }
             if (oneDimensionProfile.profile(numberOfDimensions == 1)) {
@@ -216,15 +176,13 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             }
 
             switch (vectorType) {
-                case Expression:
-                    return extractedVector;
                 case Language:
                     return materializeLanguage(extractedVector);
                 default:
                     return trySubsetPrimitive(extractedVector);
             }
         } else {
-            writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions);
+            writeVectorNode.execute(extractedVector, positions, vector, dimensions);
             RBaseNode.reportWork(this, 1);
             assert extractedVectorLength == 1;
             return extractedVector.getDataAtAsObject(0);
@@ -242,7 +200,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     }
 
     private Object trySubsetPrimitive(RAbstractVector extractedVector) {
-        if (!metadataApplied.isVisited() && positionsCheckNode.getCachedSelectedPositionsCount() == 1 && !isList()) {
+        if (!metadataApplied.isVisited() && positionsCheckNode.getCachedSelectedPositionsCount() == 1 && vectorType != RType.List && vectorType != RType.Expression) {
             /*
              * If the selected count was always 1 and no metadata was ever set we can just extract
              * the primitive value from the vector. This branch has to fold to a constant because we
@@ -256,31 +214,6 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         return extractedVector;
     }
 
-    @TruffleBoundary
-    private Object doEnvironment(REnvironment env, Object[] positions) {
-        if (mode.isSubset()) {
-            throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
-        }
-
-        String positionString = tryCastSingleString(positionsCheckNode, positions);
-        if (positionString != null) {
-            Object obj = env.get(positionString);
-            if (promiseInEnvironment.profile(obj instanceof RPromise)) {
-                obj = PromiseHelperNode.evaluateSlowPath((RPromise) obj);
-            }
-            return obj == null ? RNull.instance : obj;
-        }
-        throw error(RError.Message.WRONG_ARGS_SUBSET_ENV);
-    }
-
-    private Object doS4Object(RS4Object object, Object[] positions) {
-        if (extractS4ObjectNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            extractS4ObjectNode = insert(new ExtractS4ObjectNode(mode, exact, dropDimensions));
-        }
-        return extractS4ObjectNode.execute(object, positions);
-    }
-
     private boolean isMissingSingleDimension() {
         return numberOfDimensions == 1 && positionsCheckNode.isMissing();
     }
@@ -298,10 +231,6 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         return extractDimNames.extract(dimensionIndex, vector, pos, profile);
     }
 
-    private boolean isList() {
-        return vectorType == RType.List;
-    }
-
     private final ConditionProfile dimNamesNull = ConditionProfile.createBinaryProfile();
     private final ValueProfile foundDimNamesProfile = ValueProfile.createClassProfile();
     private final ConditionProfile selectPositionsProfile = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
index 40e456bf6c06a3da980ffc7cf9054e56d682aaef..43e9de314e70a6237d6060d7e6b55e63ba6e5abf 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
@@ -34,7 +34,6 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeInfo;
-import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
@@ -49,15 +48,11 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RS4Object;
+import com.oracle.truffle.r.runtime.data.RScalarList;
 import com.oracle.truffle.r.runtime.data.RScalarVector;
 import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -66,27 +61,22 @@ import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 final class CachedReplaceVectorNode extends CachedVectorNode {
 
     private static final Object DELETE_MARKER = new Object();
 
-    private final Class<?> vectorClass;
+    private final Class<? extends RAbstractVector> vectorClass;
     private final Class<?> valueClass;
 
     private final VectorLengthProfile targetLengthProfile = VectorLengthProfile.create();
     private final VectorLengthProfile valueLengthProfile = VectorLengthProfile.create();
     private final BranchProfile warningBranch = BranchProfile.create();
-    private final ConditionProfile valueIsNA = ConditionProfile.createBinaryProfile();
     private final BranchProfile resizeProfile = BranchProfile.create();
-    private final ConditionProfile rlanguageAttributesProfile = ConditionProfile.createBinaryProfile();
 
     private final ConditionProfile valueLengthOneProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile emptyReplacementProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile completeVectorProfile = ConditionProfile.createBinaryProfile();
 
     private final ValueProfile vectorTypeProfile = ValueProfile.createClassProfile();
 
@@ -103,9 +93,13 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
     @Child private DeleteElementsNode deleteElementsNode;
     @Child private SetNamesAttributeNode setNamesNode;
 
-    CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, Class<?> valueClass, RType valueType, boolean updatePositionNames, boolean recursive,
-                    boolean ignoreRecursive, boolean isValueGt1) {
+    // if this is non-null, the node needs to throw the error whenever it is executed
+    @CompilationFinal protected Runnable error;
+
+    CachedReplaceVectorNode(ElementAccessMode mode, RAbstractVector vector, Object[] positions, Class<?> valueClass, RType valueType, boolean updatePositionNames, boolean recursive,
+                    boolean isValueGt1) {
         super(mode, vector, positions, recursive);
+        assert vectorType.isVector();
 
         if (numberOfDimensions == 1 && positions[0] instanceof String || positions[0] instanceof RAbstractStringVector) {
             this.updatePositionNames = updatePositionNames;
@@ -117,17 +111,48 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         this.valueClass = valueClass;
         this.valueType = valueType;
         this.isValueGt1 = isValueGt1;
-        this.castType = resolveCastVectorType();
-        verifyCastType(this.castType);
-        this.castVectorNode = createCastVectorNode();
+
+        // determine the target cast type
+        if (vectorType == RType.List && mode.isSubscript()) {
+            if (valueType.isNull() && numberOfDimensions > 1) {
+                this.castType = null;
+            } else {
+                this.castType = vectorType;
+            }
+        } else if (valueType.isVector()) {
+            if (vectorType.isAtomic() && valueType.isAtomic() && (vectorType == RType.Raw ^ valueType == RType.Raw)) {
+                // mixing with raw with other atomic types is not allowed
+                this.castType = null;
+            } else {
+                this.castType = RType.maxPrecedence(valueType, vectorType);
+            }
+        } else if (valueType.isNull()) {
+            if (mode.isSubscript() && numberOfDimensions > 1) {
+                this.castType = null;
+            } else {
+                this.castType = vectorType;
+            }
+        } else {
+            this.castType = null;
+        }
+
+        if (castType == null) {
+            Message message = (mode.isSubset() || vectorType != RType.List) ? RError.Message.SUBASSIGN_TYPE_FIX : RError.Message.SUBSCRIPT_TYPES;
+            error = () -> {
+                throw error(message, valueType.getName(), vectorType.getName(), false);
+            };
+        }
+
+        if (castType != vectorType && castType != null) {
+            // All casts except list casts preserve dimension names.
+            this.castVectorNode = castType == RType.List ? CastListNodeGen.create(true, false, true) : CastTypeNode.createCast(castType, true, true, true, false);
+        }
         this.deleteElementsNode = isDeleteElements() ? new DeleteElementsNode() : null;
 
         Object[] convertedPositions = filterPositions(positions);
         this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, true, true, recursive);
-        if (vectorType == RType.S4Object) {
-            replaceS4ObjectNode = new ReplaceS4ObjectNode(mode, ignoreRecursive);
-        } else if (castType != null && !castType.isNull()) {
-            this.writeVectorNode = WriteIndexedVectorNode.create(castType, convertedPositions.length, false, true, mode.isSubscript() && !isDeleteElements(), true);
+        if (castType != null && !castType.isNull()) {
+            this.writeVectorNode = WriteIndexedVectorNode.create(castType, convertedPositions.length, false, true, true);
         }
     }
 
@@ -135,50 +160,41 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         return (values instanceof RAbstractContainer) && ((RAbstractContainer) values).getLength() > 1;
     }
 
-    public boolean isSupported(Object target, Object[] positions, Object values) {
+    public boolean isSupported(RAbstractVector target, Object[] positions, Object values) {
         if (vectorClass == target.getClass() && values.getClass() == valueClass) {
             return positionsCheckNode.isSupported(positions) && isValueLengthGreaterThanOne(values) == isValueGt1;
         }
         return false;
     }
 
-    public Object apply(Object originalVector, Object[] originalPositions, Object originalValues) {
+    public Object apply(RAbstractVector originalVector, Object[] originalPositions, Object originalValues) {
         if (error != null) {
             CompilerDirectives.transferToInterpreter();
             error.run();
         }
-        final Object[] positions = filterPositions(originalPositions);
+        Object[] positions = filterPositions(originalPositions);
         assert isSupported(originalVector, positions, originalValues);
 
-        Object castVector = vectorClass.cast(originalVector);
+        RAbstractVector vector = vectorClass.cast(originalVector);
         Object castValue = valueClass.cast(originalValues);
 
-        if (vectorType == RType.Environment) {
-            return doEnvironment((REnvironment) castVector, positions, castValue);
-        } else if (vectorType == RType.S4Object) {
-            return doS4Object((RS4Object) castVector, positions, castValue);
-        }
-
-        Object value;
+        RAbstractContainer value;
         if (valueType == RType.Null) {
-            if (vectorType == RType.Null) {
-                // we cast Null to Logical, but in the end it will fold and return Null
-                value = RType.Logical.getEmpty();
-            } else if (castType == RType.List) {
+            if (castType == RType.List) {
                 value = RDataFactory.createList(new Object[]{DELETE_MARKER});
             } else {
                 value = castType.getEmpty();
             }
         } else {
-            value = castValue;
+            if ((castType == RType.List || castType == RType.Expression) && mode.isSubscript() && !isDeleteElements() && !(castValue instanceof RScalarVector)) {
+                // wrap into a list when
+                value = RScalarList.valueOf(castValue);
+            } else {
+                value = (RAbstractContainer) castValue;
+            }
         }
 
-        int appliedValueLength;
-        if (value instanceof RAbstractContainer) {
-            appliedValueLength = valueLengthProfile.profile(((RAbstractContainer) value).getLength());
-        } else {
-            appliedValueLength = 1;
-        }
+        int appliedValueLength = valueLengthProfile.profile(value.getLength());
 
         int valueLength;
         if (this.numberOfDimensions > 1 && isDeleteElements()) {
@@ -187,39 +203,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
             valueLength = appliedValueLength;
         }
 
-        if (vectorType == RType.Null) {
-            if (valueLength == 0) {
-                return RNull.instance;
-            }
-        }
-
-        /*
-         * Unfortunately special behavior for some RTypes are necessary. We should aim for getting
-         * rid of them as much as possible in the future. N.B.: because of this 'unwrapping' any
-         * return should call wrapResult(vector, repType) to do the reverse where necessary.
-         */
-        RAbstractVector vector;
-        RLanguage.RepType repType = RLanguage.RepType.UNKNOWN;
-        switch (vectorType) {
-            case Null:
-                vector = castType.getEmpty();
-                break;
-            case PairList:
-                vector = ((RPairList) castVector).toRList();
-                break;
-            case Language:
-                repType = RContext.getRRuntimeASTAccess().getRepType((RLanguage) castVector);
-                vector = RContext.getRRuntimeASTAccess().asList((RLanguage) castVector);
-                DynamicObject attrs = ((RLanguage) castVector).getAttributes();
-                if (rlanguageAttributesProfile.profile(attrs != null && !attrs.isEmpty())) {
-                    vector.initAttributes(RAttributesLayout.copy(attrs));
-                }
-                break;
-            default:
-                vector = (RAbstractVector) castVector;
-                break;
-        }
-
         int vectorLength = targetLengthProfile.profile(vector.getLength());
         int[] vectorDimensions;
         if (numberOfDimensions == 1) {
@@ -238,29 +221,30 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
 
         int replacementLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles);
         if (emptyReplacementProfile.profile(replacementLength == 0)) {
-            /* Nothing to modify */
-            if (vectorType == RType.Language || vectorType == RType.Expression) {
-                return originalVector;
-            } else {
-                return vector.materialize();
-            }
+            // Nothing to modify
+            return vector;
         }
 
         if (valueLengthOneProfile.profile(valueLength != 1)) {
             verifyValueLength(positionProfiles, valueLength);
         }
-
-        if (!isList() && value instanceof RAbstractVector) {
-            value = ((RAbstractVector) value).castSafe(castType, valueIsNA, false);
+        if (vector instanceof RShareable) {
+            RShareable shareable = (RShareable) vector;
+            // TODO find out if we need to copy always in the recursive case
+            if (recursive || sharedConditionProfile.execute(shareable.isShared()) || valueEqualsVectorProfile.profile(vector == value)) {
+                shareable = (RShareable) vector.copy();
+                vector = (RAbstractVector) shareable;
+                assert shareable.isTemporary();
+            }
         }
-
-        vector = share(vector, value);
+        vector = sharedClassProfile.profile(vector);
+        CompilerAsserts.partialEvaluationConstant(vector.getClass());
 
         int maxOutOfBounds = positionsCheckNode.getMaxOutOfBounds(positionProfiles);
         if (maxOutOfBounds > vectorLength) {
             resizeProfile.enter();
             if (isDeleteElements() && mode.isSubscript()) {
-                return wrapResult(vector, repType);
+                return vector;
             }
             vector = resizeVector(vector, maxOutOfBounds);
         } else {
@@ -297,17 +281,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
             }
         }
 
-        if (value instanceof RAbstractContainer) {
-            writeVectorNode.enableValueNACheck((RAbstractContainer) value);
-        }
-
-        writeVectorNode.apply(vector, vectorLength, positions, value, appliedValueLength, vectorDimensions);
-        boolean complete = vector.isComplete();
-        if (completeVectorProfile.profile(complete)) {
-            if (!writeVectorNode.neverSeenNAInValue()) {
-                vector.setComplete(false);
-            }
-        }
+        writeVectorNode.execute(vector, positions, value, vectorDimensions);
 
         RBaseNode.reportWork(this, replacementLength);
 
@@ -320,48 +294,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
             updateVectorWithPositionNames(vector, positions);
         }
 
-        return wrapResult(vector, repType);
-    }
-
-    private Object wrapResult(RAbstractVector vector, RLanguage.RepType repType) {
-        switch (vectorType) {
-            case Language:
-                return RContext.getRRuntimeASTAccess().createLanguageFromList((RList) vector, repType);
-            default:
-                return vector;
-        }
-    }
-
-    private void verifyCastType(RType compatibleType) {
-        if (error == null && compatibleType == null && (vectorType.isNull() || vectorType.isVector())) {
-            Message message;
-            if (mode.isSubset()) {
-                message = RError.Message.SUBASSIGN_TYPE_FIX;
-            } else {
-                if (vectorType == RType.List) {
-                    message = RError.Message.SUBSCRIPT_TYPES;
-                } else {
-                    message = RError.Message.SUBASSIGN_TYPE_FIX;
-                }
-            }
-            error = () -> {
-                throw error(message, valueType.getName(), vectorType.getName(), false);
-            };
-        }
-    }
-
-    private CastNode createCastVectorNode() {
-        if (castType == vectorType || castType == null || castType == RType.Null) {
-            return null;
-        }
-        /*
-         * All casts except list casts preserve dimension names.
-         */
-        if (castType == RType.List) {
-            return CastListNodeGen.create(true, false, true);
-        } else {
-            return CastTypeNode.createCast(castType, true, true, true, false);
-        }
+        return vector;
     }
 
     private boolean isDeleteElements() {
@@ -372,54 +305,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         return castType == RType.List;
     }
 
-    private RType resolveCastVectorType() {
-        final RType vector;
-        // convert type for list like values
-        switch (this.vectorType) {
-            case Language:
-            case Expression:
-            case PairList:
-                vector = RType.List;
-                break;
-            case Environment:
-                vector = RType.List;
-                break;
-            default:
-                vector = this.vectorType;
-                break;
-        }
-
-        RType value = this.valueType;
-
-        if (vector == RType.List && mode.isSubscript()) {
-            if (value.isNull() && numberOfDimensions > 1) {
-                return null;
-            } else {
-                return vector;
-            }
-        } else if (vector.isVector() && value.isVector()) {
-            if (vector != value) {
-                if (vector == RType.List || value == RType.List) {
-                    return RType.List;
-                }
-                if (vector == RType.Raw || value == RType.Raw) {
-                    return null;
-                }
-            }
-            return RType.maxPrecedence(value, vector);
-        } else if (vector.isNull() || value.isNull()) {
-            if (!value.isNull()) {
-                return (mode == ElementAccessMode.FIELD_SUBSCRIPT || (mode == ElementAccessMode.SUBSCRIPT && isValueGt1)) ? RType.List : value;
-            }
-            if (mode.isSubscript() && numberOfDimensions > 1) {
-                return null;
-            }
-            return vector;
-        } else {
-            return null;
-        }
-    }
-
     private void verifyValueLength(PositionProfile[] positionProfiles, int valueLength) {
         if (mode.isSubscript()) {
             if (!isList()) {
@@ -469,34 +354,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         }
     }
 
-    private Object doEnvironment(REnvironment env, Object[] positions, Object originalValues) {
-        if (mode.isSubset()) {
-            throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
-        }
-
-        String positionString = tryCastSingleString(positionsCheckNode, positions);
-        if (positionString == null) {
-            throw error(RError.Message.WRONG_ARGS_SUBSET_ENV);
-        }
-
-        try {
-            Object value = originalValues;
-            if (value instanceof RScalarVector) {
-                value = ((RScalarVector) value).getDataAtAsObject(0);
-            }
-            env.put(positionString, value);
-        } catch (PutException ex) {
-            throw error(ex);
-        }
-        return env;
-    }
-
-    @Child private ReplaceS4ObjectNode replaceS4ObjectNode;
-
-    private Object doS4Object(RS4Object obj, Object[] positions, Object originalValues) {
-        return replaceS4ObjectNode.execute(obj, positions, originalValues);
-    }
-
     @NodeInfo(cost = NONE)
     public abstract static class ValueProfileNode extends Node {
 
@@ -520,28 +377,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
 
     private final ConditionProfile valueEqualsVectorProfile = ConditionProfile.createBinaryProfile();
 
-    /*
-     * TODO (chumer) share code between {@link #share(RAbstractVector)} and {@link
-     * #copyValueOnAssignment(RAbstractContainer)}
-     */
-    private RAbstractVector share(RAbstractVector vector, Object value) {
-        RAbstractVector returnVector = vector;
-        if (returnVector instanceof RShareable) {
-            RShareable shareable = (RShareable) returnVector;
-            // TODO find out if we need to copy always in the recursive case
-            if (recursive || sharedConditionProfile.execute(shareable.isShared()) || valueEqualsVectorProfile.profile(vector == value)) {
-                shareable = (RShareable) returnVector.copy();
-                returnVector = (RAbstractVector) shareable;
-                assert shareable.isTemporary();
-            }
-        }
-        returnVector = sharedClassProfile.profile(returnVector);
-
-        CompilerAsserts.partialEvaluationConstant(returnVector.getClass());
-
-        return returnVector;
-    }
-
     // TODO (chumer) this is way to complicated at the moment
     // not yet worth compiling. we should introduce some nodes for this
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
index 595c1452ef73f1fe6d3acff813ca57342c6b85fa..d9346356426458ac2c92f71dc664a4a5c7780476 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
@@ -22,9 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.access.vector;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -56,9 +54,6 @@ abstract class CachedVectorNode extends RBaseNode {
     protected final int numberOfDimensions;
     private final int filteredPositionsLength;
 
-    // if this is non-null, the node needs to throw the error whenever it is executed
-    @CompilationFinal protected Runnable error;
-
     @Child private GetDimAttributeNode getDimNode = GetDimAttributeNode.create();
 
     CachedVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, boolean recursive) {
@@ -71,11 +66,6 @@ abstract class CachedVectorNode extends RBaseNode {
         } else {
             this.numberOfDimensions = filteredPositionsLength;
         }
-        if (!isSubsetable(vectorType)) {
-            error = () -> {
-                throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, vectorType.getName());
-            };
-        }
     }
 
     private static int initializeFilteredPositionsCount(Object[] positions) {
@@ -136,23 +126,6 @@ abstract class CachedVectorNode extends RBaseNode {
         }
     }
 
-    private static boolean isSubsetable(RType type) {
-        if (type.isVector()) {
-            return true;
-        }
-        switch (type) {
-            case Null:
-            case Language:
-            case PairList:
-            case Environment:
-            case Expression:
-            case S4Object:
-                return true;
-            default:
-                return false;
-        }
-    }
-
     protected final int[] loadVectorDimensions(RAbstractContainer vector) {
         // N.B. (stepan) this method used to be instance method and have special handling for
         // RDataFrame, which was removed and any test case, which would require this special
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java
deleted file mode 100644
index 70df3131589553daab5ed53561f33f4a2fb20d76..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-package com.oracle.truffle.r.nodes.access.vector;
-
-import static com.oracle.truffle.r.runtime.RError.Message.OP_NOT_DEFINED_FOR_S4_CLASS;
-
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
-import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-
-public class ExtractS4ObjectNode extends Node {
-    @Child private GetS4DataSlot getS4DataSlotNode = GetS4DataSlotNodeGen.create(RType.Environment);
-    @Child private ExtractVectorNode extract;
-    private final boolean exact;
-    private final boolean dropDimensions;
-
-    public ExtractS4ObjectNode(ElementAccessMode accessMode, boolean exact, boolean dropDimensions) {
-        this.extract = ExtractVectorNode.create(accessMode, true);
-        this.exact = exact;
-        this.dropDimensions = dropDimensions;
-    }
-
-    public Object execute(RS4Object obj, Object[] positions) {
-        RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj);
-        if (dataSlot == RNull.instance) {
-            throw RError.error(RError.SHOW_CALLER, OP_NOT_DEFINED_FOR_S4_CLASS, "$");
-        }
-        return extract.execute(dataSlot, positions, createLogical(exact), createLogical(dropDimensions));
-    }
-
-    private static RAbstractLogicalVector createLogical(boolean b) {
-        return RDataFactory.createLogicalVectorFromScalar(b);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
index c3c7bcbd3519ee7244320a1c1d471a046e007950..54338db024c2be03c583839f28bf8b77641bb133 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
@@ -23,8 +23,10 @@
 package com.oracle.truffle.r.nodes.access.vector;
 
 import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.interop.ForeignAccess;
@@ -34,20 +36,31 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNodeGen.ExtractSingleNameNodeGen;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
+import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
 import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.FirstStringNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RLogical;
 import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess.RandomIterator;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.interop.Foreign2R;
 import com.oracle.truffle.r.runtime.interop.ForeignArray2R;
 import com.oracle.truffle.r.runtime.interop.ForeignArray2R.ForeignArrayData;
@@ -58,7 +71,7 @@ public abstract class ExtractVectorNode extends RBaseNode {
 
     protected static final int CACHE_LIMIT = 5;
 
-    private final ElementAccessMode mode;
+    protected final ElementAccessMode mode;
     private final boolean recursive;
     private final boolean ignoreRecursive;
 
@@ -124,23 +137,92 @@ public abstract class ExtractVectorNode extends RBaseNode {
     }
 
     @Specialization(limit = "CACHE_LIMIT", guards = {"!isForeignObject(vector)", "cached != null", "cached.isSupported(vector, positions, exact, dropDimensions)"})
-    protected Object doExtractDefaultCached(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+    protected Object doExtractDefaultCached(RAbstractContainer vector, Object[] positions, Object exact, Object dropDimensions,  //
                     @Cached("createDefaultCache(getThis(), vector, positions, exact, dropDimensions)") CachedExtractVectorNode cached) {
         assert !isRecursiveSubscript(vector, positions);
         return cached.apply(vector, positions, null, exact, dropDimensions);
     }
 
-    protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
-        return new CachedExtractVectorNode(node.getMode(), (RTypedValue) vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive);
+    protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, RAbstractContainer vector, Object[] positions, Object exact, Object dropDimensions) {
+        assert !(vector instanceof REnvironment);
+        return new CachedExtractVectorNode(node.getMode(), vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive);
     }
 
-    @Specialization(replaces = "doExtractDefaultCached", guards = "!isForeignObject(vector)")
+    @Specialization(replaces = "doExtractDefaultCached", guards = {"!isForeignObject(vector)"})
     @TruffleBoundary
-    protected Object doExtractDefaultGeneric(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+    protected Object doExtractDefaultGeneric(RAbstractContainer vector, Object[] positions, Object exact, Object dropDimensions,  //
                     @Cached("new(createDefaultCache(getThis(), vector, positions, exact, dropDimensions))") GenericVectorExtractNode generic) {
         return generic.get(this, vector, positions, exact, dropDimensions).apply(vector, positions, null, exact, dropDimensions);
     }
 
+    @Specialization
+    protected Object doExtractEnvironment(REnvironment env, Object[] positions, @SuppressWarnings("unused") Object exact, @SuppressWarnings("unused") Object dropDimensions,
+                    @Cached("createExtractName()") ExtractSingleName extractName,
+                    @Cached("new()") PromiseCheckHelperNode promiseHelper) {
+        if (mode.isSubset()) {
+            throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
+        }
+        String name = positions.length == 1 ? extractName.execute(positions[0]) : null;
+        if (name != null) {
+            Object obj = env.get(name);
+            return obj == null ? RNull.instance : promiseHelper.checkEvaluate(null, obj);
+        }
+        throw error(RError.Message.WRONG_ARGS_SUBSET_ENV);
+    }
+
+    @Specialization
+    protected Object doExtractS4Object(RS4Object obj, Object[] positions, Object exact, Object dropDimensions,
+                    @Cached("createEnvironment()") GetS4DataSlot getS4DataSlotNode,
+                    @Cached("create(mode, True)") ExtractVectorNode recursiveExtract) {
+        RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj);
+        if (dataSlot == RNull.instance) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.OP_NOT_DEFINED_FOR_S4_CLASS, "$");
+        }
+        return recursiveExtract.execute(dataSlot, positions, exact, dropDimensions);
+    }
+
+    abstract static class ExtractSingleName extends Node {
+
+        public abstract String execute(Object value);
+
+        public static ExtractSingleName createExtractName() {
+            return ExtractSingleNameNodeGen.create();
+        }
+
+        @Specialization
+        protected static String extract(String value) {
+            return value;
+        }
+
+        @Specialization(guards = "access.supports(value)")
+        protected static String extractCached(RAbstractStringVector value,
+                        @Cached("value.access()") VectorAccess access) {
+            try (RandomIterator iter = access.randomAccess(value)) {
+                if (access.getLength(iter) == 1) {
+                    return access.getString(iter, 0);
+                }
+            }
+            return null;
+        }
+
+        @Specialization(replaces = "extractCached")
+        @TruffleBoundary
+        protected static String extractGeneric(RAbstractStringVector value) {
+            return extractCached(value, value.slowPathAccess());
+        }
+
+        @Fallback
+        protected static String extractFallback(@SuppressWarnings("unused") Object value) {
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected Object doExtractRNull(RNull vector, Object[] positions, Object exact, Object dropDimensions) {
+        return RNull.instance;
+    }
+
     // TODO hack until Truffle-DSL supports this.
     protected ExtractVectorNode getThis() {
         return this;
@@ -154,7 +236,7 @@ public abstract class ExtractVectorNode extends RBaseNode {
             this.cached = insert(cachedOperation);
         }
 
-        public CachedExtractVectorNode get(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
+        public CachedExtractVectorNode get(ExtractVectorNode node, RAbstractContainer vector, Object[] positions, Object exact, Object dropDimensions) {
             CompilerAsserts.neverPartOfCompilation();
             if (!cached.isSupported(vector, positions, exact, dropDimensions)) {
                 cached = cached.replace(createDefaultCache(node, vector, positions, exact, dropDimensions));
@@ -251,4 +333,11 @@ public abstract class ExtractVectorNode extends RBaseNode {
     private static TruffleObject toJavaClass(TruffleObject obj) {
         return JavaInterop.toJavaClass(obj);
     }
+
+    @SuppressWarnings("unused")
+    @Fallback
+    protected Object access(Object object, Object[] positions, Object exact, Object dropDimensions) {
+        CompilerDirectives.transferToInterpreter();
+        throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, Predef.typeName().apply(object));
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java
deleted file mode 100644
index afc04f304e9af843de7d835e03a384c1e7808832..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-package com.oracle.truffle.r.nodes.access.vector;
-
-import static com.oracle.truffle.r.runtime.RError.Message.NO_METHOD_ASSIGNING_SUBSET_S4;
-
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
-import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-
-public class ReplaceS4ObjectNode extends Node {
-    @Child private GetS4DataSlot getS4DataSlotNode = GetS4DataSlotNodeGen.create(RType.Environment);
-    @Child private ReplaceVectorNode replaceVectorNode;
-
-    public ReplaceS4ObjectNode(ElementAccessMode mode, boolean ignoreRecursive) {
-        replaceVectorNode = ReplaceVectorNode.create(mode, ignoreRecursive);
-    }
-
-    public Object execute(RS4Object obj, Object[] positions, Object values) {
-        RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj);
-        if (dataSlot == RNull.instance) {
-            throw RError.error(RError.SHOW_CALLER, NO_METHOD_ASSIGNING_SUBSET_S4);
-        }
-        // No need to update the data slot, the value is env and they have reference semantics.
-        replaceVectorNode.execute(dataSlot, positions, values);
-        return obj;
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java
index f295281ded9fb2cd67094d7e21c4367b724dcc31..9d78ec148542ba97349f5f5ee30a1ae1b22b24a3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java
@@ -23,31 +23,49 @@
 package com.oracle.truffle.r.nodes.access.vector;
 
 import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.KeyInfo;
-import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode.ExtractSingleName;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
 import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.FirstStringNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributesLayout;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RLanguage.RepType;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RS4Object;
+import com.oracle.truffle.r.runtime.data.RScalarVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.interop.R2Foreign;
-import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
@@ -58,9 +76,9 @@ public abstract class ReplaceVectorNode extends RBaseNode {
 
     protected static final int CACHE_LIMIT = 5;
 
-    private final ElementAccessMode mode;
+    protected final ElementAccessMode mode;
     private final boolean recursive;
-    private final boolean ignoreRecursive;
+    protected final boolean ignoreRecursive;
 
     @Child private BoxPrimitiveNode boxVector = BoxPrimitiveNode.create();
     @Child private BoxPrimitiveNode boxValue = BoxPrimitiveNode.create();
@@ -81,23 +99,177 @@ public abstract class ReplaceVectorNode extends RBaseNode {
         return ReplaceVectorNodeGen.create(mode, false, ignoreRecursive);
     }
 
-    static ReplaceVectorNode createRecursive(ElementAccessMode mode) {
+    protected static ReplaceVectorNode createRecursive(ElementAccessMode mode) {
         return ReplaceVectorNodeGen.create(mode, true, false);
     }
 
-    protected Node createForeignWrite(Object[] positions) {
-        if (positions.length != 1) {
-            throw error(RError.Message.GENERIC, "Invalid number positions for foreign access.");
+    private boolean isRecursiveSubscript(Object vector, Object[] positions) {
+        return !recursive && !ignoreRecursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
+    }
+
+    protected RecursiveReplaceSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
+        if (isRecursiveSubscript(vector, positions)) {
+            return RecursiveReplaceSubscriptNode.create((RAbstractListVector) vector, positions[0]);
         }
-        return Message.WRITE.createNode();
+        return null;
     }
 
-    protected FirstStringNode createFirstString() {
-        return FirstStringNode.createWithError(RError.Message.GENERIC, "Cannot corce position to character for foreign access.");
+    @Specialization(limit = "CACHE_LIMIT", guards = {"!isForeignObject(vector)", "cached != null", "cached.isSupported(vector, positions)"})
+    protected Object doRecursive(RAbstractListVector vector, Object[] positions, Object value,  //
+                    @Cached("createRecursiveCache(vector, positions)") RecursiveReplaceSubscriptNode cached) {
+        return cached.apply(vector, positions, value);
+    }
+
+    protected CachedReplaceVectorNode createDefaultCached(RAbstractVector vector, Object[] positions, Object value) {
+        if (vector instanceof RAbstractListVector && isRecursiveSubscript(vector, positions)) {
+            return null;
+        }
+        return new CachedReplaceVectorNode(mode, vector, positions, value.getClass(), RRuntime.isForeignObject(value) ? RType.TruffleObject : ((RTypedValue) value).getRType(), true,
+                        recursive, CachedReplaceVectorNode.isValueLengthGreaterThanOne(value));
+    }
+
+    @Specialization(limit = "CACHE_LIMIT", guards = {"!isForeignObject(vector)", "cached != null", "cached.isSupported(vector, positions, value)"})
+    protected Object doReplaceCached(RAbstractVector vector, Object[] positions, Object value,  //
+                    @Cached("createDefaultCached(vector, positions, value)") CachedReplaceVectorNode cached) {
+        assert !isRecursiveSubscript(vector, positions);
+        return cached.apply(vector, positions, value);
+    }
+
+    @Specialization
+    @TruffleBoundary
+    protected Object doReplaceEnvironment(REnvironment env, Object[] positions, Object value,
+                    @Cached("createExtractName()") ExtractSingleName extractName) {
+        if (mode.isSubset()) {
+            throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
+        }
+        String name = positions.length == 1 ? extractName.execute(positions[0]) : null;
+        if (name != null) {
+            try {
+                env.put(name, value instanceof RScalarVector ? ((RScalarVector) value).getDataAtAsObject(0) : value);
+            } catch (PutException ex) {
+                throw error(ex);
+            }
+        }
+        return env;
+    }
+
+    @Specialization
+    @TruffleBoundary
+    protected Object doReplaceS4Object(RS4Object obj, Object[] positions, Object value,
+                    @Cached("createEnvironment()") GetS4DataSlot getS4DataSlotNode,
+                    @Cached("create(mode, ignoreRecursive)") ReplaceVectorNode recursiveReplace) {
+        RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj);
+        if (dataSlot == RNull.instance) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.NO_METHOD_ASSIGNING_SUBSET_S4);
+        }
+        // No need to update the data slot, the value is env and they have reference semantics.
+        recursiveReplace.execute(dataSlot, positions, value);
+        return obj;
+    }
+
+    protected ReplaceVectorNode createForContainerTypes() {
+        return ReplaceVectorNodeGen.create(mode, false, false);
+    }
+
+    @Specialization
+    protected Object doReplacementNull(@SuppressWarnings("unused") RNull vector, Object[] positions, Object value,
+                    @Cached("createForContainerTypes()") ReplaceVectorNode replace) {
+        /*
+         * Replacing inside a variable containing NULL is quite inconsistent, we try to emulate the
+         * behavior as good as possible.
+         */
+        if (value == RNull.instance) {
+            return RNull.instance;
+        }
+        RType type;
+        switch (mode) {
+            case FIELD_SUBSCRIPT:
+                type = RType.List;
+                break;
+            case SUBSCRIPT:
+                if (value instanceof RAbstractAtomicVector && ((RAbstractAtomicVector) value).getLength() == 1) {
+                    type = ((RAbstractAtomicVector) value).getRType();
+                } else {
+                    type = RType.List;
+                }
+                break;
+            case SUBSET:
+                if (value instanceof RAbstractAtomicVector) {
+                    if (((RAbstractAtomicVector) value).getLength() == 0) {
+                        return RNull.instance;
+                    } else {
+                        type = ((RAbstractAtomicVector) value).getRType();
+                    }
+                } else {
+                    type = RType.List;
+                }
+                break;
+            default:
+                throw RInternalError.shouldNotReachHere();
+        }
+        return replace.execute(type.getEmpty(), positions, value);
+    }
+
+    @Specialization
+    @TruffleBoundary
+    protected Object doReplacementLanguage(RLanguage vector, Object[] positions, Object value,
+                    @Cached("createForContainerTypes()") ReplaceVectorNode replace) {
+        RepType repType = RContext.getRRuntimeASTAccess().getRepType(vector);
+        RList result = RContext.getRRuntimeASTAccess().asList(vector);
+        DynamicObject attrs = vector.getAttributes();
+        if (attrs != null && !attrs.isEmpty()) {
+            result.initAttributes(RAttributesLayout.copy(attrs));
+        }
+        result = (RList) replace.execute(result, positions, value);
+        return RContext.getRRuntimeASTAccess().createLanguageFromList(result, repType);
+    }
+
+    @Specialization
+    @TruffleBoundary
+    protected Object doReplacementPairList(RPairList vector, Object[] positions, Object value,
+                    @Cached("createForContainerTypes()") ReplaceVectorNode replace) {
+        return replace.execute(vector.toRList(), positions, value);
+    }
+
+    protected static GenericVectorReplaceNode createGeneric() {
+        return new GenericVectorReplaceNode();
+    }
+
+    @Specialization(replaces = "doReplaceCached", guards = "!isForeignObject(vector)")
+    @TruffleBoundary
+    protected Object doReplaceDefaultGeneric(RAbstractVector vector, Object[] positions, Object value,  //
+                    @Cached("createGeneric()") GenericVectorReplaceNode generic) {
+        if (vector instanceof RAbstractListVector && isRecursiveSubscript(vector, positions)) {
+            return generic.getRecursive(this, vector, positions).apply(vector, positions, value);
+        } else {
+            return generic.get(this, vector, positions, value).apply(vector, positions, value);
+        }
+    }
+
+    protected static final class GenericVectorReplaceNode extends TruffleBoundaryNode {
+
+        @Child private RecursiveReplaceSubscriptNode cachedRecursive;
+        @Child private CachedReplaceVectorNode cached;
+
+        private RecursiveReplaceSubscriptNode getRecursive(ReplaceVectorNode node, Object vector, Object[] positions) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (cachedRecursive == null || !cachedRecursive.isSupported(vector, positions)) {
+                cachedRecursive = insert(node.createRecursiveCache(vector, positions));
+            }
+            return cachedRecursive;
+        }
+
+        private CachedReplaceVectorNode get(ReplaceVectorNode node, RAbstractVector vector, Object[] positions, Object value) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (cached == null || !cached.isSupported(vector, positions, value)) {
+                cached = insert(node.createDefaultCached(vector, positions, value));
+            }
+            return cached;
+        }
     }
 
-    protected R2Foreign createR2Foreign() {
-        return R2ForeignNodeGen.create();
+    protected FirstStringNode createFirstString() {
+        return FirstStringNode.createWithError(RError.Message.GENERIC, "Cannot corce position to character for foreign access.");
     }
 
     @Specialization(guards = {"isForeignObject(object)", "positions.length == cachedLength"})
@@ -109,7 +281,7 @@ public abstract class ReplaceVectorNode extends RBaseNode {
                     @SuppressWarnings("unused") @Cached("positions.length") int cachedLength,
                     @Cached("create()") CastStringNode castNode,
                     @Cached("createFirstString()") FirstStringNode firstString,
-                    @Cached("createR2Foreign()") R2Foreign r2Foreign) {
+                    @Cached("create()") R2Foreign r2Foreign) {
         Object writtenValue = value;
         try {
             TruffleObject result = object;
@@ -163,65 +335,10 @@ public abstract class ReplaceVectorNode extends RBaseNode {
         return JavaInterop.toJavaClass(obj);
     }
 
-    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions)"})
-    protected Object doRecursive(RAbstractListVector vector, Object[] positions, Object value,  //
-                    @Cached("createRecursiveCache(vector, positions)") RecursiveReplaceSubscriptNode cached) {
-        return cached.apply(vector, positions, value);
-    }
-
-    protected RecursiveReplaceSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
-        if (isRecursiveSubscript(vector, positions)) {
-            return RecursiveReplaceSubscriptNode.create((RAbstractListVector) vector, positions[0]);
-        }
-        return null;
-    }
-
-    private boolean isRecursiveSubscript(Object vector, Object[] positions) {
-        return !recursive && !ignoreRecursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
-    }
-
-    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, value)"})
-    protected Object doReplaceCached(Object vector, Object[] positions, Object value,  //
-                    @Cached("createDefaultCached(getThis(), vector, positions, value)") CachedReplaceVectorNode cached) {
-        assert !isRecursiveSubscript(vector, positions);
-        return cached.apply(vector, positions, value);
-    }
-
-    protected static CachedReplaceVectorNode createDefaultCached(ReplaceVectorNode node, Object vector, Object[] positions, Object value) {
-        return new CachedReplaceVectorNode(node.mode, (RTypedValue) vector, positions, value.getClass(), RRuntime.isForeignObject(value) ? RType.TruffleObject : ((RTypedValue) value).getRType(), true,
-                        node.recursive, node.ignoreRecursive, CachedReplaceVectorNode.isValueLengthGreaterThanOne(value));
-    }
-
-    public ElementAccessMode getMode() {
-        return mode;
-    }
-
-    @Specialization(replaces = "doReplaceCached")
-    @TruffleBoundary
-    protected Object doReplaceDefaultGeneric(Object vector, Object[] positions, Object value,  //
-                    @Cached("new(createDefaultCached(getThis(), vector, positions, value))") GenericVectorReplaceNode generic) {
-        return generic.get(this, vector, positions, value).apply(vector, positions, value);
-    }
-
-    // TODO hack until Truffle-DSL supports this.
-    protected ReplaceVectorNode getThis() {
-        return this;
-    }
-
-    protected static final class GenericVectorReplaceNode extends TruffleBoundaryNode {
-
-        @Child private CachedReplaceVectorNode cached;
-
-        public GenericVectorReplaceNode(CachedReplaceVectorNode cachedOperation) {
-            this.cached = insert(cachedOperation);
-        }
-
-        private CachedReplaceVectorNode get(ReplaceVectorNode node, Object vector, Object[] positions, Object value) {
-            CompilerAsserts.neverPartOfCompilation();
-            if (!cached.isSupported(vector, positions, value)) {
-                cached = cached.replace(createDefaultCached(node, vector, positions, value));
-            }
-            return cached;
-        }
+    @SuppressWarnings("unused")
+    @Fallback
+    protected Object access(Object object, Object[] positions, Object value) {
+        CompilerDirectives.transferToInterpreter();
+        throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, Predef.typeName().apply(object));
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java
index aecab8b403bb429c8b83a54b1f5f561f191b12bd..28666c2bfe310626240eaa6f4f3086ec88b1b0ab 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.access.vector;
 
+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.api.nodes.Node;
@@ -35,33 +36,84 @@ import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode;
 import com.oracle.truffle.r.nodes.profile.AlwaysOnBranchProfile;
 import com.oracle.truffle.r.nodes.profile.IntValueProfile;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RScalarVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractListBaseVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess.RandomIterator;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
+abstract class WriteIndexedVectorNode extends Node {
+
+    private final RType vectorType;
+    private final int totalDimensions;
+    private final int dimensionIndex;
+    private final boolean positionAppliesToRight;
+    private final boolean skipNA;
+    private final boolean isReplace;
+
+    private final ConditionProfile completeVectorProfile = ConditionProfile.createBinaryProfile();
+
+    protected WriteIndexedVectorNode(RType vectorType, int totalDimensions, int dimensionIndex, boolean positionAppliesToRight, boolean skipNA, boolean isReplace) {
+        this.vectorType = vectorType;
+        this.totalDimensions = totalDimensions;
+        this.dimensionIndex = dimensionIndex;
+        this.positionAppliesToRight = positionAppliesToRight;
+        this.skipNA = skipNA;
+        this.isReplace = isReplace;
+    }
+
+    public static WriteIndexedVectorNode create(RType vectorType, int totalDimensions, boolean positionAppliesToValue, boolean skipNA, boolean isReplace) {
+        return WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, totalDimensions - 1, positionAppliesToValue, skipNA, isReplace);
+    }
+
+    protected abstract void execute(RAbstractVector left, Object[] positions, RAbstractContainer right, int[] positionTargetDimensions);
+
+    protected WriteIndexedVectorAccessNode createWrite() {
+        return WriteIndexedVectorAccessNodeGen.create(vectorType, totalDimensions, dimensionIndex, positionAppliesToRight, skipNA, isReplace);
+    }
+
+    @Specialization(guards = {"leftAccess.supports(left)", "rightAccess.supports(right)"})
+    protected void write(RAbstractVector left, Object[] positions, RAbstractContainer right, int[] positionTargetDimensions,
+                    @Cached("left.access()") VectorAccess leftAccess,
+                    @Cached("right.access()") VectorAccess rightAccess,
+                    @Cached("createWrite()") WriteIndexedVectorAccessNode write) {
+        try (RandomIterator leftIter = leftAccess.randomAccess(left); RandomIterator rightIter = rightAccess.randomAccess(right)) {
+            write.apply(leftIter, leftAccess, positions, rightIter, rightAccess, right, positionTargetDimensions);
+
+            if (completeVectorProfile.profile(left.isComplete())) {
+                if (!(leftAccess.na.neverSeenNA() && rightAccess.na.neverSeenNA())) {
+                    left.setComplete(false);
+                }
+            }
+        }
+    }
+
+    @Specialization(replaces = "write")
+    @TruffleBoundary
+    protected void writeGeneric(RAbstractVector left, Object[] positions, RAbstractContainer right, int[] positionTargetDimensions,
+                    @Cached("createWrite()") WriteIndexedVectorAccessNode write) {
+        VectorAccess leftAccess = left.slowPathAccess();
+        VectorAccess rightAccess = right.slowPathAccess();
+        try (RandomIterator leftIter = leftAccess.randomAccess(left); RandomIterator rightIter = rightAccess.randomAccess(right)) {
+            write.apply(leftIter, leftAccess, positions, rightIter, rightAccess, right, positionTargetDimensions);
+        }
+    }
+}
+
 /**
  * Primitive indexed N-dimensional vector write node. It can be used for vector replaces and
  * extracts. The only difference is that replace indexes the left vector and extract indexes the
  * right vector. The index direction is indicated with the boolean flag
  * {@link #positionsApplyToRight}.
  */
-abstract class WriteIndexedVectorNode extends Node {
+abstract class WriteIndexedVectorAccessNode extends Node {
 
     private final int dimensionIndex;
     private final int totalDimensions;
@@ -84,57 +136,39 @@ abstract class WriteIndexedVectorNode extends Node {
     private final NACheck positionNACheck = NACheck.create();
     private final ConditionProfile resetIndexProfile = ConditionProfile.createBinaryProfile();
 
-    @Child private WriteIndexedScalarNode<RAbstractVector, Object> scalarNode;
-    @Child private WriteIndexedVectorNode innerVectorNode;
+    @Child private WriteIndexedVectorAccessNode innerVectorNode;
+
+    @Child private UpdateShareableChildValueNode updateStateOfListElement;
+    @Child private ShareObjectNode shareObjectNode;
 
-    @SuppressWarnings("unchecked")
-    protected WriteIndexedVectorNode(RType vectorType, int totalDimensions, int dimensionIndex, boolean positionAppliesToRight, boolean skipNA, boolean setListElementAsObject, boolean isReplace) {
-        this.scalarNode = (WriteIndexedScalarNode<RAbstractVector, Object>) createIndexedAction(vectorType, setListElementAsObject, isReplace);
+    private final boolean isReplace;
+    private final RType vectorType;
+
+    protected WriteIndexedVectorAccessNode(RType vectorType, int totalDimensions, int dimensionIndex, boolean positionAppliesToRight, boolean skipNA, boolean isReplace) {
+        this.vectorType = vectorType;
         this.dimensionIndex = dimensionIndex;
         this.totalDimensions = totalDimensions;
         this.positionsApplyToRight = positionAppliesToRight;
         this.skipNA = skipNA;
+        this.isReplace = isReplace;
         if (dimensionIndex > 0) {
-            innerVectorNode = WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, dimensionIndex - 1, positionAppliesToRight, skipNA, setListElementAsObject, isReplace);
-        }
-    }
-
-    public static WriteIndexedVectorNode create(RType vectorType, int totalDimensions, boolean positionAppliesToValue, boolean skipNA, boolean setListElementAsObject, boolean isReplace) {
-        return WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, totalDimensions - 1, positionAppliesToValue, skipNA, setListElementAsObject, isReplace);
-    }
-
-    public NACheck getValueNACheck() {
-        return scalarNode.valueNACheck;
-    }
-
-    public void enableValueNACheck(RAbstractContainer vector) {
-        getValueNACheck().enable(vector);
-        if (innerVectorNode != null) {
-            innerVectorNode.enableValueNACheck(vector);
+            innerVectorNode = WriteIndexedVectorAccessNodeGen.create(vectorType, totalDimensions, dimensionIndex - 1, positionAppliesToRight, skipNA, isReplace);
         }
-    }
-
-    public boolean neverSeenNAInValue() {
-        if (getValueNACheck().neverSeenNA()) {
-            if (innerVectorNode == null || innerVectorNode.neverSeenNAInValue()) {
-                return true;
+        if (vectorType == RType.List || vectorType == RType.Expression) {
+            if (!isReplace) {
+                updateStateOfListElement = UpdateShareableChildValueNode.create();
+            } else {
+                shareObjectNode = ShareObjectNode.create();
             }
         }
-        return false;
     }
 
-    public final void apply(RAbstractVector left, int leftLength,
-                    Object[] positions, Object right, int rightLength, int[] positionTargetDimensions) {
-        assert left.getLength() == leftLength;
+    public void apply(RandomIterator leftIter, VectorAccess leftAccess, Object[] positions, RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right,
+                    int[] positionTargetDimensions) {
         assert totalDimensions == positions.length : "totalDimensions must be constant per vector write node";
 
-        Object leftStore = left.getInternalStore();
-        Object rightStore = null;
-        if (right instanceof RAbstractContainer) {
-            RAbstractContainer rightContainer = (RAbstractContainer) right;
-            assert rightContainer.getLength() == rightLength;
-            rightStore = rightContainer.getInternalStore();
-        }
+        int leftLength = leftAccess.getLength(leftIter);
+        int rightLength = rightAccess.getLength(rightIter);
 
         int initialPositionOffset;
         if (positionsApplyToRight) {
@@ -151,17 +185,17 @@ abstract class WriteIndexedVectorNode extends Node {
             firstTargetDimension = dimensionValueProfile.profile(positionTargetDimensions[dimensionIndex]);
         }
 
-        applyImpl(left, leftStore, 0, leftLength, positionTargetDimensions, firstTargetDimension,
+        applyImpl(leftIter, leftAccess, 0, leftLength, positionTargetDimensions, firstTargetDimension,
                         positions, initialPositionOffset,
-                        right, rightStore, 0, rightLength, false);
+                        rightIter, rightAccess, right, 0, rightLength, false);
     }
 
     private final ConditionProfile positionMatchesTargetDimensionsProfile = ConditionProfile.createBinaryProfile();
 
     private int applyImpl(//
-                    RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
+                    RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
                     Object[] positions, int positionOffset,
-                    Object right, Object rightStore, int rightBase, int rightLength, boolean parentNA) {
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightBase, int rightLength, boolean parentNA) {
 
         Object position = positionClassProfile.profile(positions[dimensionIndex]);
 
@@ -172,9 +206,9 @@ abstract class WriteIndexedVectorNode extends Node {
         } else {
             newPositionOffset = positionOffsetProfile.profile(positionOffset / targetDimension);
         }
-        return execute(left, leftStore, leftBase, leftLength, targetDimensions, targetDimension,
+        return execute(leftIter, leftAccess, leftBase, leftLength, targetDimensions, targetDimension,
                         positions, position, newPositionOffset, positionLength,
-                        right, rightStore, rightBase, rightLength, parentNA);
+                        rightIter, rightAccess, right, rightBase, rightLength, parentNA);
     }
 
     private int getPositionLength(Object position) {
@@ -185,30 +219,30 @@ abstract class WriteIndexedVectorNode extends Node {
         }
     }
 
-    protected abstract int execute(RAbstractVector left, Object leftStore, int storeBase, int storeLength, Object targetDimensions, int targetDimension,
+    protected abstract int execute(RandomIterator leftIter, VectorAccess leftAccess, int storeBase, int storeLength, Object targetDimensions, int targetDimension,
                     Object[] positions, Object position, int positionOffset, int positionLength,
-                    Object right, Object rightStore, int valueBase, int valueLength, boolean parentNA);
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int valueBase, int valueLength, boolean parentNA);
 
     @Specialization
-    protected int doMissing(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
+    protected int doMissing(RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
                     Object[] positions, @SuppressWarnings("unused") RMissing position, int positionOffset, @SuppressWarnings("unused") int positionLength,
-                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA,
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightBase, int rightLength, boolean parentNA,
                     @Cached("createCountingProfile()") LoopConditionProfile profile) {
         int rightIndex = rightBase;
         profile.profileCounted(targetDimension);
         for (int positionValue = 0; profile.inject(positionValue < targetDimension); positionValue += 1) {
             rightIndex = applyInner(//
-                            left, leftStore, leftBase, leftLength, targetDimensions,
+                            leftIter, leftAccess, leftBase, leftLength, targetDimensions,
                             positions, positionOffset, positionValue,
-                            right, rightStore, rightLength, rightIndex, parentNA);
+                            rightIter, rightAccess, right, rightLength, rightIndex, parentNA);
         }
         return rightIndex;
     }
 
     @Specialization
-    protected int doLogicalPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
+    protected int doLogicalPosition(RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions, int targetDimension,
                     Object[] positions, RAbstractLogicalVector position, int positionOffset, int positionLength,
-                    Object right, Object rightStore, int rightBase, int rightLength, boolean parentNA,
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightBase, int rightLength, boolean parentNA,
                     @Cached("create()") BranchProfile wasTrue,
                     @Cached("create()") AlwaysOnBranchProfile outOfBounds,
                     @Cached("createCountingProfile()") LoopConditionProfile profile,
@@ -235,9 +269,9 @@ abstract class WriteIndexedVectorNode extends Node {
                         isNA = true;
                     }
                     rightIndex = applyInner(//
-                                    left, leftStore, leftBase, leftLength, targetDimensions,
+                                    leftIter, leftAccess, leftBase, leftLength, targetDimensions,
                                     positions, positionOffset, i,
-                                    right, rightStore, rightLength, rightIndex, isNA || parentNA);
+                                    rightIter, rightAccess, right, rightLength, rightIndex, isNA || parentNA);
                 }
                 positionIndex = Utils.incMod(positionIndex, positionLength, incModProfile);
             }
@@ -251,9 +285,9 @@ abstract class WriteIndexedVectorNode extends Node {
      * @throws SlowPathException
      */
     @Specialization(rewriteOn = SlowPathException.class)
-    protected int doIntegerSequencePosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension,
+    protected int doIntegerSequencePosition(RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension,
                     Object[] positions, RIntSequence position, int positionOffset, int positionLength,
-                    Object right, Object rightStore, int rightBase, int rightLength, boolean parentNA,
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightBase, int rightLength, boolean parentNA,
                     @Cached("create()") IntValueProfile startProfile,
                     @Cached("create()") IntValueProfile strideProfile,
                     @Cached("createBinaryProfile()") ConditionProfile conditionProfile,
@@ -272,27 +306,22 @@ abstract class WriteIndexedVectorNode extends Node {
         profile.profileCounted(positionLength);
         for (int positionValue = start; profile.inject(ascending ? positionValue < end : positionValue > end); positionValue += stride) {
             rightIndex = applyInner(//
-                            left, leftStore, leftBase, leftLength, targetDimensions,
+                            leftIter, leftAccess, leftBase, leftLength, targetDimensions,
                             positions, positionOffset, positionValue,
-                            right, rightStore, rightLength, rightIndex, parentNA);
+                            rightIter, rightAccess, right, rightLength, rightIndex, parentNA);
         }
         return rightIndex;
     }
 
     /**
      * Integer vectors iterate over the number of positions because we assume that the number of
-     * positions in an integer vector is significantly lower than the number of elements in the
-     * store. This might not be always true and could benefit from more investigation.
-     */
-    /**
-     * Integer vectors iterate over the number of positions because we assume that the number of
-     * positions in an integer vector is significantly lower than the number of elements in the
-     * store. This might not be always true and could benefit from more investigation.
+     * positions in an integer vector is significantly lower than the number of elements. This might
+     * not be always true and could benefit from more investigation.
      */
     @Specialization(replaces = "doIntegerSequencePosition")
-    protected int doIntegerPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension,
+    protected int doIntegerPosition(RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension,
                     Object[] positions, RAbstractIntVector position, int positionOffset, int positionLength,
-                    Object right, Object rightStore, int rightBase, int rightLength, boolean parentNA,
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightBase, int rightLength, boolean parentNA,
                     @Cached("createCountingProfile()") LoopConditionProfile lengthProfile) {
         positionNACheck.enable(position);
         int rightIndex = rightBase;
@@ -307,17 +336,17 @@ abstract class WriteIndexedVectorNode extends Node {
                 }
             }
             rightIndex = applyInner(//
-                            left, leftStore, leftBase, leftLength, targetDimensions,
+                            leftIter, leftAccess, leftBase, leftLength, targetDimensions,
                             positions, positionOffset, positionValue - 1,
-                            right, rightStore, rightLength, rightIndex, isNA || parentNA);
+                            rightIter, rightAccess, right, rightLength, rightIndex, isNA || parentNA);
         }
         return rightIndex;
     }
 
     private int applyInner(//
-                    RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions,
+                    RandomIterator leftIter, VectorAccess leftAccess, int leftBase, int leftLength, Object targetDimensions,
                     Object[] positions, int positionOffset, int positionValue,
-                    Object right, Object rightStore, int rightLength, int actionIndex, boolean isNA) {
+                    RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int rightLength, int actionIndex, boolean isNA) {
         int newTargetIndex = leftBase + positionValue * positionOffset;
         if (dimensionIndex == 0) {
             // for-loops leaf for innermost dimension
@@ -334,9 +363,15 @@ abstract class WriteIndexedVectorNode extends Node {
             }
 
             if (isNA) {
-                scalarNode.applyNA(left, leftStore, actionLeftIndex);
+                leftAccess.setNA(leftIter, actionLeftIndex);
+                leftAccess.na.seenNA();
             } else {
-                scalarNode.apply(left, leftStore, actionLeftIndex, right, rightStore, actionRightIndex);
+                if (vectorType == RType.List || vectorType == RType.Expression) {
+                    setListElement(leftIter, leftAccess, rightIter, rightAccess, right, actionLeftIndex, actionRightIndex);
+                } else {
+                    leftAccess.setFromSameType(leftIter, actionLeftIndex, rightAccess, rightIter, actionRightIndex);
+                }
+                rightAccess.isNA(rightIter, actionRightIndex);
             }
 
             if (resetIndexProfile.profile((actionIndex + 1) == (positionsApplyToRight ? leftLength : rightLength))) {
@@ -347,189 +382,24 @@ abstract class WriteIndexedVectorNode extends Node {
             // generate another for-loop for other dimensions
             int nextTargetDimension = innerVectorNode.dimensionValueProfile.profile(((int[]) targetDimensions)[innerVectorNode.dimensionIndex]);
             return innerVectorNode.applyImpl(//
-                            left, leftStore, newTargetIndex, leftLength, targetDimensions, nextTargetDimension,
+                            leftIter, leftAccess, newTargetIndex, leftLength, targetDimensions, nextTargetDimension,
                             positions, positionOffset,
-                            right, rightStore, actionIndex, rightLength, isNA);
-        }
-    }
-
-    private static WriteIndexedScalarNode<? extends RAbstractVector, ? extends Object> createIndexedAction(RType type, boolean setListElementAsObject, boolean isReplace) {
-        switch (type) {
-            case Logical:
-                return new WriteLogicalAction();
-            case Integer:
-                return new WriteIntegerAction();
-            case Double:
-                return new WriteDoubleAction();
-            case Complex:
-                return new WriteComplexAction();
-            case Character:
-                return new WriteCharacterAction();
-            case Raw:
-                return new WriteRawAction();
-            case Language:
-            case Expression:
-            case PairList:
-            case List:
-                return new WriteListAction(setListElementAsObject, isReplace);
-            default:
-                throw RInternalError.shouldNotReachHere("WriteIndexedScalarNode for " + type);
-        }
-    }
-
-    private abstract static class WriteIndexedScalarNode<A extends RAbstractVector, V extends Object> extends Node {
-
-        final NACheck valueNACheck = NACheck.create();
-
-        abstract void apply(A leftAccess, Object leftStore, int leftIndex, V rightAccess, Object rightStore, int rightIndex);
-
-        abstract void applyNA(A leftAccess, Object leftStore, int leftIndex);
-
-    }
-
-    private static final class WriteLogicalAction extends WriteIndexedScalarNode<RAbstractLogicalVector, RAbstractLogicalVector> {
-
-        @Override
-        void apply(RAbstractLogicalVector leftAccess, Object leftStore, int leftIndex, RAbstractLogicalVector rightAccess, Object rightStore, int rightIndex) {
-            byte value = rightAccess.getDataAt(rightStore, rightIndex);
-            leftAccess.setDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractLogicalVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RRuntime.LOGICAL_NA);
-            valueNACheck.seenNA();
-        }
-    }
-
-    private static final class WriteIntegerAction extends WriteIndexedScalarNode<RAbstractIntVector, RAbstractIntVector> {
-
-        @Override
-        void apply(RAbstractIntVector leftAccess, Object leftStore, int leftIndex, RAbstractIntVector rightAccess, Object rightStore, int rightIndex) {
-            int value = rightAccess.getDataAt(rightStore, rightIndex);
-            leftAccess.setDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractIntVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RRuntime.INT_NA);
-            valueNACheck.seenNA();
-        }
-    }
-
-    private static final class WriteDoubleAction extends WriteIndexedScalarNode<RAbstractDoubleVector, RAbstractDoubleVector> {
-
-        @Override
-        void apply(RAbstractDoubleVector leftAccess, Object leftStore, int leftIndex, RAbstractDoubleVector rightAccess, Object rightStore, int rightIndex) {
-            double value = rightAccess.getDataAt(rightStore, rightIndex);
-            leftAccess.setDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractDoubleVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RRuntime.DOUBLE_NA);
-            valueNACheck.seenNA();
-        }
-    }
-
-    private static final class WriteComplexAction extends WriteIndexedScalarNode<RAbstractComplexVector, RAbstractComplexVector> {
-
-        @Override
-        void apply(RAbstractComplexVector leftAccess, Object leftStore, int leftIndex, RAbstractComplexVector rightAccess, Object rightStore, int rightIndex) {
-            RComplex value = rightAccess.getDataAt(rightStore, rightIndex);
-            leftAccess.setDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractComplexVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RComplex.createNA());
-            valueNACheck.seenNA();
+                            rightIter, rightAccess, right, actionIndex, rightLength, isNA);
         }
     }
 
-    private static final class WriteCharacterAction extends WriteIndexedScalarNode<RAbstractStringVector, RAbstractStringVector> {
-
-        @Override
-        void apply(RAbstractStringVector leftAccess, Object leftStore, int leftIndex, RAbstractStringVector rightAccess, Object rightStore, int rightIndex) {
-            String value = rightAccess.getDataAt(rightStore, rightIndex);
-            leftAccess.setDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractStringVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RRuntime.STRING_NA);
-            valueNACheck.seenNA();
-        }
-    }
-
-    private static final class WriteRawAction extends WriteIndexedScalarNode<RAbstractRawVector, RAbstractRawVector> {
-
-        @Override
-        void apply(RAbstractRawVector leftAccess, Object leftStore, int leftIndex, RAbstractRawVector rightAccess, Object rightStore, int rightIndex) {
-            byte value = rightAccess.getRawDataAt(rightStore, rightIndex);
-            leftAccess.setRawDataAt(leftStore, leftIndex, value);
-            valueNACheck.check(value);
-        }
-
-        @Override
-        void applyNA(RAbstractRawVector leftAccess, Object leftStore, int leftIndex) {
-            // nothing to do
-        }
-    }
-
-    private static final class WriteListAction extends WriteIndexedScalarNode<RAbstractListBaseVector, Object> {
-
-        private final boolean setListElementAsObject;
-        private final boolean isReplace;
-        @Child private UpdateShareableChildValueNode updateStateOfListElement;
-        @Child private ShareObjectNode shareObjectNode;
-
-        WriteListAction(boolean setListElementAsObject, boolean isReplace) {
-            this.setListElementAsObject = setListElementAsObject;
-            this.isReplace = isReplace;
-            if (!isReplace) {
-                updateStateOfListElement = UpdateShareableChildValueNode.create();
-            } else {
-                shareObjectNode = ShareObjectNode.create();
+    private void setListElement(RandomIterator leftIter, VectorAccess leftAccess, RandomIterator rightIter, VectorAccess rightAccess, RAbstractContainer right, int leftIndex, int rightIndex) {
+        Object rightValue = rightAccess.getListElement(rightIter, rightIndex);
+        if (isReplace) {
+            // we are replacing within the same list
+            if (leftAccess.getListElement(leftIter, leftIndex) != rightValue) {
+                shareObjectNode.execute(rightValue);
             }
+        } else {
+            // we are writing into a list data that are being read from possibly another list
+            updateStateOfListElement.execute(right, rightValue);
         }
 
-        @Override
-        void apply(RAbstractListBaseVector leftAccess, Object leftStore, int leftIndex, Object rightAccess, Object rightStore, int rightIndex) {
-            Object rightValue;
-            if (setListElementAsObject) {
-                rightValue = rightAccess;
-                // unbox scalar vectors
-                if (rightValue instanceof RScalarVector) {
-                    rightValue = ((RScalarVector) rightValue).getDataAtAsObject(rightStore, 0);
-                }
-            } else {
-                rightValue = ((RAbstractContainer) rightAccess).getDataAtAsObject(rightStore, rightIndex);
-            }
-
-            if (isReplace) {
-                // we are replacing within the same list
-                if (leftAccess.getDataAtAsObject(leftStore, leftIndex) != rightValue) {
-                    shareObjectNode.execute(rightValue);
-                }
-            } else {
-                // we are writing into a list data that are being read from possibly another list
-                updateStateOfListElement.execute(rightAccess, rightValue);
-            }
-
-            leftAccess.setDataAt(leftStore, leftIndex, rightValue);
-            valueNACheck.checkListElement(rightValue);
-        }
-
-        @Override
-        void applyNA(RAbstractListBaseVector leftAccess, Object leftStore, int leftIndex) {
-            leftAccess.setDataAt(leftStore, leftIndex, RNull.instance);
-            valueNACheck.seenNA();
-        }
+        leftAccess.setListElement(leftIter, leftIndex, rightValue);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/AsS4.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/AsS4.java
index dd6ba6f6a16f2c99731e4d0a9f662530dc94afbb..2d5361356d9f10c047f8789210694473da303f1d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/AsS4.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/AsS4.java
@@ -54,7 +54,7 @@ public abstract class AsS4 extends Node {
             if (complete != 0) {
                 if (getS4DataSlot == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
-                    getS4DataSlot = insert(GetS4DataSlotNodeGen.create(RType.Any));
+                    getS4DataSlot = insert(GetS4DataSlot.create(RType.Any));
 
                 }
                 RTypedValue value = getS4DataSlot.executeObject(obj);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
index 6d2ca9ae16f77f59d89a0f82797bd97cd367ec40..37827a9f33541330ddb84de42102c0d30d1d285c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
@@ -13,7 +13,6 @@
 package com.oracle.truffle.r.nodes.objects;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -32,9 +31,7 @@ import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
 // transcribed from src/main/attrib.c
-public abstract class GetS4DataSlot extends Node {
-
-    public abstract RTypedValue executeObject(RAttributable attObj);
+public final class GetS4DataSlot extends Node {
 
     @Child private GetFixedAttributeNode s3ClassAttrAccess;
     @Child private RemoveFixedAttributeNode s3ClassAttrRemove;
@@ -48,12 +45,19 @@ public abstract class GetS4DataSlot extends Node {
 
     private final RType type;
 
-    protected GetS4DataSlot(RType type) {
+    private GetS4DataSlot(RType type) {
         this.type = type;
     }
 
-    @Specialization
-    protected RTypedValue doNewObject(RAttributable attrObj) {
+    public static GetS4DataSlot create(RType type) {
+        return new GetS4DataSlot(type);
+    }
+
+    public static GetS4DataSlot createEnvironment() {
+        return new GetS4DataSlot(RType.Environment);
+    }
+
+    public RTypedValue executeObject(RAttributable attrObj) {
         RAttributable obj = attrObj;
         Object value = null;
         if (!(obj instanceof RS4Object) || type == RType.S4Object) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
index ddba79ddb2ab050e7fa309e98b821e42fbd17ec7..267fdbba3c02a089800690063da0a5f87a468022 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
@@ -68,6 +68,10 @@ public class RRuntime {
 
     //@formatter:on
 
+    // used in DSL expressions:
+    public static final boolean True = true;
+    public static final boolean False = false;
+
     public static final String R_APP_MIME = "application/x-r";
     public static final String R_TEXT_MIME = "text/x-r";