diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
index 663fb3456f88edf533bbdbbfc2f6609579f6feb4..6f78bad2766e5866b55b01e2188e8cd499db806f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
@@ -24,86 +24,52 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asStringVector;
-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.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.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 // TODO interpret "type" and "allowNA" arguments
 @RBuiltin(name = "nchar", kind = INTERNAL, parameterNames = {"x", "type", "allowNA", "keepNA"}, behavior = PURE)
 public abstract class NChar extends RBuiltinNode {
 
-    @Child private CastStringNode convertString;
-    @Child private InheritsCheckNode factorInheritsCheck;
-
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-    public abstract Object execute(Object value, Object type, Object allowNA, Object keepNA);
-
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("x").mapIf(stringValue(), asStringVector(), asIntegerVector());
-        casts.toLogical(2).toLogical(3);
-    }
-
-    private String coerceContent(Object content) {
-        if (convertString == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            convertString = insert(CastStringNodeGen.create(false, false, false, false));
-        }
-        return (String) convertString.executeString(content);
+        casts.arg("x").mapIf(Predef.integerValue(), asIntegerVector(), asStringVector(true, false, false));
+        casts.arg("type").asStringVector().findFirst();
+        casts.arg("allowNA").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).map(toBoolean());
+        casts.arg("keepNA").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean());
     }
 
     @SuppressWarnings("unused")
     @Specialization
