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..0f91874393b1e52bcd077ba9152f6b9ec2927d58 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 @@ -31,7 +31,9 @@ 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; @@ -425,6 +427,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)); } 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(); }