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
// When set, only these definitions' bodies are walked; the rest
22
// of the skeleton is still entered so the scopes are right.
23
_only: Collections.SET[Trees.Definitions.FUNCTION]?
24
25
init(
26
logger: Logger,
27
symbol_table: Semantic.SYMBOL_TABLE,
28
namespaces: Semantic.NAMESPACES,
29
pass: Visitor
30
) is
31
super.init(logger, symbol_table, namespaces)
32
33
_pass = pass
34
_only = null
35
si
36
37
init(
38
logger: Logger,
39
symbol_table: Semantic.SYMBOL_TABLE,
40
namespaces: Semantic.NAMESPACES,
41
pass: Visitor,
42
only: Collections.SET[Trees.Definitions.FUNCTION]
43
) is
44
super.init(logger, symbol_table, namespaces)
45
46
_pass = pass
47
_only = only
48
si
49
50
// Run the pass over this function's body. We honour the pass's
51
// own `pre(function)` return value — the way the visitor framework
52
// would in a full compile:
53
//
54
// - `pre` returns false ("descend normally") — the pass relies on
55
// the framework to walk children; we walk just `function.body`
56
// so the retained interface (arguments, return type) is not
57
// re-processed.
58
//
59
// - `pre` returns true ("I'll handle children") — the pass walks
60
// children itself in `visit(function)` (compile-expressions does
61
// this via `_lambdas.visit_function_definition`'s iterative body
62
// retry). We must NOT manually walk the body here, or it gets
63
// walked twice and the second walk sees partial state left over
64
// from the first — producing spurious errors like "member not
65
// found" / "no overload" on identifiers whose narrowing was
66
// correct on the first pass.
67
//
68
// Returning true stops this skeleton walk descending the function.
69
pre(function: Trees.Definitions.FUNCTION) -> bool is
70
if let only = _only then
71
if !only.contains(function) then
72
return true
73
fi
74
fi
75
76
if function.body? then
77
let pass_handles_children = _pass.pre(function)
78
79
if !pass_handles_children then
80
function.body!.walk(_pass)
81
fi
82
83
_pass.visit(function)
84
fi
85
86
return true
87
si
88
89
// `_pass.visit(function)` already left the function scope; this
90
// skeleton walker never entered it, so its own visit(function) must
91
// do nothing rather than leave an enclosing scope.
92
visit(function: Trees.Definitions.FUNCTION) is
93
si
94
95
// Property and indexer accessor bodies are reached through the
96
// synthesised sibling get_/set_ FUNCTION nodes, so the declaration
97
// node itself is not descended.
98
pre(property: Trees.Definitions.PROPERTY) -> bool => true
99
100
pre(indexer: Trees.Definitions.INDEXER) -> bool => true
101
si
102
si