aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/drivers/borrow_diag_test.js73
-rw-r--r--tests/drivers/ext_cache_test.js13
-rw-r--r--tests/drivers/tools_contract_test.py5
-rw-r--r--tests/errors/borrows_body.zc13
-rw-r--r--tests/errors/borrows_decl.zc14
-rw-r--r--tests/errors/borrows_extern.zc11
-rw-r--r--tests/errors/borrows_fntype.zc12
-rw-r--r--tests/errors/forward_borrow_return.zc11
-rw-r--r--tests/errors/use_after_free_borrow.zc10
-rw-r--r--tests/errors/use_after_free_owner.zc9
-rw-r--r--tests/errors/use_after_global.zc10
-rw-r--r--tests/errors/use_after_inferred_give.zc2
-rw-r--r--tests/errors/use_after_move.zc11
-rw-r--r--tests/errors/use_after_push.zc10
-rw-r--r--tests/errors/use_after_reassign.zc10
-rw-r--r--tests/programs/borrows_clause.zc11
-rw-r--r--tests/programs/forward_borrow.zc16
-rwxr-xr-xtests/test.sh6
-rw-r--r--tests/xref.baseline16
19 files changed, 258 insertions, 5 deletions
diff --git a/tests/drivers/borrow_diag_test.js b/tests/drivers/borrow_diag_test.js
new file mode 100644
index 0000000..93a7561
--- /dev/null
+++ b/tests/drivers/borrow_diag_test.js
@@ -0,0 +1,73 @@
+// borrow diagnostics in the editor: the server publishes the compiler's own messages
+// (the same check `zy` runs), with the range of the conflicting use and related
+// information for where the owner ended and where it was declared.
+// usage: node tests/drivers/borrow_diag_test.js path/to/zclsp [server arguments]
+const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1;
+const assert = require('assert');
+const cp = require('child_process');
+const path = require('path');
+const uri = 'file://' + path.resolve('tests/drivers/borrow-diag-fixture.zc');
+const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from(`Content-Length: ${b.length}\r\n\r\n`), b]); };
+function diagnose(text) {
+ const msgs = [
+ { id: 1, method: 'initialize', params: {} },
+ { method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version: 1, text } } },
+ { id: 2, method: 'shutdown' },
+ { method: 'exit' }
+ ].map(m => ({ jsonrpc: '2.0', ...m }));
+ const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(msgs.map(frame)), timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 32 << 20 });
+ assert.strictEqual(r.status, 0, String(r.stderr));
+ let b = r.stdout, last = null;
+ while (b.length) {
+ const e = b.indexOf('\r\n\r\n'); assert.ok(e >= 0);
+ const n = Number(/Content-Length:\s*(\d+)/i.exec(b.subarray(0, e).toString())[1]);
+ const m = JSON.parse(b.subarray(e + 4, e + 4 + n)); b = b.subarray(e + 4 + n);
+ if (m.method === 'textDocument/publishDiagnostics') last = m.params.diagnostics;
+ }
+ assert.ok(last, 'no diagnostics published');
+ return last;
+}
+function zy(text) {
+ const zyBin = process.env.ZY || path.join(path.dirname(process.argv[2]), 'zy');
+ const r = cp.spawnSync(zyBin, [], { input: text, timeout: 30000 * TIMEOUT_SCALE });
+ const line = String(r.stdout).split('\n')[0];
+ return line.replace(/^zc: line \d+: /, '');
+}
+const at = (text, needle, nth = 0) => { let o = -1; for (let k = 0; k <= nth; k++) o = text.indexOf(needle, o + 1); const before = text.slice(0, o); return { line: before.split('\n').length - 1, character: o - before.lastIndexOf('\n') - 1 }; };
+let checks = 0;
+function same(text, label) {
+ const d = diagnose(text).filter(x => x.severity === 1);
+ assert.ok(d.length >= 1, label + ': no error');
+ const fromZy = zy(text);
+ assert.ok(fromZy && d.some(x => x.message === fromZy), `${label}: editor and zy disagree: [${d.map(x => x.message)}] vs [${fromZy}]`);
+ checks++;
+ return d.find(x => x.message === fromZy);
+}
+// a borrow used after its owner is freed
+let text = 'include rt;\nfn main() {\n\tchar* t = rt.new(4); t[0] = 65;\n\tchar* r = t + 1;\n\trt.free(t);\n\treturn r[0];\n}\n';
+let d = same(text, 'freed owner');
+assert.match(d.message, /r borrows t, and t was freed/);
+assert.deepStrictEqual(d.range.start, at(text, 'r[0]'), 'the range is the conflicting use');
+assert.ok(d.relatedInformation && d.relatedInformation.length === 2, JSON.stringify(d));
+assert.strictEqual(d.relatedInformation[0].message, 'owner t was freed here');
+assert.deepStrictEqual(d.relatedInformation[0].location.range.start, at(text, 't);'));
+assert.strictEqual(d.relatedInformation[0].location.uri, uri);
+assert.strictEqual(d.relatedInformation[1].message, 'owner t is declared here');
+assert.deepStrictEqual(d.relatedInformation[1].location.range.start, at(text, 't ='));
+// the owner itself, given to a .d parameter
+text = 'include rt;\nfn take(.d p) { return p[0]; }\nfn main() { let x = rt.new(3); take(x); return x[0]; }\n';
+d = same(text, '.d parameter');
+assert.match(d.message, /x was given to take's \.d parameter/);
+assert.strictEqual(d.relatedInformation[0].message, 'owner x was given to a .d parameter here');
+assert.deepStrictEqual(d.relatedInformation[0].location.range.start, at(text, 'x)'));
+// a function called before its body, reported at the end of the unit at the use
+text = 'include rt;\nfn main() {\n\tchar* t = rt.new(8); t[0] = 32; t[1] = 0;\n\tlet r = skip(t); rt.free(t); rt.put_c(r[0]);\n\treturn 0;\n}\nchar* skip(char* s) { return s + 1; }\n';
+d = same(text, 'forward call');
+assert.match(d.message, /skip, defined after this call, returns a borrow/);
+assert.deepStrictEqual(d.range.start, at(text, 'r[0]'));
+assert.strictEqual(d.relatedInformation[0].message, 'owner t was freed here');
+// a borrows clause checked against its body
+text = 'include rt;\nchar* first(char* a, char* b) borrows a;\nchar* first(char* a, char* b) { return b; }\nfn main() { return 0; }\n';
+d = same(text, 'borrows clause');
+assert.match(d.message, /returns a borrow of b/);
+console.log(`ok borrow diagnostics: ${checks} cases match zy, with ranges and related owner places`);
diff --git a/tests/drivers/ext_cache_test.js b/tests/drivers/ext_cache_test.js
index 93caf94..9bd3e3a 100644
--- a/tests/drivers/ext_cache_test.js
+++ b/tests/drivers/ext_cache_test.js
@@ -31,6 +31,8 @@ const stub = {
EventEmitter: class { constructor() { this.event = () => ({ dispose() {} }); } fire() {} },
Diagnostic: class { constructor(range, message, severity) { Object.assign(this, { range, message, severity }); } },
Range: class { constructor(a, b, c, d) { Object.assign(this, { start: { line: a, character: b }, end: { line: c, character: d } }); } },
+ Location: class { constructor(uri, range) { Object.assign(this, { uri, range }); } },
+ DiagnosticRelatedInformation: class { constructor(location, message) { Object.assign(this, { location, message }); } },
DiagnosticSeverity: { Error: 0, Warning: 1, Information: 2, Hint: 3 },
SemanticTokensLegend: class { constructor(t, m) { this.tokenTypes = t; this.tokenModifiers = m; } },
SemanticTokensBuilder: class { constructor() {} push() {} build() { return null; } },
@@ -177,6 +179,17 @@ const mainUri = 'file://' + main;
const expiredList = await world.completion.provideCompletionItems(expiredDoc, { line: 3, character: 1 }, { isCancellationRequested: false });
const expiredItem = expiredList && expiredList.items.find(c => c.label === 'used');
check('completion maps consumed lifetimes to the editor strikethrough tag', expiredItem && expiredItem.tags?.[0] === stub.CompletionItemTag.Deprecated && /lifetime ended/.test(expiredItem.documentation?.value));
+ // a borrow error keeps the server's related information: where the owner ended and where it was declared
+ const borrowPath = path.join(root, 'borrow.zc');
+ const borrowDoc = doc(borrowPath, 'include rt;\nfn main() {\n\tchar* t = rt.new(4); t[0] = 65;\n\tchar* r = t + 1;\n\trt.free(t);\n\treturn r[0];\n}\n');
+ fs.writeFileSync(borrowPath, borrowDoc.text);
+ world.textDocuments.push(borrowDoc);
+ if (world.listeners.onDidOpenTextDocument) world.listeners.onDidOpenTextDocument(borrowDoc);
+ await until(() => world.sets.some(s => s.uri === 'file://' + borrowPath && s.list.length));
+ const borrowSet = world.sets.filter(s => s.uri === 'file://' + borrowPath).pop();
+ const borrowDiag = borrowSet && borrowSet.list.find(d => /r borrows t/.test(d.message));
+ const related = borrowDiag && borrowDiag.relatedInformation;
+ check('borrow diagnostics pass related information through to the editor', related && related.length === 2 && related[0] instanceof stub.DiagnosticRelatedInformation && related[0].message === 'owner t was freed here' && related[0].location.range.start.line === 4 && related[1].message === 'owner t is declared here', JSON.stringify(borrowSet && borrowSet.list));
const actionDoc = doc(path.join(root, 'imports.zc'), 'fn main() { rt.put_n(7); }');
const actions = await world.actions.provideCodeActions(actionDoc, new stub.Range(0, 15, 0, 15), {}, { isCancellationRequested: false });
check('quick fixes reach the editor as workspace edits', actions.some(a => a.kind === 'quickfix' && a.edit.edits.some(e => e.newText === 'include rt;\n')));
diff --git a/tests/drivers/tools_contract_test.py b/tests/drivers/tools_contract_test.py
index 6a98678..9323a47 100644
--- a/tests/drivers/tools_contract_test.py
+++ b/tests/drivers/tools_contract_test.py
@@ -28,9 +28,12 @@ if args.group == 'image':
changed = words[:]; changed[index] ^= 2
r = command(['build/bin/mkgenome'], pack(changed))
assert r.returncode != 0 and b'not a genome' in r.stderr and not r.stdout
+ # the header words are named, with both values, and a layout mismatch points at rt.zc and the map
+ assert [b'FTOP', b'ZFP', b'third word'][index] in r.stderr, r.stderr
+ if index < 2: assert b'the map has 0x' in r.stderr and b'src/runtime/rt.zc' in r.stderr and b'make clean' in r.stderr, r.stderr
r = command(['build/bin/mkgenome'], b'zc: line 1: something is wrong\nzc: 1 error\n')
assert r.returncode != 0 and b'something is wrong' in r.stderr and not r.stdout
- print('ok mkgenome roundtrip, truncation, alignment, map-header mutation and diagnostic contracts')
+ print('ok mkgenome roundtrip, truncation, alignment, map-header mutation (naming FTOP, ZFP and both values) and diagnostic contracts')
elif args.group == 'map':
for preset in ['default', 'small', 'run', 'lsp', 'fixed']:
with tempfile.TemporaryDirectory(prefix='tinyvm preset ') as directory:
diff --git a/tests/errors/borrows_body.zc b/tests/errors/borrows_body.zc
new file mode 100644
index 0000000..e6c92c6
--- /dev/null
+++ b/tests/errors/borrows_body.zc
@@ -0,0 +1,13 @@
+namespace;
+include rt;
+
+// expect: first: returns a borrow of b, but its borrows clause doesn't name it
+// the body is checked against the prototype's borrows clause.
+char* first(char* a, char* b) borrows a;
+fn main() {
+ char* t = rt.new(4); t[0] = 'x'; t[1] = 0;
+ char* u = rt.new(4); u[0] = 'y'; u[1] = 0;
+ let r = first(t, u); rt.put_c(r[0]); rt.free(u); rt.free(t);
+ return 0;
+}
+char* first(char* a, char* b) { return b; }
diff --git a/tests/errors/borrows_decl.zc b/tests/errors/borrows_decl.zc
new file mode 100644
index 0000000..5229979
--- /dev/null
+++ b/tests/errors/borrows_decl.zc
@@ -0,0 +1,14 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows t, and t was freed on line 11 (t declared on line 8)
+// a prototype's borrows clause ties the result to the first argument's owner only.
+char* first(char* a, char* b) borrows a;
+fn main() {
+ char* t = rt.new(4); t[0] = 'x'; t[1] = 0;
+ char* u = rt.new(4); u[0] = 'y'; u[1] = 0;
+ let r = first(t, u); rt.free(u); rt.put_c(r[0]);
+ rt.free(t); rt.put_c(r[0]);
+ return 0;
+}
+char* first(char* a, char* b) { return a; }
diff --git a/tests/errors/borrows_extern.zc b/tests/errors/borrows_extern.zc
new file mode 100644
index 0000000..e9285d4
--- /dev/null
+++ b/tests/errors/borrows_extern.zc
@@ -0,0 +1,11 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows t, and t was freed on line 9 (t declared on line 8)
+// an extern declares its borrow return by parameter position.
+extern char* strchr(char*, int) borrows 0;
+fn main() {
+ char* t = rt.new(4); t[0] = 'x'; t[1] = 0;
+ let r = strchr(t, 'x'); rt.free(t); rt.put_c(r[0]);
+ return 0;
+}
diff --git a/tests/errors/borrows_fntype.zc b/tests/errors/borrows_fntype.zc
new file mode 100644
index 0000000..a73b0d8
--- /dev/null
+++ b/tests/errors/borrows_fntype.zc
@@ -0,0 +1,12 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows t, and t was freed on line 10 (t declared on line 9)
+// a function type carries a borrows clause, by parameter position.
+char* skip(char* s) { return s + 1; }
+fn main() {
+ char*(char*) borrows 0 f = skip;
+ char* t = rt.new(4); t[0] = 'x'; t[1] = 'y'; t[2] = 0;
+ let r = f(t); rt.free(t); rt.put_c(r[0]);
+ return 0;
+}
diff --git a/tests/errors/forward_borrow_return.zc b/tests/errors/forward_borrow_return.zc
new file mode 100644
index 0000000..6c3ae93
--- /dev/null
+++ b/tests/errors/forward_borrow_return.zc
@@ -0,0 +1,11 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows t, and t was freed on line 8 (t declared on line 7); skip, defined after this call, returns a borrow of its argument
+// skip is called before its body: its borrow return is checked when the body is read.
+fn main() {
+ char* t = rt.new(8); t[0] = ' '; t[1] = 'a'; t[2] = 0;
+ let r = skip(t); rt.free(t); rt.put_c(r[0]);
+ return 0;
+}
+char* skip(char* s) { int i = 0; while (s[i] == ' ') i++; return s + i; }
diff --git a/tests/errors/use_after_free_borrow.zc b/tests/errors/use_after_free_borrow.zc
new file mode 100644
index 0000000..ed4e9d3
--- /dev/null
+++ b/tests/errors/use_after_free_borrow.zc
@@ -0,0 +1,10 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows t, and t was freed on line 8 (t declared on line 6)
+fn main() {
+ char* t = rt.new(4); t[0] = 65;
+ char* r = t + 1;
+ rt.free(t);
+ return r[0];
+}
diff --git a/tests/errors/use_after_free_owner.zc b/tests/errors/use_after_free_owner.zc
new file mode 100644
index 0000000..e530bf5
--- /dev/null
+++ b/tests/errors/use_after_free_owner.zc
@@ -0,0 +1,9 @@
+namespace;
+include rt;
+
+// expect: t: used after it was given away; t was freed on line 7 (declared on line 6)
+fn main() {
+ let t = rt.new(4);
+ rt.free(t);
+ return t[0];
+}
diff --git a/tests/errors/use_after_global.zc b/tests/errors/use_after_global.zc
new file mode 100644
index 0000000..01a69c0
--- /dev/null
+++ b/tests/errors/use_after_global.zc
@@ -0,0 +1,10 @@
+namespace;
+include rt;
+
+// expect: a: used after it was given away; a was stored in a global or untracked memory on line 8 (declared on line 7)
+word g;
+fn main() {
+ let a = rt.new(4);
+ g = a;
+ return a[0];
+}
diff --git a/tests/errors/use_after_inferred_give.zc b/tests/errors/use_after_inferred_give.zc
index 19841cb..e393c29 100644
--- a/tests/errors/use_after_inferred_give.zc
+++ b/tests/errors/use_after_inferred_give.zc
@@ -1,6 +1,6 @@
namespace;
include rt;
-// expect: x: used after it was given away; owner x was transferred on line 6 (declared on line 6)
+// expect: x: used after it was given away; x was given to take's .d parameter on line 6 (declared on line 6)
fn take(.d p) { return p[0]; }
fn main() { let x = rt.new(3); take(x); return x[0]; }
diff --git a/tests/errors/use_after_move.zc b/tests/errors/use_after_move.zc
new file mode 100644
index 0000000..a5a9abb
--- /dev/null
+++ b/tests/errors/use_after_move.zc
@@ -0,0 +1,11 @@
+namespace;
+include rt;
+
+// expect: a: used after it was given away; a was moved into b on line 9 (declared on line 6)
+fn main() {
+ let a = rt.new(4);
+ let b = rt.new(4);
+ rt.free(b);
+ b = a;
+ return a[0] + b[0];
+}
diff --git a/tests/errors/use_after_push.zc b/tests/errors/use_after_push.zc
new file mode 100644
index 0000000..a4baf69
--- /dev/null
+++ b/tests/errors/use_after_push.zc
@@ -0,0 +1,10 @@
+namespace;
+include rt;
+
+// expect: a: used after it was given away; a was pushed into an array on line 8 (declared on line 7)
+fn main() {
+ word[?] items = rt.new(word[?]);
+ let a = rt.new(4);
+ push(items, a);
+ return a[0];
+}
diff --git a/tests/errors/use_after_reassign.zc b/tests/errors/use_after_reassign.zc
new file mode 100644
index 0000000..a295591
--- /dev/null
+++ b/tests/errors/use_after_reassign.zc
@@ -0,0 +1,10 @@
+namespace;
+include rt;
+
+// expect: r: used after it was given away; r borrows a, and a was freed by reassigning it on line 8 (a declared on line 6)
+fn main() {
+ let a = rt.new(4);
+ char* r = a + 1;
+ a = rt.new(4);
+ return r[0];
+}
diff --git a/tests/programs/borrows_clause.zc b/tests/programs/borrows_clause.zc
new file mode 100644
index 0000000..6f43581
--- /dev/null
+++ b/tests/programs/borrows_clause.zc
@@ -0,0 +1,11 @@
+namespace;
+include rt;
+// a borrows clause on a prototype and its definition: freeing the other argument keeps the result.
+char* first(char* a, char* b) borrows a;
+fn main() {
+ char* t = rt.new(4); t[0] = 'x'; t[1] = 0;
+ char* u = rt.new(4); u[0] = 'y'; u[1] = 0;
+ let r = first(t, u); rt.free(u); rt.put_c(r[0]); rt.free(t);
+ return 0;
+}
+char* first(char* a, char* b) borrows a { return a; }
diff --git a/tests/programs/forward_borrow.zc b/tests/programs/forward_borrow.zc
new file mode 100644
index 0000000..7cc44b1
--- /dev/null
+++ b/tests/programs/forward_borrow.zc
@@ -0,0 +1,16 @@
+namespace;
+include rt;
+// forward calls whose results do not outlive the owner, or do not borrow it, are accepted.
+fn main() {
+ char* t = rt.new(8); t[0] = ' '; t[1] = 'a'; t[2] = 0;
+ let n = count(t); rt.free(t); rt.put_n(n);
+ let u = rt.new(8); u[0] = 'b'; u[1] = 0;
+ let r = skip(u); rt.put_c(r[0]); rt.free(u);
+ let v = rt.new(8); v[0] = 0;
+ let q = skip(v); rt.free(v); q = 0; rt.put_n(q);
+ let w = rt.new(8); w[0] = 0;
+ let k = skip(w)[0]; rt.free(w); rt.put_n(k);
+ return 0;
+}
+int count(char* s) { int i = 0; while (s[i]) i++; return i; }
+char* skip(char* s) { int i = 0; while (s[i] == ' ') i++; return s + i; }
diff --git a/tests/test.sh b/tests/test.sh
index 7745a66..6d8453e 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -216,6 +216,8 @@ ABC" "$l o3 lifts byte loads and stores"
chk "$($vm < tests/programs/o3.zc)" "3142 42 43 9 57 98781 520625 10192 -18782 1152" "$l o3 regions: loops, calls, recursion, switch, arrays, more vars than registers"
chk "$($vm < tests/programs/o3scopes.zc)" "116" "$l o3 registers shared by scopes, cached fixed arrays"
chk "$($vm < tests/programs/borrow_rules.zc)" "$(cat tests/programs/borrow_rules.expected)" "$l inferred transfers, projections and shared closure cells"
+ chk "$($vm < tests/programs/forward_borrow.zc)" "2b00" "$l forward calls: results that do not borrow, or are not used after the owner ends, are accepted"
+ chk "$($vm < tests/programs/borrows_clause.zc)" "x" "$l a borrows clause ties the result to the named argument only"
chk "$($vm < tests/programs/replace_owner.zc)" "$(cat tests/programs/replace_owner.expected)" "$l replacing a released owner moves ownership and cleans up once"
chk "$($vm < tests/programs/temporary_borrows.zc)" "$(cat tests/programs/temporary_borrows.expected)" "$l temporary borrows preserve aliases and release allocations"
chk "$($vm < tests/programs/array_values.zc)" "$(cat tests/programs/array_values.expected)" "$l array initializers, designators, zero filling, nested and compound literals"
@@ -635,6 +637,10 @@ s_lsp() {
node tests/drivers/completion_test.js $lsp > $T/completion.out 2>&1; r=$?
grep -v '^ok' $T/completion.out
chk $r 0 "lsp: fuzzy completions, signatures, scope and expired lifetimes"
+ $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zy
+ ZY=$T/zy node tests/drivers/borrow_diag_test.js $lsp > $T/borrowdiag.out 2>&1; r=$?
+ grep -v '^ok' $T/borrowdiag.out
+ chk $r 0 "lsp: borrow errors are zy's own messages, with the use's range and the owner as related information"
node tests/drivers/analysis_cache_test.js $lsp > $T/analysiscache.out 2>&1; r=$?
grep -v '^ok' $T/analysiscache.out
chk $r 0 "lsp: analysis cache reuse and invalidation"
diff --git a/tests/xref.baseline b/tests/xref.baseline
index c0e2233..26b6931 100644
--- a/tests/xref.baseline
+++ b/tests/xref.baseline
@@ -17,12 +17,14 @@ af_mutation_tick af_mutate
aname add_binop reserve_assignment
ar_flat array_designator
ar_target array_designator
+arg_site args array_push
argfact args chk_reset
argloc arrvalue chk_reset
argorigin args
argorigin_mask args
argowner args
argowner_fact args
+argowner_mask args
argtemporary args
arrloc arrvalue chk_reset
arrroot arrvalue
@@ -88,7 +90,7 @@ closure_this closure drop_attach
cmode chk_begin chk_end
code_chunk chk_reset reserve_code syntax_resident
ctxsp ctx_pop ctx_push zc_init
-curinc chk_unit effect_finish gcompile inc_compile
+curinc chk_unit effect_finish gcompile inc_compile shadow_finish
dbuf doc_char
dbuf_initial
dfr defer_drop defer_one kw_defer
@@ -96,7 +98,7 @@ dfr_base chk_reset ctx_pop fn_begin
dfr_ctx defer_one
dfr_lsp defer_one
dfr_on chk_reset defer_one
-dgbuf chk_begin dg_close dg_expand dg_open
+dgbuf chk_begin dg_close dg_expand dg_open dg_place
dgcap chk_begin dg_expand dg_open
dgdrop chk_unit dg_close dg_open
dggrow dg_open
@@ -105,6 +107,9 @@ dgn chk_begin dg_close
dgopen chk_begin dg_close dg_open
dgpos chk_begin dg_close
dgprev chk_reset expect
+dgrel dg_related
+dgrelcap dg_related
+dgreln chk_begin dg_close dg_related
dgseen dg_dup
dgseen_cap dg_dup
dgseen_initial
@@ -137,8 +142,11 @@ effect_current ctx_pop effect_note fn_begin fndef kw_asm zc_init
effect_edges effect_arg zc_init
effect_floor compile_list_context ctx_pop unit_top zc_init
effect_requirements effect_finish effect_require_mode zc_init
+effect_shadows shadow_finish shadow_take shadow_use
effect_symbols effect_record reserve_symbols zc_init
effect_value array_cap array_len array_pop arrvalue bin call ctx_pop drop_attach field fn_begin heap_box kw_share_value methcall postfix_tail primary ternary unary
+end_whom argcheck assigncheck end_owner
+end_why argcheck assigncheck end_owner reassign_free retcheck
enum_open kw_enum
epichain chk_reset ctx_pop fn_begin kw_return
evcap ev_put
@@ -306,6 +314,7 @@ local_end_info assigncheck end_owner newloc reserve_locals
local_ended assigncheck end_owner newloc reserve_locals
local_origin assigncheck newloc reserve_locals
local_origin_mask assigncheck newloc reserve_locals
+local_shadow newloc reserve_locals shadow_take shadow_use
loccap reserve_locals
loopbase ctx_pop fn_begin
loopfacts_cap loop_push reserve_R_LOOP
@@ -427,7 +436,7 @@ o3pend_initial
o3pendcap kw_o3
o3rg o3promote
o3sl o3promote
-panic body_scan capture_scan chk_begin chk_reset die diefound dien effect_finish expect hl_begin prescan replace_function resync topsync unit_end
+panic body_scan capture_scan chk_begin chk_reset die diefound dien effect_finish expect hl_begin prescan replace_function resync shadow_finish topsync unit_end
pe_first enum_tagged reserve_tagged_enums
pe_first_initial
pe_n enum_tagged reserve_tagged_enums
@@ -711,6 +720,7 @@ xgive argcheck args chk_reset
xlend assigncheck expr unary
xlenloc array_len bin call closure enhcall methcall postfix_tail primary unary
xprojection ctx_pop field postfix_tail primary
+xshadow assigncheck call shadow_take
xstamp array_dynamic_literal array_len bin call closure enhcall methcall newtyped postfix_tail primary unary
xstr array_dynamic_literal array_len array_literal_tokens bin blockexpr call closure drop_attach heap_box incdec methcall postfix_tail primary reassign_free unary
xty args array_cap array_dynamic_literal array_len array_literal_tokens array_pop array_push array_resize bin blockexpr call closure ctx_pop drop_attach drop_member enhcall expr fcoerce field ftest heap_box kw_cfunc kw_share_value kw_sizeof kw_typeof kwcall methcall newtyped postfix_tail primary reassign_free ternary unary