Appearance
| 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 |