summaryrefslogtreecommitdiff
path: root/chapter25
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-03 14:03:48 -0700
committerCliff Click <cliffc@acm.org>2026-10-03 14:03:48 -0700
commit22df891893d30e9906d0b14fc3ae5d8e67d500b2 (patch)
tree2bda142f92febc8b26f1cabd4bb0c46d1864f34b /chapter25
parentc3958e34c0de21df52e57e47e4f47ffe6464f138 (diff)
downloadSimple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.tar.gz
Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.zip
Chapter renumber, and split ch 17
Renumber chapters 10a, 10b, 11, 12, 13, 14 -> 10, 11, 13, 12, [11,14]. Split ch 17 into a 17a and 17b; shuffle around im/mutable syntax to be consistent, and present it more slowly. THe new immutable syntax is pushed through all later chapters
Diffstat (limited to 'chapter25')
-rw-r--r--chapter25/Makefile2
-rw-r--r--chapter25/README.md4
-rw-r--r--chapter25/ROADMAP.md16
-rw-r--r--chapter25/docs/examples/BubbleSort.smp2
-rw-r--r--chapter25/docs/examples/Dijkstra.smp2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java95
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java5
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java68
-rw-r--r--chapter25/src/main/smp/sys/aryi64.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java36
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java284
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java280
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java377
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java83
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java32
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java120
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java109
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java (renamed from chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java)78
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java12
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java18
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java26
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java20
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java23
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java14
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java67
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java14
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp8
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp4
56 files changed, 1021 insertions, 897 deletions
diff --git a/chapter25/Makefile b/chapter25/Makefile
index 980342c8..aa7f0a17 100644
--- a/chapter25/Makefile
+++ b/chapter25/Makefile
@@ -71,7 +71,7 @@ include $(firstword $(wildcard ../isa/isa.mk isa/isa.mk))
test_classes := $(patsubst $(TST)/%java,$(CLZDIR)/test/%class,$(test_javas))
# These tests do not rely on the stdlib
test_alone := com.seaofnodes.simple.BrainFuckTest com.seaofnodes.simple.MergeSortTest com.seaofnodes.simple.TypeTest
-test_raw_nums0 := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21
+test_raw_nums0 := 01 02 03 04 05 06 07 08 09 10 12 13 14Float 14 15 16 17a 17b 18 19 20 21
test_raw_nums1 := 22 23 24
# These tests DO rely on the stdlib
test_raw_sys := 25
diff --git a/chapter25/README.md b/chapter25/README.md
index 3261637c..7ab7c89e 100644
--- a/chapter25/README.md
+++ b/chapter25/README.md
@@ -156,6 +156,10 @@ regressions. `make release` builds the compiler jar and native library artifacts
`make tags` builds editor tags. Native tools and the selected CPU/ABI must match
the environment; the Makefile currently defaults to x86-64/win64.
+Serialized IR now uses the `C0D2` header: pointer types include an independent
+read-only access bit. Older object files must be rebuilt; the reader rejects
+their old header rather than interpreting their types with the new layout.
+
Compiler changes can invalidate both serialized IR and native code in `sys.o`.
Rebuild it before interpreting linked-program test results. A source-only subset
is not equivalent to this chapter's full `make tests`.
diff --git a/chapter25/ROADMAP.md b/chapter25/ROADMAP.md
index 981f65e3..30495a69 100644
--- a/chapter25/ROADMAP.md
+++ b/chapter25/ROADMAP.md
@@ -120,11 +120,11 @@ Any number of named fields are allowed:
```java
struct Class {
- str className;
- str professor;
+ !str !className;
+ !str !professor;
int time;
int credits;
- Class prerequisite;
+ !Class !prerequisite;
u16 #; // Limit of 65535 students per class
Student [];
};
@@ -195,7 +195,7 @@ new object, only growing as needed.
```java
val vec1 = new vecInt[0].add(2); // len=1, capacity=1
-val vec2 = vec1.add(3); // NEW vecInt returned, only is deleted; len=2; capacity=2;
+val vec2 = vec1.add(3); // NEW !vecInt !returned, only is deleted; len=2; capacity=2;
vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)`
vec2[0]; // OK
vec2[1]; // OK
@@ -284,7 +284,7 @@ extra indirection with each usage.
```java
struct xstr {
- str !_str; // The extra indirection is here
+ !str !_str; // The extra indirection is here
val add = { str str -> _str = str.add2(str); };
val write = { int fd -> _str.write(fd); };
};
@@ -355,7 +355,7 @@ struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; };
// Current Simple rules: always by-reference
struct ByRef {
- Complex c; // 4-byte pointer to a Complex
+ !Complex !c; // 4-byte pointer to a Complex
};
print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c
@@ -363,7 +363,7 @@ print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c
// The '*' syntax indicator can be something else, e.g. a keyword "inline"
// I am pronouncing '*' as "contents of"
struct ByValue {
- *Complex c; // c is inlined, full 16 bytes into ByValue
+ *!Complex !c; // c is inlined, full 16 bytes into ByValue
};
print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load
@@ -382,7 +382,7 @@ val *c = val.c; // Error, cannot take "contents of" a value
ref.c = val.*c; // Assign into ref.c the "contents of" val.c
// Arrays of inlined structures
-var ary = new *Complex[99]; // Array of 99 Complex objects, inlined
+var ary = new *Complex[99]; // Array of 99 !Complex !objects, inlined
// Calling methods has the same syntax
ref.c.len();
diff --git a/chapter25/docs/examples/BubbleSort.smp b/chapter25/docs/examples/BubbleSort.smp
index 1b3b83e6..ca6906b5 100644
--- a/chapter25/docs/examples/BubbleSort.smp
+++ b/chapter25/docs/examples/BubbleSort.smp
@@ -4,7 +4,7 @@
//
// Bubble sort, with reading (parsing) and writing (formatting) the data.
-val sort = { sys.aryi64 nums ->
+val sort = { !sys.aryi64 nums ->
sys.aryi64?! more = nums;
while( more ) {
more = null;
diff --git a/chapter25/docs/examples/Dijkstra.smp b/chapter25/docs/examples/Dijkstra.smp
index d2f1dc4f..fd595eb5 100644
--- a/chapter25/docs/examples/Dijkstra.smp
+++ b/chapter25/docs/examples/Dijkstra.smp
@@ -2,7 +2,7 @@
// An implementation of
// https://sampleprograms.io/projects/dijkstra/
-val shortestPath = { sys.aryi64 matrix, int nodes, int src, int dst ->
+val shortestPath = { !sys.aryi64 matrix, int nodes, int src, int dst ->
i64 infinity = (1 << 63)-1;
var dist = new i64[nodes];
var visited = new sys.adt.bitset();
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
index 19894357..42c83f93 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
@@ -217,13 +217,17 @@ public class Parser {
* Parses a function body, assuming the header is parsed.
*/
private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String[] ids) {
+ return parseFunctionBody(sig, loc, null, ids);
+ }
+
+ private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String[] ids) {
// Record & restore the existing scope vars. The function parse might
// update fields, so when the parse is done reset back to the pre-parse
// state.
Node[] preParse = _scope.save();
// Parse function body normally
- ReturnNode ret = _parseFunctionBody(null, sig,loc,ids);
+ ReturnNode ret = _parseFunctionBody(null, sig,loc,finals,ids);
// Reset all fields to pre-parse days
_scope.restore(preParse);
@@ -246,6 +250,10 @@ public class Parser {
// Unstack the {break,continue,return} scopes.
private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, String[] ids) {
+ return _parseFunctionBody(funName, sig, loc, null, ids);
+ }
+
+ private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String[] ids) {
boolean isInit = FunNode.isInit(funName);
boolean isCtor = isInit || isExplicitConstructorName(funName);
String typeName = isCtor ? ((TypeMemPtr)sig._sig[0])._obj._name : null;
@@ -278,7 +286,7 @@ public class Parser {
for( int i=0; i<ids.length; i++ ) {
Type t = sig.arg(i);
// Args take a default input if called from Start/external-world
- _scope.define(ids[i], t, i==0 && ids[0]=="self", new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc);
+ _scope.define(ids[i], t, (i==0 && ids[0]=="self") || (finals!=null && finals.get(i)), new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc);
}
// Parse the function body.
@@ -434,7 +442,7 @@ public class Parser {
// value; otherwise a user constructor must initialize it.
Node val = _returnScope.in(vidx);
if( upgrade && v._uninit &&
- !(val._type instanceof TypeNil tn && tn.notNull() && !val._type.isHigh()) ) continue;
+ !initialized(val._type) ) continue;
// Store value into extended struct
Node prior = mmm.keep();
Node st = peep(new StoreNode(null, fld._fname, fld._alias, fld._t, null, prior, self, off(tself,fld._fname), val, fld._final));
@@ -455,6 +463,10 @@ public class Parser {
// Validate each user constructor, or an implicit empty one, before the
// declaration scope goes away. Later fields use their declaration defaults.
+ private static boolean initialized(Type t) {
+ return t!=Type.BOTTOM && !t.isHigh() && (!(t instanceof TypeNil tn) || tn.notNull());
+ }
+
private void checkInitFields(TypeStruct ts, int base) {
if( _returnScope==null || _returnScope.ctrl()._type.isHigh() ) return;
boolean implicit = _ctorExits.isEmpty();
@@ -467,7 +479,7 @@ public class Parser {
Var v = _scope.var(idx);
if( !v._uninit || v._fref ) continue;
Type t = exit.getOrDefault(v,_returnScope.in(idx)._type);
- if( !(t instanceof TypeNil tn && tn.notNull() && !t.isHigh()) )
+ if( !initialized(t) )
throw error("'"+ts._name+"' is not fully initialized, field '"+fld._fname+
(implicit ? "' needs to be set in a constructor" :
"' is only partially set in the constructor"),v._loc);
@@ -1025,6 +1037,10 @@ public class Parser {
// Widen ints to floats; narrow wide int types.
// Early error if types do not match variable.
private Node convertExpr( Node expr, Type t ) {
+ if( !expr._type.accessISA(t) )
+ throw error("Type "+expr._type.str()+" is not of declared type "+t.str());
+ if( t instanceof TypeMemPtr tmp && tmp.isFinal() )
+ expr = peep(new ReadOnlyNode(expr));
// A FunPtr carries the semantic edge to its function's Return. Do not
// hide that edge behind a Convert which can fold to a plain constant.
if( expr instanceof FunPtrNode && t instanceof TypeFunPtr )
@@ -1090,17 +1106,24 @@ public class Parser {
return require(n,";");
}
- /** Parse final: [!]var['=' asgn]
+ // The binding modifier sits immediately before the name. It never
+ // changes permissions on the referenced object.
+ private boolean bindingFinal(Type t) {
+ if( match("!") ) return false;
+ if( match("~") ) return true;
+ return t==Type.TOP || t instanceof TypeNil;
+ }
+
+ /** Parse final: [!|~]var['=' asgn]
*/
private Node parseDeclaration(Type t) {
assert t!=null;
// Has var/val instead of a user-declared type
boolean inferType = t==Type.TOP || t==Type.BOTTOM;
- boolean hasBang = match("!");
+ boolean xfinal = bindingFinal(t);
Lexer loc = loc();
String name = requireId();
// Optional initializing expression follows
- boolean xfinal = false;
boolean fld_final = false; // Field is final, but not deeply final
boolean uninit = false; // Required non-null constructor field
Node expr;
@@ -1121,18 +1144,6 @@ public class Parser {
} else {
expr = parseAsgn();
}
- // TOP means val and val is always final
- xfinal = (t==Type.TOP) ||
- expr instanceof ExternNode ||
- // BOTTOM is var and var is always not-final
- (t!=Type.BOTTOM &&
- // no Bang AND
- !hasBang &&
- // not-null (expecting null to be set to not-null)
- expr._type != Type.NIL &&
- // Pointers are final by default; int/flt are not-final by default.
- (t instanceof TypeNil));
-
// var/val, then type comes from expression
if( inferType ) {
if( expr._type==Type.NIL )
@@ -1141,12 +1152,6 @@ public class Parser {
if( !xfinal ) t = mutableInferredType(t);
}
- // Final is deep on ptrs
- if( xfinal && t instanceof TypeMemPtr tmp ) {
- t = tmp.makeRO();
- expr = peep(new ReadOnlyNode(expr));
- }
-
// expr is a constant function
if( t instanceof TypeFunPtr decl && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
t = decl.makeFrom(tfp.fidx());
@@ -1168,8 +1173,8 @@ public class Parser {
// Required pointers retain their declared shape, but include
// null until every constructor path assigns them.
case TypeNil tn -> tn.nullable() ? con(Type.NIL) : con(t.meet(Type.NIL));
- case TypeInteger ti -> _code.ZERO;
- case TypeFloat tf -> con(TypeFloat.FZERO);
+ case TypeInteger ti -> xfinal ? con(Type.BOTTOM) : _code.ZERO;
+ case TypeFloat tf -> con(xfinal ? Type.BOTTOM : TypeFloat.FZERO);
// Bottom signals type inference: they must be initialized in
// the constructor and that's when we'll discover the type.
case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); }
@@ -1177,9 +1182,9 @@ public class Parser {
// Every non-null pointer without a default is constructor-required.
// Without `!` it is also shallow-final; mutability and definite
// initialization are independent properties.
- if( t instanceof TypeNil tn && !tn.nullable() ) {
+ if( (xfinal && !(t instanceof TypeNil)) || (t instanceof TypeNil tn && !tn.nullable()) ) {
uninit = true;
- if( !hasBang ) fld_final = true;
+ if( xfinal ) fld_final = true;
}
}
@@ -1430,8 +1435,10 @@ public class Parser {
// Otherwise you get a type name
int old1 = pos();
+ boolean writable = match("!");
+ boolean readonly = !writable && match("~");
String tname = _lexer.matchId();
- if( tname==null ) return null;
+ if( tname==null ) return posT(old1);
// Convert the type name to a type.
Type t0 = TYPES.get(tname), t1 = t0;
@@ -1544,6 +1551,13 @@ public class Parser {
if( t1 instanceof TypeStruct ts1 )
t1 = TypeMemPtr.make(ts1);
+ // A qualifier before the name applies to the struct reference, not
+ // to an enclosing array. Arrays have their own [] / [~] qualifier.
+ if( t1 instanceof TypeMemPtr tmp ) {
+ if( readonly || !writable ) t1 = tmp.makeRO();
+ } else if( writable || readonly )
+ return posT(old1);
+
// Nest arrays and '?' as needed
Type t2 = t1;
while( true ) {
@@ -1554,7 +1568,7 @@ public class Parser {
} else
throw error("Type "+t0+" cannot be null");
} else if( match("[~]") ) {
- t2 = TypeMemPtr.make(typeAry(t2,true));
+ t2 = TypeMemPtr.make(typeAry(t2,true)).makeRO();
} else if( match("[]") ) {
t2 = TypeMemPtr.make(typeAry(t2,false));
} else
@@ -1566,7 +1580,7 @@ public class Parser {
// Check valid forward ref, after parsing all the type extra bits.
// Cannot check earlier, because cannot find required 'id' until after "[]?" syntax
int old2 = pos();
- match("!");
+ if( !match("!") ) match("~");
String id = _lexer.matchId();
if( !(peek(',') || peek(';') || peek('=') || match("->")) || id==null )
return posT(old1); // Reset lexer to reparse
@@ -1616,9 +1630,8 @@ public class Parser {
ta = TypeStruct.makeAry(tname,TypeInteger.U32,_code.alias(rcname),t,_code.alias(rcname), false );
TYPES.put(tname,ta);
}
- if( !efinal ) return ta;
// Already have the aliases, just efinal is wrong
- return TypeStruct.makeAry(tname,TypeInteger.U32,ta._fields[0]._alias,t,ta._fields[1]._alias,true );
+ return TypeStruct.makeAry(tname,TypeInteger.U32,ta._fields[0]._alias,t,ta._fields[1]._alias,efinal );
}
private Type implicitSelfType() {
@@ -2187,7 +2200,7 @@ public class Parser {
// array body.
TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", slen, lenAlias, body, elemAlias, true),true);
assert str.isConstant();
- return con(str);
+ return con(str.makeRO());
}
/**
@@ -2305,6 +2318,8 @@ public class Parser {
Node mem = mem();
// Store to field
mem.keep();
+ if( expr._type instanceof TypeMemPtr view && view._ro && ctrl()._type!=Type.XCONTROL )
+ throw error("Cannot modify final field '"+name+"'");
Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), lift, false).peephole().keep();
storeMem(st,alias,mem);
st.unkeep();
@@ -2330,6 +2345,8 @@ public class Parser {
throw error( "'" + ts._name + "' is not fully initialized, field '" + fld._fname + "' needs to be set in a constructor" );
Node op = opAssign(ch,load, decl ).keep();
mem.keep();
+ if( expr._type instanceof TypeMemPtr view && view._ro && ctrl()._type!=Type.XCONTROL )
+ throw error("Cannot modify final field '"+name+"'");
Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), op, false).peephole().keep();
storeMem(st,alias,mem);
st.unkeep();
@@ -2375,6 +2392,7 @@ public class Parser {
private Node func() {
Ary<Type> ts = new Ary<>(Type.class);
Ary<String> ids = new Ary<>(String.class);
+ java.util.BitSet finals = new java.util.BitSet();
Type self = implicitSelfType();
ts.push(self);
ids.push(self == TypePtr.PTR ? "#self" : "self");
@@ -2385,6 +2403,7 @@ public class Parser {
while( true ) {
Type t = type(); // Arg type
if( t==null ) break;
+ finals.set(ids.size(),bindingFinal(t));
String id = requireId();
ts .push(t ); // Push type/arg pairs
ids.push(id);
@@ -2393,7 +2412,7 @@ public class Parser {
require("->");
// Make a concrete function type, with a fidx
TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),Type.BOTTOM,_code.fidx(_ref._cname));
- ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry());
+ ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry());
return new FunPtrNode(tfp,_code._start,ret).peephole();
}
@@ -2401,6 +2420,7 @@ public class Parser {
require("{");
Ary<Type> ts = new Ary<>(Type.class);
Ary<String> ids = new Ary<>(String.class);
+ java.util.BitSet finals = new java.util.BitSet();
TypeStruct self = _scope.constructorSelf();
assert self != null;
ts.push(TypeMemPtr.make(constructorRecv(self)));
@@ -2416,6 +2436,7 @@ public class Parser {
while( true ) {
Type t = type();
if( t==null ) break;
+ finals.set(ids.size(),bindingFinal(t));
String id = requireId();
ts .push(t );
ids.push(id);
@@ -2424,7 +2445,7 @@ public class Parser {
require("->");
TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),TypeMem.makePrivate(self),_code.fidx(_ref._cname));
Node[] preParse = _scope.save();
- ReturnNode ret = _parseFunctionBody((self._name+"."+name).intern(),tfp,loc,ids.asAry());
+ ReturnNode ret = _parseFunctionBody((self._name+"."+name).intern(),tfp,loc,finals,ids.asAry());
_scope.restore(preParse);
require("}");
return new FunPtrNode(tfp,_code._start,ret).peephole();
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
index ee5c48df..a8cd315f 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -63,8 +63,9 @@ abstract public class Serialize {
Type.TAGOFFS();
BAOS baos = new BAOS();
+ // C0D2 adds independent pointer access permissions to serialized types.
// A - Print a header
- baos.write('C').write('0').write('D').write('E');
+ baos.write('C').write('0').write('D').write('2');
// Count unique Types
var types = new HashMap<Type,Integer>();
@@ -217,8 +218,8 @@ abstract public class Serialize {
Type.TAGOFFS();
// A - Read a header
- if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='E' )
- throw new IllegalArgumentException("Missing magic word");
+ if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='2' )
+ throw new IllegalArgumentException("Unsupported serialized IR format; rebuild the object file");
// B - Packed read of #strings, then strings
int ndependents = bais.packed4(); // Number of dependent object files
@@ -246,8 +247,8 @@ abstract public class Serialize {
String[] strs = elf._strs;
if( strs == null ) {
// A - Read a header
- if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != 'E' )
- throw new IllegalArgumentException( "Missing magic word" );
+ if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != '2' )
+ throw new IllegalArgumentException( "Unsupported serialized IR format; rebuild the object file" );
// B - Packed read of #strings, then strings
int ndependents = bais.packed4(); // Number of dependent object files
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 2e921301..162f9c24 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -227,7 +227,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
// references here. Compare against its recursively upgraded form,
// so deep mutability requirements participate before inlining.
Type formal = tfp.arg(i).upgradeType(Parser.TYPES);
- if( !arg._type.isa(formal) )
+ if( !arg._type.isa(formal) || !arg._type.accessISA(formal) )
{ addDep(arg); return false; }
}
return true;
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 84f1e705..4372e2d5 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -187,7 +187,7 @@ public class CallNode extends CFGNode {
// Check for args
for( int i=0; i<tfp.nargs(); i++ )
- if( !arg(i+2)._type.isa(tfp.arg(i)) ) {
+ if( !arg(i+2)._type.isa(tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) ) {
// Constructors return private memory and carry hidden self and
// selfMem arguments ahead of the source-level arguments.
int argno = i - (tfp.ret() instanceof TypeMem ? 2 : 0);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
index 4ee5521a..b038366a 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
@@ -35,7 +35,7 @@ public class ConvertNode extends Node {
Type src = val()._type;
if( CodeGen.CODE._phase == CodeGen.Phase.Opto ) {
if( src.isHigh() ) return Type.TOP;
- if( src.isa(_dst) ) return src;
+ if( (src.isa(_dst) && src.accessISA(_dst)) ) return src;
}
return _dst;
}
@@ -43,7 +43,7 @@ public class ConvertNode extends Node {
@Override public Node idealize() {
Type src = val()._type;
if( src==Type.BOTTOM || src==Type.TOP ) return null;
- if( src.isa(_dst) ) return val();
+ if( (src.isa(_dst) && src.accessISA(_dst)) ) return val();
// Managed arrays are represented by an object pointer, while C wants
// the address of the first element. The representation cast itself is
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index a2345d92..a557f97c 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -85,6 +85,7 @@ public class LoadNode extends MemOpNode {
return tmp.isHigh() || tmp._obj.isHigh() ? TypeScalar.TOP : TypeScalar.BOT;
Type t = pfld._t;
+ boolean readonly = t instanceof TypeMemPtr view && view._ro;
// Load member of constant array
if( t instanceof TypeConAry ary )
t = ary.elem(); // TODO: if offset is known, can peek the constant
@@ -93,7 +94,7 @@ public class LoadNode extends MemOpNode {
// A deeply read-only base produces a deeply read-only value. The
// generic declared field type must not cast this information away.
- if( ptr0.isFinal() )
+ if( ptr0.isFinal() || readonly )
t = t.makeRO();
return scalar(t);
}
@@ -306,7 +307,7 @@ public class LoadNode extends MemOpNode {
// Read-Only is a deep property, and cannot be cast-away
private Node castRO(Node rez) {
- if( ptr()._type.isFinal() && !rez._type.isFinal() )
+ if( (ptr()._type.isFinal() || declaredType().isFinal()) && !rez._type.isFinal() )
return new ReadOnlyNode(rez).peephole();
return rez;
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 7e2963f2..415ade34 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -269,7 +269,7 @@ public class StoreNode extends MemOpNode {
return null;
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
Field f = tmp._obj.field(_name);
- if( f!=null && f._final && !_init )
+ if( f!=null && (tmp._ro || f._final) && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
return null;
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
index d1af6ca8..980daf0f 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -468,6 +468,10 @@ public class Type /*implements Cloneable*/ {
boolean _isConstant() { return _type==TNIL; }
// Are all reachable struct Fields are final?
+ // Writable array slots are invariant in their element access permissions.
+ // Otherwise an alias could insert a readonly pointer into a writable slot.
+ public boolean accessISA(Type dst) { return true; }
+
public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); }
boolean _isFinal() { assert is_nokids(); return true; }
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index cfd213b7..175b1c63 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -27,12 +27,15 @@ public class TypeMemPtr extends TypeNil {
// (TOP ,true ) - a nil
public TypeStruct _obj;
+ // Deep read-only access through this pointer; independent of slot finality.
+ public boolean _ro;
+
public boolean _one; // Singleton pointer value; the pointer itself is constant.
public boolean _pub; // Escaped/public pointer. False means constructor-private.
private static final Ary<TypeMemPtr> FREE = new Ary<>(TypeMemPtr.class);
private TypeMemPtr(byte nil, TypeStruct obj, boolean one, boolean pub) { super(TMEMPTR,nil); init(nil,obj,one,pub); }
- private TypeMemPtr init(byte nil, TypeStruct obj, boolean one, boolean pub) { _nil=nil; _obj=obj; _one=one; _pub=pub; return this; }
+ private TypeMemPtr init(byte nil, TypeStruct obj, boolean one, boolean pub) { _nil=nil; _obj=obj; _ro=obj==TypeStruct.BOT; _one=one; _pub=pub; return this; }
// Return a filled-in TypeMemPtr; either from free list or alloc new.
static TypeMemPtr malloc(byte nil, TypeStruct obj, boolean one, boolean pub) {
return FREE.isEmpty() ? new TypeMemPtr(nil,obj,one,pub) : FREE.pop().init(nil,obj,one,pub);
@@ -43,7 +46,10 @@ public class TypeMemPtr extends TypeNil {
return make(nil,obj,one,true);
}
public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean pub) {
- TypeMemPtr tmp = malloc(nil, obj, one, pub);
+ return make(nil,obj,one, pub,obj==TypeStruct.BOT);
+ }
+ private static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean pub, boolean ro) {
+ TypeMemPtr tmp = malloc(nil, obj, one, pub).access(ro);
TypeMemPtr t2 = tmp.intern();
if( t2==tmp ) return tmp;
return VISIT.isEmpty() ? t2.free(tmp) : t2.delayFree(tmp);
@@ -66,15 +72,36 @@ public class TypeMemPtr extends TypeNil {
public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); }
public static TypeMemPtr makePrivate (TypeStruct obj) { return make((byte)2, obj, false, false); }
- public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one, _pub); }
+ public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one, _pub,_ro); }
public TypeMemPtr makeNullable() { return makeFrom((byte)3); }
/** Inferred mutable local: nullable, public and non-singleton. */
public TypeMemPtr makeVar() { return (TypeMemPtr)makeStorage(); }
/** Public-memory shape: nullable and non-singleton, preserving the referent. */
- @Override TypeMemPtr _makeStorage() { return make(_nil,_obj._makeStorage(),false,true); }
- @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one, _pub); }
+ @Override TypeMemPtr _makeStorage() { return make(_nil,_obj._makeStorage(),false,true,_ro); }
+ @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one, _pub,_ro); }
@Override public Type nonZero() { return makeFrom((byte)(_nil <= 1 ? 1 : 2)); }
+ private TypeMemPtr access(boolean ro) { _ro=ro; return this; }
+
+ public TypeMemPtr withAccess(boolean ro) {
+ if( _ro==ro ) return this;
+ TypeMemPtr ptr = malloc(_nil, _obj, _one, _pub);
+ ptr._ro = ro;
+ TypeMemPtr res = ptr.intern();
+ if( res==ptr ) return ptr;
+ return VISIT.isEmpty() ? res.free(ptr) : res.delayFree(ptr);
+ }
+
+ // Writable slots can load and store references, so their element access
+ // must agree in both directions. A read-only outer view can weaken access.
+ @Override public boolean accessISA(Type dst) {
+ if( !(dst instanceof TypeMemPtr ptr) ) return true;
+ if( _ro && !ptr._ro ) return false;
+ if( ptr._ro || _obj._fields==null || ptr._obj._fields==null || !_obj.isAry() || !ptr._obj.isAry() ) return true;
+ Type a = _obj._fields[1]._t, b = ptr._obj._fields[1]._t;
+ return a.accessISA(b) && b.accessISA(a);
+ }
+
// An abstract pointer, pointing to either a Struct or an Array.
// Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
public static final TypeMemPtr BOT = make((byte)3, TypeStruct.BOT);
@@ -82,7 +109,7 @@ public class TypeMemPtr extends TypeNil {
public static final TypeMemPtr NOTBOT = make((byte)2,TypeStruct.BOT);
public static final TypeMemPtr TEST= make((byte)2, TypeStruct.TEST);
- public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); }
+ public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); ts.add(TEST.makeRO()); }
@Override
public TypeNil xmeet(Type t) {
@@ -91,28 +118,28 @@ public class TypeMemPtr extends TypeNil {
// Can't keep _one if mixing two unequal singletons; the result is not
// either singleton. Once either side is public, the meet is public.
boolean one = _one && that._one && (_obj==that._obj || !(_obj.isConAry() || that._obj.isConAry()));
- return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), one, _pub | that._pub);
+ return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), one, _pub | that._pub,_ro | that._ro);
}
@Override
- TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), !_one, !_pub); }
+ TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), !_one, !_pub).access(!_ro); }
@Override TypeMemPtr rdual() {
if( _dual!=null ) return dual();
assert !_terned;
- TypeMemPtr d = malloc(dual0(), null, !_one, !_pub);
+ TypeMemPtr d = malloc(dual0(), null, !_one, !_pub).access(!_ro);
(_dual = d)._dual = this; // Cross link duals
d._obj = _obj._terned ? _obj.dual() : _obj.rdual();
return d;
}
// RHS is NIL; do not deep-dual when crossing the center line
- @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); }
+ @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj,false,true,_ro); }
@Override boolean _isConstant() { return _one; }
- @Override boolean _isFinal() { return _obj._isFinal(); }
- @Override TypeMemPtr _makeRO() { return makeFrom(_obj._makeRO()); }
- @Override TypeMemPtr _close( String name, HashMap<String, Type> TYPES ) { return malloc(_nil,_obj._close(name, TYPES ),_one,_pub); }
+ @Override boolean _isFinal() { return _ro; }
+ @Override TypeMemPtr _makeRO() { return withAccess(true); }
+ @Override TypeMemPtr _close( String name, HashMap<String, Type> TYPES ) { return malloc(_nil,_obj._close(name, TYPES ),_one,_pub).access(_ro); }
@Override Type _upgradeType(HashMap<String,Type> TYPES) {
return makeFrom((TypeStruct)_obj._upgradeType(TYPES));
@@ -120,33 +147,34 @@ public class TypeMemPtr extends TypeNil {
@Override public int log_size() { return 3; } // (1<<3)==8-byte pointers
- @Override int hash() { return _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0) ^ (_pub ? 4096 : 0); }
+ @Override int hash() { return (_ro ? 8192 : 0) ^ _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0) ^ (_pub ? 4096 : 0); }
@Override boolean eq(Type t) {
TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
- return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj == ptr._obj;
+ return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj == ptr._obj;
}
@Override boolean cycle_eq(Type t) {
if( t._type != TMEMPTR ) return false;
TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
- return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj.cycle_eq(ptr._obj);
+ return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj.cycle_eq(ptr._obj);
}
@Override public int nkids() { return 1; }
@Override public Type at( int idx ) { return _obj; }
@Override public void set( int idx, Type t ) { _obj = (TypeStruct)t; }
- // Reserve tags for null/not, one/general and public/private
- @Override int TAGOFF() { return 8; }
+ // Reserve tags for null/not, one/general, public/private and read-only access
+ @Override int TAGOFF() { return 16; }
@Override public void packed( BAOS baos, HashMap<String,Integer> strs ) {
assert _nil>=2;
baos.write(TAGOFFS[_type]
+ (_nil==2 ? 0 : 1)
+ (!_one ? 0 : 2)
- + (!_pub ? 0 : 4) );
+ + (!_pub ? 0 : 4)
+ + (!_ro ? 0 : 8) );
}
static TypeMemPtr packed( int tag, BAOS bais ) {
- return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4);
+ return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4).access((tag&8)==8);
}
@Override public String str() {
diff --git a/chapter25/src/main/smp/sys/aryi64.smp b/chapter25/src/main/smp/sys/aryi64.smp
index 22ee082e..98b72605 100644
--- a/chapter25/src/main/smp/sys/aryi64.smp
+++ b/chapter25/src/main/smp/sys/aryi64.smp
@@ -53,7 +53,7 @@ struct aryi64 {
// parseAryI64 & aryi64.str are bijections modulo whitespace.
// Returns null if the parse failed, without giving a clue as to why.
val parseAryI64 = { u8[~] str ->
- scan !s = new scan(str);
+ !scan !s = new scan(str);
if( !s.match('[') ) return null;
// Array of parsed results
var nums = new aryi64(new i64[1]);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index f41a1904..cdafb43b 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -24,7 +24,7 @@ public class Chapter10Test {
private static final String NULLABLE_POINT_SOURCE = """
struct Point { int x; new Point = { int v -> x = v; }; };
- Point?[] !points = new Point?[2];
+ !Point?[] !points = new !Point?[2];
points[arg] = new Point(42);
Point? p = points[1];
""";
@@ -117,7 +117,7 @@ struct _Foo {
int x;
};
_Foo? foo = null;
-_Bar !bar = new _Bar;
+!_Bar !bar = new _Bar;
bar.a = 1;
bar.a = 2;
return bar.a;
@@ -130,7 +130,7 @@ return bar.a;
public void testExample() {
CodeGen code = new CodeGen("""
struct Vector2D { int x; int y; };
-Vector2D !v = new Vector2D;
+!Vector2D !v = new Vector2D;
v.x = 1;
if (arg)
v.y = 2;
@@ -169,7 +169,7 @@ arg=0+new s0.0;
public void testLoop() {
CodeGen code = new CodeGen("""
struct Bar { int a; };
-Bar !bar = new Bar;
+!Bar !bar = new Bar;
while (arg) {
bar.a = bar.a + 2;
arg = arg + 1;
@@ -184,7 +184,7 @@ return bar.a;
public void testIf() {
CodeGen code = new CodeGen("""
struct Bar { int a; };
-Bar !bar = new Bar;
+!Bar !bar = new Bar;
if (arg) bar = null;
bar.a = 1;
return bar.a;
@@ -197,7 +197,7 @@ return bar.a;
public void testIf2() {
CodeGen code = new CodeGen("""
struct Bar { int a; };
-Bar? !bar = null;
+!Bar? !bar = null;
if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
@@ -210,7 +210,7 @@ return bar.a;
public void testIf3() {
CodeGen code = new CodeGen("""
struct Bar { int a; };
-Bar bar = null;
+!Bar !bar = null;
if (arg) bar = null;
bar.a = 1;
return bar.a;
@@ -223,7 +223,7 @@ return bar.a;
public void testIfOrNull() {
CodeGen code = new CodeGen("""
struct _Bar { int a; };
-_Bar? !bar = new _Bar;
+!_Bar? !bar = new _Bar;
if (arg) bar = null;
if( bar ) bar.a = 1;
return bar.a;
@@ -237,7 +237,7 @@ return bar.a;
CodeGen code = new CodeGen(
"""
struct _Bar { int a; };
-_Bar? !bar = new _Bar;
+!_Bar? !bar = new _Bar;
if (arg) bar = null;
int rez = 3;
if( !bar ) rez=4;
@@ -252,7 +252,7 @@ return rez;
public void testWhileWithNullInside() {
CodeGen code = new CodeGen("""
struct s0 {int v0;};
-s0? !v0 = new s0;
+!s0? !v0 = new s0;
int ret = 0;
while(arg) {
ret = v0.v0;
@@ -273,7 +273,7 @@ struct s0 {
int v0;
};
s0? v1=new s0;
-s0? v1;
+!s0? !v1;
v1=new s0;
""");
try { code.parse(); fail(); }
@@ -289,7 +289,7 @@ struct Iter {
int x;
int len;
};
-Iter !i = new Iter;
+!Iter !i = new Iter;
i.len = arg;
int sum=0;
while( i.x < i.len ) {
@@ -307,9 +307,9 @@ return sum;
public void test1() {
CodeGen code = new CodeGen("""
struct s0 {int v0;};
-s0 !ret = new s0;
+!s0 !ret = new s0;
while(arg) {
- s0 !v0 = new s0;
+ !s0 !v0 = new s0;
v0.v0 = arg;
arg = arg-1;
if (arg==5) ret=v0;
@@ -325,8 +325,8 @@ return ret;
public void test2() {
CodeGen code = new CodeGen("""
struct _s0 {int v0;};
-_s0 !ret = new _s0;
-_s0 !v0 = new _s0;
+!_s0 !ret = new _s0;
+!_s0 !v0 = new _s0;
while(arg) {
v0.v0 = arg;
arg = arg-1;
@@ -343,9 +343,9 @@ return ret.v0;
public void test3() {
CodeGen code = new CodeGen("""
struct _s0 {int v0;};
-_s0 !ret = new _s0;
+!_s0 !ret = new _s0;
while(arg < 10) {
- _s0 !v1 = new _s0;
+ !_s0 !v1 = new _s0;
if (arg == 5) ret=v1;
arg = arg + 1;
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java
deleted file mode 100644
index d515df8d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ /dev/null
@@ -1,284 +0,0 @@
-package com.seaofnodes.simple;
-import com.seaofnodes.simple.codegen.CodeGen;
-
-
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
-public class Chapter11Test {
-
- @Test
- public void testPrimes() {
- CodeGen code = new CodeGen(
-"""
-if( arg < 2 ) return 0;
-int primeCount = 1;
-int prime = 3;
-while( prime <= arg ) {
- int isPrime = 1;
- // Check for even case, so the next loop need only check odds
- if( (prime/2)*2 == prime )
- continue;
- // Check odds up to sqrt of prime
- int j = 3;
- while( j*j <= prime ) {
- if( (prime/j)*j == prime ) {
- isPrime = 0;
- break;
- }
- j = j + 2;
- }
- if( isPrime )
- primeCount = primeCount + 1;
- prime = prime + 2;
-}
-return primeCount;
-""");
- code.parse().opto();
- assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print());
- assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
- assertEquals("1", Eval2.eval(code, 2)); // 2
- assertEquals("2", Eval2.eval(code, 3)); // 2, 3
- assertEquals("2", Eval2.eval(code, 4)); // 2, 3
- assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5
- assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7
- }
-
- @Test
- public void testClassField() {
- CodeGen code = new CodeGen(
-"""
-if( arg < 2 ) return 0;
-int primeCount = 1;
-while( arg-- )
- primeCount++;
-return primeCount;
-""");
- code.driver(CodeGen.Phase.LocalSched);
- assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
- assertEquals("3", Eval2.eval(code, 2)); //
-}
-
-
- @Test
- public void testAntiDeps1() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v=new _S;
-v.f = 2;
-_S q=new _S;
-int i=q.f;
-i=v.f;
-if (arg) v.f=1;
-return i;
-""");
- code.parse().opto();
- assertEquals("return 2;", code.print());
- assertEquals("2", Eval2.eval(code, 0));
- assertEquals("2", Eval2.eval(code, 1));
- }
-
- @Test
- public void testAntiDeps2() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-v.f = arg;
-_S !t = new _S;
-int i = 0;
-if (arg) {
- if (arg+1) v = t;
- i = v.f;
-} else {
- v.f = 2;
-}
-return i;
-""");
- code.parse().opto();
- assertEquals("return Phi(Region,.f,0);", code.print());
- }
-
- @Test
- public void testAntiDeps3() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v0 = new _S;
-_S? v1;
-if (arg) v1 = new _S;
-if (v1) {
- v0.f = v1.f;
-} else {
- v0.f = 2;
-}
-return v0.f;
-""");
- code.parse().opto();
- assertEquals("return Phi(Region,.f,2);", code.print());
- }
-
-
- @Test
- public void testAntiDeps4() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-v.f = arg;
-_S t = new _S;
-int i = v.f;
-if (arg+1) arg= 0;
-while (arg) v.f = 2;
-return i;
-""");
- code.parse().opto();
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testAntiDeps5() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-while(1) {
- while(arg+1) { arg=arg-1; }
- if (arg) break;
- v.f = 2;
-}
-return v.f;
-""");
- code.parse().opto();
- assertEquals("return Phi(Loop,0,2);", code.print());
- for( int arg : new int[]{-1,0,1,3,100} )
- assertEquals("0",Eval2.eval(code,arg));
- }
-
- @Test
- public void testAntiDeps6() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int v; };
-_S !ptr=new _S;
-while( -arg )
- ptr = new _S;
-while(1)
- arg = arg+ptr.v;
-""");
- code.parse().opto();
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testAntiDeps7() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-_S t = new _S;
-int i = v.f;
-while (arg) {
- v.f = arg;
- arg = arg-1;
-}
-return i;
-""");
- code.parse().opto();
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testAntiDeps8() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-_S t = new _S;
-while(arg) {
- arg=arg-1;
- int f = v.f;
- v.f = 2;
- if (arg) arg = f;
- else v.f = 3;
-}
-return arg;
-""");
- code.parse().opto();
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testAntiDeps9() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-_S t = new _S;
-if (arg) {
- v.f=2;
- int i=t.f;
- v.f=i;
-}
-return v.f;
-""");
- code.parse().opto();
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testExample2() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-_S !v = new _S;
-int i = arg;
-while (arg > 0) {
- int j = i/3;
- if (arg == 5)
- v.f = j;
- arg = arg - 1;
-}
-return v;
- """);
- code.parse().opto();
- //assertEquals("return new _S;", code.print());
- }
-
- @Test
- public void testScheduleUse() {
- CodeGen code = new CodeGen(
-"""
-int v0=0;
-while(0>=0) {
- u1 v1=0;
- v1=v0;
- if(v1*0)
- v0=-v1;
-}
-return 0;
-""");
- code.parse().opto();
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testLoopCarriedDep() {
- CodeGen code = new CodeGen(
-"""
-u32 v0=0;
-{
- int v1=0;
- while(v1) {
- v1=1>>>v0!=0;
- v0=v1/0;
- }
- return v1;
-}
-""");
- code.parse().opto();
- assertEquals("return 0;", code.print());
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 0f4bb0b4..a71cc167 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -3,45 +3,149 @@ 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 Chapter12Test {
- @Test public void testSubZeroFloat() {
- var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
- // Compare strings so +0.0 and -0.0 remain distinct.
- assertEquals("0.0", Eval2.eval(code,0));
- assertEquals("-1.0", Eval2.eval(code,1));
+ @Test
+ public void testJig() {
+ CodeGen code = new CodeGen(
+"""
+return 3;
+""");
+ code.parse().opto();
+ assertEquals("return 3;", code.print());
+ assertEquals("3", Eval2.eval(code, 0));
}
- @Test public void testDeadNumericReturns() {
- for( String[] test : new String[][] {
- {"if(1) return 7; else return 2.5;", "7"},
- {"if(0) return 7; else return 2.5;", "2.5"},
- {"return 7; return 2.5;", "7"},
- {"return 2.5; return 7;", "2.5"}
- } ) {
- String src = test[0];
- var code = new CodeGen(src).parse().opto().typeCheck();
- assertEquals(src,test[1],Eval2.eval(code,0));
- }
+ @Test
+ public void testLinkedList0() {
+ CodeGen code = new CodeGen(
+"""
+struct LLI { !LLI? !next; int i; };
+!LLI? !head = null;
+while( arg ) {
+ !LLI !x = new LLI;
+ x.next = head;
+ x.i = arg;
+ head = x;
+ arg = arg-1;
+}
+return head.next.i;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
}
+ @Test
+ public void testLinkedList1() {
+ CodeGen code = new CodeGen(
+"""
+struct _LLI { !_LLI? !next; int q; };
+!_LLI? !head = null;
+while( arg ) {
+ !_LLI !x = new _LLI;
+ x.next = head;
+ x.q = arg;
+ head = x;
+ arg = arg-1;
+}
+if( !head ) return 0;
+_LLI? next = head.next;
+if( next==null ) return 1;
+return next.q;
+""");
+ code.parse().opto().typeCheck();
+ assertEquals("return Phi(Region,1,.q,0);", code.print());
+ assertEquals("2", Eval2.eval(code, 3));
+ }
@Test
- public void testJig() {
+ public void testCoRecur() {
CodeGen code = new CodeGen(
"""
-return 0;
+struct _int0 { int i; !Test._flt0? !f; };
+struct _flt0 { flt f; !_int0? !i; };
+!_int0 !i0 = new _int0;
+i0.i = 17;
+!_flt0 !f0 = new _flt0;
+f0.f = 3.14;
+i0.f = f0;
+f0.i = i0;
+return f0.i.f.i.i;
+""");
+ code.parse().opto().typeCheck();
+ assertEquals("return 17;", code.print());
+ }
+
+ @Test
+ public void testNullRef0() {
+ CodeGen code = new CodeGen(
+"""
+struct _N { !_N? !next; int i; };
+_N n = new _N;
+return n.next;
""");
code.parse().opto();
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
+ assertEquals("return null;", code.print());
}
@Test
- public void testFloat() {
+ public void testNullRef1() {
CodeGen code = new CodeGen(
"""
-flt x = 3.14;
+struct M { int m; };
+struct N { !M !next; int i; new N = { !M m -> next = m; }; };
+N n = new N(new M);
+return n.next;
+""");
+ code.parse().opto();
+ assertEquals("Stop[ return (const)Test.M; return MEM[ 2:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 7:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.i=0;]; return Test.N; ]", code.print());
+ }
+
+ @Test
+ public void testNullRef2() {
+ CodeGen code = new CodeGen(
+"""
+struct M { int m; };
+struct N { !M !next; int i; new N = { !M m -> next = m; }; };
+N n = new N(null);
+return n.next;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); }
+ }
+
+ @Test
+ public void testNullRef3() {
+ CodeGen code = new CodeGen(
+"""
+struct N { !N? !next; int i; };
+!N !n = new N;
+n.i = 3.14;
+return n.i;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); }
+ }
+
+ @Test
+ public void testNullRef4() {
+ CodeGen code = new CodeGen("return -null-5/null-5;");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); }
+ }
+
+ @Test public void testNullRef5() {
+ CodeGen code = new CodeGen("return null+42;");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); }
+ }
+
+ @Test
+ public void testEmpty() {
+ CodeGen code = new CodeGen(
+"""
+struct _S{};
return 0;
""");
code.parse().opto();
@@ -50,34 +154,128 @@ return 0;
}
@Test
- public void testSquareRoot() {
+ public void testForwardRef0() {
CodeGen code = new CodeGen(
"""
-flt guess = arg;
-while( 1 ) {
- flt next = (arg/guess + guess)/2;
- if( next == guess ) break;
- guess = next;
-}
-return guess;
+struct S1 { S2? s; };
+return new S2;
""");
- // TODO: One of the problems with <clinit> returning an OS error code,
- // is it chops all results to int.
- code.parse().opto();
- assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print());
- assertEquals("3.0", Eval2.eval(code, 9));
- assertEquals("1.414213562373095", Eval2.eval(code, 2));
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@Test
- public void testFPOps() {
+ public void testForwardRef1() {
CodeGen code = new CodeGen(
"""
-flt x = arg;
-return x+1==x;
+struct _S1 { !_S2? !s; };
+struct _S2 { int x; };
+return (new _S1).s=new _S2;
""");
code.parse().opto();
- assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
- assertEquals("0", Eval2.eval(code, 1));
+ assertEquals("return Test._S2;", code.print());
+ }
+
+ @Test
+ public void testcheckNull() {
+ CodeGen code = new CodeGen(
+"""
+struct I { int i; };
+struct P { !I? !pi; };
+!P !p1 = new P;
+!P !p2 = new P;
+p2.pi = new I;
+p2.pi.i = 2;
+if (arg) p1 = new P;
+return p1.pi.i + 1;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
+ }
+
+ @Test
+ public void testCoRecur2() {
+ CodeGen code = new CodeGen(
+"""
+struct _A { !Test._B? !f0; !Test._C? !f1; }; !_A !a = new _A;
+struct _B { !Test._C? !f0; !Test._A? !f1; }; !_B !b = new _B;
+struct _C { !Test._A? !f0; !Test._B? !f1; }; !_C !c = new _C;
+
+a.f0=b; a.f1=c;
+b.f0=c; b.f1=a;
+c.f0=a; c.f1=b;
+
+return a.f0.f1.f0.f1.f0;
+
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return Test._B;", code.print());
+ assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testCoRecur3() {
+ CodeGen code = new CodeGen(
+"""
+struct _A { !Test._L? !a; !Test._T? !b; !Test._F? !c; }; !_A !a = new _A;
+struct _B { !Test._M? !a; !Test._U? !b; !Test._G? !c; }; !_B !b = new _B;
+struct _C { !Test._N? !a; !Test._V? !b; !Test._H? !c; }; !_C !c = new _C;
+struct _D { !Test._O? !a; !Test._W? !b; !Test._I? !c; }; !_D !d = new _D;
+struct _E { !Test._P? !a; !Test._X? !b; !Test._J? !c; }; !_E !e = new _E;
+struct _F { !Test._Q? !a; !Test._Y? !b; !Test._K? !c; }; !_F !f = new _F;
+struct _G { !Test._R? !a; !Test._Z? !b; !Test._L? !c; }; !_G !g = new _G;
+struct _H { !Test._S? !a; !Test._A? !b; !Test._M? !c; }; !_H !h = new _H;
+struct _I { !Test._T? !a; !Test._B? !b; !Test._N? !c; }; !_I !i = new _I;
+struct _J { !Test._U? !a; !Test._C? !b; !Test._O? !c; }; !_J !j = new _J;
+struct _K { !Test._V? !a; !Test._D? !b; !Test._P? !c; }; !_K !k = new _K;
+struct _L { !Test._W? !a; !Test._E? !b; !Test._Q? !c; }; !_L !l = new _L;
+struct _M { !Test._X? !a; !Test._F? !b; !Test._R? !c; }; !_M !m = new _M;
+struct _N { !Test._Y? !a; !Test._G? !b; !Test._S? !c; }; !_N !n = new _N;
+struct _O { !Test._Z? !a; !Test._H? !b; !Test._T? !c; }; !_O !o = new _O;
+struct _P { !Test._A? !a; !Test._I? !b; !Test._U? !c; }; !_P !p = new _P;
+struct _Q { !Test._B? !a; !Test._J? !b; !Test._V? !c; }; !_Q !q = new _Q;
+struct _R { !Test._C? !a; !Test._K? !b; !Test._W? !c; }; !_R !r = new _R;
+struct _S { !Test._D? !a; !Test._L? !b; !Test._X? !c; }; !_S !s = new _S;
+struct _T { !Test._E? !a; !Test._M? !b; !Test._Y? !c; }; !_T !t = new _T;
+struct _U { !Test._F? !a; !Test._N? !b; !Test._Z? !c; }; !_U !u = new _U;
+struct _V { !Test._G? !a; !Test._O? !b; !Test._A? !c; }; !_V !v = new _V;
+struct _W { !Test._H? !a; !Test._P? !b; !Test._B? !c; }; !_W !w = new _W;
+struct _X { !Test._I? !a; !Test._Q? !b; !Test._C? !c; }; !_X !x = new _X;
+struct _Y { !Test._J? !a; !Test._R? !b; !Test._D? !c; }; !_Y !y = new _Y;
+struct _Z { !Test._K? !a; !Test._S? !b; !Test._E? !c; }; !_Z !z = new _Z;
+
+a.a=l; a.b=t; a.c=f;
+b.a=m; b.b=u; b.c=g;
+c.a=n; c.b=v; c.c=h;
+d.a=o; d.b=w; d.c=i;
+e.a=p; e.b=x; e.c=j;
+f.a=q; f.b=y; f.c=k;
+g.a=r; g.b=z; g.c=l;
+h.a=s; h.b=a; h.c=m;
+i.a=t; i.b=b; i.c=n;
+j.a=u; j.b=c; j.c=o;
+k.a=v; k.b=d; k.c=p;
+l.a=w; l.b=e; l.c=q;
+m.a=x; m.b=f; m.c=r;
+n.a=y; n.b=g; n.c=s;
+o.a=z; o.b=h; o.c=t;
+p.a=a; p.b=i; p.c=u;
+q.a=b; q.b=j; q.c=v;
+r.a=c; r.b=k; r.c=w;
+s.a=d; s.b=l; s.c=x;
+t.a=e; t.b=m; t.c=y;
+u.a=f; u.b=n; u.c=z;
+v.a=g; v.b=o; v.c=a;
+w.a=h; w.b=p; w.c=b;
+x.a=i; x.b=q; x.c=c;
+y.a=j; y.b=r; y.c=d;
+z.a=k; z.b=s; z.c=e;
+
+return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c;
+
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return Test._R;", code.print());
+ assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0));
}
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index fedd59ce..7cfa5817 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -1,281 +1,284 @@
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 Chapter13Test {
+
@Test
- public void testJig() {
+ public void testPrimes() {
CodeGen code = new CodeGen(
"""
-return 3;
+if( arg < 2 ) return 0;
+int primeCount = 1;
+int prime = 3;
+while( prime <= arg ) {
+ int isPrime = 1;
+ // Check for even case, so the next loop need only check odds
+ if( (prime/2)*2 == prime )
+ continue;
+ // Check odds up to sqrt of prime
+ int j = 3;
+ while( j*j <= prime ) {
+ if( (prime/j)*j == prime ) {
+ isPrime = 0;
+ break;
+ }
+ j = j + 2;
+ }
+ if( isPrime )
+ primeCount = primeCount + 1;
+ prime = prime + 2;
+}
+return primeCount;
""");
code.parse().opto();
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
+ assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print());
+ assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
+ assertEquals("1", Eval2.eval(code, 2)); // 2
+ assertEquals("2", Eval2.eval(code, 3)); // 2, 3
+ assertEquals("2", Eval2.eval(code, 4)); // 2, 3
+ assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5
+ assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7
}
@Test
- public void testLinkedList0() {
+ public void testClassField() {
CodeGen code = new CodeGen(
"""
-struct LLI { LLI? next; int i; };
-LLI? !head = null;
-while( arg ) {
- LLI !x = new LLI;
- x.next = head;
- x.i = arg;
- head = x;
- arg = arg-1;
-}
-return head.next.i;
+if( arg < 2 ) return 0;
+int primeCount = 1;
+while( arg-- )
+ primeCount++;
+return primeCount;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
- }
+ code.driver(CodeGen.Phase.LocalSched);
+ assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
+ assertEquals("3", Eval2.eval(code, 2)); //
+}
+
@Test
- public void testLinkedList1() {
+ public void testAntiDeps1() {
CodeGen code = new CodeGen(
"""
-struct _LLI { _LLI? next; int q; };
-_LLI? !head = null;
-while( arg ) {
- _LLI !x = new _LLI;
- x.next = head;
- x.q = arg;
- head = x;
- arg = arg-1;
-}
-if( !head ) return 0;
-_LLI? next = head.next;
-if( next==null ) return 1;
-return next.q;
+struct _S { int f; };
+!_S !v=new _S;
+v.f = 2;
+_S q=new _S;
+int i=q.f;
+i=v.f;
+if (arg) v.f=1;
+return i;
""");
- code.parse().opto().typeCheck();
- assertEquals("return Phi(Region,1,.q,0);", code.print());
- assertEquals("2", Eval2.eval(code, 3));
+ code.parse().opto();
+ assertEquals("return 2;", code.print());
+ assertEquals("2", Eval2.eval(code, 0));
+ assertEquals("2", Eval2.eval(code, 1));
}
@Test
- public void testCoRecur() {
+ public void testAntiDeps2() {
CodeGen code = new CodeGen(
"""
-struct _int0 { int i; Test._flt0? f; };
-struct _flt0 { flt f; _int0? i; };
-_int0 !i0 = new _int0;
-i0.i = 17;
-_flt0 !f0 = new _flt0;
-f0.f = 3.14;
-i0.f = f0;
-f0.i = i0;
-return f0.i.f.i.i;
+struct _S { int f; };
+!_S !v = new _S;
+v.f = arg;
+!_S !t = new _S;
+int i = 0;
+if (arg) {
+ if (arg+1) v = t;
+ i = v.f;
+} else {
+ v.f = 2;
+}
+return i;
""");
- code.parse().opto().typeCheck();
- assertEquals("return 17;", code.print());
+ code.parse().opto();
+ assertEquals("return Phi(Region,.f,0);", code.print());
}
@Test
- public void testNullRef0() {
+ public void testAntiDeps3() {
CodeGen code = new CodeGen(
"""
-struct _N { _N? next; int i; };
-_N n = new _N;
-return n.next;
+struct _S { int f; };
+!_S !v0 = new _S;
+!_S? !v1;
+if (arg) v1 = new _S;
+if (v1) {
+ v0.f = v1.f;
+} else {
+ v0.f = 2;
+}
+return v0.f;
""");
code.parse().opto();
- assertEquals("return null;", code.print());
+ assertEquals("return Phi(Region,.f,2);", code.print());
}
+
@Test
- public void testNullRef1() {
+ public void testAntiDeps4() {
CodeGen code = new CodeGen(
"""
-struct M { int m; };
-struct N { M next; int i; new N = { M m -> next = m; }; };
-N n = new N(new M);
-return n.next;
+struct _S { int f; };
+!_S !v = new _S;
+v.f = arg;
+_S t = new _S;
+int i = v.f;
+if (arg+1) arg= 0;
+while (arg) v.f = 2;
+return i;
""");
code.parse().opto();
- assertEquals("Stop[ return (const)Test.M; return MEM[ 2:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 7:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.i=0;]; return Test.N; ]", code.print());
+ assertEquals("return arg;", code.print());
}
@Test
- public void testNullRef2() {
+ public void testAntiDeps5() {
CodeGen code = new CodeGen(
"""
-struct M { int m; };
-struct N { M next; int i; new N = { M m -> next = m; }; };
-N n = new N(null);
-return n.next;
+struct _S { int f; };
+!_S !v = new _S;
+while(1) {
+ while(arg+1) { arg=arg-1; }
+ if (arg) break;
+ v.f = 2;
+}
+return v.f;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); }
+ code.parse().opto();
+ assertEquals("return Phi(Loop,0,2);", code.print());
+ for( int arg : new int[]{-1,0,1,3,100} )
+ assertEquals("0",Eval2.eval(code,arg));
}
@Test
- public void testNullRef3() {
+ public void testAntiDeps6() {
CodeGen code = new CodeGen(
"""
-struct N { N? next; int i; };
-N !n = new N;
-n.i = 3.14;
-return n.i;
+struct _S { int v; };
+!_S !ptr=new _S;
+while( -arg )
+ ptr = new _S;
+while(1)
+ arg = arg+ptr.v;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); }
- }
-
- @Test
- public void testNullRef4() {
- CodeGen code = new CodeGen("return -null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); }
- }
-
- @Test public void testNullRef5() {
- CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); }
+ code.parse().opto();
+ assertEquals("return Top;", code.print());
}
@Test
- public void testEmpty() {
+ public void testAntiDeps7() {
CodeGen code = new CodeGen(
"""
-struct _S{};
-return 0;
+struct _S { int f; };
+!_S !v = new _S;
+_S t = new _S;
+int i = v.f;
+while (arg) {
+ v.f = arg;
+ arg = arg-1;
+}
+return i;
""");
code.parse().opto();
assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
}
@Test
- public void testForwardRef0() {
+ public void testAntiDeps8() {
CodeGen code = new CodeGen(
"""
-struct S1 { S2? s; };
-return new S2;
+struct _S { int f; };
+!_S !v = new _S;
+_S t = new _S;
+while(arg) {
+ arg=arg-1;
+ int f = v.f;
+ v.f = 2;
+ if (arg) arg = f;
+ else v.f = 3;
+}
+return arg;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
+ code.parse().opto();
+ assertEquals("return 0;", code.print());
}
@Test
- public void testForwardRef1() {
+ public void testAntiDeps9() {
CodeGen code = new CodeGen(
"""
-struct _S1 { _S2? s; };
-struct _S2 { int x; };
-return (new _S1).s=new _S2;
+struct _S { int f; };
+!_S !v = new _S;
+_S t = new _S;
+if (arg) {
+ v.f=2;
+ int i=t.f;
+ v.f=i;
+}
+return v.f;
""");
code.parse().opto();
- assertEquals("return Test._S2;", code.print());
+ assertEquals("return 0;", code.print());
}
@Test
- public void testcheckNull() {
+ public void testExample2() {
CodeGen code = new CodeGen(
"""
-struct I { int i; };
-struct P { I? pi; };
-P !p1 = new P;
-P !p2 = new P;
-p2.pi = new I;
-p2.pi.i = 2;
-if (arg) p1 = new P;
-return p1.pi.i + 1;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
+struct _S { int f; };
+!_S !v = new _S;
+int i = arg;
+while (arg > 0) {
+ int j = i/3;
+ if (arg == 5)
+ v.f = j;
+ arg = arg - 1;
+}
+return v;
+ """);
+ code.parse().opto();
+ //assertEquals("return new _S;", code.print());
}
@Test
- public void testCoRecur2() {
+ public void testScheduleUse() {
CodeGen code = new CodeGen(
"""
-struct _A { Test._B? f0; Test._C? f1; }; _A !a = new _A;
-struct _B { Test._C? f0; Test._A? f1; }; _B !b = new _B;
-struct _C { Test._A? f0; Test._B? f1; }; _C !c = new _C;
-
-a.f0=b; a.f1=c;
-b.f0=c; b.f1=a;
-c.f0=a; c.f1=b;
-
-return a.f0.f1.f0.f1.f0;
-
+int v0=0;
+while(0>=0) {
+ u1 v1=0;
+ v1=v0;
+ if(v1*0)
+ v0=-v1;
+}
+return 0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
- assertEquals("return Test._B;", code.print());
- assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0));
+ code.parse().opto();
+ assertEquals("return Top;", code.print());
}
@Test
- public void testCoRecur3() {
+ public void testLoopCarriedDep() {
CodeGen code = new CodeGen(
"""
-struct _A { Test._L? a; Test._T? b; Test._F? c; }; _A !a = new _A;
-struct _B { Test._M? a; Test._U? b; Test._G? c; }; _B !b = new _B;
-struct _C { Test._N? a; Test._V? b; Test._H? c; }; _C !c = new _C;
-struct _D { Test._O? a; Test._W? b; Test._I? c; }; _D !d = new _D;
-struct _E { Test._P? a; Test._X? b; Test._J? c; }; _E !e = new _E;
-struct _F { Test._Q? a; Test._Y? b; Test._K? c; }; _F !f = new _F;
-struct _G { Test._R? a; Test._Z? b; Test._L? c; }; _G !g = new _G;
-struct _H { Test._S? a; Test._A? b; Test._M? c; }; _H !h = new _H;
-struct _I { Test._T? a; Test._B? b; Test._N? c; }; _I !i = new _I;
-struct _J { Test._U? a; Test._C? b; Test._O? c; }; _J !j = new _J;
-struct _K { Test._V? a; Test._D? b; Test._P? c; }; _K !k = new _K;
-struct _L { Test._W? a; Test._E? b; Test._Q? c; }; _L !l = new _L;
-struct _M { Test._X? a; Test._F? b; Test._R? c; }; _M !m = new _M;
-struct _N { Test._Y? a; Test._G? b; Test._S? c; }; _N !n = new _N;
-struct _O { Test._Z? a; Test._H? b; Test._T? c; }; _O !o = new _O;
-struct _P { Test._A? a; Test._I? b; Test._U? c; }; _P !p = new _P;
-struct _Q { Test._B? a; Test._J? b; Test._V? c; }; _Q !q = new _Q;
-struct _R { Test._C? a; Test._K? b; Test._W? c; }; _R !r = new _R;
-struct _S { Test._D? a; Test._L? b; Test._X? c; }; _S !s = new _S;
-struct _T { Test._E? a; Test._M? b; Test._Y? c; }; _T !t = new _T;
-struct _U { Test._F? a; Test._N? b; Test._Z? c; }; _U !u = new _U;
-struct _V { Test._G? a; Test._O? b; Test._A? c; }; _V !v = new _V;
-struct _W { Test._H? a; Test._P? b; Test._B? c; }; _W !w = new _W;
-struct _X { Test._I? a; Test._Q? b; Test._C? c; }; _X !x = new _X;
-struct _Y { Test._J? a; Test._R? b; Test._D? c; }; _Y !y = new _Y;
-struct _Z { Test._K? a; Test._S? b; Test._E? c; }; _Z !z = new _Z;
-
-a.a=l; a.b=t; a.c=f;
-b.a=m; b.b=u; b.c=g;
-c.a=n; c.b=v; c.c=h;
-d.a=o; d.b=w; d.c=i;
-e.a=p; e.b=x; e.c=j;
-f.a=q; f.b=y; f.c=k;
-g.a=r; g.b=z; g.c=l;
-h.a=s; h.b=a; h.c=m;
-i.a=t; i.b=b; i.c=n;
-j.a=u; j.b=c; j.c=o;
-k.a=v; k.b=d; k.c=p;
-l.a=w; l.b=e; l.c=q;
-m.a=x; m.b=f; m.c=r;
-n.a=y; n.b=g; n.c=s;
-o.a=z; o.b=h; o.c=t;
-p.a=a; p.b=i; p.c=u;
-q.a=b; q.b=j; q.c=v;
-r.a=c; r.b=k; r.c=w;
-s.a=d; s.b=l; s.c=x;
-t.a=e; t.b=m; t.c=y;
-u.a=f; u.b=n; u.c=z;
-v.a=g; v.b=o; v.c=a;
-w.a=h; w.b=p; w.c=b;
-x.a=i; x.b=q; x.c=c;
-y.a=j; y.b=r; y.c=d;
-z.a=k; z.b=s; z.c=e;
-
-return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c;
-
+u32 v0=0;
+{
+ int v1=0;
+ while(v1) {
+ v1=1>>>v0!=0;
+ v0=v1/0;
+ }
+ return v1;
+}
""");
- code.parse().opto().typeCheck().GCM().localSched();
- assertEquals("return Test._R;", code.print());
- assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0));
+ code.parse().opto();
+ assertEquals("return 0;", code.print());
}
+
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
new file mode 100644
index 00000000..20274581
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -0,0 +1,83 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class Chapter14FloatTest {
+ @Test public void testSubZeroFloat() {
+ var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
+ // Compare strings so +0.0 and -0.0 remain distinct.
+ assertEquals("0.0", Eval2.eval(code,0));
+ assertEquals("-1.0", Eval2.eval(code,1));
+ }
+
+ @Test public void testDeadNumericReturns() {
+ for( String[] test : new String[][] {
+ {"if(1) return 7; else return 2.5;", "7"},
+ {"if(0) return 7; else return 2.5;", "2.5"},
+ {"return 7; return 2.5;", "7"},
+ {"return 2.5; return 7;", "2.5"}
+ } ) {
+ String src = test[0];
+ var code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals(src,test[1],Eval2.eval(code,0));
+ }
+ }
+
+
+ @Test
+ public void testJig() {
+ CodeGen code = new CodeGen(
+"""
+return 0;
+""");
+ code.parse().opto();
+ assertEquals("return 0;", code.print());
+ assertEquals("0", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testFloat() {
+ CodeGen code = new CodeGen(
+"""
+flt x = 3.14;
+return 0;
+""");
+ code.parse().opto();
+ assertEquals("return 0;", code.print());
+ assertEquals("0", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testSquareRoot() {
+ CodeGen code = new CodeGen(
+"""
+flt guess = arg;
+while( 1 ) {
+ flt next = (arg/guess + guess)/2;
+ if( next == guess ) break;
+ guess = next;
+}
+return guess;
+""");
+ // TODO: One of the problems with <clinit> returning an OS error code,
+ // is it chops all results to int.
+ code.parse().opto();
+ assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print());
+ assertEquals("3.0", Eval2.eval(code, 9));
+ assertEquals("1.414213562373095", Eval2.eval(code, 2));
+ }
+
+ @Test
+ public void testFPOps() {
+ CodeGen code = new CodeGen(
+"""
+flt x = arg;
+return x+1==x;
+""");
+ code.parse().opto();
+ assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
+ assertEquals("0", Eval2.eval(code, 1));
+ }
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index 807905a8..f0ad6c2d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -93,7 +93,7 @@ return b;
CodeGen code = new CodeGen(
"""
struct _Foo { u1 b; };
-_Foo !f = new _Foo;
+!_Foo !f = new _Foo;
f.b = 123;
return f.b;
""");
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 24552062..85dc7b47 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -25,8 +25,8 @@ return 3.14;
public void testCyclic() {
CodeGen code = new CodeGen(
"""
-struct _C { _C? l; };
-_C !c = new _C;
+struct _C { !_C? !l; };
+!_C !c = new _C;
c.l = c;
return c;
""");
@@ -98,7 +98,7 @@ return a[0];
CodeGen code = new CodeGen(
"""
struct _A { int i; };
-_A?[] !a = new _A?[2];
+!_A?[] !a = new !_A?[2];
return a;
""");
code.parse().opto();
@@ -112,16 +112,16 @@ return a;
"""
struct _S { int x; flt y; };
// A new _S
-_S !s = new _S; s.x=99; s.y = 3.14;
+!_S !s = new _S; s.x=99; s.y = 3.14;
// Double-d array of _Ss. Fill in one row.
-_S?[]?[] !iss = new _S?[]?[2];
-iss[0] = new _S?[7];
+!_S?[]?[] !iss = new !_S?[]?[2];
+iss[0] = new !_S?[7];
iss[0][2] = s;
// Now pull out the filled-in value, with null checks
flt rez;
-_S?[]? is = iss[arg];
+!_S?[]? is = iss[arg];
if( !is ) rez = 1.2;
else {
_S? i = is[2];
@@ -142,11 +142,11 @@ return rez;
"""
struct _S { int x; flt y; };
// A new _S
-_S !s = new _S; s.x=99; s.y = 3.14;
+!_S !s = new _S; s.x=99; s.y = 3.14;
// Double-d array of _Ss. Fill in one row.
-_S?[]?[] !iss = new _S?[]?[2];
-iss[0] = new _S?[7];
+!_S?[]?[] !iss = new !_S?[]?[2];
+iss[0] = new !_S?[7];
iss[0][2] = s;
// Now pull out the filled-in value, with null checks
@@ -167,9 +167,9 @@ return rez;""");
CodeGen code = new CodeGen(
"""
// Can we define a forward-reference array?
-struct _Tree { _Tree?[]? _kids; };
-_Tree !root = new _Tree;
-root._kids = new _Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
+struct _Tree { !_Tree?[]? !_kids; };
+!_Tree !root = new _Tree;
+root._kids = new !_Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
root._kids[0] = new _Tree;
return root;
""");
@@ -182,7 +182,7 @@ return root;
public void testNestedStructAddMemProj() {
CodeGen code = new CodeGen(
"""
-struct _S { int a; int[] b; new _S = { int[] ary -> b=ary; }; };
+struct _S { int a; int[] !b; new _S = { int[] ary -> b=ary; }; };
return 0;
""");
code.parse().opto();
@@ -227,8 +227,8 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
CodeGen code = new CodeGen(
"""
struct _S {int i; flt f;};
-_S !s1 = new _S;
-_S !s2 = new _S;
+!_S !s1 = new _S;
+!_S !s2 = new _S;
s2.i = 3;
s2.f = 2.0;
if (arg) s1 = new _S;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 80b0a2bf..23ec549d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -42,31 +42,6 @@ return x+y;
assertEquals("5", Eval2.eval(code, 0));
}
-// @Test
-// public void testFinal0() {
-// CodeGen code = new CodeGen(
-//"""
-//int !x=2;
-//x=3;
-//return x;
-//""");
-// try { code.parse().opto(); fail(); }
-// catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
-// }
-
- @Test
- public void testFinal1() {
- CodeGen code = new CodeGen(
-"""
-int x=2, y=3;
-if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x
-return y;
-""");
- code.parse().opto();
- assertEquals("return Phi(Region,9,3);", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- assertEquals("9", Eval2.eval(code, 1));
- }
@Test
public void testConstruct0() {
@@ -81,18 +56,6 @@ return z.x;
}
@Test
- public void testConstruct1() {
- CodeGen code = new CodeGen("""
-struct _X { int !x; new _X = { int xx -> x=xx; }; };
-_X z = new _X(3);
-return z.x;
-""");
- code.parse().opto();
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
public void testConstruct2() {
CodeGen code = new CodeGen("""
struct _X { int x=3; new _X = { int xx -> x=xx; }; };
@@ -106,86 +69,11 @@ return z.x;
@Test
- public void testStructFinal0() {
- CodeGen code = new CodeGen("""
-struct _Point { int !x, !y; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
-_Point p = new _Point(3,4);
-return p;
-""");
- code.parse().opto();
- assertEquals("return (const)Test._Point;", code.print());
- assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0));
- }
-
- @Test
- public void testStructFinal1() {
- CodeGen code = new CodeGen("""
-struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
-val p = new _Point(5,6);
-p.x++;
-return p;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal2() {
- CodeGen code = new CodeGen("""
-struct _Point { int x=3, y=4; };
-val p = new _Point;
-p.x++;
-return p;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal3() {
- CodeGen code = new CodeGen("""
-struct _Point { var x; var y; };
-_Point p = new _Point;
-p.x++;
-return p;
-""");
- try { code.parse().opto(); fail(); }
- catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal4() {
- CodeGen code = new CodeGen("""
-struct _Point { val x=2; val y=4; };
-_Point p = new _Point;
-p.x++;
-return p;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal5() {
- CodeGen code = new CodeGen("""
-struct _Point { var x=3; var y=4; };
-_Point !p = new _Point;
-p.x++;
-return p;
-""");
- code.parse().opto();
- assertEquals("return Test._Point;", code.print());
- assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0));
- }
-
- // Same as the Chapter13 test with the same name, but using the new
- // constructor syntax
- @Test
public void testLinkedList1() {
CodeGen code = new CodeGen(
"""
-struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; };
-_LLI? !head = null;
+struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; };
+!_LLI? !head = null;
while( arg ) {
head = new _LLI(head,arg);
arg = arg-1;
@@ -206,7 +94,7 @@ return next.i;
public void testLinkedList2() {
CodeGen code = new CodeGen(
"""
-struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int a ->
+struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int a ->
next=n;
int !tmp=a;
while( a > 10 ) {
@@ -215,7 +103,7 @@ struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int a ->
}
i=tmp;
}; };
-_LLI? !head = null;
+!_LLI? !head = null;
while( arg ) {
head = new _LLI(head,arg);
arg = arg-1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
new file mode 100644
index 00000000..52acff57
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -0,0 +1,109 @@
+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 Chapter17aTest {
+
+ @Test
+ public void testFinal1() {
+ CodeGen code = new CodeGen(
+"""
+int x=2, y=3;
+if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x
+return y;
+""");
+ code.parse().opto();
+ assertEquals("return Phi(Region,9,3);", code.print());
+ assertEquals("3", Eval2.eval(code, 0));
+ assertEquals("9", Eval2.eval(code, 1));
+ }
+
+ @Test
+ public void testConstruct1() {
+ CodeGen code = new CodeGen("""
+struct _X { int ~x; new _X = { int xx -> x=xx; }; };
+_X z = new _X(3);
+return z.x;
+""");
+ code.parse().opto();
+ assertEquals("return 3;", code.print());
+ assertEquals("3", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testStructFinal0() {
+ CodeGen code = new CodeGen("""
+struct _Point { int ~x, ~y; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
+_Point p = new _Point(3,4);
+return p;
+""");
+ code.parse().opto();
+ assertEquals("return (const)Test._Point;", code.print());
+ assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testStructFinal1() {
+ CodeGen code = new CodeGen("""
+struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
+~_Point p = new _Point(5,6);
+p.x++;
+return p;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ }
+
+ @Test
+ public void testStructFinal2() {
+ CodeGen code = new CodeGen("""
+struct _Point { int x=3, y=4; };
+~_Point p = new _Point;
+p.x++;
+return p;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ }
+
+ @Test
+ public void testStructFinal3() {
+ CodeGen code = new CodeGen("""
+struct _Point { var x; var y; };
+_Point p = new _Point;
+p.x++;
+return p;
+""");
+ try { code.parse().opto(); fail(); }
+ catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
+ }
+
+ @Test
+ public void testStructFinal4() {
+ CodeGen code = new CodeGen("""
+struct _Point { val x=2; val y=4; };
+_Point p = new _Point;
+p.x++;
+return p;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
+ }
+
+ @Test
+ public void testStructFinal5() {
+ CodeGen code = new CodeGen("""
+struct _Point { var x=3; var y=4; };
+!_Point !p = new _Point;
+p.x++;
+return p;
+""");
+ code.parse().opto();
+ assertEquals("return Test._Point;", code.print());
+ assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0));
+ }
+
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 79aecbff..ab1a59e1 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -5,7 +5,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-public class Chapter17Test {
+public class Chapter17bTest {
@Test
public void testJig() {
@@ -80,7 +80,7 @@ return xs[0];
public void testInc5() {
CodeGen code = new CodeGen("""
struct _S { u16 x; };
-_S !s = new _S;
+!_S !s = new _S;
s.x--;
return s.x;
""");
@@ -156,33 +156,33 @@ return s.x;
}
@Test public void testVar4() {
- CodeGen code = new CodeGen("struct _S{int x;}; _S? s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable ");
+ CodeGen code = new CodeGen("struct _S{int x;}; !_S? !s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable ");
code.parse().opto();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
- CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; _S? s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable ");
+ CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; !_S? !s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable ");
code.parse().opto();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@Test public void testVar6() {
- CodeGen code = new CodeGen("struct S{int x;}; val s = new S; s.x++; return s.x; // Error initializer so x is immutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S; s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test public void testVar7() {
- CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; val s = new S(3); s.x++; return s.x; // Error initializer so x is immutable ");
+ CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; ~S s = new S(3); s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test public void testVar8() {
- CodeGen code = new CodeGen("struct _S{int x;}; _S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable ");
+ CodeGen code = new CodeGen("struct _S{int x;}; !_S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable ");
code.parse().opto();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
@@ -196,7 +196,7 @@ return s.x;
}
@Test public void testVar10() {
- CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; ~S s=new S; s.x++; return s.x; // Error, has val so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -204,15 +204,15 @@ return s.x;
@Test public void testVar11() {
CodeGen code = new CodeGen("""
struct Bar { int x; };
-Bar !bar = new Bar;
+!Bar !bar = new Bar;
bar.x = 3; // Ok, bar is mutable
-struct Foo { Bar? !bar; int y; new Foo = { Bar? b -> bar = b; }; };
-Foo !foo = new Foo(bar);
+struct Foo { !Bar? !bar; int y; new Foo = { !Bar? b -> bar = b; }; };
+!Foo !foo = new Foo(bar);
foo.bar = bar; // Ok foo is mutable
foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
-val xfoo = foo; // Throw away mutability
+~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
return xfoo.bar.x;
""");
@@ -223,15 +223,15 @@ return xfoo.bar.x;
@Test public void testVar12() {
CodeGen code = new CodeGen("""
struct _Bar { int x; };
-_Bar !bar = new _Bar;
+!_Bar !bar = new _Bar;
bar.x = 3; // Ok, bar is mutable
-struct _Foo { _Bar? !bar; int y; };
-_Foo !foo = new _Foo;
+struct _Foo { !_Bar? !bar; int y; };
+!_Foo !foo = new _Foo;
foo.bar = bar; // Ok bar is mutable
foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
-val xfoo = foo; // Throw away mutability
+~_Foo xfoo = foo; // Throw away mutability
int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4
foo.bar.x++; // Bumps to 5
int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5
@@ -256,7 +256,7 @@ int i,i++;
public void testVar14() {
CodeGen code = new CodeGen("""
struct B {};
-struct A { B b; new A = { ->
+struct A { !B !b; new A = { ->
return b; // read before init
b = new B;
}; };
@@ -269,10 +269,10 @@ A x = new A;
@Test
public void testVar15() {
for( String fields : new String[] {
- "B b; new A = { -> if (arg) b = new B; };",
- "B b; new A = { -> if (arg) return 0; b = new B; };",
- "B b; new A = { B b -> b = new B; };", // Argument shadows field
- "new A = { -> }; B b;", // Field declared later
+ "!B !b; new A = { -> if (arg) b = new B; };",
+ "!B !b; new A = { -> if (arg) return 0; b = new B; };",
+ "!B !b; new A = { !B b -> b = new B; };", // Argument shadows field
+ "new A = { -> }; !B !b;", // Field declared later
} ) {
CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return 0;");
try { code.parse(); fail(fields); }
@@ -282,16 +282,16 @@ A x = new A;
@Test
public void testDefaultConstructorFields() {
- for( String field : new String[] { "B b;", "B !b;" } ) {
+ for( String field : new String[] { "!B !b;", "!B !b;" } ) {
CodeGen code = new CodeGen("struct B {}; struct A { "+field+" }; return new A;");
try { code.parse(); fail(field); }
catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
for( String fields : new String[] {
"B b = new B;",
- "B b; b = new B;",
+ "!B !b; b = new B;",
"new A = { -> }; B b = new B;",
- "B b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };",
+ "!B !b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };",
} ) {
CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return new A.b != null;");
code.parse().opto().typeCheck();
@@ -299,7 +299,7 @@ A x = new A;
assertEquals("1",Eval2.eval(code,1));
}
// No returning path means no object can escape uninitialized.
- new CodeGen("struct B {}; struct A { B b; new A = { -> while (true) {} }; }; return 0;").parse();
+ new CodeGen("struct B {}; struct A { !B !b; new A = { -> while (true) {} }; }; return 0;").parse();
}
@Test
@@ -308,7 +308,7 @@ A x = new A;
"""
struct S{ new S = { -> x = 2; }; };
val x = 1;
-val s = new S;
+~S s = new S;
return x;
"""
);
@@ -323,10 +323,10 @@ return x;
struct Person {
bool focused;
};
-val focus = { Person p ->
+val focus = { !Person p ->
p.focused = true;
};
-Person me; // Person !me = new Person;
+!Person !me; // !Person !me = new Person;
focus(me);
return me;
"""
@@ -361,8 +361,8 @@ return arg ? 0 : arg;
public void testShortCircuitGuard() {
CodeGen code = new CodeGen("""
struct S { int !x; };
-val get = { S s -> s.x; };
-S? !s = arg ? new S;
+val get = { !S s -> s.x; };
+!S? !s = arg ? new S;
if( !s || !arg ) return 0;
return get(s);
""");
@@ -400,7 +400,7 @@ return b ? b.x++ + b.x++ : -1;
public void testTrinary4() {
// This test case will benefit from an unzipping transformation
CodeGen code = new CodeGen("""
-struct _Bar { _Bar? next; int x; new _Bar = { _Bar? n, int xx -> next=n; x=xx; }; };
+struct _Bar { !_Bar? !next; int x; new _Bar = { !_Bar? !n, int xx -> next=n; x=xx; }; };
var b = arg ? new _Bar((arg==2) ? new _Bar(null,2), 1);
return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0"
""");
@@ -515,8 +515,8 @@ for(;;arg++;) {}
public void testForward0() {
CodeGen code = new CodeGen("""
struct _A {
- B? f1;
- B? f2;
+ !B? !f1;
+ !B? !f2;
};
return new _A;
""");
@@ -529,11 +529,11 @@ return new _A;
public void testForward1() {
CodeGen code = new CodeGen("""
struct _A {
- B?[]? nil_array_of_b;
- B?[] array_of_b;
- new _A = { B?[] bs -> array_of_b = bs; };
+ !B?[]? !nil_array_of_b;
+ !B?[] !array_of_b;
+ new _A = { !B?[] bs -> array_of_b = bs; };
};
-return new _A(new B?[0]).array_of_b;
+return new _A(new !B?[0]).array_of_b;
""");
code.parse().opto();
assertEquals("return []*B?;", code.print());
@@ -544,8 +544,8 @@ return new _A(new B?[0]).array_of_b;
@Test
public void testLinkedList2() {
CodeGen code = new CodeGen("""
-struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; };
-_LLI? !head = null;
+struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; };
+!_LLI? !head = null;
while( arg-- )
head = new _LLI(head,arg);
int sum=0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 2c61fb44..adc05f6f 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -318,7 +318,7 @@ return 2;
public void testFcn8() {
CodeGen code = new CodeGen(
"""
-{int -> int}? _i2i = null;
+{int -> int}? !_i2i = null;
var _id = {{int->int} f-> return f;};
for(;;) {
if (_i2i) return _i2i(arg);
@@ -336,7 +336,7 @@ for(;;) {
public void testFcn9() {
CodeGen code = new CodeGen(
"""
-{int -> int}? i2i = null;
+{int -> int}? !i2i = null;
for(;;) {
if (i2i) return i2i(arg);
var x = {int i-> return i;};
@@ -357,13 +357,13 @@ struct _Person {
int age;
};
-val _fcn = { _Person?[] ps, int x ->
+val _fcn = { !_Person?[] !ps, int x ->
val tmp = ps[x];
if( ps[x] )
ps[x].age++;
};
-var ps = new _Person?[2];
+var ps = new !_Person?[2];
ps[0] = new _Person;
ps[1] = new _Person;
return _fcn(ps,1);
@@ -475,7 +475,7 @@ struct S {
{ int } f = { -> return x(); };
};
val x = { -> return 1; };
-S? s = null;
+!S? !s = null;
for(;;) {
if (s) return s.f;
}
@@ -509,7 +509,7 @@ return _f2f(_o)(1);
struct _Person {
int coffee_count;
};
-_Person !p = new _Person;
+!_Person !p = new _Person;
p.coffee_count += 1;
return p.coffee_count;
""");
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 5ab7ba97..8cd5137c 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -46,13 +46,13 @@ return 0;
String src =
"""
struct String {
- u8[~] cs;
+ u8[~] !cs;
int _hashCode;
new String = { u8[~] s -> cs=s; };
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -62,7 +62,7 @@ val equals = { String self, String s ->
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
@@ -71,7 +71,7 @@ val _hashCodeString = { String self ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
@@ -89,14 +89,14 @@ hashCode(new String("Hello, World!"));
String src =
"""
struct String {
- u8[~] cs;
+ u8[~] !cs;
int _hashCode;
new String = { u8[~] s -> cs=s; };
};
-val f = { String self -> 7; };
+val f = { !String self -> 7; };
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? 3
: (self._hashCode = f(self));
@@ -283,7 +283,7 @@ return sum;""");
public void testAlloc1() {
CodeGen code = new CodeGen(
"""
-struct _S { int a; _S? c; };
+struct _S { int a; !_S? !c; };
return new _S;""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return Test._S;", code.print());
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 245ea745..262b8604 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -15,7 +15,7 @@ public class Chapter20Test {
// A real integer return merged with a synthetic exit.
"if(arg) while(1) {} return arg+1;",
// TOP must not widen a non-null pointer or constrain a float register.
- "struct S { int x; }; S !s=new S; if(arg) while(1) {} return s;",
+ "struct S { int x; }; !S !s=new S; if(arg) while(1) {} return s;",
"if(arg) while(1) {} return 1.5;",
// Several synthetic exits into the same return.
"if(arg==1) while(1) {} if(arg==2) while(1) {} return arg+3;",
@@ -23,7 +23,7 @@ public class Chapter20Test {
"while(1) { arg+=1; }",
"if(arg) while(1) {} while(1) {}",
// Loop-carried memory and scalar self-conflicts exercise spilling.
- "struct S { int x; int y; }; S !a=new S; if(arg) while(1) { a.x+=arg; a.y+=a.x; } return a.x+a.y;"
+ "struct S { int x; int y; }; !S !a=new S; if(arg) while(1) { a.x+=arg; a.y+=a.x; } return a.x+a.y;"
};
for( String target : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : sources ) {
@@ -44,7 +44,7 @@ public class Chapter20Test {
@Test public void testNarrowStoreMasks() {
for( String type : new String[]{"i8","u8","i16","u16"} ) {
- CodeGen code = new CodeGen("struct S { "+type+" x; }; S !s=new S; s.x=arg; return 0;")
+ CodeGen code = new CodeGen("struct S { "+type+" x; }; !S !s=new S; s.x=arg; return 0;")
.driver(CodeGen.Phase.RegAlloc,"x86_64_v2","SystemV");
int stores=0;
for( var bb : code._cfg )
@@ -60,9 +60,9 @@ public class Chapter20Test {
@Test public void testInlinedReturnValue() {
String src = """
struct S { int x; };
-val f = { S s -> s.x = g(); };
+val f = { !S s -> s.x = g(); };
val g = { -> 123; };
-S !s = new S;
+!S !s = new S;
return f(s);
""";
// The entry of g can disappear before its Return is folded into f.
@@ -225,13 +225,13 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
String src =
"""
struct String {
- u8[] cs;
+ u8[] !cs;
int _hashCode;
new String = { u8[] data -> cs=data; };
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -241,13 +241,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 93a9e63f..bfce93b9 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -17,8 +17,8 @@ public class Chapter21Test {
@Test public void testReadBeforeConditionalStore() {
var code = new CodeGen("""
- struct S { int x; }; S !a=new S; S !b=new S;
- a.x=10; b.x=20; S !p=a; if(arg&1) p=b;
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg&1) p=b;
int sum=0;
for(int i=0; i<6; i++) {
sum+=a.x;
@@ -281,7 +281,7 @@ return new _s0.v1;
}
@Test public void testInfinite() {
- String src = "struct _S { int i; }; _S !s = new _S; while(1) s.i++; return s.i;";
+ String src = "struct _S { int i; }; !_S !s = new _S; while(1) s.i++; return s.i;";
testCPU(src,"x86_64_v2", "SystemV",0,"return Top;");
testCPU(src,"riscv" , "SystemV",2,"return Top;");
testCPU(src,"arm" , "SystemV",2,"return Top;");
@@ -311,7 +311,7 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
"""
struct _S { int f; };
var v0 = new _S;
-_S? v1;
+!_S? !v1;
if (arg) v1 = new _S;
if (v1) {
v0.f = v1.f;
@@ -330,13 +330,13 @@ return v0;
String src =
"""
struct String {
- u8[] cs;
+ u8[] !cs;
int _hashCode;
new String = { u8[] data -> cs=data; };
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -346,13 +346,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
@@ -369,13 +369,13 @@ val _hashCodeString = { String self ->
String src =
"""
struct String {
- u8[] cs;
+ u8[] !cs;
int _hashCode;
new String = { u8[] data -> cs=data; };
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -385,13 +385,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
@@ -570,7 +570,7 @@ val fib = { int n ->
i32 age;
};
- val fcn = { Person?[] ps, int x ->
+ val fcn = { !Person?[] !ps, int x ->
if( ps[x] )
ps[x].age++;
};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index 82211985..ed678334 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -141,7 +141,7 @@ return new s0.v1;
@Test public void testSextFail() throws IOException {
String src = """
struct _Person { i32 age;};
-_Person !p = new _Person;
+!_Person !p = new _Person;
p.age = (arg<<17)>>17;
return 0;
""";
@@ -164,7 +164,7 @@ return 0;
@Test public void testSextFail2() throws IOException {
String src = """
struct _Person { i32 age;};
- _Person !p = new _Person;
+ !_Person !p = new _Person;
p.age = (arg<<48)>>48;
return 0;
""";
@@ -188,7 +188,7 @@ return 0;
String src = """
// Should fold away sign extend
struct _Person { i8 age;};
-_Person !p = new _Person;
+!_Person !p = new _Person;
p.age = (arg<<48)>>48;
return 0;
""";
@@ -216,7 +216,7 @@ struct Person { // Exports a field typed singleton+final *class:Person{age:i32}
i32 age;
};
-val fcn = { Person?[] ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also
+val fcn = { !Person?[] !ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also
if( ps[x] )
ps[x].age++;
};
@@ -270,12 +270,12 @@ val fcn = { Person?[] ps, int x -> // exports a field with a constant fcn ptr; e
public void testCoRecur() {
String src = """
val x = 5; // aa.az; // Error to self-define forward ref
-struct _A { Test._B? b; Test._C? c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; };
-struct _B { Test._A? a; Test._C? c; f32 bx; val bz = x*3; new _B = { f32 x, Test._A? aa -> bx=x; a=aa; }; };
-struct _C { Test._A? a; Test._B? b; f64 cx; val cz = x*x; new _C = { f64 x, Test._A? aa, Test._B? bb -> cx=x; a=aa; b=bb; }; };
-_A !aa = new _A(17);
-_B !bb = new _B(3.14f, aa);
-_C !cc = new _C(2.73, aa, bb);
+struct _A { !Test._B? !b; !Test._C? !c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; };
+struct _B { !Test._A? !a; !Test._C? !c; f32 bx; val bz = x*3; new _B = { f32 x, !Test._A? aa -> bx=x; a=aa; }; };
+struct _C { !Test._A? !a; !Test._B? !b; f64 cx; val cz = x*x; new _C = { f64 x, !Test._A? aa, !Test._B? bb -> cx=x; a=aa; b=bb; }; };
+!_A !aa = new _A(17);
+!_B !bb = new _B(3.14f, aa);
+!_C !cc = new _C(2.73, aa, bb);
aa.b = bb;
aa.c = cc;
bb.c = cc;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index dad1dde4..7c46567a 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -142,7 +142,7 @@ return !(!s || !arg) ? 1 : 0;
public void testAndPtr() throws IOException {
// Todo: have one src here
String src = """
-struct S { S? fld; new S = { S? f -> fld=f; }; };
+struct S { !S? !fld; new S = { !S? f -> fld=f; }; };
val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null));
return ptr && ptr.fld ? "true" : "false";
""";
@@ -257,7 +257,7 @@ return (a && sq_noInline(0))
// A hypothetical scanner class
struct _Scan {
int !x;
- u8[~] buf;
+ u8[~] !buf;
new _Scan = { u8[~] b -> buf=b; };
// Skip whitespace
val skip = { ->
@@ -272,7 +272,7 @@ struct _Scan {
return true;
};
};
-_Scan !_s = new _Scan(" q");
+!_Scan !_s = new _Scan(" q");
return _s.peek('q');
""";
@@ -288,7 +288,7 @@ return _s.peek('q');
// A hypothetical scanner class
struct _Scan {
int !x;
- u8[~] buf;
+ u8[~] !buf;
new _Scan = { u8[~] b -> buf=b; };
// Skip whitespace
val skip = { ->
@@ -297,14 +297,14 @@ struct _Scan {
return self;
};
};
-val s = new _Scan(" q");
+~_Scan s = new _Scan(" q");
return s.skip().x;
""";
try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
catch( Exception e ) {
// Bad error message, but basically requires a mutable 'x' field.
- assertEquals("Argument #0 isa *Test._Scan {i64 x; *[]u8[final] buf; { *Test._Scan -> *Test._Scan {i64 !x; *[]u8 buf; {[ 5]} skip; } #[ 5]} skip; }, but must be a *Test._Scan {i64 !x; *[]u8[final] buf; { *Test._Scan -> *Test._Scan #[ 5]} skip; }",e.getMessage());
+ org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa"));
}
};
@@ -315,7 +315,7 @@ return s.skip().x;
// A hypothetical scanner class
struct _Scan {
int !x;
- u8[~] buf;
+ u8[~] !buf;
new _Scan = { u8[~] b -> buf=b; };
// Skip whitespace
val skip = { ->
@@ -327,7 +327,7 @@ struct _Scan {
buf[x++]==ch;
};
};
-_Scan !_s = new _Scan(" [1,2]");
+!_Scan !_s = new _Scan(" [1,2]");
return _s.require('[');
""";
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index cad16f2b..b06f90d8 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -25,10 +25,20 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter25Test {
+
+ @Ignore
+ @Test
+ public void testJig() {
+ String src = "int x = arg/17; if( arg == 0 ) return arg + x; return arg + x;";
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
+ assertEquals("return (arg+3);", code.print());
+ assertEquals("3",Eval2.eval(code,0));
+ }
+
@Test public void testNeverReturnPointerRiscV() {
new CodeGen("""
struct _S { int x; };
- _S !s = new _S;
+ !_S !s = new _S;
if(arg) while(1) { s.x += arg; }
return s;
""").driver(CodeGen.Phase.Encoding,"riscv","SystemV");
@@ -119,17 +129,6 @@ public class Chapter25Test {
com.seaofnodes.simple.codegen.PrintRegTestSupport.checkConstantPool();
}
-
- @Ignore
- @Test
- public void testJig() {
- String src = "struct I { int x; }; struct F { I p; }; return new F.p.x;";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("2",Eval2.eval(code,0));
- }
-
-
-
private static final String SYS_BLDDIR = "build/objs/lib_"+TestC.CPU_ABI;
private static final File SYS_FILE = new File(SYS_BLDDIR+"/sys.o");
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 0d67d3c5..48f739cf 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
@@ -73,7 +73,7 @@ public class CompoundAssignmentTest {
@Test public void testFields() {
for (String op : OPS) {
long expected = apply(op, -8, 3);
- check("struct S { int x; }; S !s=new S; s.x=arg; int r=(s.x " +
+ check("struct S { int x; }; !S !s=new S; s.x=arg; int r=(s.x " +
op + " 3); return s.x*10+r;", -8, expected*11);
}
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 2786b403..9406f480 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -22,8 +22,8 @@ public class MemoryEncodingTest {
""",
// One possibly aliasing arm must reject the unchanged-memory proof.
"""
- struct S { int x; }; S !a=new S; S !b=new S;
- a.x=10; b.x=20; S !p=a; if(arg&1) p=b;
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg&1) p=b;
int sum=0;
for(int i=0; i<6; i++) {
if(i==2) p.x=30;
@@ -34,8 +34,8 @@ public class MemoryEncodingTest {
""",
// Unchanged memory does not imply an unchanged loop-carried pointer.
"""
- struct S { int x; }; S !a=new S; S !b=new S;
- a.x=10; b.x=20; S !p=a; int sum=0;
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; int sum=0;
for(int i=0; i<6; i++) {
if(i==2) a.x=30;
sum+=p.x;
@@ -45,7 +45,7 @@ public class MemoryEncodingTest {
""",
// All arms fold, but their values differ: build a value Phi.
"""
- struct S { int x; }; S !a=new S; a.x=1; int sum=0;
+ struct S { int x; }; !S !a=new S; a.x=1; int sum=0;
for(int i=0; i<6; i++) {
sum+=a.x;
if(i&1) a.x=2;
@@ -78,9 +78,9 @@ public class MemoryEncodingTest {
@Test public void testKeepReadsBeforeWrites() {
var code = new CodeGen("""
struct S { int x; int y; };
- S !a = new S; S !b = new S;
+ !S !a = new S; !S !b = new S;
a.x=arg; b.x=arg+1;
- S !p=a; S !q=b;
+ !S !p=a; !S !q=b;
if (arg<0) { p=b; q=a; }
int v;
if (arg>1) { v=p.x; p.x=41; p.y=5; }
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index ba8fc178..f5d3d146 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -22,7 +22,7 @@ val merge_sort = { int[] a ->
_split_merge(a, 0, a#, b);
};
-val _split_merge = { int[] b, int begin, int end, int[] a ->
+val _split_merge = { int[] !b, int begin, int end, int[] a ->
if (end - begin <= 1)
return 0;
int middle = (end + begin) / 2;
@@ -32,7 +32,7 @@ val _split_merge = { int[] b, int begin, int end, int[] a ->
return 0;
};
-val _merge = { int[] b, int begin, int middle, int end, int[] a ->
+val _merge = { int[] !b, int begin, int middle, int end, int[] a ->
int i = begin, j = middle;
for (int k = begin; k < end; k++) {
@@ -47,7 +47,7 @@ val _merge = { int[] b, int begin, int middle, int end, int[] a ->
}
};
-val _copy_array = { int[] a, int begin, int end, int[] b ->
+val _copy_array = { int[] !a, int begin, int end, int[] b ->
for (int k = begin; k < end; k++)
b[k] = a[k];
};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
new file mode 100644
index 00000000..60e4c5aa
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
@@ -0,0 +1,67 @@
+package com.seaofnodes.simple;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+
+public class MutabilityTest {
+ private static void ok(String src, String result) {
+ var code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals(result,Eval2.eval(code,0));
+ }
+ private static void bad(String src) {
+ try {
+ new CodeGen(src).parse().opto().typeCheck();
+ } catch( RuntimeException expected ) {
+ String msg = expected.getMessage();
+ assertTrue("Expected a permission error, got: "+expected,
+ msg!=null && (msg.contains("final") || msg.contains("not of declared type") || msg.contains("Cannot store") || msg.contains("Argument #")));
+ return;
+ }
+ fail("Accepted forbidden write: "+src);
+ }
+
+ @Test public void testIndependentBindingsAndViews() {
+ ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p.x=7; return p.x;", "7");
+ ok("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(3); p=new P(7); return p.x;", "7");
+ bad("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p=new P(0); return 0;");
+ bad("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(0); p.x=7; return 0;");
+ bad("int ~x=3; x=7; return x;");
+ ok("int !x=3; x=7; return x;", "7");
+ }
+
+ @Test public void testDeepViewAndAliases() {
+ ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); ~P ~r=p; p.x=7; return r.x;", "7");
+ bad("struct P { int x; new P = { int v -> x=v; }; }; struct Box { !P p; new Box = { !P v -> p=v; }; }; !P p=new P(0); Box b=new Box(p); b.p.x=7; return 0;");
+ bad("struct P { int x; new P = { int v -> x=v; }; }; P p=new P(0); !P q=p; q.x=7; return 0;");
+ bad("struct P { P? next; int x; new P = { P? v -> next=v; }; }; !P p=new P(new P(null)); p.next.x=7; return 0;");
+ bad("struct P { !P? next; int x; new P = { !P? v -> next=v; }; }; P p=new P(new P(null)); p.next.x=7; return 0;");
+ }
+
+ @Test public void testInferencePreservesAccess() {
+ ok("struct P { int x; }; val p=new P; p.x=7; return p.x;", "7");
+ bad("struct P { int x; }; val p=new P; p=new P; return 0;");
+ bad("struct P { int x; }; P p=new P; var q=p; q.x=7; return 0;");
+ }
+
+ @Test public void testParameterPermissions() {
+ ok("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; return f(new P);", "7");
+ ok("val f={ int ~x -> return x; }; return f(7);", "7");
+ bad("val f={ int ~x -> x=7; return x; }; return f(3);");
+ bad("struct P { int x; }; val f={ P p -> p.x=7; return p.x; }; return f(new P);");
+ bad("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; P p=new P; return f(p);");
+ bad("struct P { int x; }; val f={ P?[] a -> return a#; }; !P?[] a=new !P?[1]; return f(a);");
+ }
+
+ @Test public void testArrayLayers() {
+ bad("struct P { int x; }; !P?[] rw=new !P?[1]; P?[] ro=rw; rw[0]=new P; ro[0].x=7; return 0;");
+ ok("u8[] a=new u8[2]; a[0]=7; return a[0];", "7");
+ ok("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(7); return a[0].x;", "7");
+ bad("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(0); a[0].x=7; return 0;");
+ ok("struct P { int x; new P = { int v -> x=v; }; }; !P?[] a=new !P?[1]; a[0]=new P(0); a[0].x=7; return a[0].x;", "7");
+ bad("u8[~] a=new u8[1]; a[0]=7; return 0;");
+ ok("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; return rows#;", "1");
+ bad("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; rows[0][0]=7; return 0;");
+ bad("u8[]?[~] rows=new u8[]?[1]; rows[0]=new u8[1]; return 0;");
+ }
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
index b9cba5e8..f6099607 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -90,7 +90,7 @@ public class TypeTest {
Assert.assertNotEquals(ptr1, ptr2);
Assert.assertEquals(ptr1, ptr1.dual().dual());
- Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil));
+ Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT).withAccess(false), ptr1.meet(ptr2nil));
TypeMemPtr TOP = TypeMemPtr.TOP;
TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT);
@@ -99,7 +99,7 @@ public class TypeTest {
Type PTR_meet_NULL = NULL.meet(PTR);
Assert.assertEquals(BOT, PTR_meet_NULL);
Type ptr1_meet_ptr2 = ptr1.meet(ptr2);
- Assert.assertEquals(PTR, ptr1_meet_ptr2);
+ Assert.assertEquals(PTR.withAccess(false), ptr1_meet_ptr2);
Type NULL_join_ptr1 = NULL.join(ptr1);
Assert.assertEquals(TypePtr.XNPTR, NULL_join_ptr1);
Type NULL_join_PTR = PTR.join(NULL);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index 7ecde7c0..c5e94047 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -445,7 +445,7 @@ public class ScriptGenerator {
for (int i=0; i<fields.length; i++) {
var fieldName = getRandomName();
var type = getType(true);
- printIndentation().append(type.name).append(" ").append(fieldName).append(";\n");
+ printIndentation().append(declType(type)).append(" !").append(fieldName).append(";\n");
fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct);
}
indentation -= INDENTATION;
@@ -642,15 +642,17 @@ public class ScriptGenerator {
return 0;
}
- /**
- * Generate a declaration statement.
- * @return 0
- */
+ // This generator mutates references and fields; request both permissions.
+ private static String declType(Type type) {
+ return (type instanceof TypeStruct || type instanceof TypeNullable ? "!" : "") + type.name;
+ }
+
+ /** Generate a declaration statement. */
public int genDecl() {
var type = getType();
var name = getVarName();
// Always make them mutable
- sb.append(generateInvalid() ? getRandomName() : type.name).append(" !").append(name);
+ sb.append(generateInvalid() ? getRandomName() : declType(type)).append(" !").append(name);
if (!(type instanceof TypeNullable) || random.nextBoolean()) {
sb.append("=");
genExpression(type, true, type==TYPE_BOOL);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp
index 8c56636f..f66ad621 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp
@@ -4,7 +4,7 @@ struct String {
int _hashCode;
};
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -13,13 +13,13 @@ val equals = { String self, String s ->
return true;
};
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
if( self.cs ) {
for( int i=0; i< self.cs#; i++ )
@@ -29,7 +29,7 @@ val _hashCodeString = { String self ->
return hash;
};
-String !s = new String(new u8[17]);
+!String !s = new String(new u8[17]);
s.cs[0] = 67; // C
s.cs[1] = 108; // l
hashCode(s);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp
index 86220b7a..586ccb85 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp
@@ -6,7 +6,7 @@ struct String {
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -16,13 +16,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
if( self.cs )
for( int i=0; i< self.cs#; i++ )
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp
index baf77c92..950e282d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp
@@ -1,6 +1,6 @@
struct S { int f; };
var v0 = new S;
-S? v1;
+!S? !v1;
if (arg) v1 = new S;
if (v1) {
v0.f = v1.f;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp
index 1e4557d3..5e517b61 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp
@@ -1 +1 @@
-struct S { int i; }; S !s = new S; while(1) s.i++;
+struct S { int i; }; !S !s = new S; while(1) s.i++;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp
index 06c2861e..09285034 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp
@@ -3,7 +3,7 @@ struct Person {
int age;
};
-val fcn = { Person?[] ps, int x ->
+val fcn = { !Person?[] ps, int x ->
if( ps[x] )
ps[x].age++;
};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp
index 3a696d66..f3d34b0a 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp
@@ -5,7 +5,7 @@ struct String {
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -15,13 +15,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp
index 523fc6f9..f9866025 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp
@@ -1 +1 @@
-struct S { int i; }; S !s = new S; while(1) s.i++; return s.i;
+struct S { int i; }; !S !s = new S; while(1) s.i++; return s.i;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp
index a3cafc9a..f860091d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp
@@ -2,7 +2,7 @@ struct Person {
i32 age;
};
-val fcn = { Person?[] ps, int x ->
+val fcn = { !Person?[] ps, int x ->
if( ps[x] )
ps[x].age++;
};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp
index 2d431705..9558bca4 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp
@@ -1,4 +1,4 @@
struct Person { i32 age;};
-Person !p = new Person;
+!Person !p = new Person;
p.age = (arg<<17)>>17;
return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp
index 01e070c8..d3a27319 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp
@@ -1,5 +1,5 @@
struct Person { i32 age;};
- Person !p = new Person;
+ !Person !p = new Person;
p.age = (arg<<48)>>48;
return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp
index 6d11a431..fdf4a7e9 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp
@@ -1,5 +1,5 @@
// Should fold away sign extend
struct Person { i8 age;};
-Person !p = new Person;
+!Person !p = new Person;
p.age = (arg<<48)>>48;
return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp
index 1a4d55c1..4ef375e9 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp
@@ -6,7 +6,7 @@ struct String {
};
// Compare two Strings
-val equals = { String self, String s ->
+val equals = { !String !self, !String s ->
if( self == s ) return true;
if( self.cs# != s.cs# ) return false;
for( int i=0; i< self.cs#; i++ )
@@ -16,13 +16,13 @@ val equals = { String self, String s ->
};
// Return the String hashCode (cached, and never 0)
-val hashCode = { String self ->
+val hashCode = { !String self ->
self._hashCode
? self._hashCode
: (self._hashCode = _hashCodeString(self));
};
-val _hashCodeString = { String self ->
+val _hashCodeString = { !String self ->
int hash=0;
for( int i=0; i< self.cs#; i++ )
hash = hash*31 + self.cs[i];
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp
index 5d300ede..556a1375 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp
@@ -1,6 +1,6 @@
{i32 i64 u32 -> u32} write="C";
val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-struct S { S? fld; new S={ S? f -> fld=f; }; };
+struct S { !S? !fld; new S={ !S? f -> fld=f; }; };
val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null));
if( ptr && ptr.fld ) {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp
index 8eb86522..00fcb589 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp
@@ -16,5 +16,5 @@ struct Scan {
return true;
};
};
-Scan !s = new Scan(" q");
+!Scan !s = new Scan(" q");
return s.peek('q');
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp
index 3ba33216..27a19988 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp
@@ -13,5 +13,5 @@ struct Scan {
buf[x++]==ch;
};
};
-Scan !s = new Scan(" [1,2]");
+!Scan !s = new Scan(" [1,2]");
return s.require('[');
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp
index a4c8a9f5..cf6d93ab 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp
@@ -2,7 +2,7 @@ struct S { int f; };
int x=0;
int result=arg&1;
if(!result) {
- S !s=new S;
+ !S !s=new S;
while(x<3) x++;
s.f=++x;
result=!!s.f;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp
index 775b92b5..370bfb8d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp
@@ -1,8 +1,8 @@
struct S { int f; };
-S !s=new S;
+!S !s=new S;
int result=arg&1;
if(!result) {
- S !t=new S;
+ !S !t=new S;
while(s.f<3) s.f++;
s.f++;
t.f=s.f;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp
index a61ca332..7aeeaa35 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp
@@ -137,7 +137,7 @@ struct aryi64 {
};
};
val parseAryI64 = { u8[~] str ->
- Scan !s = new Scan(str);
+ !Scan !s = new Scan(str);
if( !s.match('[') ) return null;
// Array of parsed results
var nums = new aryi64(new i64[1]);
@@ -185,7 +185,7 @@ struct aryi64 {
//
// Bubble sort, with reading (parsing) and writing (formatting) the data.
-val sort = { aryi64 nums ->
+val sort = { !aryi64 nums ->
var done = false;
while( !done ) {
done = true;