summaryrefslogtreecommitdiff
path: root/chapter23/src/test/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-09-19 10:30:13 -0700
committerCliff Click <cliffc@acm.org>2026-09-19 10:30:13 -0700
commitbf3e5632ddf21c109f7ef0f61bcc62e2ed0f9d49 (patch)
treebe2972819600b0a9790fceb882b5464c7c12857a /chapter23/src/test/java/com
parent200162345900136663027b6deae21d72cf30818c (diff)
downloadSimple-bf3e5632ddf21c109f7ef0f61bcc62e2ed0f9d49.tar.gz
Simple-bf3e5632ddf21c109f7ef0f61bcc62e2ed0f9d49.zip
Handle double-negation preserving not-null knowledge
Diffstat (limited to 'chapter23/src/test/java/com')
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java55
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Eval2.java8
2 files changed, 62 insertions, 1 deletions
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index ec363239..c1b5e3a8 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -7,6 +7,61 @@ 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];
+ points[arg] = new Point { x = 42; };
+ 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;",
+ "if (!(p == null || arg == 0)) return p.x; return -1;"
+ } ) {
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ assertEquals(body,"-1",Eval2.eval(code,0));
+ assertEquals(body,"42",Eval2.eval(code,1));
+ }
+ }
+
+ @Test
+ public void testShortCircuitGuardScheduling() {
+ // The call result exists only on the RHS path, not above the merge.
+ CodeGen code = new CodeGen("""
+ val f = { int n -> (n+1)&7; };
+ int x = 0;
+ if (!!(arg && (x=f(arg)))) return x;
+ return -1;
+ """).driver(CodeGen.Phase.TypeCheck);
+ assertEquals("-1",Eval2.eval(code,0));
+ assertEquals("2",Eval2.eval(code,1));
+ assertEquals("-1",Eval2.eval(code,7));
+ code.driver(CodeGen.Phase.LocalSched);
+ }
+
+ @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 CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ fail(body);
+ } catch( RuntimeException e ) {
+ assertEquals(body,"Might be null accessing 'x'",e.getMessage());
+ }
+ }
+ }
+
@Test
public void testFuzzer() {
CodeGen code = new CodeGen(
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
index 094b5143..1254c0d3 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -271,7 +271,7 @@ public abstract class Eval2 {
case MulFNode mlf -> d(mlf.in(1)) * d(mlf.in(2));
case MulNode mul -> x(mul.in(1)) * x(mul.in(2));
case NewNode alloc-> alloc(alloc);
- case NotNode not -> x(not.in(1)) == 0 ? 1L : 0L;
+ case NotNode not -> not(not);
case OrNode or -> x(or .in(1)) | x(or .in(2));
case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0));
case ReadOnlyNode read -> val(read.in(1));
@@ -288,6 +288,12 @@ public abstract class Eval2 {
};
}
+ private static Object not( NotNode not ) {
+ Object n = val(not.in(1));
+ return ( n==null || (n instanceof Double D && D==0.0) || n instanceof Long L && L==0 )
+ ? 1L : 0L;
+ }
+
// Fetch without unboxing, searching up-Frame
static Object val( Node n ) { return F.get(n); }
// Fetch and unbox as primitive long