Skip to content
← Back

src/semantic/closure_arg_resolver.ghul

1
namespace Semantic is
2
use Logging
3
use Types.Type
4
use Ghul.Pipes
5
6
// Resolve closure argument types from the lambda's AST argument
7
// expressions, mutating each argument symbol's `.type` and
8
// recording any captured type-variables on the closure. Picks
9
// among four resolution branches in priority order:
10
//
11
// 1. Symbol already carries a usable (non-inferred / non-
12
// error) type — keep it. This is set by a previous walk
13
// (overload resolution's second pass set the symbol's
14
// type from the chosen formal, or a function-level retry
15
// is re-walking the body).
16
//
17
// 2. Argument has a written type expression that isn't INFER
18
// — use it directly.
19
//
20
// 3. An implied type from the enclosing context
21
// (function.constraint.arguments) is available and
22
// "usable" — install it on the symbol. "Usable" means the
23
// type is concrete OR an in-scope type-variable. Foreign
24
// type-variables (carried over from an overload-resolution
25
// constraint where the candidate is a different generic
26
// function) are rejected — see #1210 commentary. Any
27
// in-scope type-variables embedded in the implied type are
28
// recorded via `closure.add_type_argument_reference` so IL
29
// emission generates a generic closure method with
30
// matching `!!N` slots.
31
//
32
// 4. Fall back to the iterative-inference path: try the
33
// argument variable's accumulated LUB. On success install
34
// that as the resolved type. On failure (still inferred /
35
// error) emit "cannot infer type here" and install a
36
// fresh INFERRED_VARIABLE_TYPE placeholder, leaving the
37
// outer body-retry loop to resolve from body operations.
38
//
39
// `resolve(...)` returns true when the loop completed and the
40
// closure's `arguments` / `argument_names` were populated.
41
// Returns false on an unexpected argument shape (anything other
42
// than a `Syntax.Trees.Expressions.VARIABLE`) without writing
43
// anything back to the closure.
44
//
45
// A parameter written as a destructure pattern carries its pattern
46
// on that node's `left`. It stays one physical argument here — the
47
// resolution above settles the aggregate type, and the pattern's
48
// leaves take their types from it.
49
class CLOSURE_ARG_RESOLVER(_logger: Logger) is
50
super()
51
52
resolve(
53
arguments: Collections.List[Syntax.Trees.Expressions.Expression],
54
closure: Symbols.Closure,
55
implied_argument_types: Collections.List[Type]?
56
) -> bool is
57
let argument_names = Collections.LIST[string]()
58
let argument_types = Collections.LIST[Type]()
59
60
if !_resolve_all(arguments, closure, implied_argument_types, argument_names, argument_types, null) then
61
return false
62
fi
63
64
closure.packed_parameters = null
65
closure.arguments = Collections.LIST[Type](argument_types)
66
closure.argument_names = argument_names
67
68
return true
69
si
70
71
// The same resolution for a literal written with an argument pack
72
// spread out, going into a formal that takes the pack as one
73
// tuple. The parameters stay the ones the literal declared, typed
74
// from the tuple's elements where it has settled and as any other
75
// parameter is where it has not. The closure is recorded taking
76
// the tuple, so the type it has is the formal's own shape.
77
resolve_packed(
78
arguments: Collections.List[Syntax.Trees.Expressions.Expression],
79
closure: Symbols.Closure,
80
expected_tuple: Type?,
81
fixed_types: Collections.List[Type]
82
) -> bool is
83
let fixed = fixed_types.count
84
let pack_count = arguments.count - fixed
85
86
let group = closure.pack_group ?? Symbols.LOCAL_ARGUMENT(closure.location, closure, "$pack")
87
88
group.is_defined = true
89
90
closure.pack_group = group
91
92
// What the formal says the tuple is, where it says something a
93
// parameter can take. Where it does not - the tuple is another
94
// function's own type parameters, or a guess that does not
95
// destructure - the tuple is whatever resolving the call has
96
// bound the literal's one physical parameter to so far.
97
let implied =
98
_elements_of(if expected_tuple? then SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(expected_tuple) else null fi, pack_count, closure) ??
99
_elements_of(group.try_get_inferred_type(), pack_count, closure)
100
101
let argument_names = Collections.LIST[string]()
102
let argument_types = Collections.LIST[Type]()
103
let symbols = Collections.LIST[Symbols.Variable]()
104
105
// By position: the formal's own parameters first, then the
106
// pack's elements where they are known.
107
let implied_types = Collections.LIST[Type]()
108
109
for fixed_type in fixed_types do
110
// A parameter of the formal's own that nothing has settled
111
// yet is not a type a parameter can take. An error type in
112
// its place is one no parameter takes, which leaves that
113
// parameter to be inferred from the body.
114
implied_types.add(if fixed_type.is_settled then fixed_type else Types.ERROR() fi)
115
od
116
117
if let known = implied then
118
implied_types.add_range(known.elements)
119
fi
120
121
if !_resolve_all(arguments, closure, implied_types, argument_names, argument_types, symbols) then
122
return false
123
fi
124
125
let element_types = Collections.LIST[Type]()
126
let packed = Collections.LIST[Symbols.Variable]()
127
128
for i in fixed..arguments.count do
129
element_types.add(argument_types[i])
130
packed.add(symbols[i])
131
od
132
133
// A parameter written with a type is filled from the element
134
// at its position, so that element has to be one it accepts.
135
if let known = implied then
136
for i in 0..pack_count do
137
let written = element_types[i]
138
139
if written.is_settled /\ !written.is_assignable_from(known.elements[i]) then
140
_logger.error(
141
arguments[fixed + i].location,
142
"expected argument of type {written} but {known.elements[i]} supplied"
143
)
144
fi
145
od
146
fi
147
148
let group_type: Type mut
149
150
if let known = implied then
151
group_type = known.tuple
152
elif element_types |> all(t => t.is_settled) then
153
// Nothing else says what the tuple is, so the parameters do.
154
group_type = IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(element_types, null)
155
else
156
// Still open: the one physical parameter stands for it, so
157
// that resolving the call against the literal can bind it.
158
let placeholder = Types.INFERRED_VARIABLE_TYPE(group)
159
160
OBLIGATIONS.defer("parameter", placeholder, null)
161
162
group_type = placeholder
163
fi
164
165
group.set_type(group_type)
166
167
let physical_types = Collections.LIST[Type]()
168
let physical_names = Collections.LIST[string]()
169
170
for i in 0..fixed do
171
physical_types.add(argument_types[i])
172
physical_names.add(argument_names[i])
173
od
174
175
physical_types.add(group_type)
176
physical_names.add("$pack")
177
178
closure.packed_parameters = packed
179
closure.arguments = physical_types
180
closure.argument_names = physical_names
181
182
return true
183
si
184
185
// The types `tuple` destructures into by position, where it is
186
// settled, names nothing out of the literal's scope, and has
187
// `count` elements. Absent otherwise.
188
_elements_of(
189
tuple: Type?,
190
count: int,
191
closure: Symbols.Closure
192
) -> (tuple: Type, elements: Collections.LIST[Type])? is
193
if !tuple? \/ !tuple.is_settled \/ !_is_implied_type_usable(tuple, closure) then
194
return null
195
fi
196
197
let strategy = Syntax.Process.DESTRUCTURE_RESOLVER.resolve_strategy(tuple, count)
198
199
let elements = Collections.LIST[Type]()
200
201
for i in 0..count do
202
if strategy.is_deconstruct then
203
if let element_type = strategy.deconstruct_function!.arguments[i].get_element_type() then
204
elements.add(element_type)
205
fi
206
elif i < strategy.members.count then
207
if let member = strategy.members[i], member_type = member.type then
208
elements.add(member_type)
209
fi
210
fi
211
od
212
213
if elements.count != count then
214
return null
215
fi
216
217
return (tuple = tuple, elements = elements)
218
si
219
220
_resolve_all(
221
arguments: Collections.List[Syntax.Trees.Expressions.Expression],
222
closure: Symbols.Closure,
223
implied_argument_types: Collections.List[Type]?,
224
argument_names: Collections.LIST[string],
225
argument_types: Collections.LIST[Type],
226
symbols: Collections.LIST[Symbols.Variable]?
227
) -> bool is
228
for index in 0..arguments.count do
229
let a = arguments[index]
230
231
if !isa Syntax.Trees.Expressions.VARIABLE(a) then
232
// A lambda parameter group is speculated as a tuple
233
// expression and only then converted to a pattern,
234
// so a `~` leaf arrives here as a unary operator
235
// the conversion could not take, rather than as a
236
// marked leaf. Name it, since the generic message
237
// below says nothing about what is wrong.
238
if let marker_location = _find_match_marker(a) then
239
_logger.error(
240
marker_location,
241
"a match marker is only allowed in a refutable pattern (if let, while let or case-when arm)"
242
)
243
244
return false
245
fi
246
247
_logger.error(a.location, "unexpected kind of argument ({a.get_type()})")
248
return false
249
fi
250
251
let argument = a
252
253
argument_names.add(argument.name.name)
254
255
let symbol = cast Symbols.Variable?(closure.find_direct(argument.name.name))!
256
257
// A parameter holds a value from the moment its literal is
258
// entered, whatever pass this is and however many retries
259
// its type takes to settle. Without this, `is_defined`
260
// stays false for a plain (non-destructured) parameter
261
// forever, and a reference to it from within its own body
262
// reads as the self-referencing-`let`-initializer case to
263
// `SHADOWED_CALLABLE_FINDER.find` — which
264
// skips that call's sentinel-type guard and, when an
265
// enclosing scope happens to declare a same-named
266
// callable, spuriously falls back and captures it.
267
symbol.is_defined = true
268
269
let argument_type = _resolve_one(a, argument, symbol, index, implied_argument_types, closure)
270
271
argument_types.add(argument_type)
272
273
if symbols? then
274
symbols.add(symbol)
275
fi
276
277
// The physical argument's type is what the pattern
278
// unpacks; push it through the leaves so the body sees
279
// each bound name at its element type.
280
if let argument.left? then
281
_assign_destructure_element_types(left, argument_type, closure)
282
fi
283
od
284
285
return true
286
si
287
288
// Marks every name a pattern binds as holding a value, for a walk
289
// on which the aggregate has no members to type them from yet.
290
_define_leaves(left: Syntax.Trees.Variables.VariableLeft, closure: Symbols.Closure) is
291
for element in left.elements! do
292
if element.is_simple_name then
293
if let symbol = cast Symbols.Variable?(closure.find_direct(element.name!.name)) then
294
symbol.define()
295
fi
296
else
297
_define_leaves(element, closure)
298
fi
299
od
300
si
301
302
// Push a destructured parameter's aggregate type down onto the
303
// names its pattern binds, so the body sees each leaf at its
304
// element type. Shares DESTRUCTURE_RESOLVER with `let`
305
// destructuring and with a named function's destructured
306
// formal argument, so a value tuple, a `deconstruct(...)`
307
// source and positional members all resolve the same way.
308
_assign_destructure_element_types(
309
left: Syntax.Trees.Variables.VariableLeft,
310
from_type: Type?,
311
closure: Symbols.Closure
312
) is
313
// The aggregate may be a placeholder whose origin has since
314
// settled - a pack slot pinned by a later call, say - and the
315
// members to destructure are the settled tuple's.
316
let aggregate = if from_type? then SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(from_type) else null fi
317
318
// One still being inferred has no members to type the leaves
319
// from yet: an obligation of this walk, with the leaves defined
320
// so the body can name them, and their types left to a walk on
321
// which the aggregate has settled.
322
if aggregate? /\ aggregate.is_inferred then
323
OBLIGATIONS.defer("destructure", aggregate, left.location)
324
_define_leaves(left, closure)
325
326
return
327
fi
328
329
let elements = left.elements!
330
331
let strategy =
332
Syntax.Process.DESTRUCTURE_RESOLVER.resolve_strategy_reporting(
333
_logger, left.location, aggregate, elements.count, null
334
)
335
336
for i in 0..elements.count do
337
let element = elements[i]
338
339
let element_type =
340
if strategy.is_deconstruct then
341
strategy.deconstruct_function!.arguments[i].get_element_type()
342
else
343
let member = strategy.members[i] in
344
if member? then member.type else null fi
345
fi
346
347
if element.is_simple_name then
348
let symbol = cast Symbols.Variable?(closure.find_direct(element.name!.name))
349
350
if let typed_symbol = cast Types.SettableTyped?(symbol) then
351
// The cast above only yields present when `symbol`
352
// itself was, so `symbol` is present here too.
353
symbol!.define()
354
typed_symbol.set_type(if element_type? then element_type else Types.ERROR() fi)
355
fi
356
else
357
_assign_destructure_element_types(element, element_type, closure)
358
fi
359
od
360
si
361
362
// The location of a `~` match marker anywhere in a parameter
363
// group, or absent when there is none.
364
_find_match_marker(expression: Syntax.Trees.Expressions.Expression) -> Source.LOCATION? is
365
if let unary: Syntax.Trees.Expressions.UNARY = expression then
366
if unary.operation.name =~ "~" then
367
return unary.location
368
fi
369
370
return _find_match_marker(unary.right)
371
fi
372
373
if let tuple: Syntax.Trees.Expressions.TUPLE = expression then
374
for element in tuple.elements.expressions do
375
if let found = _find_match_marker(element) then
376
return found
377
fi
378
od
379
fi
380
381
return null
382
si
383
384
_resolve_one(
385
a: Syntax.Trees.Expressions.Expression,
386
argument: Syntax.Trees.Expressions.VARIABLE,
387
symbol: Symbols.Variable,
388
index: int,
389
implied_argument_types: Collections.List[Type]?,
390
closure: Symbols.Closure
391
) -> Type is
392
if symbol.type? /\ symbol.type.is_settled then
393
return symbol.type!
394
fi
395
396
// A type holding a placeholder whose origin has since settled
397
// - a pack slot pinned by a later call, an argument bounded by
398
// a call in the body - collapses to what it settled to, so the
399
// closure's signature names the answer rather than the
400
// placeholder that stood for it.
401
if let existing = symbol.type then
402
let collapsed = SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(existing)
403
404
if collapsed != existing /\ collapsed.is_settled then
405
symbol.set_type(collapsed)
406
_record_method_level_type_arguments(collapsed, symbol)
407
408
return collapsed
409
fi
410
fi
411
412
if !isa Syntax.Trees.TypeExpressions.INFER(argument.type_expression) then
413
let te_type = argument.type_expression.type
414
assert te_type? else "argument type-expression has no resolved type"
415
return te_type
416
fi
417
418
// The implied types come from the formal the call pushed,
419
// so a literal of a different arity has nothing at this
420
// index. That is a call the resolver will reject; falling
421
// through to inference here keeps this walk from reading
422
// off the end of the list first.
423
if implied_argument_types? /\ index < implied_argument_types.count then
424
let implied_argument_type = implied_argument_types[index]
425
426
if _is_implied_type_usable(implied_argument_type, closure) then
427
symbol.set_type(implied_argument_type)
428
_record_method_level_type_arguments(implied_argument_type, symbol)
429
return implied_argument_type
430
fi
431
fi
432
433
return _resolve_from_inference_or_placeholder(a, symbol)
434
si
435
436
_resolve_from_inference_or_placeholder(
437
a: Syntax.Trees.Expressions.Expression,
438
symbol: Symbols.Variable
439
) -> Type is
440
let inferred = symbol.try_get_inferred_type()
441
442
if inferred? /\ !inferred.is_sentinel then
443
symbol.set_type(inferred)
444
_record_method_level_type_arguments(inferred, symbol)
445
446
// A shape whose parts are still to be inferred - the
447
// return of a function only ever called - leaves the
448
// parameter as unsettled as no type at all.
449
if !inferred.is_settled then
450
OBLIGATIONS.defer("parameter", Types.INFERRED_VARIABLE_TYPE(symbol), null)
451
fi
452
453
return inferred
454
fi
455
456
// Nothing has typed the parameter yet: an obligation of this
457
// walk on its placeholder, reported at the parameter by the
458
// fixing step if no later walk settles it.
459
let placeholder = Types.INFERRED_VARIABLE_TYPE(symbol)
460
symbol.set_type(placeholder)
461
462
OBLIGATIONS.defer("parameter", placeholder, null)
463
464
return placeholder
465
si
466
467
// Same intent as the implied-type-path walk: when a closure
468
// arg's type contains method-level type variables (T from an
469
// enclosing generic method, not class-level T from a Box[T]'s
470
// members) the closure has to capture them so IL emission
471
// generates a generic closure method with matching `!!N`
472
// slots. For the inference path the type carries no AST node,
473
// so the explicit walk-and-record is the only way the
474
// RECORD_TYPE_ARGUMENT_USES pass — which keys off written
475
// type expressions — would otherwise miss them.
476
_record_method_level_type_arguments(t: Type, symbol: Symbols.Variable) is
477
if !isa Symbols.Closure(symbol.owner) then
478
return
479
fi
480
481
let closure = cast Symbols.Closure(symbol.owner)
482
483
let collected = Collections.LIST[Symbols.Symbol]()
484
INFERENCE_HELPERS.collect_method_level_type_variables(t, collected)
485
486
for u in collected do
487
closure.add_type_argument_reference(u)
488
od
489
si
490
491
// True when the implied type for a closure argument is safe
492
// to install as the symbol's type. Concrete types are always
493
// usable. Type-variable-containing types are usable only when
494
// every embedded type-variable is declared by a lexical
495
// ancestor of the closure — i.e. it's in scope. Foreign type
496
// variables (from overload-resolution constraints carrying
497
// the *candidate* function's type vars) are rejected so the
498
// iterative-inference fallback runs and resolves the arg
499
// from body operations.
500
//
501
// ERROR-bearing types are also rejected. The call-site retry
502
// walks tuple actuals under the candidate's still-generic
503
// formal, and partial-binding can produce an `(ERROR, ERROR)`
504
// tuple substitution when an earlier walk left ERROR fragments
505
// around. Installing that as the closure arg's type silently
506
// turns the destructure-on-arg path into a tuple-with-ERRORs
507
// destructure, which the existing path accepts without an
508
// error — `g2` ends up with ERROR type, IL gen runs (no errors
509
// logged), and ICEs in Type.gen_type. Rejecting the implied
510
// pushes us into the placeholder-or-LUB path, which emits
511
// "cannot infer type here" so IL gen short-circuits cleanly.
512
_is_implied_type_usable(t: Type, closure: Symbols.Closure) -> bool is
513
// ghūl closures capture locals by value, so the inner
514
// lambda can't assign to a primitive local in this
515
// scope. BOX[bool] gives us a mutable holder the lambda
516
// can write to via property assignment.
517
let any_foreign = Ghul.BOX(false)
518
let any_error = Ghul.BOX(false)
519
520
t.walk((u: Type) is
521
if u.is_error then
522
any_error.value = true
523
fi
524
525
if u.is_type_variable /\ !_is_type_variable_in_scope(u.symbol, closure) then
526
any_foreign.value = true
527
fi
528
si)
529
530
return !any_foreign.value /\ !any_error.value
531
si
532
533
// Look up the type variable's name from the closure's scope.
534
// If `find_enclosing(name)` resolves to the same symbol,
535
// the type variable is lexically in scope at the lambda.
536
// For foreign type variables (the candidate function's type
537
// vars in an overload-resolution constraint), the lookup
538
// either returns null or returns a DIFFERENT same-named
539
// symbol — either way, not-in-scope, so we return false and
540
// let the iterative-inference path resolve from body
541
// operations.
542
_is_type_variable_in_scope(type_variable: Symbols.Symbol?, closure: Symbols.Closure?) -> bool is
543
if !type_variable? \/ !closure? then
544
return false
545
fi
546
547
let found = closure.find_enclosing(type_variable.name)
548
549
return found? /\ found == type_variable
550
si
551
si
552
si