summaryrefslogtreecommitdiff
path: root/chapter29/src/test/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter29/src/test/java/com')
-rw-r--r--chapter29/src/test/java/com/seaofnodes/simple/ConstructorCompletionTest.java77
-rw-r--r--chapter29/src/test/java/com/seaofnodes/simple/Eval2.java3
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