-    protected RIntVector nchar(RNull value, String type, byte allowNA, byte keepNA) {
+    protected RIntVector nchar(RNull value, String type, boolean allowNA, boolean keepNA) {
         return RDataFactory.createEmptyIntVector();
     }
 
     @SuppressWarnings("unused")
     @Specialization
-    protected int nchar(int value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected int nchar(double value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected int nchar(byte value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected RIntVector ncharInt(RAbstractIntVector vector, String type, byte allowNA, byte keepNA) {
+    protected RIntVector ncharInt(RAbstractIntVector vector, String type, boolean allowNA, boolean keepNA,
+                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                    @Cached("create()") RAttributeProfiles attrProfiles) {
         int len = vector.getLength();
         int[] result = new int[len];
-        for (int i = 0; i < len; i++) {
+        loopProfile.profileCounted(len);
+        for (int i = 0; loopProfile.inject(i < len); i++) {
             int x = vector.getDataAt(i);
             if (x == RRuntime.INT_NA) {
                 result[i] = 2;
@@ -111,68 +77,20 @@ public abstract class NChar extends RBuiltinNode {
                 result[i] = (int) (Math.log10(x) + 1); // not the fastest one
             }
         }
-
-        return RDataFactory.createIntVector(result, false);
+        return RDataFactory.createIntVector(result, true, vector.getNames(attrProfiles));
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() == 0")
-    protected RIntVector ncharL0(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
-        return RDataFactory.createEmptyIntVector();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() == 1")
-    protected int ncharL1(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
-        return vector.getDataAt(0).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() > 1")
-    protected RIntVector nchar(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
+    @Specialization
+    protected RIntVector nchar(RAbstractStringVector vector, String type, boolean allowNA, boolean keepNA,
+                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                    @Cached("create()") RAttributeProfiles attrProfiles) {
         int len = vector.getLength();
         int[] result = new int[len];
-        for (int i = 0; i < len; i++) {
+        loopProfile.profileCounted(len);
+        for (int i = 0; loopProfile.inject(i < len); i++) {
             result[i] = vector.getDataAt(i).length();
         }
-        return RDataFactory.createIntVector(result, vector.isComplete(), vector.getNames(attrProfiles));
-    }
-
-    protected static NChar createRecursive() {
-        return NCharNodeGen.create(null);
-    }
-
-    /*
-     * this builtin is sometimes used with only 3 arguments - keepNA defaults to FALSE.
-     */
-    @Specialization
-    protected Object ncharNoKeepNA(Object obj, Object type, Object allowNA, @SuppressWarnings("unused") RMissing keepNA, //
-                    @Cached("createRecursive()") NChar rec) {
-        return rec.execute(obj, type, allowNA, RRuntime.LOGICAL_FALSE);
-    }
-
-    @SuppressWarnings("unused")
-    @Fallback
-    protected RIntVector nchar(Object obj, Object type, Object allowNA, Object keepNA) {
-        if (factorInheritsCheck == null) {
-            CompilerDirectives.transferToInterpreter();
-            factorInheritsCheck = insert(new InheritsCheckNode(RRuntime.CLASS_FACTOR));
-        }
-
-        if (factorInheritsCheck.execute(obj)) {
-            throw RError.error(this, RError.Message.REQUIRES_CHAR_VECTOR, "nchar");
-        }
-
-        if (obj instanceof RAbstractVector) {
-            RAbstractVector vector = (RAbstractVector) obj;
-            int len = vector.getLength();
-            int[] result = new int[len];
-            for (int i = 0; i < len; i++) {
-                result[i] = coerceContent(vector.getDataAtAsObject(i)).length();
-            }
-            return RDataFactory.createIntVector(result, vector.isComplete(), vector.getNames(attrProfiles));
-        } else {
-            throw RError.error(this, RError.Message.CANNOT_COERCE, RRuntime.classToString(obj.getClass()), "character");
-        }
+        return RDataFactory.createIntVector(result, true, vector.getNames(attrProfiles));
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
index 46745aeb1d27b99292fa055c3152057cf66e55f9..ad51fe31264a770277b90b30b8c55f8cdefc2d44 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
@@ -635,6 +635,10 @@ public final class CastBuilder {
             return phaseBuilder -> CastIntegerNodeGen.create(false, false, false);
         }
 
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asIntegerVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastIntegerNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asDouble() {
             return phaseBuilder -> CastDoubleBaseNodeGen.create(false, false, false);
         }
@@ -643,6 +647,10 @@ public final class CastBuilder {
             return phaseBuilder -> CastDoubleNodeGen.create(false, false, false);
         }
 
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asDoubleVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastDoubleNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asString() {
             return phaseBuilder -> CastStringBaseNodeGen.create(false, false, false);
         }
@@ -651,6 +659,10 @@ public final class CastBuilder {
             return phaseBuilder -> CastStringNodeGen.create(false, false, false, false);
         }
 
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asStringVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false);
+        }
+
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogical() {
             return phaseBuilder -> CastLogicalBaseNodeGen.create(false, false, false);
         }
@@ -659,6 +671,10 @@ public final class CastBuilder {
             return phaseBuilder -> CastLogicalNodeGen.create(false, false, false);
         }
 
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogicalVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastLogicalNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
         public static <T> FindFirstNodeBuilder<T> findFirst(RBaseNode callObj, RError.Message message, Object... messageArgs) {
             return new FindFirstNodeBuilder<>(callObj, message, messageArgs);
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
index 5a9e8431f7076b159c7d96cb6489e408d06ba3f3..463283d4f62ec3068a9dc0f947249dc5457121b8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 public abstract class CastNode extends UnaryNode {
 
     @TruffleBoundary
-    public static void handleArgumentError(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
+    protected static void handleArgumentError(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
         if (RContext.getInstance() == null) {
             throw new IllegalArgumentException(String.format(message.message, CastBuilder.substituteArgPlaceholder(arg, messageArgs)));
         } else {
@@ -44,7 +44,7 @@ public abstract class CastNode extends UnaryNode {
     }
 
     @TruffleBoundary
-    public static void handleArgumentWarning(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
+    protected static void handleArgumentWarning(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
         if (message == null) {
             return;
         }
@@ -56,5 +56,4 @@ public abstract class CastNode extends UnaryNode {
             RError.warning(callObj, message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
         }
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
index 958f51bc0104bd4f826770085a94b97bf6b254ef..4d4442785cacde16406952f80b72e079651dc076 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
@@ -56,11 +56,11 @@ public abstract class ConditionalMapNode extends CastNode {
 
     @Specialization(guards = "doMap(x)")
     protected Object map(Object x) {
-        return trueBranch.execute(x);
+        return trueBranch == null ? x : trueBranch.execute(x);
     }
 
     @Specialization(guards = "!doMap(x)")
     protected Object noMap(Object x) {
-        return x;
+        return falseBranch == null ? x : falseBranch.execute(x);
     }
 }