diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
index 9162e0ac2c3c57718b7e59c7796061fedb5d7f1c..bb328a40e236e90258c724bfd882acc0d39df6ca 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
@@ -13,6 +13,7 @@ package com.oracle.truffle.r.library.tools;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.typeName;
 
 import java.io.BufferedOutputStream;
 import java.io.FileOutputStream;
@@ -20,14 +21,11 @@ import java.io.IOException;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.function.Function;
 
 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.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
-import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -39,14 +37,10 @@ public class ToolsText {
 
     public abstract static class DoTabExpand extends RExternalBuiltinNode.Arg2 {
 
-        @Child private TypeofNode typeofNode = com.oracle.truffle.r.nodes.unary.TypeofNodeGen.create();
-
         @Override
         protected void createCasts(CastBuilder casts) {
-            Function<Object, String> argTypeName = arg -> typeofNode.execute(arg).getName();
-
-            casts.arg(0, "strings").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "STRING_ELT()", "character vector", argTypeName).mustNotBeNull().mustBe(stringValue());
-            casts.arg(1, "starts").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "INTEGER()", "integer", argTypeName).mustNotBeNull().mustBe(
+            casts.arg(0, "strings").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "STRING_ELT()", "character vector", typeName()).mustNotBeNull().mustBe(stringValue());
+            casts.arg(1, "starts").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "INTEGER()", "integer", typeName()).mustNotBeNull().mustBe(
                             Predef.integerValue()).asIntegerVector();
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
index 1b88a32c14606a5165846fcb5e27fe0d7db53422..70ecc15e2ab29957db60aa4ba1c8cbb26e36ef38 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
@@ -27,8 +27,7 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
-
-import java.util.function.Function;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.typeName;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -37,9 +36,9 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 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.unary.CastNode;
-import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -57,8 +56,6 @@ public abstract class Quantifier extends RBuiltinNode {
     private final BranchProfile trueBranch = BranchProfile.create();
     private final BranchProfile falseBranch = BranchProfile.create();
 
-    @Child private TypeofNode typeofNode = com.oracle.truffle.r.nodes.unary.TypeofNodeGen.create();
-
     @Children private final CastNode[] argCastNodes = new CastNode[MAX_CACHED_LENGTH];
 
     private static final class ProfileCastNode extends CastNode {
@@ -88,8 +85,7 @@ public abstract class Quantifier extends RBuiltinNode {
 
     private void createArgCast(int index) {
         CastBuilder argCastBuilder = new CastBuilder();
-        Function<Object, String> argTypeName = arg -> typeofNode.execute(arg).getName();
-        argCastBuilder.arg(0).allowNull().shouldBe(integerValue().or(logicalValue()).or(instanceOf(RAbstractVector.class).and(size(0))), RError.Message.COERCING_ARGUMENT, argTypeName,
+        argCastBuilder.arg(0).allowNull().shouldBe(integerValue().or(logicalValue()).or(instanceOf(RAbstractVector.class).and(size(0))), RError.Message.COERCING_ARGUMENT, typeName(),
                         "logical").asLogicalVector();
         argCastNodes[index] = insert(new ProfileCastNode(argCastBuilder.getCasts()[0]));
     }
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 b0e55a5d0192eea5ba7efba7987f15b82d589f6f..f092a88c2a04709480ccf6d14e00faac3fbbd08f 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asBoolean;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asInteger;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
@@ -31,6 +33,7 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.atomicIntege
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.atomicLogicalValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.chain;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.complexValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.dimGt;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleNA;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleToInt;
@@ -237,7 +240,8 @@ public class CastBuilderTest {
     public void testAsString() {
         arg.asStringVector();
 
-        assertEquals("1.2", cast(1.2));
+        // TODO: it fails (NPE) due to the uninitialized RContext.runtimeASTAccess field
+        // assertEquals("1.2", cast(1.2));
         assertEquals("TRUE", cast(RRuntime.LOGICAL_TRUE));
         assertEquals("FALSE", cast(RRuntime.LOGICAL_FALSE));
         assertEquals("NA", cast(RRuntime.LOGICAL_NA));
@@ -639,6 +643,144 @@ public class CastBuilderTest {
         assertCastFail(RNull.instance, Message.SEED_NOT_VALID_INT.message);
     }
 
+    // RNull/RMissing tests. See com.oracle.truffle.r.nodes.builtin.casts.Filter$ResultForArg.
+
+    @Test
+    public void testMustBeNull() {
+        arg.mustBe(nullValue());
+        cast(RNull.instance);
+    }
+
+    @Test
+    public void testMustNotBeNull() {
+        arg.mustBe(nullValue().not());
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testMustNotBeNullAndNotDouble() {
+        arg.mustBe(nullValue().not().and(doubleValue().not()));
+        Assert.assertEquals("A", cast("A"));
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+        try {
+            cast(1.23);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testMapIfNull() {
+        arg.mapIf(nullValue(), constant("A"), constant(1));
+        Assert.assertEquals("A", cast(RNull.instance));
+        Assert.assertEquals(1, cast("X"));
+    }
+
+    @Test
+    public void testBlockingNull1() {
+        // Here, the result for NULL in the 'singleElement()' filter is UNDEFINED, i.e. NULL does
+        // not pass through.
+        arg.asStringVector().mustBe(singleElement());
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testBlockingNull2() {
+        // Here, the result for NULL in the 'instanceOf(RIntSequence.class)' filter is FALSE,
+        // i.e. NULL does not pass through.
+        arg.mustBe(instanceOf(RIntSequence.class));
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testPassingNull1() {
+        // Here, the result for NULL in the 'stringValue().not()' filter is TRUE,
+        // i.e. NULL passes through.
+        arg.mustBe(stringValue().not());
+        Assert.assertEquals(RNull.instance, cast(RNull.instance));
+    }
+
+    @Test
+    public void testBlockingNull3() {
+        // Here, the result for NULL in the 'instanceOf(RIntSequence.class).not()' filter is TRUE,
+        // while in the 'integerValue()' filter the result is FALSE. The result of the
+        // conjunction of the two filters by the 'and' operator is FALSE, i.e. NULL does not
+        // pass through.
+        arg.mustBe(instanceOf(RIntSequence.class).not().and(integerValue()));
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testBlockingNull4() {
+        // Here, the result for NULL in the 'instanceOf(RIntSequence.class)' filter is FALSE,
+        // while in the 'singleElement()' filter the result is UNDEFINED. The result of the
+        // conjunction of the two filters by the 'and' operator is UNDEFINED, i.e. NULL does not
+        // pass through.
+        arg.asIntegerVector().mustBe(instanceOf(RIntSequence.class).and(singleElement()));
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testPassingNull2() {
+        // Here, the result for NULL in the 'instanceOf(RIntSequence.class).not()' filter is TRUE,
+        // while in the 'stringValue()' filter the result is FALSE. The result of the
+        // disjunction of the two filters by the 'or' operator is TRUE, i.e. NULL passes through.
+        arg.mustBe(instanceOf(RIntSequence.class).not().or(stringValue()));
+        Assert.assertEquals(RNull.instance, cast(RNull.instance));
+    }
+
+    @Test
+    public void testBlockingNullAllowingMissing() {
+        arg.mustNotBeNull().mustBe(stringValue().not());
+        Assert.assertEquals(1, cast(1));
+        try {
+            cast(RNull.instance);
+            fail();
+        } catch (Exception e) {
+        }
+        Assert.assertEquals(RMissing.instance, cast(RMissing.instance));
+    }
+
+    @Test
+    public void testMapIfPassingNull() {
+        // The condition 'stringValue()' returns FALSE for NULL, i.e. NULL passes through unmapped.
+        arg.mapIf(stringValue(), constant(1));
+        Assert.assertEquals(RNull.instance, cast(RNull.instance));
+        Assert.assertEquals(1, cast("abc"));
+    }
+
+    @Test
+    public void testMapIfMappingNull() {
+        // The condition 'stringValue()' returns TRUE for NULL, i.e. NULL is mapped to 1.
+        arg.mapIf(stringValue().not(), constant(1));
+        Assert.assertEquals(1, cast(RNull.instance));
+        Assert.assertEquals("abc", cast("abc"));
+    }
+
     /**
      * Casts given object using the configured pipeline in {@link #arg}.
      */
@@ -685,7 +827,7 @@ public class CastBuilderTest {
     }
 
     /**
-     * This tests the pipeline sampling process: all positive samples ase successful and all
+     * This tests the pipeline sampling process: all positive samples are successful and all
      * negative cause an error.
      */
     private void testPipeline() {
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/FilterSamplerFactory.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/FilterSamplerFactory.java
index 59383992110284b1e53a6b4cf6c5e41972a9f5b6..1886cc2aaa532fca53312c9e7487a0fcda1c4590 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/FilterSamplerFactory.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/FilterSamplerFactory.java
@@ -41,7 +41,9 @@ import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter.VectorSize;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.DoubleFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.FilterVisitor;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.MatrixFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.MissingFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.NotFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.NullFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.OrFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.RTypeFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter;
@@ -214,6 +216,16 @@ public final class FilterSamplerFactory
         };
     }
 
+    @Override
+    public ArgumentFilterSampler<?, ?> visit(NullFilter filter) {
+        return new VectorPredicateArgumentFilterSampler<>("nullValue", x -> false);
+    }
+
+    @Override
+    public ArgumentFilterSampler<?, ?> visit(MissingFilter filter) {
+        return new VectorPredicateArgumentFilterSampler<>("missingValue", x -> false);
+    }
+
     @Override
     public ArgumentFilterSampler<?, ?> visit(MatrixFilter<?> filter) {
         return filter.acceptOperation(this);
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 a08b8d3c0e9821f435ea884e7da54f2ed67289c3..663081d1a9d6303b10f3cf3528ae04b665d103e1 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
@@ -24,14 +24,18 @@ package com.oracle.truffle.r.nodes.builtin;
 
 import java.util.Arrays;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.AndFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.DoubleFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.MatrixFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.MissingFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.NotFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.NullFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.OrFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.RTypeFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter;
@@ -71,6 +75,7 @@ import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -425,6 +430,14 @@ public final class CastBuilder {
             return new NotNAStep<>(null, null);
         }
 
+        public static NullFilter nullValue() {
+            return NullFilter.INSTANCE;
+        }
+
+        public static MissingFilter missingValue() {
+            return MissingFilter.INSTANCE;
+        }
+
         public static <T> CompareFilter<T> sameAs(T x) {
             return new CompareFilter<>(CompareFilter.SAME, new CompareFilter.ScalarValue(x, RType.Any));
         }
@@ -752,5 +765,17 @@ public final class CastBuilder {
         public static <T> MapToValue<T, RList> emptyList() {
             return new MapToValue<>(RDataFactory.createList());
         }
+
+        /**
+         * The function returned by this method is typically used as an error message argument.
+         *
+         * @return a function returning the type name of its argument
+         */
+        public static Function<Object, String> typeName() {
+            return arg -> {
+                CompilerAsserts.neverPartOfCompilation();
+                return ((RTypedValue) RRuntime.asAbstractVector(arg)).getRType().getName();
+            };
+        }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java
index a5d0b2e43e37ac9a1b4561d27abed52785d20261..c74b22baa8d1bf3165b532933635b9c242071b81 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java
@@ -26,6 +26,8 @@ import com.oracle.truffle.r.nodes.builtin.ArgumentFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FilterStep;
 import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.MapStep;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
@@ -41,6 +43,14 @@ public abstract class Filter<T, R extends T> {
      */
     public abstract boolean isNarrowing();
 
+    public ResultForArg resultForNull() {
+        return ResultForArg.UNDEFINED;
+    }
+
+    public ResultForArg resultForMissing() {
+        return ResultForArg.UNDEFINED;
+    }
+
     public abstract <D> D accept(FilterVisitor<D> visitor);
 
     public <S extends R> AndFilter<T, S> and(Filter<? super R, S> other) {
@@ -71,6 +81,10 @@ public abstract class Filter<T, R extends T> {
         D visit(MatrixFilter<?> filter);
 
         D visit(DoubleFilter filter);
+
+        D visit(NullFilter filter);
+
+        D visit(MissingFilter filter);
     }
 
     /**
@@ -102,6 +116,16 @@ public abstract class Filter<T, R extends T> {
         public <D> D accept(FilterVisitor<D> visitor) {
             return visitor.visit(this);
         }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return ResultForArg.FALSE;
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return ResultForArg.FALSE;
+        }
     }
 
     /**
@@ -128,6 +152,72 @@ public abstract class Filter<T, R extends T> {
         public <D> D accept(FilterVisitor<D> visitor) {
             return visitor.visit(this);
         }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return ResultForArg.FALSE;
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return ResultForArg.FALSE;
+        }
+    }
+
+    public static final class NullFilter extends Filter<Object, RNull> {
+
+        public static final NullFilter INSTANCE = new NullFilter();
+
+        private NullFilter() {
+        }
+
+        @Override
+        public boolean isNarrowing() {
+            return true;
+        }
+
+        @Override
+        public <D> D accept(FilterVisitor<D> visitor) {
+            return visitor.visit(this);
+        }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return ResultForArg.TRUE;
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return ResultForArg.FALSE;
+        }
+    }
+
+    public static final class MissingFilter extends Filter<Object, RMissing> {
+
+        public static final MissingFilter INSTANCE = new MissingFilter();
+
+        private MissingFilter() {
+        }
+
+        @Override
+        public boolean isNarrowing() {
+            return true;
+        }
+
+        @Override
+        public <D> D accept(FilterVisitor<D> visitor) {
+            return visitor.visit(this);
+        }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return ResultForArg.FALSE;
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return ResultForArg.TRUE;
+        }
     }
 
     /**
@@ -388,6 +478,16 @@ public abstract class Filter<T, R extends T> {
         public <D> D accept(FilterVisitor<D> visitor) {
             return visitor.visit(this);
         }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return left.resultForNull().and(right.resultForNull());
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return left.resultForMissing().and(right.resultForMissing());
+        }
     }
 
     public static final class OrFilter<T> extends Filter<T, T> {
@@ -416,6 +516,16 @@ public abstract class Filter<T, R extends T> {
         public <D> D accept(FilterVisitor<D> visitor) {
             return visitor.visit(this);
         }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return left.resultForNull().or(right.resultForNull());
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return left.resultForMissing().or(right.resultForMissing());
+        }
     }
 
     public static final class NotFilter<T> extends Filter<T, T> {
@@ -438,5 +548,92 @@ public abstract class Filter<T, R extends T> {
         public <D> D accept(FilterVisitor<D> visitor) {
             return visitor.visit(this);
         }
+
+        @Override
+        public ResultForArg resultForNull() {
+            return filter.resultForNull().not();
+        }
+
+        @Override
+        public ResultForArg resultForMissing() {
+            return filter.resultForMissing().not();
+        }
+    }
+
+    /**
+     * This is an enumeration of possible fixed outcomes of a filter's test method for a given input
+     * value. It is used now only in connection with {@link RNull} and {@link RMissing} as input
+     * values.
+     * <P>
+     * The <code>FALSE</code>, resp. <code>TRUE</code>, indicates that the filter will always return
+     * <code>false</code>, resp. <code>true</code>, for the given input value.
+     * <p>
+     * The <code>UNSUPPORTED</code> indicates that the the given input value is out of the filter's
+     * domain.
+     *
+     * @see Filter#resultForNull()
+     * @see Filter#resultForMissing()
+     */
+    public enum ResultForArg {
+        TRUE {
+
+            @Override
+            public ResultForArg not() {
+                return FALSE;
+            }
+
+            @Override
+            public ResultForArg and(ResultForArg other) {
+                return other;
+            }
+
+            @Override
+            public ResultForArg or(ResultForArg other) {
+                return TRUE;
+            }
+
+        },
+        FALSE {
+
+            @Override
+            public ResultForArg not() {
+                return TRUE;
+            }
+
+            @Override
+            public ResultForArg and(ResultForArg other) {
+                return FALSE;
+            }
+
+            @Override
+            public ResultForArg or(ResultForArg other) {
+                return other;
+            }
+
+        },
+        UNDEFINED {
+
+            @Override
+            public ResultForArg not() {
+                return UNDEFINED;
+            }
+
+            @Override
+            public ResultForArg and(ResultForArg other) {
+                return UNDEFINED;
+            }
+
+            @Override
+            public ResultForArg or(ResultForArg other) {
+                return other;
+            }
+        };
+
+        public abstract ResultForArg not();
+
+        public abstract ResultForArg and(ResultForArg other);
+
+        public abstract ResultForArg or(ResultForArg other);
     }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java
index b4a6b63bbc6a128d60df7c906e6c26a57d762745..fdb073e07706cfb39c9828068c6bc43ab909cb89 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java
@@ -38,9 +38,12 @@ import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter.VectorSize;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.DoubleFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.FilterVisitor;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.MatrixFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.MissingFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.NotFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.NullFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.OrFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.RTypeFilter;
+import com.oracle.truffle.r.nodes.builtin.casts.Filter.ResultForArg;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapByteToBoolean;
 import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapDoubleToInt;
@@ -373,7 +376,8 @@ public final class PipelineToCastNode {
         public CastNode visit(FilterStep<?, ?> step) {
             ArgumentFilter<?, ?> filter = filterFactory.createFilter(step.getFilter());
             MessageData msg = getDefaultIfNull(step.getMessage(), step.isWarning());
-            return FilterNode.create(filter, step.isWarning(), msg.getCallObj(), msg.getMessage(), msg.getMessageArgs(), boxPrimitives);
+            return FilterNode.create(filter, step.isWarning(), msg.getCallObj(), msg.getMessage(), msg.getMessageArgs(), boxPrimitives, ResultForArg.TRUE.equals(step.getFilter().resultForNull()),
+                            ResultForArg.TRUE.equals(step.getFilter().resultForMissing()));
         }
 
         @Override
@@ -435,7 +439,8 @@ public final class PipelineToCastNode {
             ArgumentFilter<?, ?> condition = filterFactory.createFilter(step.getFilter());
             CastNode trueCastNode = PipelineToCastNode.convert(step.getTrueBranch(), this);
             CastNode falseCastNode = PipelineToCastNode.convert(step.getFalseBranch(), this);
-            return ConditionalMapNode.create(condition, trueCastNode, falseCastNode);
+            return ConditionalMapNode.create(condition, trueCastNode, falseCastNode, ResultForArg.TRUE.equals(step.getFilter().resultForNull()),
+                            ResultForArg.TRUE.equals(step.getFilter().resultForMissing()));
         }
 
         private MessageData getDefaultErrorIfNull(MessageData message) {
@@ -534,6 +539,16 @@ public final class PipelineToCastNode {
             return (ArgumentFilter<Object, Object>) arg -> !toNegate.test(arg);
         }
 
+        @Override
+        public ArgumentFilter<?, ?> visit(NullFilter filter) {
+            return (ArgumentFilter<Object, Object>) arg -> false;
+        }
+
+        @Override
+        public ArgumentFilter<?, ?> visit(MissingFilter filter) {
+            return (ArgumentFilter<Object, Object>) arg -> false;
+        }
+
         @Override
         public ArgumentFilter<?, ?> visit(MatrixFilter<?> filter) {
             return filter.acceptOperation(this);
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 b8cebe877798dabd1920bb2c54757a182ecbab58..c6c19f8f13d4a23dd5a3a3cefefc2ce7271cec9b 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -32,19 +32,25 @@ public abstract class ConditionalMapNode extends CastNode {
 
     private final ArgumentFilter<?, ?> argFilter;
     private final ConditionProfile conditionProfile = ConditionProfile.createBinaryProfile();
+    private final boolean resultForNull;
+    private final boolean resultForMissing;
 
     @Child private CastNode trueBranch;
     @Child private CastNode falseBranch;
 
-    protected ConditionalMapNode(ArgumentFilter<?, ?> argFilter, CastNode trueBranch, CastNode falseBranch) {
+    protected ConditionalMapNode(ArgumentFilter<?, ?> argFilter, CastNode trueBranch, CastNode falseBranch, boolean resultForNull,
+                    boolean resultForMissing) {
         this.argFilter = argFilter;
         this.trueBranch = trueBranch;
         this.falseBranch = falseBranch;
+        this.resultForNull = resultForNull;
+        this.resultForMissing = resultForMissing;
     }
 
     public static ConditionalMapNode create(ArgumentFilter<?, ?> argFilter, CastNode trueBranch,
-                    CastNode falseBranch) {
-        return ConditionalMapNodeGen.create(argFilter, trueBranch, falseBranch);
+                    CastNode falseBranch, boolean resultForNull,
+                    boolean resultForMissing) {
+        return ConditionalMapNodeGen.create(argFilter, trueBranch, falseBranch, resultForNull, resultForMissing);
     }
 
     public ArgumentFilter<?, ?> getFilter() {
@@ -60,17 +66,29 @@ public abstract class ConditionalMapNode extends CastNode {
     }
 
     @Specialization
-    protected RNull executeNull(@SuppressWarnings("unused") RNull x) {
-        return RNull.instance;
+    protected Object executeNull(RNull x) {
+        if (resultForNull) {
+            return trueBranch == null ? x : trueBranch.execute(x);
+        } else {
+            return falseBranch == null ? x : falseBranch.execute(x);
+        }
     }
 
     @Specialization
-    protected RMissing executeMissing(@SuppressWarnings("unused") RMissing x) {
-        return RMissing.instance;
+    protected Object executeMissing(RMissing x) {
+        if (resultForMissing) {
+            return trueBranch == null ? x : trueBranch.execute(x);
+        } else {
+            return falseBranch == null ? x : falseBranch.execute(x);
+        }
     }
 
+    protected static boolean isNotNullOrMissing(Object x) {
+        return x != RNull.instance && x != RMissing.instance;
+    }
+
+    @Specialization(guards = "isNotNullOrMissing(x)")
     @SuppressWarnings("unchecked")
-    @Specialization
     protected Object executeRest(Object x) {
         if (conditionProfile.profile(((ArgumentFilter<Object, Object>) argFilter).test(x))) {
             return trueBranch == null ? x : trueBranch.execute(x);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
index b2c6b9c9f0c2b5831619c41b7bf57d61ec578ae7..779c4f14778a5d45ac3a99d893c6063c570ded1c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
@@ -43,6 +43,8 @@ public abstract class FilterNode extends CastNode {
     private final Object[] messageArgs;
     private final boolean boxPrimitives;
     private final boolean isWarning;
+    private final boolean resultForNull;
+    private final boolean resultForMissing;
 
     private final BranchProfile warningProfile = BranchProfile.create();
     private final ConditionProfile conditionProfile = ConditionProfile.createBinaryProfile();
@@ -50,17 +52,21 @@ public abstract class FilterNode extends CastNode {
 
     @Child private BoxPrimitiveNode boxPrimitiveNode = BoxPrimitiveNodeGen.create();
 
-    protected FilterNode(ArgumentFilter<?, ?> filter, boolean isWarning, RBaseNode callObj, RError.Message message, Object[] messageArgs, boolean boxPrimitives) {
+    protected FilterNode(ArgumentFilter<?, ?> filter, boolean isWarning, RBaseNode callObj, RError.Message message, Object[] messageArgs, boolean boxPrimitives, boolean resultForNull,
+                    boolean resultForMissing) {
         this.filter = filter;
         this.isWarning = isWarning;
         this.callObj = callObj == null ? this : callObj;
         this.message = message;
         this.messageArgs = messageArgs;
         this.boxPrimitives = boxPrimitives;
+        this.resultForNull = resultForNull;
+        this.resultForMissing = resultForMissing;
     }
 
-    public static FilterNode create(ArgumentFilter<?, ?> filter, boolean isWarning, RBaseNode callObj, RError.Message message, Object[] messageArgs, boolean boxPrimitives) {
-        return FilterNodeGen.create(filter, isWarning, callObj, message, messageArgs, boxPrimitives);
+    public static FilterNode create(ArgumentFilter<?, ?> filter, boolean isWarning, RBaseNode callObj, RError.Message message, Object[] messageArgs, boolean boxPrimitives, boolean resultForNull,
+                    boolean resultForMissing) {
+        return FilterNodeGen.create(filter, isWarning, callObj, message, messageArgs, boxPrimitives, resultForNull, resultForMissing);
     }
 
     public ArgumentFilter getFilter() {
@@ -83,16 +89,26 @@ public abstract class FilterNode extends CastNode {
     }
 
     @Specialization
-    protected RNull executeNull(@SuppressWarnings("unused") RNull x) {
-        return RNull.instance;
+    protected Object executeNull(RNull x) {
+        if (!resultForNull) {
+            handleMessage(x);
+        }
+        return x;
     }
 
     @Specialization
-    protected RMissing executeMissing(@SuppressWarnings("unused") RMissing x) {
-        return RMissing.instance;
+    protected Object executeMissing(RMissing x) {
+        if (!resultForMissing) {
+            handleMessage(x);
+        }
+        return x;
     }
 
-    @Specialization
+    protected static boolean isNotNullOrMissing(Object x) {
+        return x != RNull.instance && x != RMissing.instance;
+    }
+
+    @Specialization(guards = "isNotNullOrMissing(x)")
     public Object executeRest(Object x) {
         if (!conditionProfile.profile(evalCondition(valueProfile.profile(x)))) {
             handleMessage(x);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
index f709a59d66a730e3adc7cad28e463431066cd07a..397f7cb57b26d98c74ad59bb2d2d13b381fbe2c8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
@@ -27,6 +27,7 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
@@ -154,6 +155,11 @@ public abstract class NonNANode extends CastNode {
         return x;
     }
 
+    @Specialization
+    protected Object onMissing(RMissing x) {
+        return x;
+    }
+
     protected boolean isComplete(RAbstractContainer x) {
         return x.isComplete();
     }