summaryrefslogtreecommitdiff
path: root/chapter15/src
diff options
context:
space:
mode:
Diffstat (limited to 'chapter15/src')
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/ScopeNode.java7
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter10Test.java41
2 files changed, 48 insertions, 0 deletions
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 877ae4a4..9c4c4403 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -252,6 +252,13 @@ public class ScopeNode extends Node {
// combinations, and replaces the variable with the upcast variant.
public Node upcast( Node ctrl, Node pred, boolean invert ) {
if( ctrl._type==Type.XCONTROL ) return null;
+ // A single negation is handled below. For !!p (including p != null),
+ // also refine the underlying p without changing the Boolean result.
+ if( pred instanceof NotNode not && not.in(1) instanceof NotNode ) {
+ pred.keep(); // Recursive peepholes may rediscover this Not.
+ upcast(ctrl,not.in(1),!invert);
+ pred.unkeep();
+ }
// Invert the If conditional
if( invert )
pred = pred instanceof NotNode not ? not.in(1) : IterPeeps.add(new NotNode(pred).peephole());
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index def9aaa6..cf817b28 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -7,6 +7,47 @@ import static org.junit.Assert.fail;
public class Chapter10Test {
+ // Issue #246: null-check guards start in Chapter 10; arrays arrive in Chapter 15.
+ private static final String NULLABLE_POINT_SOURCE = """
+ struct Point { int x; };
+ Point?[] points = new Point?[2];
+ Point point = new Point;
+ point.x = 42;
+ points[arg] = point;
+ Point? p = points[1];
+ """;
+
+ @Test
+ public void testNullGuards() {
+ for( String body : new String[] {
+ "if (p != null) return p.x; return -1;",
+ "if (null != p) return p.x; return -1;",
+ "if (!!!!p) return p.x; return -1;",
+ "if (!!!p) return -1; return p.x;",
+ "int b = !!p; if (b) return p.x + b - 1; return -1;"
+ } ) {
+ StopNode stop = new Parser(NULLABLE_POINT_SOURCE+body).parse().iterate();
+ assertEquals(body,-1L,com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,0));
+ assertEquals(body,42L,com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,1));
+ }
+ }
+
+ @Test
+ public void testNullGuardErrors() {
+ for( String body : new String[] {
+ "return p.x;",
+ "if (!!points) return p.x; return -1;",
+ "if (!!p) { int x = p.x; } return p.x;"
+ } ) {
+ try {
+ new Parser(NULLABLE_POINT_SOURCE+body).parse().iterate();
+ fail(body);
+ } catch( RuntimeException e ) {
+ assertEquals(body,"Might be null accessing 'x'",e.getMessage());
+ }
+ }
+ }
+
@Test
public void testFuzzer() {
Parser parser = new Parser(