Skip to content
← Back

src/semantic/function_reference_resolver.ghul

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