Appearance
| 1 | namespace Semantic is | |
| 2 | use Source.LOCATION; | |
| 3 | ||
| 4 | use Types.Type; | |
| 5 | ||
| 6 | use IR.Values; | |
| 7 | ||
| 8 | // Converts a global function, static/instance/struct method, or | |
| 9 | // the single member of an overloaded group into a function/ | |
| 10 | // delegate value directly - `ldftn` on the method plus whatever | |
| 11 | // receiver an instance method needs, no closure frame, since a | |
| 12 | // named function has nothing to capture. | |
| 13 | class FUNCTION_REFERENCE_RESOLVER( | |
| 14 | _logger: Logging.Logger, | |
| 15 | _symbol_table: SYMBOL_TABLE, | |
| 16 | _symbol_loader: SYMBOL_LOADER, | |
| 17 | _innate_symbol_lookup: Lookups.InnateSymbolLookup, | |
| 18 | _overload_resolver: OVERLOAD_RESOLVER | |
| 19 | ) is | |
| 20 | _delegate_shape: DELEGATE_SHAPE; | |
| 21 | ||
| 22 | super(); | |
| 23 | ||
| 24 | init(..) is | |
| 25 | _delegate_shape = DELEGATE_SHAPE(); | |
| 26 | si | |
| 27 | ||
| 28 | // Returns null when `symbol` isn't a function reference in | |
| 29 | // value position, or is an overloaded group with no target | |
| 30 | // shape available yet to pick a member by - the caller then | |
| 31 | // falls back to its ordinary `symbol.load` path. | |
| 32 | try_load( | |
| 33 | location: LOCATION, | |
| 34 | symbol: Symbols.Symbol, | |
| 35 | from: Value?, | |
| 36 | expected_type: Type?, | |
| 37 | is_call_target: bool | |
| 38 | ) -> Value? is | |
| 39 | if is_call_target \/ isa Symbols.Closure(symbol) then | |
| 40 | return null; | |
| 41 | fi | |
| 42 | ||
| 43 | let single: Symbols.Function? mut = cast Symbols.Function?(symbol); | |
| 44 | ||
| 45 | if !single? then | |
| 46 | let group = cast Symbols.FUNCTION_GROUP?(symbol); | |
| 47 | ||
| 48 | if !group? \/ group.count == 0 then | |
| 49 | return null; | |
| 50 | fi | |
| 51 | ||
| 52 | if group.count == 1 then | |
| 53 | single = group.functions[0]; | |
| 54 | else | |
| 55 | let target_shape = get_target_call_shape(expected_type); | |
| 56 | ||
| 57 | if !target_shape? then | |
| 58 | return null; | |
| 59 | fi | |
| 60 | ||
| 61 | single = _resolve_group_member(location, group, from, target_shape); | |
| 62 | ||
| 63 | if !single? then | |
| 64 | return DUMMY(Types.ERROR(), location); | |
| 65 | fi | |
| 66 | fi | |
| 67 | fi | |
| 68 | ||
| 69 | return _build_value(location, single, from, expected_type); | |
| 70 | si | |
| 71 | ||
| 72 | // The function-type shape a value must present to be usable | |
| 73 | // as `expected_type`: `expected_type` itself when it's already | |
| 74 | // a ghūl function type, or the call shape of its `invoke` when | |
| 75 | // it's a named .NET delegate. Null when `expected_type` is | |
| 76 | // absent, or present but not callable at all. | |
| 77 | get_target_call_shape(expected_type: Type?) -> Type? is | |
| 78 | if !expected_type? then | |
| 79 | return null; | |
| 80 | fi | |
| 81 | ||
| 82 | if expected_type.is_function then | |
| 83 | return expected_type; | |
| 84 | fi | |
| 85 | ||
| 86 | return _delegate_shape.try_get_function_type(expected_type, _innate_symbol_lookup); | |
| 87 | si | |
| 88 | ||
| 89 | // Picks the group member whose formal parameters accept | |
| 90 | // `target_shape`'s parameter types, via the same | |
| 91 | // OVERLOAD_RESOLVER a call site uses: those parameter types | |
| 92 | // stand in for the actual argument types (the direction a | |
| 93 | // value of this shape would be invoked with), and its return | |
| 94 | // type is the tie-break constraint. | |
| 95 | _resolve_group_member( | |
| 96 | location: LOCATION, | |
| 97 | group: Symbols.FUNCTION_GROUP, | |
| 98 | from: Value?, | |
| 99 | target_shape: Type | |
| 100 | ) -> Symbols.Function? is | |
| 101 | let shape_arguments = target_shape.arguments; | |
| 102 | let parameter_count = shape_arguments.count - 1; | |
| 103 | ||
| 104 | let parameter_types = Collections.LIST[Type](parameter_count); | |
| 105 | ||
| 106 | for i in 0..parameter_count do | |
| 107 | parameter_types.add(shape_arguments[i]); | |
| 108 | od | |
| 109 | ||
| 110 | let return_constraint = shape_arguments[parameter_count]; | |
| 111 | ||
| 112 | let want_instance = | |
| 113 | if from? then | |
| 114 | from.is_consumable | |
| 115 | else | |
| 116 | _symbol_table.current_instance_context? | |
| 117 | fi; | |
| 118 | ||
| 119 | let result = _overload_resolver.resolve(location, group, parameter_types, false, want_instance, false, null, return_constraint); | |
| 120 | ||
| 121 | if !result? then | |
| 122 | return null; | |
| 123 | fi | |
| 124 | ||
| 125 | return result.function; | |
| 126 | si | |
| 127 | ||
| 128 | // Checked against `expected_type` when it resolves to a | |
| 129 | // callable shape via get_target_call_shape; a non-callable | |
| 130 | // (or absent) `expected_type` yields a null shape here, and | |
| 131 | // the function's own native call shape is used instead, | |
| 132 | // leaving the ordinary assignability check at the use site | |
| 133 | // to accept or reject it against the real target. | |
| 134 | _build_value( | |
| 135 | location: LOCATION, | |
| 136 | function: Symbols.Function, | |
| 137 | from: Value?, | |
| 138 | expected_type: Type? | |
| 139 | ) -> Value is | |
| 140 | let source_shape = function.get_full_type(_innate_symbol_lookup); | |
| 141 | let target_shape = get_target_call_shape(expected_type); | |
| 142 | ||
| 143 | if target_shape? /\ !target_shape.is_assignable_from(source_shape) then | |
| 144 | _logger.error(location, "{function} is not compatible with {expected_type!}"); | |
| 145 | return DUMMY(Types.ERROR(), location); | |
| 146 | fi | |
| 147 | ||
| 148 | // `target_shape` is the invoke *shape* used for the | |
| 149 | // assignability check above - when `expected_type` is a | |
| 150 | // real named delegate rather than a ghūl function type, | |
| 151 | // that shape is only the delegate's call signature, not | |
| 152 | // the delegate type itself. The built value has to carry | |
| 153 | // the delegate type so `Load.DELEGATE.gen` constructs it | |
| 154 | // with `newobj` against the right `.ctor`. | |
| 155 | let result_type = if target_shape? then expected_type! else source_shape fi; | |
| 156 | ||
| 157 | let target: Value = | |
| 158 | if !function.is_instance then | |
| 159 | NULL(Types.NULL()); | |
| 160 | elif from? then | |
| 161 | from; | |
| 162 | else | |
| 163 | _symbol_loader.load_self(location); | |
| 164 | fi; | |
| 165 | ||
| 166 | // `super.method` binds to the base implementation, the | |
| 167 | // same as an ordinary `super.method()` call site - never | |
| 168 | // virtual, whatever the method's own is_virtual says. | |
| 169 | let is_virtual = function.is_instance /\ function.is_virtual /\ !target.is_super; | |
| 170 | ||
| 171 | let function_pointer = Load.FUNCTION_POINTER(function, is_virtual); | |
| 172 | ||
| 173 | return Load.DELEGATE(result_type, function_pointer, target); | |
| 174 | si | |
| 175 | si | |
| 176 | si |