Skip to content
← Back

src/semantic/closure_arg_resolver.ghul

1
namespace Semantic is
2
use Logging;
3
use Types.Type;
4
5
// Resolve closure argument types from the lambda's AST argument
6
// expressions, mutating each argument symbol's `.type` and
7
// recording any captured type-variables on the closure. Picks
8
// among four resolution branches in priority order:
9
//
10
// 1. Symbol already carries a usable (non-inferred / non-
11
// error) type — keep it. This is set by a previous walk
12
// (overload resolution's second pass set the symbol's
13
// type from the chosen formal, or a function-level retry
14
// is re-walking the body).
15
//
16
// 2. Argument has a written type expression that isn't INFER
17
// — use it directly.
18
//
19
// 3. An implied type from the enclosing context
20
// (function.constraint.arguments) is available and
21
// "usable" — install it on the symbol. "Usable" means the
22
// type is concrete OR an in-scope type-variable. Foreign
23
// type-variables (carried over from an overload-resolution
24
// constraint where the candidate is a different generic
25
// function) are rejected — see #1210 commentary. Any
26
// in-scope type-variables embedded in the implied type are
27
// recorded via `closure.add_type_argument_reference` so IL
28
// emission generates a generic closure method with
29
// matching `!!N` slots.
30
//
31
// 4. Fall back to the iterative-inference path: try the
32
// argument variable's accumulated LUB. On success install
33
// that as the resolved type. On failure (still inferred /
34
// error) emit "cannot infer type here" and install a
35
// fresh INFERRED_VARIABLE_TYPE placeholder, leaving the
36
// outer body-retry loop to resolve from body operations.
37
//
38
// `resolve(...)` returns true when the loop completed and the
39
// closure's `arguments` / `argument_names` were populated.
40
// Returns false on an unexpected argument shape (anything other
41
// than a `Syntax.Trees.Expressions.VARIABLE`) without writing
42
// anything back to the closure.
43
//
44
// A parameter written as a destructure pattern carries its pattern
45
// on that node's `left`. It stays one physical argument here — the
46
// resolution above settles the aggregate type, and the pattern's
47
// leaves take their types from it.
48
class CLOSURE_ARG_RESOLVER(_logger: Logger) is
49
super();
50
51
resolve(
52
arguments: Collections.List[Syntax.Trees.Expressions.Expression],
53
closure: Symbols.Closure,
54
implied_argument_types: Collections.List[Type]?
55
) -> bool is
56
let argument_names = Collections.LIST[string]();
57
let argument_types = Collections.LIST[Type]();
58
59
for index in 0..arguments.count do
60
let a = arguments[index];
61
62
if !isa Syntax.Trees.Expressions.VARIABLE(a) then
63
_logger.error(a.location, "unexpected kind of argument ({a.get_type()})");
64
return false;
65
fi
66
67
let argument = a;
68
69
argument_names.add(argument.name.name);
70
71
let symbol = cast Symbols.Variable?(closure.find_direct(argument.name.name))!;
72
73
let argument_type = _resolve_one(a, argument, symbol, index, implied_argument_types, closure);
74
75
argument_types.add(argument_type);
76
77
// The physical argument's type is what the pattern
78
// unpacks; push it through the leaves so the body sees
79
// each bound name at its element type.
80
if let argument.left? then
81
_assign_destructure_element_types(left, argument_type, closure);
82
fi
83
od
84
85
closure.arguments = Collections.LIST[Type](argument_types);
86
closure.argument_names = argument_names;
87
88
return true;
89
si
90
91
// Push a destructured parameter's aggregate type down onto the
92
// names its pattern binds, so the body sees each leaf at its
93
// element type. Shares DESTRUCTURE_RESOLVER with `let`
94
// destructuring and with a named function's destructured
95
// formal argument, so a value tuple, a `deconstruct(...)`
96
// source and positional members all resolve the same way.
97
_assign_destructure_element_types(
98
left: Syntax.Trees.Variables.VariableLeft,
99
from_type: Type?,
100
closure: Symbols.Closure
101
) is
102
let elements = left.elements!;
103
104
let strategy =
105
Syntax.Process.DESTRUCTURE_RESOLVER.resolve_strategy_reporting(
106
_logger, left.location, from_type, elements.count, null
107
);
108
109
for i in 0..elements.count do
110
let element = elements[i];
111
112
let element_type =
113
if strategy.is_deconstruct then
114
strategy.deconstruct_function!.arguments[i].get_element_type()
115
else
116
let member = strategy.members[i] in
117
if member? then member.type else null fi
118
fi;
119
120
if element.is_simple_name then
121
let symbol = cast Symbols.Variable?(closure.find_direct(element.name!.name));
122
123
if let typed_symbol = cast Types.SettableTyped?(symbol) then
124
symbol.define();
125
typed_symbol.set_type(if element_type? then element_type else Types.ERROR() fi);
126
fi
127
else
128
_assign_destructure_element_types(element, element_type, closure);
129
fi
130
od
131
si
132
133
_resolve_one(
134
a: Syntax.Trees.Expressions.Expression,
135
argument: Syntax.Trees.Expressions.VARIABLE,
136
symbol: Symbols.Variable,
137
index: int,
138
implied_argument_types: Collections.List[Type]?,
139
closure: Symbols.Closure
140
) -> Type is
141
if symbol.type? /\ symbol.type.is_settled then
142
return symbol.type!;
143
fi
144
145
if !isa Syntax.Trees.TypeExpressions.INFER(argument.type_expression) then
146
let te_type = argument.type_expression.type;
147
assert te_type? else "argument type-expression has no resolved type";
148
return te_type;
149
fi
150
151
if implied_argument_types? then
152
let implied_argument_type = implied_argument_types[index];
153
154
if _is_implied_type_usable(implied_argument_type, closure) then
155
symbol.set_type(implied_argument_type);
156
_record_method_level_type_arguments(implied_argument_type, symbol);
157
return implied_argument_type;
158
fi
159
fi
160
161
return _resolve_from_inference_or_placeholder(a, symbol);
162
si
163
164
_resolve_from_inference_or_placeholder(
165
a: Syntax.Trees.Expressions.Expression,
166
symbol: Symbols.Variable
167
) -> Type is
168
let inferred = symbol.try_get_inferred_type();
169
170
if inferred? /\ !inferred.is_sentinel then
171
symbol.set_type(inferred);
172
_record_method_level_type_arguments(inferred, symbol);
173
return inferred;
174
fi
175
176
_logger.error(a.location, "cannot infer type here");
177
178
let placeholder = Types.INFERRED_VARIABLE_TYPE(symbol);
179
symbol.set_type(placeholder);
180
return placeholder;
181
si
182
183
// Same intent as the implied-type-path walk: when a closure
184
// arg's type contains method-level type variables (T from an
185
// enclosing generic method, not class-level T from a Box[T]'s
186
// members) the closure has to capture them so IL emission
187
// generates a generic closure method with matching `!!N`
188
// slots. For the inference path the type carries no AST node,
189
// so the explicit walk-and-record is the only way the
190
// RECORD_TYPE_ARGUMENT_USES pass — which keys off written
191
// type expressions — would otherwise miss them.
192
_record_method_level_type_arguments(t: Type, symbol: Symbols.Variable) is
193
if !isa Symbols.Closure(symbol.owner) then
194
return;
195
fi
196
197
let closure = cast Symbols.Closure(symbol.owner);
198
199
let collected = Collections.LIST[Symbols.Symbol]();
200
INFERENCE_HELPERS.collect_method_level_type_variables(t, collected);
201
202
for u in collected do
203
closure.add_type_argument_reference(u);
204
od
205
si
206
207
// True when the implied type for a closure argument is safe
208
// to install as the symbol's type. Concrete types are always
209
// usable. Type-variable-containing types are usable only when
210
// every embedded type-variable is declared by a lexical
211
// ancestor of the closure — i.e. it's in scope. Foreign type
212
// variables (from overload-resolution constraints carrying
213
// the *candidate* function's type vars) are rejected so the
214
// iterative-inference fallback runs and resolves the arg
215
// from body operations.
216
//
217
// ERROR-bearing types are also rejected. The call-site retry
218
// walks tuple actuals under the candidate's still-generic
219
// formal, and partial-binding can produce an `(ERROR, ERROR)`
220
// tuple substitution when an earlier walk left ERROR fragments
221
// around. Installing that as the closure arg's type silently
222
// turns the destructure-on-arg path into a tuple-with-ERRORs
223
// destructure, which the existing path accepts without an
224
// error — `g2` ends up with ERROR type, IL gen runs (no errors
225
// logged), and ICEs in Type.gen_type. Rejecting the implied
226
// pushes us into the placeholder-or-LUB path, which emits
227
// "cannot infer type here" so IL gen short-circuits cleanly.
228
_is_implied_type_usable(t: Type, closure: Symbols.Closure) -> bool is
229
// ghūl closures capture locals by value, so the inner
230
// lambda can't assign to a primitive local in this
231
// scope. BOX[bool] gives us a mutable holder the lambda
232
// can write to via property assignment.
233
let any_foreign = Ghul.BOX(false);
234
let any_error = Ghul.BOX(false);
235
236
t.walk((u: Type) is
237
if u.is_error then
238
any_error.value = true;
239
fi
240
241
if u.is_type_variable /\ !_is_type_variable_in_scope(u.symbol, closure) then
242
any_foreign.value = true;
243
fi
244
si);
245
246
return !any_foreign.value /\ !any_error.value;
247
si
248
249
// Look up the type variable's name from the closure's scope.
250
// If `find_enclosing(name)` resolves to the same symbol,
251
// the type variable is lexically in scope at the lambda.
252
// For foreign type variables (the candidate function's type
253
// vars in an overload-resolution constraint), the lookup
254
// either returns null or returns a DIFFERENT same-named
255
// symbol — either way, not-in-scope, so we return false and
256
// let the iterative-inference path resolve from body
257
// operations.
258
_is_type_variable_in_scope(type_variable: Symbols.Symbol?, closure: Symbols.Closure?) -> bool is
259
if !type_variable? \/ !closure? then
260
return false;
261
fi
262
263
let found = closure.find_enclosing(type_variable.name);
264
265
return found? /\ found == type_variable;
266
si
267
si
268
si