Appearance
| 1 | namespace Semantic is | |
| 2 | use Types.Type; | |
| 3 | ||
| 4 | // Small inference-time helpers shared between the call-site | |
| 5 | // match propagation in COMPILE_CALLS and the closure-arg inference path | |
| 6 | // in CLOSURE_ARG_RESOLVER. Both call sites build their behaviour | |
| 7 | // around these pure functions, which keeps the type-walking | |
| 8 | // logic out of the visitor classes and unit-testable on its own. | |
| 9 | class INFERENCE_HELPERS is | |
| 10 | // When `t` is an alternative of a closed root — a union | |
| 11 | // variant, or a ghūl-declared subclass of a closed root | |
| 12 | // class — return the specialised closed-root type that | |
| 13 | // alternative inherits. Used at the placeholder-formal | |
| 14 | // match propagation site to keep the lambda-arg LUB | |
| 15 | // monotonic across siblings of the same closed root: | |
| 16 | // pushing the leaf would pin the closure's signature to | |
| 17 | // that sibling even though recursion through | |
| 18 | // `rec(field: Root[T])` would later imply the root. | |
| 19 | // | |
| 20 | // Covers both bare Types.NAMED and Types.GENERIC over a | |
| 21 | // Symbols.GENERIC. The shape is uniform: t's first | |
| 22 | // ancestor is the immediate parent; if that parent's | |
| 23 | // symbol is a closed root we widen, otherwise we leave t | |
| 24 | // alone. For variants the parent is always the union (a | |
| 25 | // closed root by definition); for closed-class subclasses | |
| 26 | // it is the closed base class. | |
| 27 | widen_to_closed_root(t: Type?) -> Type? static is | |
| 28 | if !t? then | |
| 29 | return null; | |
| 30 | fi | |
| 31 | ||
| 32 | if !isa Types.GENERIC(t) then | |
| 33 | if t.ancestors.count > 0 then | |
| 34 | let ancestor = t.ancestors[0]; | |
| 35 | if ancestor.symbol.is_closed_root then | |
| 36 | return ancestor; | |
| 37 | fi | |
| 38 | fi | |
| 39 | return t; | |
| 40 | fi | |
| 41 | ||
| 42 | let generic = cast Types.GENERIC(t); | |
| 43 | let symbol = cast Symbols.GENERIC?(generic.symbol); | |
| 44 | ||
| 45 | if !symbol? then | |
| 46 | return t; | |
| 47 | fi | |
| 48 | ||
| 49 | if symbol.ancestors.count == 0 then | |
| 50 | return t; | |
| 51 | fi | |
| 52 | ||
| 53 | let ancestor = symbol.get_ancestor(0); | |
| 54 | ||
| 55 | if !ancestor.symbol.is_closed_root then | |
| 56 | return t; | |
| 57 | fi | |
| 58 | ||
| 59 | return ancestor; | |
| 60 | si | |
| 61 | ||
| 62 | // Walk `t` and append every method-level type-variable symbol | |
| 63 | // it references into `result`. Method-level meaning a generic | |
| 64 | // function's type parameter (`!!N` in IL), not a class-level | |
| 65 | // type parameter (`!N`) which the enclosing instance carries. | |
| 66 | // Used by the closure-arg inference path to drive | |
| 67 | // closure.add_type_argument_reference: an inferred argument | |
| 68 | // type has no AST node for RECORD_TYPE_ARGUMENT_USES to walk, | |
| 69 | // so the captured-type-args have to be lifted explicitly. | |
| 70 | collect_method_level_type_variables( | |
| 71 | t: Type?, | |
| 72 | result: Collections.MutableList[Symbols.Symbol] | |
| 73 | ) static is | |
| 74 | if !t? then | |
| 75 | return; | |
| 76 | fi | |
| 77 | ||
| 78 | t.walk((u: Type) is | |
| 79 | if u.is_type_variable /\ !u.is_classy_generic_argument then | |
| 80 | result.add(u.symbol); | |
| 81 | fi | |
| 82 | si); | |
| 83 | si | |
| 84 | si | |
| 85 | si |