summaryrefslogtreecommitdiff
path: root/chapter28b/src/test/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter28b/src/test/java/com')
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter10Test.java20
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter12Test.java2
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java2
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter19Test.java2
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java22
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java38
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java65
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java22
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/EscapeFlowTest.java112
9 files changed, 226 insertions, 59 deletions
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 6892db0c..165eba21 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -157,7 +157,7 @@ else
return v;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:___ 3:.x=0; 4:.y=0;]; return Test.Vector2D; ]", code.print());
+ assertEquals("Stop[ return Test.Vector2D; return Test.Vector2D; ]", code.print());
}
@Test
@@ -195,7 +195,7 @@ while (arg) {
return bar.a;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return MEM[ 2:___ 3:.a=0;]; return Test.Bar; ]", code.print());
+ assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return Test.Bar; ]", code.print());
}
@Test
@@ -244,10 +244,10 @@ struct _Bar { int a; };
!_Bar? !bar = new _Bar;
if (arg) bar = null;
if( bar ) bar.a = 1;
-return bar.a;
+return bar;
""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,1,.a);", code.print());
+ code.driver(CodeGen.Phase.TypeCheck);
+ assertEquals("return Phi(Region,null,Test._Bar);", code.print());
}
@Test
@@ -317,7 +317,7 @@ while( i.x < i.len ) {
return sum;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return MEM[ 2:___ 3:.x=0; 4:.len=0;]; return Test.Iter; ]", code.print());
+ assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return Test.Iter; ]", code.print());
}
@@ -336,7 +336,7 @@ while(arg) {
return ret;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return MEM[ 2:___ 3:.v0=0;]; return Test.s0; ]", code.print());
+ assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return Test.s0; ]", code.print());
}
@Test
@@ -389,7 +389,7 @@ s0 b = new s0();
return a.x * 100 + b.x * 10 + cnt;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return 112; return MEM[ 2:___ 3:___ 4:.x=1;]; return Test.s0; ]", code.print());
+ assertEquals("Stop[ return 112; return Test.s0; ]", code.print());
}
@@ -432,7 +432,7 @@ else return new s0;
if(new s0.f0) return 0;
""");
code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("Stop[ return Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
+ assertEquals("Stop[ return Test.s0; return Test.s0; ]", code.print());
}
@Test
@@ -460,7 +460,7 @@ s0 v1 = v0;
return v1;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return (const)Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
+ assertEquals("Stop[ return (const)Test.s0; return Test.s0; ]", code.print());
}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index fae7611d..973b15d9 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -101,7 +101,7 @@ N n = new N(new M);
return n.next;
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return (const)Test.M; return MEM[ 2:___ 3:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 8:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:___ 8:.i=0;]; return Test.N; ]", code.print());
+ assertEquals("Stop[ return (const)Test.M; return Test.M; return Test.N; ]", code.print());
}
@Test
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 632b9539..8e109991 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -345,7 +345,7 @@ for(;;) {
}
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Top; return Parm_i(x,i64); ]", code.print());
+ assertEquals("return Top;", code.print());
assertNull( Eval2.eval( code, 0 ) ); // Infinite loop, <clinit> never exits
}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index d5fe207a..3cc00922 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -118,7 +118,7 @@ val hashCode = { !String self ->
hashCode(new String("Hello, World!"));
""";
CodeGen code = new CodeGen(src).driver(Phase.LocalSched);
- assertEquals("Stop[ return Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return MEM[ 2:___ 3:___ 4:___ 5:.cs=Parm_s(Test.String.String,*[]u8[final]); 6:._hashCode=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:._hashCode=0;]; return Test.String; return Phi(Region,0,1,0,1); return Phi(Region,(!=0)._hashCode,Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code.print());
+ assertEquals("Stop[ return Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Test.String; return Phi(Region,0,1,0,1); return Phi(Region,(!=0)._hashCode,Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code.print());
assertEquals("4029215624828139541", Eval2.eval(code, 2));
}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 66a318ae..7eb1f255 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -241,13 +241,6 @@ val equals = { !String !self, !String s ->
return true;
};
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
@@ -255,11 +248,18 @@ val _hashCodeString = { !String self ->
if( !hash ) hash = 123456789;
return hash;
};
+
+// Return the String hashCode (cached, and never 0)
+val hashCode = { !String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
""";
- // The implicit return exposes _hashCodeString, so retain its body too.
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",9,null);
- testCPU(src,"arm" , "SystemV",7,null);
+ // Return the public hashCode wrapper; keep its helper private.
+ testCPU(src,"x86_64_v2", "SystemV",12,null);
+ testCPU(src,"riscv" , "SystemV",8,null);
+ testCPU(src,"arm" , "SystemV",6,null);
}
@Test
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index f1dc54e2..e15c2581 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -368,13 +368,6 @@ val equals = { !String !self, !String s ->
return true;
};
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
@@ -382,11 +375,18 @@ val _hashCodeString = { !String self ->
if( !hash ) hash = 123456789;
return hash;
};
+
+// Return the String hashCode (cached, and never 0)
+val hashCode = { !String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
""";
- // The implicit return exposes _hashCodeString, so retain its body too.
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",9,null);
- testCPU(src,"arm" , "SystemV",7,null);
+ // Return the public hashCode wrapper; keep its helper private.
+ testCPU(src,"x86_64_v2", "SystemV",12,null);
+ testCPU(src,"riscv" , "SystemV",8,null);
+ testCPU(src,"arm" , "SystemV",6,null);
}
@Test public void testStringExport() throws IOException {
@@ -408,13 +408,6 @@ val equals = { !String !self, !String s ->
return true;
};
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
@@ -422,8 +415,15 @@ val _hashCodeString = { !String self ->
if( !hash ) hash = 123456789;
return hash;
};
+
+// Return the String hashCode (cached, and never 0)
+val hashCode = { !String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
""";
- TestC.runC(src, "stringHash", "",21);
+ TestC.runC(src, "stringHash", "",12);
}
@Test public void testLoop2() {
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index f5eba8f5..1ad1e129 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -12,6 +12,8 @@ import com.seaofnodes.simple.node.CallNode;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.LoopNode;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.type.TypeTuple;
import com.seaofnodes.simple.type.XInt;
import com.seaofnodes.simple.util.Ary;
import java.io.File;
@@ -73,21 +75,25 @@ public class Chapter25Test {
for( long seed : new long[]{1,123,987654321} ) {
CodeGen code = new CodeGen(src,seed,true).driver(CodeGen.Phase.Parse);
FunNode visible = null;
+ int[] privateFidxs = XInt.EMPTY;
int checked = 0;
for( FunNode fun : code._linker ) {
if( fun==null || fun._name==null ) continue;
if( fun._name.equals("visible") ) {
- assertTrue(code.publicFIDX(fun.sig().fidx()));
visible = fun;
checked++;
}
if( fun._name.equals("_hidden") || fun._name.equals("hiddenClassMethod") ) {
- assertFalse(code.publicFIDX(fun.sig().fidx()));
+ privateFidxs = XInt.make(privateFidxs,fun.sig().fidx());
checked++;
}
}
assertEquals(3,checked);
code.driver(CodeGen.Phase.TypeCheck);
+ TypeMem outside = ((TypeTuple)code._start._type).mem();
+ assertTrue(XInt.bit(outside._escFs,visible.sig().fidx()));
+ for( int fidx = XInt.next(privateFidxs,0); fidx>=0; fidx = XInt.next(privateFidxs,fidx) )
+ assertFalse(XInt.bit(outside._escFs,fidx));
assertTrue(visible.unknownCallers());
assertEquals(TypeInteger.U8,visible.parm(3)._type);
assertEquals("0",Eval2.eval(code,0));
@@ -101,25 +107,52 @@ public class Chapter25Test {
val _private = { int x -> x+1; };
return 0;
""").parse();
- int checked = 0;
+ int published = -1, unpublished = -1;
for( FunNode fun : code._linker ) {
if( fun==null || fun._name==null ) continue;
- if( fun._name.equals("_hidden") ) {
- // A public field can publish a privately named function.
- assertTrue(code.publicFIDX(fun.sig().fidx()));
- checked++;
- }
- if( fun._name.equals("_private") ) {
- assertFalse(code.publicFIDX(fun.sig().fidx()));
- checked++;
- }
+ if( fun._name.equals("_hidden") ) published = fun.sig().fidx();
+ if( fun._name.equals("_private") ) unpublished = fun.sig().fidx();
}
- assertEquals(2,checked);
+ assertTrue(published>=0 && unpublished>=0);
var clz = code.compunit()._clz;
- assertTrue(XInt.bit(code.publicAliases(),clz.field("visible")._alias));
- assertFalse(XInt.bit(code.publicAliases(),clz.field("_hidden")._alias));
- assertFalse(XInt.bit(code.publicAliases(),clz.field("_private")._alias));
code.driver(CodeGen.Phase.TypeCheck);
+ TypeMem outside = ((TypeTuple)code._start._type).mem();
+ // A public field can publish a privately named function.
+ assertTrue(XInt.bit(outside._escFs,published));
+ assertFalse(XInt.bit(outside._escFs,unpublished));
+ assertTrue(XInt.bit(outside._escAs,clz.field("visible")._alias));
+ assertFalse(XInt.bit(outside._escAs,clz.field("_hidden")._alias));
+ assertFalse(XInt.bit(outside._escAs,clz.field("_private")._alias));
+ }
+
+ @Test public void testImportedEscapeExclusions() throws IOException {
+ Path build=Path.of("build/objs");
+ Files.createDirectories(build);
+ Path dir=Files.createTempDirectory(build,"escape_exclusions_");
+ CodeGen library=new CodeGen(null,dir.toString(),null,"Lib","""
+ val _hidden_noInline={int n -> n+100;};
+ val apply_noInline={{int->int} f,int n -> f(n);};
+ return apply_noInline;
+ """,123,true,TypeInteger.BOT).driver("x86_64_v2","win64",false,false);
+ int savedHidden=((com.seaofnodes.simple.type.TypeFunPtr)
+ library.compunit()._clz.field("_hidden_noInline")._t).fidx();
+ // Allocate unrelated IDs before loading Lib so saved exclusions must
+ // remap by owner/slot, not by their numeric positions in the file.
+ CodeGen code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Client","""
+ val _other={int n -> n-1;};
+ val inc={int n -> n+1;};
+ return Lib.apply_noInline(inc,arg);
+ """,987654321,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ var clz=(com.seaofnodes.simple.type.TypeStruct)Parser.TYPES.get("class:Lib");
+ int hidden=((com.seaofnodes.simple.type.TypeFunPtr)clz.field("_hidden_noInline")._t).fidx();
+ assertNotEquals(savedHidden,hidden);
+ TypeMem outside=((TypeTuple)code._start._type).mem();
+ assertTrue(XInt.isHigh(outside._escFs));
+ assertFalse(XInt.bit(outside._escFs,hidden));
+ assertEquals("42",Eval2.eval(code,41));
+ Files.delete(dir.resolve("Lib.o"));
+ Files.delete(dir);
}
@Test public void testNeverReturnPointerRiscV() {
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
index 38b4d8a2..dfd7a78c 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
@@ -157,6 +157,28 @@ public class ClassInitDependenciesTest {
assertTrue(touches(code,deps,"Root/B","Root"));
}
+ @Test public void testUnknownCallbackCycle() throws Exception {
+ for( long seed : new long[]{1,123,987654321} ) {
+ try {
+ compile("A",Map.of(
+ "A", """
+ { {int->int} -> {int->int} } bounce="C";
+ val callback_noInline={int n -> B.value;};
+ val f=bounce(callback_noInline);
+ int value=f(1);
+ """,
+ "B", "int value=A.value;"),seed);
+ fail("Expected callback initialization cycle");
+ } catch( Parser.ParseException ex ) {
+ String message=ex.getMessage();
+ assertTrue(message,message.startsWith("Cyclic class initialization:"));
+ assertTrue(message,message.contains(" -> unknown call"));
+ assertTrue(message,message.contains("unknown call -> "));
+ assertTrue(message,message.contains("Root.B -> Root.A"));
+ }
+ }
+ }
+
@Test public void testCycles() throws Exception {
cycle("A",Map.of("A","val get_noInline={ ->\n B.value;\n};\nint value=get_noInline();",
"B","int value=A.value;"),"A -> B -> A");
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/EscapeFlowTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/EscapeFlowTest.java
new file mode 100644
index 00000000..71a281b5
--- /dev/null
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/EscapeFlowTest.java
@@ -0,0 +1,112 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class EscapeFlowTest {
+ // A cofinite outside set must not revive private allocation helpers.
+ @Test public void testOpaqueRoundTrip() {
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code = new CodeGen("""
+ struct _A { B? !b; new _A = { B? p -> b=p; }; };
+ val wrap = { B? b -> return new _A(b); };
+ return wrap;
+ """,seed,true).driver(CodeGen.Phase.TypeCheck);
+ TypeMem outside = ((TypeTuple)code._start._type).mem();
+ assertTrue(XInt.isHigh(outside._escFs));
+ assertNotSame(XInt.FULL,outside._escFs);
+ for( FunNode fun : code._linker )
+ if( fun!=null && fun._name!=null && fun._name.contains("_A.") )
+ assertFalse(fun._name,fun.unknownCallers());
+ }
+ }
+
+ // Foreign callbacks remain possible. The default callback argument reads
+ // Start memory rather than a frozen declaration constant.
+ @Test public void testUnknownCallerCallback() {
+ CodeGen code = new CodeGen("""
+ val _hidden_noInline = { int n -> return n+2; };
+ val apply_noInline = { {int->int} f,int n -> return f(n); };
+ return apply_noInline;
+ """).parse();
+ TypeMemPtr self = (TypeMemPtr)named(code,"apply_noInline").parm(2).in(1)._type;
+ assertTrue(self._one);
+ assertEquals("class:Test",self._obj._name);
+ code.driver(CodeGen.Phase.TypeCheck);
+ FunNode apply = named(code,"apply_noInline");
+ assertTrue(apply.unknownCallers());
+ ParmNode callback = apply.parm(3);
+ assertTrue(callback.in(1) instanceof ProjNode);
+ TypeFunPtr type = (TypeFunPtr)callback._type;
+ assertTrue(XInt.isHigh(type.fidxs()));
+ assertSame(TypeInteger.BOT,apply.ret().expr()._type);
+ // Only <clinit> and apply escape; forwarding #self must not expose _hidden.
+ assertEquals("Stop[ return { apply_noInline}; return #2; ]",code.print());
+ }
+
+ @Test public void testNativeCallbackRoundTrip() {
+ CodeGen code = new CodeGen("""
+ val _callback_noInline = { int n -> return n+1; };
+ { {int->int} -> {int->int} } bounce = "C";
+ return bounce(_callback_noInline)(arg);
+ """).driver(CodeGen.Phase.TypeCheck);
+ FunNode callback = named(code,"_callback_noInline");
+ assertTrue(callback.unknownCallers());
+ assertTrue(XInt.bit(((TypeTuple)code._start._type).mem()._escFs,callback.sig().fidx()));
+ }
+
+ // Call remains CONTROL. Changing only the outside set must wake CallEnd
+ // and extend the possible callback returned by the native factory.
+ @Test public void testOutsideReturnDependency() {
+ CodeGen code = new CodeGen("return 0;").parse();
+ code._phase = CodeGen.Phase.Opto;
+ code.externFunc(100,"factory");
+ TypeFunPtr callback = TypeFunPtr.make(false,TypeFunPtr.TEMPTY,TypeInteger.BOT);
+ TypeFunPtr factory = TypeFunPtr.make1((byte)2,true,TypeFunPtr.TEMPTY,callback,100);
+ CallNode call = new CallNode(null,ConstantNode.raw(Type.CONTROL),
+ ConstantNode.raw(TypeMem.START),ConstantNode.raw(factory)).init();
+ CallEndNode end = new CallEndNode(call,TypeRPC.BOT);
+ int[] foreign = XInt.dual(XInt.make(7));
+ code._start._type = TypeTuple.make(Type.CONTROL,TypeMem.START.makeFrom(foreign,XInt.EMPTY),TypeInteger.BOT);
+ TypeTuple before = (TypeTuple)end.compute();
+ assertFalse(XInt.bit(((TypeFunPtr)before.ret()).fidxs(),7));
+ boolean depends = false;
+ for( int i = 0; i<code._start.nDeps(); i++ ) depends |= code._start.dep(i)==end;
+ assertTrue(depends);
+ code._start._type = TypeTuple.make(Type.CONTROL,TypeMem.START.makeFrom(XInt.FULL,XInt.EMPTY),TypeInteger.BOT);
+ TypeTuple after = (TypeTuple)end.compute();
+ assertTrue(before.isa(after));
+ assertTrue(XInt.bit(((TypeFunPtr)after.ret()).fidxs(),7));
+ }
+
+ // Public construction exposes the allocator; private-memory helpers
+ // remain internal and can inline into it.
+ @Test public void testPublicAllocator() {
+ CodeGen code = new CodeGen("""
+ struct Box { int n; new Box = { int x -> n=x; }; };
+ return new Box(arg).n;
+ """).parse();
+ FunNode allocator = named(code,"Test.Box.<new>");
+ int[] helpers = XInt.EMPTY;
+ for( FunNode fun : code._linker )
+ if( fun!=null && fun.sig().ret() instanceof TypeMem )
+ helpers = XInt.make(helpers,fun.sig().fidx());
+ assertNotSame(XInt.EMPTY,helpers);
+ code.driver(CodeGen.Phase.TypeCheck);
+ TypeMem outside = ((TypeTuple)code._start._type).mem();
+ assertTrue(allocator.unknownCallers());
+ assertTrue(XInt.bit(outside._escFs,allocator.sig().fidx()));
+ for( int fidx = XInt.next(helpers,0); fidx>=0; fidx = XInt.next(helpers,fidx) )
+ assertFalse(XInt.bit(outside._escFs,fidx));
+ assertEquals("42",Eval2.eval(code,42));
+ }
+
+ private static FunNode named(CodeGen code, String name) {
+ for( FunNode fun : code._linker )
+ if( fun!=null && name.equals(fun._name) ) return fun;
+ throw new AssertionError("Missing function "+name);
+ }
+}