Skip to content
← Back

src/syntax/process/body_rewalker.ghul

1
namespace Syntax.Process is
2
use Logging;
3
4
use Trees;
5
6
// Drives one body-visiting pass over only the new body subtrees of an
7
// incrementally re-walked file. Walks the declaration skeleton entering
8
// the retained scopes (inherited ScopedVisitor behaviour); at each
9
// function it gives the pass its per-function frame —
10
// `pre`/`visit(function)`, which enter and leave the retained function
11
// scope and set up any per-function state such as compile-expressions'
12
// flow analysis — but then walks only `function.body`.
13
//
14
// The retained interface — arguments, return type, indexer / property
15
// parameters — is therefore never re-processed. That is both the point
16
// of an incremental re-walk and a correctness requirement: re-resolving
17
// an already-typed retained argument poisons it ("set type twice").
18
class BODY_REWALKER: ScopedVisitor is
19
_pass: Visitor;
20
21
init(
22
logger: Logger,
23
symbol_table: Semantic.SYMBOL_TABLE,
24
namespaces: Semantic.NAMESPACES,
25
pass: Visitor
26
) is
27
super.init(logger, symbol_table, namespaces);
28
29
_pass = pass;
30
si
31
32
// Run the pass over this function's body. We honour the pass's
33
// own `pre(function)` return value — the way the visitor framework
34
// would in a full compile:
35
//
36
// - `pre` returns false ("descend normally") — the pass relies on
37
// the framework to walk children; we walk just `function.body`
38
// so the retained interface (arguments, return type) is not
39
// re-processed.
40
//
41
// - `pre` returns true ("I'll handle children") — the pass walks
42
// children itself in `visit(function)` (compile-expressions does
43
// this via `_lambdas.visit_function_definition`'s iterative body
44
// retry). We must NOT manually walk the body here, or it gets
45
// walked twice and the second walk sees partial state left over
46
// from the first — producing spurious errors like "member not
47
// found" / "no overload" on identifiers whose narrowing was
48
// correct on the first pass.
49
//
50
// Returning true stops this skeleton walk descending the function.
51
pre(function: Trees.Definitions.FUNCTION) -> bool is
52
if function.body? then
53
let pass_handles_children = _pass.pre(function);
54
55
if !pass_handles_children then
56
function.body!.walk(_pass);
57
fi
58
59
_pass.visit(function);
60
fi
61
62
return true;
63
si
64
65
// `_pass.visit(function)` already left the function scope; this
66
// skeleton walker never entered it, so its own visit(function) must
67
// do nothing rather than leave an enclosing scope.
68
visit(function: Trees.Definitions.FUNCTION) is
69
si
70
71
// Property and indexer accessor bodies are reached through the
72
// synthesised sibling get_/set_ FUNCTION nodes, so the declaration
73
// node itself is not descended.
74
pre(property: Trees.Definitions.PROPERTY) -> bool => true;
75
76
pre(indexer: Trees.Definitions.INDEXER) -> bool => true;
77
si
78
si