package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class Chapter10Test { @Test public void testDeadReferenceReturn() { for( String body : new String[] { "return new S; return 0;", "if(1) return new S; else return 0;" } ) { String src = "struct S { int x; }; " + body; var code = new CodeGen(src).parse().opto().typeCheck(); // This chapter also has constructor/class-init Returns in the CompUnit. var entry = (com.seaofnodes.simple.node.FunNode)code._start.uctrl(); org.junit.Assert.assertTrue(body, entry.ret().expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr); } } private static final String NULLABLE_POINT_SOURCE = """ struct Point { int x; new Point = { int v -> x = v; }; }; !Point?[] !points = new !Point?[2]; points[arg] = new Point(42); Point? p = points[1]; """; @Test public void testNullGuards() { // Issue #246: != null lowers to !!p. Follow every negation, on either arm. 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;", "val b = !!p; if (b) return p.x + b - 1; return -1;", "if (!(p == null || arg == 0)) return p.x; return -1;" } ) for( long seed : new long[] { 1, 42, 126 } ) { CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body,seed,true) .driver(CodeGen.Phase.TypeCheck); assertEquals(body,"-1",Eval2.eval(code,0)); assertEquals(body,"42",Eval2.eval(code,1)); code.driver(CodeGen.Phase.LocalSched); } } @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 (p == null) return p.x; return -1;", "if (!!points) return p.x; return -1;", "if (!!p) { int x = p.x; } return p.x;" } ) { try { new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck); fail(body); } catch( Parser.ParseException e ) { assertEquals(body,"Might be null accessing 'x'",e.getMessage()); } } } @Test public void testFuzzer() { CodeGen code = new CodeGen( """ int a = arg/3; int b = arg*5; int x = arg*7; int y = arg/11; int p; int g; int h; if( (arg/13)==0 ) { p = x + y; g = x; h = y; } else { p = a + b; g = a; h = b; } int r = g+h; return p-r; """); code.parse().opto(); assertEquals("return 0;", code.print()); } @Test public void testStruct() { CodeGen code = new CodeGen(""" struct _Bar { int a; int b; }; struct _Foo { int x; }; _Foo? foo = null; !_Bar !bar = new _Bar; bar.a = 1; bar.a = 2; return bar.a; """); code.parse().opto(); assertEquals("return 2;", code.print()); } @Test public void testExample() { CodeGen code = new CodeGen(""" struct Vector2D { int x; int y; }; !Vector2D !v = new Vector2D; v.x = 1; if (arg) v.y = 2; else v.y = 3; return v; """); code.parse().opto(); assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:.x=0; 3:.y=0;]; return Test.Vector2D; ]", code.print()); } @Test public void testBug() { CodeGen code = new CodeGen(""" struct s0 { int v0; }; s0? v1=null; int v3=v1.zAicm; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); } } @Test public void testBug2() { CodeGen code = new CodeGen(""" struct s0 { int v0; }; arg=0+new s0.0; """); try { code.parse(); fail(); } catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); } } @Test public void testLoop() { CodeGen code = new CodeGen(""" struct Bar { int a; }; !Bar !bar = new Bar; while (arg) { bar.a = bar.a + 2; arg = arg + 1; } return bar.a; """); code.parse().opto(); assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return MEM[ 2:.a=0;]; return Test.Bar; ]", code.print()); } @Test public void testIf() { CodeGen code = new CodeGen(""" struct Bar { int a; }; !Bar !bar = new Bar; if (arg) bar = null; bar.a = 1; return bar.a; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar",e.getMessage()); } } @Test public void testIf2() { CodeGen code = new CodeGen(""" struct Bar { int a; }; !Bar? !bar = null; if (arg) bar = new Bar; bar.a = 1; return bar.a; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } } @Test public void testIf3() { CodeGen code = new CodeGen(""" struct Bar { int a; }; !Bar !bar = null; if (arg) bar = null; bar.a = 1; return bar.a; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar", e.getMessage()); } } @Test public void testIfOrNull() { CodeGen code = new CodeGen(""" struct _Bar { int a; }; !_Bar? !bar = new _Bar; if (arg) bar = null; if( bar ) bar.a = 1; return bar.a; """); code.parse().opto(); assertEquals("return Phi(Region,1,.a);", code.print()); } @Test public void testIfOrNull2() { CodeGen code = new CodeGen( """ struct _Bar { int a; }; !_Bar? !bar = new _Bar; if (arg) bar = null; int rez = 3; if( !bar ) rez=4; else bar.a = 1; return rez; """); code.parse().opto(); assertEquals("return Phi(Region,4,3);", code.print()); } @Test public void testWhileWithNullInside() { CodeGen code = new CodeGen(""" struct s0 {int v0;}; !s0? !v0 = new s0; int ret = 0; while(arg) { ret = v0.v0; v0 = null; arg = arg - 1; } return ret; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'v0'", e.getMessage()); } } @Test public void testRedeclareStruct() { CodeGen code = new CodeGen(""" struct s0 { int v0; }; s0? v1=new s0; !s0? !v1; v1=new s0; """); try { code.parse(); fail(); } catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); } } @Test public void testIter() { // Build and use an iterator CodeGen code = new CodeGen( """ struct Iter { int x; int len; }; !Iter !i = new Iter; i.len = arg; int sum=0; while( i.x < i.len ) { sum = sum + i.x; i.x = i.x + 1; } return sum; """); code.parse().opto(); assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return MEM[ 2:.x=0; 3:.len=0;]; return Test.Iter; ]", code.print()); } @Test public void test1() { CodeGen code = new CodeGen(""" struct s0 {int v0;}; !s0 !ret = new s0; while(arg) { !s0 !v0 = new s0; v0.v0 = arg; arg = arg-1; if (arg==5) ret=v0; } return ret; """); code.parse().opto(); assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return MEM[ 2:.v0=0;]; return Test.s0; ]", code.print()); } @Test public void test2() { CodeGen code = new CodeGen(""" struct _s0 {int v0;}; !_s0 !ret = new _s0; !_s0 !v0 = new _s0; while(arg) { v0.v0 = arg; arg = arg-1; if (arg==5) ret=v0; } return ret.v0; """); code.parse().opto(); assertEquals("return .v0;", code.print()); } @Test public void test3() { CodeGen code = new CodeGen(""" struct _s0 {int v0;}; !_s0 !ret = new _s0; while(arg < 10) { !_s0 !v1 = new _s0; if (arg == 5) ret=v1; arg = arg + 1; } return ret.v0; """); code.parse().opto(); assertEquals("return .v0;", code.print()); } @Test public void testModGlobalInConstructor() { CodeGen code = new CodeGen( """ int cnt = 1; // cnt is incremented exactly once, it is not part of s0 constructor // hence s0.x is always 1, and cnt remains at 2 struct s0 { int x = cnt++; }; s0 a = new s0(); s0 b = new s0(); // a.x==1, b.x==1, cnt==2 return a.x * 100 + b.x * 10 + cnt; """); code.parse().iter().opto(); assertEquals("Stop[ return 112; return MEM[ 2:___ 3:.x=1;]; return Test.s0; ]", code.print()); } @Test public void testBug3() { CodeGen code = new CodeGen(""" struct s0 { int f0; }; return new s0; int v0=null.f0; """); try { code.parse(); fail(); } catch( Exception e ) { assertEquals("Syntax error, expected `;` but found `.`",e.getMessage()); } } @Test public void testBug4() { CodeGen code = new CodeGen(""" if(0) { while(0) if(arg) continue; int v0=0; while(1) { int v2=-arg; v0=v2; } } return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); } @Test public void testBug5() { CodeGen code = new CodeGen(""" struct s0 { int f0; }; if(0) return 0; else return new s0; if(new s0.f0) return 0; """); code.parse().iter().opto().typeCheck(); assertEquals("Stop[ return Test.s0; return MEM[ 2:.f0=0;]; return Test.s0; ]", code.print()); } @Test public void testBug6MissedWorklist() { CodeGen code = new CodeGen(""" while(0) {} int v4=0; while(0