diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
index c2a0cb887709ca2e9ac96630f520e0923c3db8c4..af6abfd1cd8adcc7790c0f1671ba73214f58f1ef 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
@@ -18,6 +18,7 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -43,7 +44,8 @@ public abstract class Cdist extends RExternalBuiltinNode.Arg4 {
     @Specialization(guards = "method == cachedMethod")
     protected RDoubleVector cdist(RAbstractDoubleVector x, @SuppressWarnings("unused") int method, RList list, double p, @SuppressWarnings("unused") @Cached("method") int cachedMethod,
                     @Cached("getMethod(method)") Method methodObj,
-                    @Cached("create()") SetAttributeNode setAttrNode) {
+                    @Cached("create()") SetAttributeNode setAttrNode,
+                    @Cached("create()") SetClassAttributeNode setClassAttrNode) {
         int nr = RRuntime.nrows(x);
         int nc = RRuntime.ncols(x);
         int n = nr * (nr - 1) / 2; /* avoid int overflow for N ~ 50,000 */
@@ -57,7 +59,7 @@ public abstract class Cdist extends RExternalBuiltinNode.Arg4 {
             String name = names.getDataAt(i);
             Object listValue = list.getDataAt(i);
             if (name.equals(RRuntime.CLASS_ATTR_KEY)) {
-                result.setClassAttr(listValue instanceof RStringVector ? (RStringVector) listValue : RDataFactory.createStringVectorFromScalar((String) listValue));
+                setClassAttrNode.execute(result, listValue instanceof RStringVector ? (RStringVector) listValue : RDataFactory.createStringVectorFromScalar((String) listValue));
             } else {
                 setAttrNode.execute(resultAttrs, name, listValue);
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
index d5579cf6151b85e0587b23fa51245d3452b81101..ef88564b409673c60b1b9e05f9a8c9565d8854ed 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
@@ -122,6 +122,6 @@ public abstract class Attributes extends RBuiltinNode {
     }
 
     private static boolean hasAttributes(RAttributable attributable) {
-        return attributable.getAttributes() != null && attributable.getAttributes().size() > 0;
+        return attributable.getAttributes() != null && !attributable.getAttributes().isEmpty();
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
index c9a3e2826bcf8d325844e894e04922fb2c9f369a..9836382db9156393a1fa64d3e2f8920a2128585c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
@@ -36,7 +36,9 @@ import java.util.HashMap;
 import java.util.TimeZone;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -280,7 +282,8 @@ public class DatePOSIXFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RDoubleVector posix2date(RAbstractListVector x) {
+        protected RDoubleVector posix2date(RAbstractListVector x,
+                        @Cached("create()") SetClassAttributeNode setClassAttrNode) {
             RAbstractVector secVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(0));
             RAbstractVector minVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(1));
             RAbstractVector hourVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(2));
@@ -314,7 +317,7 @@ public class DatePOSIXFunctions {
                 }
             }
             RDoubleVector result = RDataFactory.createDoubleVector(data, complete);
-            result.setClassAttr(CLASS_ATTR);
+            setClassAttrNode.execute(result, CLASS_ATTR);
             return result;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
index 91b440abc58300884c1fe7ce11b3eb9cdef5690f..b40823209066398f4129d631f1ff02d4ee801705 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
@@ -22,7 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
@@ -32,6 +36,7 @@ import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -102,6 +107,9 @@ public class DynLoadFunctions {
 
     @RBuiltin(name = "getLoadedDLLs", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class GetLoadedDLLs extends RBuiltinNode {
+
+        @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
         @Specialization
         @TruffleBoundary
         protected RList doGetLoadedDLLs() {
@@ -115,7 +123,7 @@ public class DynLoadFunctions {
                 data[i] = dllInfo.toRList();
             }
             RList result = RDataFactory.createList(data, RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR));
-            result.setClassAttr(RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS));
+            setClassAttrNode.execute(result, RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS));
             return result;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index 83bade1e3165ab0807caa023896b8a47890d2a4c..c9dbe8b0ba2d04b46c492967c385033af323ada0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -56,7 +56,9 @@ import java.util.stream.Stream;
 
 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.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
@@ -266,6 +268,8 @@ public class FileFunctions {
         private static final RStringVector NAMES_VECTOR = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
         private static final RStringVector OCTMODE = RDataFactory.createStringVectorFromScalar("octmode");
 
+        @Child private SetClassAttributeNode setClassAttrNode;
+
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("extra_cols").asLogicalVector().findFirst().map(toBoolean());
@@ -400,12 +404,19 @@ public class FileFunctions {
             // @formatter:on
         }
 
-        private static Object createColumnResult(Column column, Object data, boolean complete) {
+        private Object createColumnResult(Column column, Object data, boolean complete) {
             // @formatter:off
             switch(column) {
                 case size: return RDataFactory.createDoubleVector((double[]) data, complete);
                 case isdir: return RDataFactory.createLogicalVector((byte[]) data, complete);
-                case mode: RIntVector res = RDataFactory.createIntVector((int[]) data, complete); res.setClassAttr(OCTMODE); return res;
+                case mode:
+                    if (setClassAttrNode == null) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        setClassAttrNode = insert(SetClassAttributeNode.create());
+                    }
+                    RIntVector res = RDataFactory.createIntVector((int[]) data, complete);
+                    setClassAttrNode.execute(res, OCTMODE);
+                    return res;
                 case mtime: case ctime: case atime:
                 case uid: case gid: return RDataFactory.createIntVector((int[]) data, complete);
                 case uname: case grname: return RDataFactory.createStringVector((String[]) data, complete);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
index 0ff9871785cb6fb0523d27ae4bae2b6f98f303e7..04403d4fbfa08dfd3911b9516908144f8df1f931 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
@@ -15,7 +15,9 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
@@ -51,8 +53,10 @@ public abstract class FormatC extends RBuiltinNode {
 
     @SuppressWarnings("unused")
     @Specialization
-    RAttributable formatC(RAbstractContainer x, String mode, int width, int digits, String format, String flag, int iStrlen) {
+    RAttributable formatC(RAbstractContainer x, String mode, int width, int digits, String format, String flag, int iStrlen,
+                    @Cached("create()") SetClassAttributeNode setClassAttrNode) {
         RStringVector res = castStringVector(x);
-        return res.setClassAttr(null);
+        setClassAttrNode.reset(res);
+        return res;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 7b1275c1b1d5ffbe7a3539868b2d97a1a451e9a4..2bffe1729c707c3512447a5c71924a3aa09ada6a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -27,6 +27,7 @@ import java.nio.ByteBuffer;
 
 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.Specialization;
 import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -35,6 +36,7 @@ import com.oracle.truffle.api.nodes.LoopNode;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
@@ -284,7 +286,8 @@ public class HiddenInternalFunctions {
 
         @Specialization(guards = "isDLLInfo(externalPtr)")
         @TruffleBoundary
-        protected RList getRegisteredRoutines(RExternalPtr externalPtr) {
+        protected RList getRegisteredRoutines(RExternalPtr externalPtr,
+                        @Cached("create()") SetClassAttributeNode setClassAttrNode) {
             Object[] data = new Object[NAMES.getLength()];
             DLL.DLLInfo dllInfo = (DLLInfo) externalPtr.getExternalObject();
             RInternalError.guarantee(dllInfo != null);
@@ -301,7 +304,7 @@ public class HiddenInternalFunctions {
                     symbolData[i] = symbolInfo.createRSymbolObject(rnt, true);
                 }
                 RList symbolDataList = RDataFactory.createList(symbolData);
-                symbolDataList.setClassAttr(NATIVE_ROUTINE_LIST);
+                setClassAttrNode.execute(symbolDataList, NATIVE_ROUTINE_LIST);
                 data[nst.ordinal()] = symbolDataList;
             }
             return RDataFactory.createList(data, NAMES);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
index be182b91b23a5b1ef9147f6ae56359df1f2d456c..ea86a45e96d6237e66ad4ff52cf3c3a1b3d8417e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
@@ -27,6 +27,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -44,6 +45,8 @@ public abstract class ProcTime extends RBuiltinNode {
 
     private static RStringVector RNAMES;
 
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
     @Specialization
     @TruffleBoundary
     protected RDoubleVector procTime() {
@@ -65,7 +68,8 @@ public abstract class ProcTime extends RBuiltinNode {
             RNAMES = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
         }
         RDoubleVector result = RDataFactory.createDoubleVector(data, complete, RNAMES);
-        result.setClassAttr(PROC_TIME_CLASS);
+        setClassAttrNode.execute(result, PROC_TIME_CLASS);
+
         return result;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index 11bc95e938d5a7b6c2e3a9127e07b22fb9b2d582..a7104255300fc675b12928e8c6be4c528e3f913f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -31,9 +31,12 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 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.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
@@ -66,6 +69,7 @@ public abstract class UpdateAttr extends RBuiltinNode {
     @Child private CastIntegerNode castInteger;
     @Child private CastToVectorNode castVector;
     @Child private CastListNode castList;
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     @CompilationFinal private String cachedName = "";
     @CompilationFinal private String cachedInternedName = "";
@@ -144,7 +148,12 @@ public abstract class UpdateAttr extends RBuiltinNode {
         } else if (internedName == RRuntime.DIMNAMES_ATTR_KEY) {
             return updateDimNames(result, value);
         } else if (internedName == RRuntime.CLASS_ATTR_KEY) {
-            return (RAbstractContainer) result.setClassAttr(null);
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+            setClassAttrNode.reset(result);
+            return result;
         } else if (internedName == RRuntime.ROWNAMES_ATTR_KEY) {
             result.setRowNames(null);
         } else if (result.getAttributes() != null) {
@@ -181,7 +190,12 @@ public abstract class UpdateAttr extends RBuiltinNode {
         } else if (internedName == RRuntime.DIMNAMES_ATTR_KEY) {
             return updateDimNames(result, value);
         } else if (internedName == RRuntime.CLASS_ATTR_KEY) {
-            return (RAbstractContainer) result.setClassAttr(convertClassAttrFromObject(value));
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+            setClassAttrNode.execute(result, convertClassAttrFromObject(value));
+            return result;
         } else if (internedName == RRuntime.ROWNAMES_ATTR_KEY) {
             result.setRowNames(castVector(value));
         } else {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
index 31ce0f85a414a66a52bc6d3757e9398960508d74..de564de8968fe28cd0597085e810fb9fc0f8d70e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
@@ -30,7 +30,11 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
@@ -60,6 +64,9 @@ public abstract class UpdateAttributes extends RBuiltinNode {
     @Child private UpdateDimNames updateDimNames;
     @Child private CastIntegerNode castInteger;
     @Child private CastToVectorNode castVector;
+    @Child private SetClassAttributeNode setClassAttrNode;
+    @Child private InitAttributesNode initAttrNode;
+    @Child private SetAttributeNode setAttrNode;
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -189,11 +196,16 @@ public abstract class UpdateAttributes extends RBuiltinNode {
             } else if (attrName.equals(RRuntime.DIMNAMES_ATTR_KEY)) {
                 res = updateDimNames(res, value);
             } else if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
+                if (setClassAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setClassAttrNode = insert(SetClassAttributeNode.create());
+                }
                 if (value == RNull.instance) {
-                    res = (RAbstractContainer) result.setClassAttr(null);
+                    setClassAttrNode.reset(result);
                 } else {
-                    res = (RAbstractContainer) result.setClassAttr(UpdateAttr.convertClassAttrFromObject(value));
+                    setClassAttrNode.execute(res, UpdateAttr.convertClassAttrFromObject(value));
                 }
+                res = result;
             } else if (attrName.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
                 res.setRowNames(castVector(value));
             } else {
@@ -222,7 +234,12 @@ public abstract class UpdateAttributes extends RBuiltinNode {
         Object obj = getNonShared(o);
         RAttributable attrObj = (RAttributable) obj;
         attrObj.removeAllAttributes();
-        attrObj.setClassAttr(null);
+
+        if (setClassAttrNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            setClassAttrNode = insert(SetClassAttributeNode.create());
+        }
+        setClassAttrNode.reset(attrObj);
         return obj;
     }
 
@@ -251,9 +268,22 @@ public abstract class UpdateAttributes extends RBuiltinNode {
                 if (attrValue == null) {
                     throw RError.error(this, RError.Message.SET_INVALID_CLASS_ATTR);
                 }
-                attrObj.setClassAttr(UpdateAttr.convertClassAttrFromObject(attrValue));
+
+                if (setClassAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setClassAttrNode = insert(SetClassAttributeNode.create());
+                }
+                setClassAttrNode.execute(attrObj, UpdateAttr.convertClassAttrFromObject(attrValue));
             } else {
-                attrObj.setAttr(attrName.intern(), operand.getDataAt(i));
+                if (setAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setAttrNode = insert(SetAttributeNode.create());
+                }
+                if (initAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    initAttrNode = insert(InitAttributesNode.create());
+                }
+                setAttrNode.execute(initAttrNode.execute(attrObj), attrName.intern(), operand.getDataAt(i));
             }
         }
         return obj;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
index 62799507ade961086ec2c43d64c72aa344899b69..674c82de1633d6a8529a51e3fe5e6de7b882d048 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -18,6 +18,9 @@ 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.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.binary.CastTypeNode;
 import com.oracle.truffle.r.nodes.binary.CastTypeNodeGen;
@@ -48,6 +51,7 @@ public abstract class UpdateClass extends RBuiltinNode {
 
     @Child private CastTypeNode castTypeNode;
     @Child private TypeofNode typeof;
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -61,7 +65,8 @@ public abstract class UpdateClass extends RBuiltinNode {
     @TruffleBoundary
     protected Object setClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         RAbstractContainer result = reuseNonShared(arg);
-        return result.setClassAttr(null);
+        setClassAttrNode.reset(result);
+        return result;
     }
 
     @Specialization(limit = "CACHE_LIMIT", guards = "cachedClassName == className")
@@ -114,73 +119,75 @@ public abstract class UpdateClass extends RBuiltinNode {
             }
         }
 
-        return result.setClassAttr(RDataFactory.createStringVector(className));
+        setClassAttrNode.execute(result, RDataFactory.createStringVector(className));
+        return result;
     }
 
     @Specialization
     @TruffleBoundary
     protected Object setClass(RAbstractContainer arg, RStringVector className) {
         RAbstractContainer result = reuseNonShared(arg);
-        return result.setClassAttr(className);
+        setClassAttrNode.execute(result, className);
+        return result;
     }
 
     @Specialization
     protected Object setClass(RFunction arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RFunction arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(REnvironment arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(REnvironment arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RSymbol arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RSymbol arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RExternalPtr arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RExternalPtr arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RS4Object arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RS4Object arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
index f05261a585f42113af69f41576f5a2897eddc6df..375dc81129642bbb03ba5b814b44f63f51236bfe 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
@@ -29,6 +29,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
@@ -45,6 +46,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class UpdateOldClass extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
+    @Child private SetClassAttributeNode setClassAttributeNode = SetClassAttributeNode.create();
 
     @Specialization(guards = "!isStringVector(className)")
     protected Object setOldClass(RAbstractContainer arg, RAbstractVector className) {
@@ -73,14 +75,16 @@ public abstract class UpdateOldClass extends RBuiltinNode {
     @TruffleBoundary
     protected Object setOldClass(RAbstractContainer arg, RStringVector className) {
         RAbstractContainer result = (RAbstractContainer) arg.getNonShared();
-        return result.setClassAttr(className);
+        setClassAttributeNode.execute(result, className);
+        return result;
     }
 
     @Specialization
     @TruffleBoundary
     protected Object setOldClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         RAbstractContainer result = (RAbstractContainer) arg.getNonShared();
-        return result.setClassAttr(null);
+        setClassAttributeNode.reset(result);
+        return result;
     }
 
     protected boolean isStringVector(RAbstractVector className) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
index a2b8ddcf234c55e836d8af7397a79d7e2ea40423..2828a0bcc5dc8d618467bfdb08544a31a5e5a6a3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
@@ -20,6 +20,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.attributes.ArrayAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.binary.CastTypeNode;
@@ -47,6 +48,7 @@ public abstract class UpdateStorageMode extends RBuiltinNode {
     @Child private TypeofNode typeof;
     @Child private CastTypeNode castTypeNode;
     @Child private IsFactorNode isFactor;
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     private final BranchProfile errorProfile = BranchProfile.create();
 
@@ -80,10 +82,16 @@ public abstract class UpdateStorageMode extends RBuiltinNode {
                             String attrName = attr.getName();
                             Object v = attr.getValue();
                             if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
+
+                                if (setClassAttrNode == null) {
+                                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                                    setClassAttrNode = insert(SetClassAttributeNode.create());
+                                }
+
                                 if (v == RNull.instance) {
-                                    rresult = (RAbstractContainer) rresult.setClassAttr(null);
+                                    setClassAttrNode.reset(rresult);
                                 } else {
-                                    rresult = (RAbstractContainer) rresult.setClassAttr((RStringVector) v);
+                                    setClassAttrNode.execute(rresult, v);
                                 }
                             } else {
                                 rresult.setAttr(Utils.intern(attrName), v);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
index 771bc4bd590e1e2d170aacb2159ceca8c77d16c5..00771501a3095b30554c164c0fa60bd45bf81f47 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
@@ -27,6 +27,8 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -49,6 +51,8 @@ public abstract class Tilde extends RBuiltinNode {
 
     private static final RStringVector FORMULA_CLASS = RDataFactory.createStringVectorFromScalar(RRuntime.FORMULA_CLASS);
 
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
     @Override
     public Object[] getDefaultParameterValues() {
         return new Object[]{RMissing.instance, RMissing.instance};
@@ -58,7 +62,7 @@ public abstract class Tilde extends RBuiltinNode {
     protected RLanguage tilde(VirtualFrame frame, @SuppressWarnings("unused") Object response, @SuppressWarnings("unused") Object model) {
         RCallNode call = (RCallNode) ((RBaseNode) getParent()).asRSyntaxNode();
         RLanguage lang = RDataFactory.createLanguage(call);
-        lang.setClassAttr(FORMULA_CLASS);
+        setClassAttrNode.execute(lang, FORMULA_CLASS);
         REnvironment env = REnvironment.frameToEnvironment(frame.materialize());
         lang.setAttr(RRuntime.DOT_ENVIRONMENT, env);
         return lang;
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
index d17626bd61f79b6c3f08b544914cebc5fdca54c1..b0e55a5d0192eea5ba7efba7987f15b82d589f6f 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
@@ -64,7 +64,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 import org.junit.After;
@@ -75,7 +74,6 @@ import org.junit.Test;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.casts.PipelineConfig;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.InitialPhaseBuilder;
-import com.oracle.truffle.r.nodes.builtin.casts.fluent.PipelineConfigBuilder;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.PreinitialPhaseBuilder;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
 import com.oracle.truffle.r.nodes.casts.FilterSamplerFactory;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
index 5d41dae1dbc3861f24152719abd03b86707050e0..bb698a4a4540121e0655b46d7bf2f290ccdf2767 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
@@ -59,19 +59,7 @@ public abstract class RemoveFixedAttributeNode extends FixedAttributeAccessNode
 
     public abstract void execute(DynamicObject attrs);
 
-    @Specialization(limit = "3", //
-                    guards = {"shapeCheck(shape, attrs)"}, //
-                    assumptions = {"shape.getValidAssumption()"})
-    protected void removeAttrCached(DynamicObject attrs,
-                    @Cached("lookupShape(attrs)") Shape shape,
-                    @Cached("lookupProperty(shape, name)") Property property) {
-        if (property != null) {
-            Shape newShape = attrs.getShape().removeProperty(property);
-            attrs.setShapeAndResize(shape, newShape);
-        }
-    }
-
-    @Specialization(contains = "removeAttrCached")
+    @Specialization
     @TruffleBoundary
     protected void removeAttrFallback(DynamicObject attrs) {
         attrs.delete(this.name);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetClassAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetClassAttributeNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef5dbf6859ecef9b78eef9d39e3ff1e00d758101
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetClassAttributeNode.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.attributes;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+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.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RInteger;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+public abstract class SetClassAttributeNode extends RBaseNode {
+
+    public static SetClassAttributeNode create() {
+        return SetClassAttributeNodeGen.create();
+    }
+
+    public abstract void execute(RAttributable x, Object classAttr);
+
+    public void reset(RAttributable x) {
+        execute(x, RNull.instance);
+    }
+
+    @Specialization
+    protected <T> void handleVectorNullClass(RVector<T> vector, @SuppressWarnings("unused") RNull classAttr,
+                    @Cached("createClass()") RemoveFixedAttributeNode removeClassAttrNode,
+                    @Cached("createClass()") SetFixedAttributeNode setClassAttrNode,
+                    @Cached("create()") BranchProfile nullAttrProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile nullClassProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile notNullClassProfile) {
+        handleVector(vector, null, removeClassAttrNode, setClassAttrNode, nullAttrProfile, nullClassProfile, notNullClassProfile);
+    }
+
+    @Specialization
+    protected <T> void handleVector(RVector<T> vector, RStringVector classAttr,
+                    @Cached("createClass()") RemoveFixedAttributeNode removeClassAttrNode,
+                    @Cached("createClass()") SetFixedAttributeNode setClassAttrNode,
+                    @Cached("create()") BranchProfile nullAttrProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile nullClassProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile notNullClassProfile) {
+
+        DynamicObject attrs = vector.getAttributes();
+        if (attrs == null && classAttr != null && classAttr.getLength() != 0) {
+            nullAttrProfile.enter();
+            attrs = vector.initAttributes();
+        }
+        if (nullClassProfile.profile(attrs != null && (classAttr == null || classAttr.getLength() == 0))) {
+            removeAttributeMapping(vector, attrs, removeClassAttrNode);
+        } else if (notNullClassProfile.profile(classAttr != null && classAttr.getLength() != 0)) {
+            for (int i = 0; i < classAttr.getLength(); i++) {
+                String attr = classAttr.getDataAt(i);
+                if (RRuntime.CLASS_FACTOR.equals(attr)) {
+                    // TODO: Isn't this redundant when the same operation is done after the loop?
+                    setClassAttrNode.execute(attrs, classAttr);
+                    if (vector.getElementClass() != RInteger.class) {
+                        // N.B. there used to be conversion to integer under certain circumstances.
+                        // However, it seems that it was dead/obsolete code so it was removed.
+                        // Notes: this can only happen if the class is set by hand to some
+                        // non-integral vector, i.e. attr(doubles, 'class') <- 'factor'. GnuR also
+                        // does not update the 'class' attr with other, possibly
+                        // valid classes when it reaches this error.
+                        throw RError.error(RError.SHOW_CALLER2, RError.Message.ADDING_INVALID_CLASS, "factor");
+                    }
+                }
+            }
+            setClassAttrNode.execute(attrs, classAttr);
+        }
+    }
+
+    @Specialization
+    protected void handleAttributable(RAttributable x, @SuppressWarnings("unused") RNull classAttr) {
+        x.setClassAttr(null);
+    }
+
+    @Specialization
+    protected void handleAttributable(RAttributable x, RStringVector classAttr) {
+        x.setClassAttr(classAttr);
+    }
+
+    private static void removeAttributeMapping(RAttributable x, DynamicObject attrs, RemoveFixedAttributeNode removeClassAttrNode) {
+        if (attrs != null) {
+            removeClassAttrNode.execute(attrs);
+            if (attrs.isEmpty()) {
+                x.initAttributes(null);
+            }
+        }
+    }
+
+}
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 3b7dc03d2d930df8d37386cc0fd332e510823f56..2c495a67ce421a9c7dccf4e07beaf546033c93fa 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
@@ -19,6 +19,8 @@ import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.RemoveFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
@@ -42,6 +44,7 @@ public abstract class GetS4DataSlot extends Node {
     @Child private GetFixedAttributeNode dotDataAttrAccess;
     @Child private GetFixedAttributeNode dotXDataAttrAccess;
     @Child private TypeofNode typeOf = TypeofNodeGen.create();
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     private final BranchProfile shareable = BranchProfile.create();
 
@@ -72,6 +75,12 @@ public abstract class GetS4DataSlot extends Node {
                 shareable.enter();
                 obj = (RAttributable) ((RShareable) obj).copy();
             }
+
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+
             if (s3Class != null) {
                 if (s3ClassAttrRemove == null) {
                     assert castToVector == null;
@@ -81,9 +90,9 @@ public abstract class GetS4DataSlot extends Node {
 
                 }
                 s3ClassAttrRemove.execute(obj.initAttributes());
-                obj = obj.setClassAttr((RStringVector) castToVector.execute(s3Class));
+                setClassAttrNode.execute(obj, castToVector.execute(s3Class));
             } else {
-                obj = obj.setClassAttr(null);
+                setClassAttrNode.reset(obj);
             }
             obj.unsetS4();
             if (type == RType.S4Object) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
index b02d46ea7312a68103d2a0488edad5a155c4bc29..0929f696f491a144ca332bab105074b6109ec94d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
@@ -12,10 +12,12 @@
  */
 package com.oracle.truffle.r.nodes.objects;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
@@ -36,6 +38,7 @@ public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
     @Child private AccessSlotNode accessSlotPrototypeName = AccessSlotNodeGen.create(true, null, null);
     @Child private DuplicateNode duplicate = DuplicateNodeGen.create(true);
     @Child private GetFixedAttributeNode pckgAttrAccess = GetFixedAttributeNode.create(RRuntime.PCKG_ATTR_KEY);
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     @Child private CastNode castStringScalar;
     @Child private CastNode castLogicalScalar;
@@ -62,7 +65,13 @@ public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
         RAttributable valueAttr = (RAttributable) value;
         if (valueAttr instanceof RS4Object ||
                         (e instanceof RAttributable && ((RAttributable) e).getAttributes() != null && pckgAttrAccess.execute(((RAttributable) e).getAttributes()) != null)) {
-            valueAttr = valueAttr.setClassAttr((RStringVector) e);
+
+            if (setClassAttrNode == null) {
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+            }
+
+            setClassAttrNode.execute(valueAttr, e);
             valueAttr.setS4();
         }
         return value;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
index 9d53e8623b97de1e410dd4ae8b0619e7fae4f224..0b01117753dd91c60670b8d9d1fb3c9c72bfc694 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
@@ -22,11 +22,14 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
+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.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.r.nodes.attributes.ArrayAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetClassAttributeNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -45,6 +48,8 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public abstract class CastListNode extends CastBaseNode {
 
+    @Child private SetClassAttributeNode setClassAttrNode;
+
     public abstract RList executeList(Object o);
 
     protected CastListNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
@@ -93,7 +98,13 @@ public abstract class CastListNode extends CastBaseNode {
             // result may already have names, so can't call RVector.copyAttributesFrom
             for (RAttributesLayout.RAttribute attr : attrAttrAccess.execute(operandAttrs)) {
                 if (attr.getName().equals(RRuntime.CLASS_ATTR_KEY)) {
-                    result.setClassAttr((RStringVector) attr.getValue());
+
+                    if (setClassAttrNode == null) {
+                        setClassAttrNode = insert(SetClassAttributeNode.create());
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                    }
+
+                    setClassAttrNode.execute(result, attr.getValue());
                 } else {
                     result.setAttr(attr.getName(), attr.getValue());
                 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 4d9c43afdf1371f48b3e288e73d3c4bd153d17de..d0983a3caf8b7884217835b32be0f036feb33274 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -533,6 +533,10 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         return setClassAttrInternal(vector, classAttr);
     }
 
+    public abstract class CNode extends RBaseNode {
+
+    }
+
     private static RAbstractContainer setClassAttrInternal(RVector<?> vector, RStringVector classAttr) {
         if (vector.attributes == null && classAttr != null && classAttr.getLength() != 0) {
             vector.initAttributes();