diff options
Diffstat (limited to 'chapter29/src/test/java/com')
| -rw-r--r-- | chapter29/src/test/java/com/seaofnodes/simple/ConstructorCompletionTest.java | 77 | ||||
| -rw-r--r-- | chapter29/src/test/java/com/seaofnodes/simple/Eval2.java | 3 |
2 files changed, 78 insertions, 2 deletions
diff --git a/chapter29/src/test/java/com/seaofnodes/simple/ConstructorCompletionTest.java b/chapter29/src/test/java/com/seaofnodes/simple/ConstructorCompletionTest.java new file mode 100644 index 00000000..6a229a4f --- /dev/null +++ b/chapter29/src/test/java/com/seaofnodes/simple/ConstructorCompletionTest.java @@ -0,0 +1,77 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.parser.Parser; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ConstructorCompletionTest { + private void rejects(String fields, String field, boolean explicit) { + String src = "struct B {}; struct A { "+fields+" }; return 0;"; + try { + new CodeGen(src).parse(); + fail(src); + } catch( Parser.ParseException e ) { + assertEquals("'Test.A' is not fully initialized, field '"+field+ + (explicit ? "' is only partially set in the constructor" : + "' needs to be set in a constructor"),e.getMessage()); + } + } + + @Test public void testMissingNumericFields() { + for( String type : new String[]{"int", "u8", "f32"} ) { + rejects(type+"~ x;","x",false); + rejects(type+"~ x; new A = { -> };","x",true); + // Converting the merged BOTTOM to the numeric storage type used + // to hide the missing assignment from the returned memory. + rejects(type+"~ x; new A = { -> if(arg) x = 1; };","x",true); + rejects(type+"~ x; new A = { -> if(arg) return 0; x = 1; };","x",true); + rejects(type+"~ x; new A = { -> while(arg--) x = 1; };","x",true); + } + } + + @Test public void testInitializerAndConstructorReturns() { + rejects("B b;","b",false); + rejects("new A = { -> if(arg) b = new B; }; B b;","b",true); + String[] fields = { + "int~ x; new A = { -> if(arg) { x = 3; return 0; } x = 7; };", + // <init> has no value for x; the user constructor supplies it. + "new A = { -> x = 7; }; int~ x;", + // The user constructor inherits the declaration's default. + "new A = { -> }; int~ x = 7;", + "B? b; int x;", + }; + String[][] values = {{"7","3"}, {"7","7"}, {"7","7"}, {"0","0"}}; + for( int i = 0; i<fields.length; i++ ) { + CodeGen code = new CodeGen("struct B {}; struct A { "+fields[i]+" }; return new A().x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals(values[i][0],Eval2.eval(code,0)); + assertEquals(values[i][1],Eval2.eval(code,1)); + } + } + + @Test public void testNonreturningPaths() { + // An initializer parsed in dead enclosing code can have TOP memory + // rather than a struct-shaped result. + new CodeGen("while(false) { struct A { int x; }; } return 0;").parse(); + new CodeGen("struct A { int~ x; new A = { -> while(true) {} }; }; return 0;").parse(); + rejects("int~ x; new A = { -> if(arg) return 0; while(true) {} };","x",true); + CodeGen code = new CodeGen(""" + struct A { + int~ x; + new A = { -> if(arg) { x = 7; return 0; } while(true) {} }; + }; + return new A().x; + """).driver(CodeGen.Phase.TypeCheck); + assertEquals("7",Eval2.eval(code,1)); + } + + @Test public void testEarlierConstructorBinding() { + // The older binding spelling is parsed before the following field. + // Its Return lacks that field, so completion must consult <init> too. + rejects("val A = { -> }; B b;","b",true); + rejects("int~ x; val A = { -> if(arg) x = 1; };","x",true); + new CodeGen("struct B {}; struct A { val A = { -> }; B b = new B; }; return 0;").parse(); + new CodeGen("struct A { val A = { -> while(true) {} }; int~ x; }; return 0;").parse(); + } +} diff --git a/chapter29/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter29/src/test/java/com/seaofnodes/simple/Eval2.java index 6e5331b2..8d0e3d5b 100644 --- a/chapter29/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/chapter29/src/test/java/com/seaofnodes/simple/Eval2.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple; -import com.seaofnodes.simple.parser.Parser; import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.IRPrinter; @@ -372,7 +371,7 @@ public abstract class Eval2 { yield new Object[0]; } - } else if( Parser.startsClzPrefix(tmp._obj._name) ) { + } else if( TypeStruct.startsClzPrefix(tmp._obj._name) ) { yield classObject(tmp._obj); } else { // Constant object with all zeros |
