Skip to content
← Back

src/semantic/overload_resolver.ghul

1
namespace Semantic is
2
use System.Exception
3
4
use Ghul.Pipes
5
6
use Logging
7
use Source
8
9
use Types.Type
10
11
// `score` is the summed per-argument match quality, kept for
12
// ranking candidates against each other. `needs_retry` is the
13
// explicit signal that a function-literal actual with implicit
14
// parameter types matched without its real signature, so a
15
// constraint-push re-walk of the arguments could improve the
16
// result. Callers must consult `needs_retry` rather than testing
17
// `score` against a MATCH constant: the sum of several coercion
18
// scores can collide with any single MATCH value.
19
class OVERLOAD_RESOLVE_RESULT(function: Symbols.Function, score: Types.MATCH, needs_retry: bool) is
20
si
21
22
class OVERLOAD_MATCHES_RESULT(
23
results: Collections.List[Symbols.Function],
24
best_result_index: int,
25
current_parameter_index: int public
26
) is
27
si
28
29
class OVERLOAD_RESOLVER(_logger: Logger) is
30
match_propagator: MATCH_PROPAGATOR public
31
_callee_parameter_slot: CALLEE_PARAMETER_SLOT
32
_delegate_shape: DELEGATE_SHAPE
33
34
super()
35
36
init(..) is
37
match_propagator = MATCH_PROPAGATOR(_logger)
38
_callee_parameter_slot = CALLEE_PARAMETER_SLOT()
39
_delegate_shape = DELEGATE_SHAPE()
40
si
41
42
resolve(
43
location: LOCATION,
44
group: Symbols.FUNCTION_GROUP,
45
arguments: Collections.List[Type],
46
want_infer: bool,
47
want_instance: bool,
48
is_constructor_call: bool
49
) -> OVERLOAD_RESOLVE_RESULT? =>
50
resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, null, null)
51
52
// `restrict_to`, when non-null, limits resolution to the given
53
// candidates instead of the whole group - used by named-
54
// argument calls, which have already culled the group to the
55
// overloads whose parameter names match.
56
resolve(
57
location: LOCATION,
58
group: Symbols.FUNCTION_GROUP,
59
arguments: Collections.List[Type],
60
want_infer: bool,
61
want_instance: bool,
62
is_constructor_call: bool,
63
restrict_to: Collections.List[Symbols.Function]?
64
) -> OVERLOAD_RESOLVE_RESULT? =>
65
resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, restrict_to, null)
66
67
// `return_constraint`, when non-null, supplies the return-type
68
// context (typically `function.return_type` of the enclosing
69
// function for a call in return-position, or the LHS slot type
70
// for an assignment-position call). Used as a tie-breaker when
71
// arg-side scoring leaves multiple candidates tied: a candidate
72
// whose return type is assignable to the constraint wins over
73
// one that isn't.
74
resolve(
75
location: LOCATION,
76
group: Symbols.FUNCTION_GROUP,
77
arguments: Collections.List[Type],
78
want_infer: bool,
79
want_instance: bool,
80
is_constructor_call: bool,
81
restrict_to: Collections.List[Symbols.Function]?,
82
return_constraint: Type?
83
) -> OVERLOAD_RESOLVE_RESULT?
84
is
85
let mark = _logger.mark()
86
87
try
88
return _resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, restrict_to, return_constraint)
89
catch e: Exception
90
_logger.release(mark)
91
92
_logger.exception(location, e, "exception resolving overload: {group} arguments {arguments}")
93
return null
94
95
finally
96
_logger.release(mark)
97
yrt
98
si
99
100
find_matches(
101
group: Symbols.FUNCTION_GROUP,
102
arguments: Collections.List[Type]
103
) -> OVERLOAD_MATCHES_RESULT?
104
is
105
let mark = _logger.mark()
106
107
try
108
return _find_matches(group, arguments)
109
catch e: Exception
110
_logger.release(mark)
111
112
_logger.exception(group.location, e, "exception resolving overload: {group} arguments {arguments}")
113
return null
114
115
finally
116
_logger.release(mark)
117
yrt
118
si
119
120
_resolve(
121
location: LOCATION,
122
group: Symbols.FUNCTION_GROUP,
123
arguments: Collections.List[Type],
124
want_infer: bool,
125
want_instance: bool,
126
is_constructor_call: bool,
127
restrict_to: Collections.List[Symbols.Function]?,
128
return_constraint: Type?
129
) -> OVERLOAD_RESOLVE_RESULT?
130
is
131
// FIXME: this could just as well be applied to any parameters of generic type, not just anon functions
132
// an argument entry may be null
133
@suppress("presence-test-non-optional")
134
let needs_second_call = arguments |> any(a => a? /\ a.is_function_with_any_implicit_argument_types)
135
136
let saw_delegate_target mut = false
137
138
let is_ambiguous mut = false
139
140
let best_score mut = cast int (Types.MATCH.DIFFERENT)
141
let result: Symbols.Function? mut = _
142
143
let ambiguous_matches: Collections.LIST[Symbols.Function]? mut = null
144
145
let search_functions = if restrict_to? then restrict_to else group.functions fi
146
147
// A static constructor is invoked by the CLR, never by an
148
// explicit call or `T(...)` construction, so it must not be
149
// an overload candidate even though it shares the `init`
150
// group with the instance constructors.
151
let functions_to_search = search_functions |> filter(f => (want_instance \/ !f.is_instance) /\ !f.is_static_constructor)
152
153
// Whether more than one candidate takes this many arguments. A
154
// delegate the argument cannot become only decides between
155
// candidates; against a lone candidate the conversion is left to
156
// fail where it is made, which says more about why.
157
let has_rivals = (functions_to_search |> filter(f => f.arguments.count == arguments.count) |> count()) > 1
158
159
// Per candidate that bound any type argument, what it bound.
160
// Kept rather than acted on: the bindings are offered to a
161
// placeholder actual once the winner is known, since
162
// ranking visits every candidate that scores at least as
163
// well as the best so far.
164
let candidate_bindings = Collections.LIST[(candidate: Symbols.Function, bindings: Types.GENERIC_ARGUMENT_BIND_RESULTS)]()
165
166
// We need to return PARTIAL if any actual argument types are wild. 'PARTIAL' provides
167
// the caller with the best match we can find, and the caller is expected to use that
168
// to bind any unknown types in the actual arguments and then try overload resolution
169
// again
170
171
for f in functions_to_search do
172
let actual: Symbols.Function? mut = f
173
174
// A candidate with no formals and nothing to bind is the
175
// answer as soon as it is found: ranking has no argument
176
// to compare and no type parameter to pin from the call
177
// site, so it can only put a group holding the same
178
// member redeclared along two inherited paths - which
179
// member lookup answers, not overload resolution - to a
180
// tie it cannot break. A generic one does have something
181
// to pin, from the type the call site expects, so it
182
// takes the ordinary path.
183
if f.arguments.count == 0 /\ arguments.count == 0 /\ !f.is_generic then
184
return OVERLOAD_RESOLVE_RESULT(f, Types.MATCH.SAME, false)
185
elif f.arguments.count == arguments.count then
186
let want_try_bind_owner_generic_arguments mut = false
187
let want_try_bind_function_generic_arguments mut = false
188
189
if is_constructor_call then
190
want_try_bind_owner_generic_arguments = true
191
want_try_bind_function_generic_arguments = false
192
elif f.is_instance then
193
// Iterative-inference: an instance method's
194
// owner type-args may be unbound when the
195
// receiver was constructed without explicit
196
// type-args and without args binding T (e.g.
197
// `let m = Box(); m.set(42)`). Allow owner-
198
// generic-arg binding here so the resolver can
199
// pick T = int from the actual argument and
200
// produce the specialized owner. For receivers
201
// whose type-args are already bound, the
202
// specialization is a no-op.
203
want_try_bind_owner_generic_arguments = true
204
want_try_bind_function_generic_arguments = true
205
elif f.is_generic then
206
want_try_bind_owner_generic_arguments = true
207
want_try_bind_function_generic_arguments = true
208
else
209
want_try_bind_owner_generic_arguments = true
210
want_try_bind_function_generic_arguments = false
211
fi
212
213
let try_bind_generic_arguments mut = false
214
let score mut = cast int(Types.MATCH.SAME)
215
216
// A global function's owner is a namespace, not a
217
// Classy, so this cast is null whenever
218
// want_try_bind_owner_generic_arguments will never
219
// find owner bindings to act on below. Left
220
// optional and unwrapped only at the two call sites
221
// that are reachable exclusively through a
222
// successful owner binding, which cannot happen
223
// for a non-Classy owner.
224
let owner_symbol = cast Symbols.Classy?(f.owner)
225
226
for i in 0..f.arguments.count do
227
let match: Types.MATCH mut
228
229
let f_arg = f.arguments[i]
230
let arg = arguments[i]
231
232
// an argument may be null
233
@suppress("presence-test-non-optional")
234
if arg? then
235
match = f_arg.compare(arg)
236
237
// A function literal can be compiled as the
238
// delegate this formal asks for, but it has
239
// been walked as a plain function type and so
240
// compares DIFFERENT. Report a partial match
241
// instead: the retry pass pushes this formal
242
// down and re-walks the argument, which either
243
// produces the delegate or fails the second
244
// resolve on its own terms.
245
// A literal whose parameter count differs from
246
// the delegate's can never become it, so where
247
// another overload competes this one is no
248
// candidate at all.
249
if
250
match == Types.MATCH.DIFFERENT /\
251
arg.is_function /\
252
_delegate_shape.is_named_delegate(f_arg, IoC.CONTAINER.instance.innate_symbol_lookup) /\
253
(!has_rivals \/ _delegate_shape.could_take_arity(f_arg, ARGUMENT_PACK.parameter_count(arg), IoC.CONTAINER.instance.innate_symbol_lookup))
254
then
255
match = Types.MATCH.PARTIAL
256
saw_delegate_target = true
257
fi
258
259
if match == Types.MATCH.DIFFERENT then
260
// if any argument type compare returns DIFFERENT then
261
// this overload cannot match the supplied arguments
262
// even allowing for type argument inference of any
263
// type arguments in the formal arguments or of
264
// unknown types in the actual arguments, so
265
// bail on this overload immediately:
266
score = cast int(Types.MATCH.DIFFERENT)
267
break
268
elif match == Types.MATCH.WILD then
269
// either or both of the following has occurred:
270
// 1. the formal argument type is 'wild', i.e. its type expression
271
// includes at least one generic type argument that could be free
272
// to be bound to the type in corresponding position in the actual
273
// argument type. The type argument could be appear inside the type
274
// expression at any depth, for example `List[T]` or `int -> T`
275
// 2. the actual argument type is 'any', i.e. its type expression
276
// includes at least one unknown type where the actual type can
277
// potentially be inferred based on the formal argument type
278
279
// we need first to figure out which it is. If it's both then type
280
// inference probably isn't possible, but we will still attempt it
281
282
if f_arg.is_wild then
283
// this formal argument is wild, we need to figure out if any
284
// type arguments in it could be free in this context
285
286
// if the overload is an instance method we then can't supply
287
// actual type parameters to its owning class/struct either explicitly
288
// or via inference - they're already applied to the instance we're
289
// calling the method on
290
291
// if the overload is a static method, we can potentially supply
292
// actual type arguments for its owning class
293
294
// and in either case we can supply actual type arguments for the
295
// method itself
296
297
// if the function is a global function then we can supply actual
298
// type arguments for it
299
300
// if the function is a constructor, and we're calling it for a
301
// constructor expression, it cannot have generic arguments but
302
// its owning type can, and we do want to supply them if we
303
// can infer them from this overload
304
305
match = Types.MATCH.SAME
306
307
try_bind_generic_arguments = true
308
else
309
// Only the actual is wild: it names a type
310
// variable of some enclosing declaration, which
311
// is fixed where the call is written rather than
312
// free to be bound by it. A formal that genuinely
313
// accepts such a value - `object?`, or the
314
// variable's own bound - has already compared
315
// assignable through the ancestor walk, so
316
// anything still reported wild here is a formal
317
// the argument cannot satisfy under any
318
// instantiation.
319
score = cast int(Types.MATCH.DIFFERENT)
320
break
321
fi
322
elif arg.is_error \/ arg.contains_inferred then
323
// The actual argument is an ERROR/placeholder sentinel
324
// or a composite carrying one inside. Treat as a
325
// matching argument and see if that produces an
326
// unambiguous overload result — if so the caller
327
// uses the chosen overload's formal type to infer
328
// back into the placeholder via match propagation.
329
330
// FIXME not sure it makes sense to be setting score here - should be match
331
score = cast int(Types.MATCH.PARTIAL)
332
elif f_arg.is_wild /\ isa Types.NULL(arg) then
333
// A wild optional formal (`T?`) trivially accepts a
334
// bare null without pinning its type variable, so
335
// compare returns a plain assignable match rather than
336
// WILD. Still route through generic-argument binding:
337
// the type variable is left free otherwise, and the
338
// selected overload would emit with an unresolved `!!N`
339
// that fails to load at run time. Errors and inference
340
// sentinels are handled by the branch above; this is
341
// only the genuine null literal.
342
try_bind_generic_arguments = true
343
elif
344
is_constructor_call /\
345
f_arg.is_wild /\
346
owner_symbol? /\
347
owner_symbol.is_generic
348
then
349
// A constructor's formal can compare as an exact
350
// match rather than wild when the actual is the
351
// very type variable it mentions: `NODE(value)`
352
// written inside `NODE[T]`, with `value: T`. That
353
// `T` is the enclosing class's, fixed where the
354
// call is written, and binding the class's type
355
// arguments from it is still how the construction
356
// gets its type, here `NODE[T]`. Without the
357
// binding the open class's constructor is chosen,
358
// and nothing ever pins its type argument. A
359
// constructor of an already constructed type,
360
// `NODE[T](value)`, has its type arguments and
361
// nothing left to bind.
362
try_bind_generic_arguments = true
363
fi
364
else
365
// An actual or formal argument type is unresolved
366
// (null) - reachable in analysis mode when an
367
// expression's type has not yet been established.
368
// Treat as a moderate-quality match so resolution
369
// degrades gracefully instead of dereferencing null.
370
match = Types.MATCH.ASSIGNABLE
371
fi
372
373
score = cast int(score) + cast int(match)
374
od
375
376
if try_bind_generic_arguments /\ score <= best_score /\ score < cast int(Types.MATCH.DIFFERENT) then
377
// if we saw any generic argument types in any of the function formal argument types
378
// then we need to try to bind them to concrete types from the corresponding actual
379
// argument types
380
381
let function_generic_argument_bindings =
382
if want_try_bind_function_generic_arguments then
383
f.try_bind_generic_arguments(location, arguments)
384
else
385
null
386
fi
387
388
let owner_generic_argument_bindings =
389
if want_try_bind_owner_generic_arguments then
390
f.try_bind_owner_generic_arguments(location, arguments)
391
else
392
null
393
fi
394
395
if let bindings = function_generic_argument_bindings ?? owner_generic_argument_bindings then
396
candidate_bindings.add((candidate = f, bindings = bindings))
397
fi
398
399
if function_generic_argument_bindings? then
400
if function_generic_argument_bindings.is_bound then
401
actual = f.specialize_function(function_generic_argument_bindings.map, null)
402
elif needs_second_call then
403
actual = f.specialize_function(function_generic_argument_bindings.map, null)
404
score = cast int(Types.MATCH.PARTIAL)
405
else
406
score = cast int(Types.MATCH.DIFFERENT)
407
fi
408
elif owner_generic_argument_bindings? then
409
if owner_generic_argument_bindings.is_bound then
410
let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_symbol!, owner_generic_argument_bindings.map)
411
412
if specialized_owner? then
413
actual = specialized_owner.find_specialized_function(f)
414
else
415
score = cast int(Types.MATCH.DIFFERENT)
416
fi
417
elif needs_second_call then
418
let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_symbol!, owner_generic_argument_bindings.map)
419
420
if specialized_owner? then
421
actual = specialized_owner.find_specialized_function(f)
422
score = cast int(Types.MATCH.PARTIAL)
423
else
424
score = cast int(Types.MATCH.DIFFERENT)
425
fi
426
else
427
score = cast int(Types.MATCH.DIFFERENT)
428
fi
429
else
430
score = cast int(Types.MATCH.DIFFERENT)
431
fi
432
fi
433
434
if score == best_score /\ score != cast int(Types.MATCH.DIFFERENT) /\ actual? then
435
if !ambiguous_matches? then
436
ambiguous_matches = Collections.LIST[Symbols.Function]()
437
fi
438
439
if ambiguous_matches.count == 0 /\ result? then
440
ambiguous_matches.add(result)
441
fi
442
443
ambiguous_matches.add(actual)
444
445
is_ambiguous = true
446
elif score < best_score /\ actual? then
447
if ambiguous_matches? then
448
ambiguous_matches.clear()
449
fi
450
451
is_ambiguous = false
452
best_score = score
453
result = actual
454
fi
455
fi
456
od
457
458
// Every filter from here down picks a winner by comparing
459
// what the candidates' formals look like against each
460
// other - never against the actuals - so a tie is only
461
// real information when the actuals themselves carried
462
// some: an ambiguity formed purely because every actual is
463
// still an unresolved placeholder (every formal compared
464
// ASSIGNABLE against a sentinel) says nothing about which
465
// candidate the call means, and picking one anyway commits
466
// an arbitrary type as a constraint on the placeholder's
467
// origin, which can permanently block a later, correct
468
// constraint from ever being accepted. `can_rank` is the
469
// same guard FORMAL_FIT_FILTER and CANDIDATE_SPECIFICITY
470
// already apply below; reusing it here keeps every
471
// tie-break in this chain agreeing on when a tie is worth
472
// breaking at all.
473
//
474
// Left enabled for a `want_infer` caller: such a caller
475
// reaches the "call is ambiguous" report a few lines below
476
// when nothing here picks a winner, which is worse than an
477
// arbitrary-but-harmless pick when that caller's own
478
// top-level diagnostic already reports the real failure
479
// once the placeholder never resolves. A `!want_infer`
480
// caller instead declines silently and retries, which is
481
// where committing an arbitrary constraint is unsound.
482
let safe_to_narrow_tie = want_infer \/ CANDIDATE_SPECIFICITY.can_rank(arguments)
483
484
if is_ambiguous /\ safe_to_narrow_tie then
485
let non_object_matches =
486
ambiguous_matches! |>
487
filter(f => !(f.arguments |> any(a => a.is_object)))
488
489
let count = non_object_matches |> count()
490
491
if count == 1 then
492
result = non_object_matches |> only()
493
is_ambiguous = false
494
elif count > 1 then
495
ambiguous_matches = Collections.LIST(non_object_matches)
496
fi
497
fi
498
499
// Return-type-context filter: when the caller supplied a
500
// return-type constraint (e.g. a return statement whose
501
// function returns `Tasks.TASK[int]`, or an assignment-
502
// position call's LHS type), prefer candidates whose
503
// return type is assignable to that constraint. Picks
504
// between `from_exception(ex) -> Tasks.TASK` vs
505
// `from_exception[T](ex) -> Tasks.TASK[T]` where T can
506
// bind from the constraint — the latter is the
507
// user's intent and the former would fail the return-
508
// statement's own assignability check. Runs BEFORE the
509
// non-generic filter so a generic candidate matching the
510
// constraint wins over a non-generic one that doesn't.
511
//
512
// For a generic candidate whose return type carries type
513
// variables that the constraint can pin (e.g.
514
// `Tasks.TASK[T]` against `Tasks.TASK[int]`), specialize
515
// the chosen function so its emitted return type binds T
516
// from the constraint. Without this the caller would
517
// accept the candidate as the right overload but reject
518
// its result against the constraint at the next
519
// assignability check.
520
if is_ambiguous /\ safe_to_narrow_tie /\ return_constraint? /\ !return_constraint.is_sentinel then
521
let constraint_matches =
522
ambiguous_matches! |>
523
filter(f => RETURN_CONSTRAINT_FILTER.matches(f, return_constraint))
524
525
let count = constraint_matches |> count()
526
527
if count == 1 then
528
result = constraint_matches |> only()
529
is_ambiguous = false
530
531
let specialized = RETURN_CONSTRAINT_FILTER.try_specialize(location, result, return_constraint)
532
if specialized? then
533
result = specialized
534
fi
535
elif count > 1 then
536
ambiguous_matches = Collections.LIST(constraint_matches)
537
fi
538
fi
539
540
if is_ambiguous /\ safe_to_narrow_tie then
541
// Prefer non-generic candidates over function-generic
542
// ones (concrete `<>(int, int)` beats specialized
543
// `<>[T: struct](T, T)` for `int <> int`). A concrete
544
// candidate has no `specialized_from` link AND no
545
// function-level type-arguments; a specialized form
546
// of a function-generic has `specialized_from` set.
547
let non_generic_matches =
548
ambiguous_matches! |>
549
filter(f =>
550
!f.specialized_from? /\
551
f.generic_arguments.count == 0)
552
553
let count = non_generic_matches |> count()
554
555
if count == 1 then
556
result = non_generic_matches |> only()
557
is_ambiguous = false
558
elif count > 1 then
559
ambiguous_matches = Collections.LIST(non_generic_matches)
560
fi
561
fi
562
563
// Then: prefer the candidate whose specialized formals fit
564
// the actuals most closely. A formal carrying a free type
565
// variable scored as an exact match above, before the
566
// variable was bound, so candidates that fit quite
567
// differently can arrive here tied.
568
//
569
// Runs below the filters above rather than beside them:
570
// where a concrete candidate ties a generic one on score
571
// it matched every actual exactly, so the two agree, and
572
// this only has to speak where they have not.
573
if is_ambiguous then
574
let closest_matches = FORMAL_FIT_FILTER.pick(ambiguous_matches!, arguments)
575
576
if closest_matches? then
577
if closest_matches.count == 1 then
578
result = closest_matches[0]
579
is_ambiguous = false
580
else
581
ambiguous_matches = Collections.LIST(closest_matches)
582
fi
583
fi
584
fi
585
586
// Last: prefer the candidate whose formals sit furthest
587
// down the inheritance graph, which is the one that
588
// widened the actuals least. Runs after every filter
589
// above, so it only ever sees ambiguities they left
590
// unsettled - which are reported as errors otherwise.
591
if is_ambiguous then
592
let most_specific = CANDIDATE_SPECIFICITY.pick(ambiguous_matches!, arguments)
593
594
if most_specific? then
595
result = most_specific
596
is_ambiguous = false
597
fi
598
fi
599
600
if result? /\ !is_ambiguous then
601
// A chosen candidate whose return type still carries
602
// its own type parameter had nothing in the arguments
603
// to bind it: `zero_of[T](n: int) -> T` mentions T
604
// nowhere a supplied argument could pin it. The
605
// call-site's return-type constraint is the only
606
// remaining source, so bind from that when there is
607
// one. Where there is not, the call is left carrying
608
// the type parameter and whoever commits that type
609
// reports it - resolution runs speculatively here, so
610
// a diagnostic raised at this point would fire on
611
// candidates the retry would have settled.
612
let caller = IoC.CONTAINER.instance.symbol_table.current_function
613
614
if
615
result.return_type? /\
616
result.return_type.has_function_generic_argument_foreign_to(caller)
617
then
618
let specialized =
619
if return_constraint? /\ !return_constraint.is_sentinel then
620
RETURN_CONSTRAINT_FILTER.try_specialize(location, result, return_constraint)
621
else
622
null
623
fi
624
625
if specialized? then
626
result = specialized
627
fi
628
fi
629
630
// Back-feed the chosen overload's formal types as
631
// constraints to any actual whose type is an
632
// INFERRED_VARIABLE_TYPE placeholder. This is the
633
// single primitive that drives iterative inference:
634
// when a placeholder participates in overload
635
// resolution and a concrete formal wins on the other
636
// side, the formal becomes a constraint on the
637
// placeholder's origin symbol. The retry loop in
638
// COMPILE_EXPRESSIONS.visit(FUNCTION) then re-walks
639
// the body with the narrowed type. Operators (which
640
// are method calls in ghūl) flow through this path
641
// for free.
642
// Only now does the chosen candidate's binding reach a
643
// placeholder actual. A candidate that bound cleanly and
644
// then lost - on score, or on one of the tie-breaks
645
// above - said nothing about what the call means, and
646
// the lower bound it would have pushed is never
647
// withdrawn.
648
SELECTED_CANDIDATE_BINDINGS.offer(result, candidate_bindings)
649
650
_propagate_chosen_match_args(result, arguments)
651
652
if needs_second_call then
653
best_score = cast int(Types.MATCH.PARTIAL)
654
fi
655
656
return OVERLOAD_RESOLVE_RESULT(result, cast Types.MATCH(best_score), needs_second_call \/ saw_delegate_target)
657
fi
658
659
if
660
arguments |> any(a => a.is_error \/ (!want_infer /\ a.is_sentinel))
661
then
662
return null
663
fi
664
665
let tried = Collections.LIST[Symbols.Function](20)
666
667
for f in functions_to_search do
668
// A candidate declaring an argument pack is one the call
669
// may have meant with the pack spread out, whatever number
670
// of arguments that came to, so it is worth showing.
671
if f.arguments.count == arguments.count \/ _declares_argument_pack(f) then
672
tried.add(f)
673
fi
674
od
675
676
let maybe_static mut = ""
677
678
if !want_instance then
679
maybe_static = "static "
680
fi
681
682
if is_ambiguous then
683
_logger.error(
684
location,
685
"call is ambiguous {group.name}({arguments |> join(", ")}), tried {get_sorted_function_list_as_string(ambiguous_matches!)}"
686
)
687
elif tried.count > 0 then
688
// When an actual still carries another function's
689
// method-level type parameter, the callee was found
690
// but a type argument could not be settled — prefer
691
// the inference diagnostic over "no overload found".
692
let caller = IoC.CONTAINER.instance.symbol_table.current_function
693
694
if arguments |> any(a => a.has_function_generic_argument_foreign_to(caller)) then
695
_logger.error(location, "cannot infer type here")
696
else
697
_logger.error(
698
location,
699
"no {maybe_static}overload found for {group.name}({arguments |> join(", ")}), tried {get_sorted_function_list_as_string(tried)}"
700
)
701
fi
702
else
703
_logger.error(location, "no {maybe_static}overload found for {group.name}({arguments |> join(", ")})")
704
fi
705
706
return null
707
si
708
709
_declares_argument_pack(function: Symbols.Function) -> bool is
710
for argument in function.generic_arguments do
711
if argument.symbol.is_argument_pack then
712
return true
713
fi
714
od
715
716
return false
717
si
718
719
get_sorted_function_list_as_string(functions: Collections.Iterable[Symbols.Function]) -> string static =>
720
functions |>
721
map(f => f.to_string()) |>
722
sort() |>
723
join()
724
725
// For each (formal, actual) pair on the chosen overload, push
726
// type-arg constraints into any INFERRED_VARIABLE_TYPE
727
// placeholders found on either side. The accumulator is
728
// retrieved on the next iteration's lambda arg-compile / con-
729
// structor re-walk to resolve the placeholder to a concrete
730
// type. When add_constraint returns true (a real new constraint
731
// landed), signal progress to the retry loop via
732
// mark_consumed_any.
733
_propagate_chosen_match_args(
734
chosen: Symbols.Function,
735
actual_types: Collections.List[Type]
736
) is
737
// `x =~ y` resolves over `T?` exactly when it resolves over
738
// `T`, so its formals say nothing about whether an operand
739
// is optional, and a placeholder operand learns no bound
740
// from them.
741
if chosen.name =~ "=~" then
742
return
743
fi
744
745
let unspecialized = chosen.unspecialized_arguments ?? chosen.arguments
746
let caller = IoC.CONTAINER.instance.symbol_table.current_function
747
let count = if chosen.arguments.count < actual_types.count then chosen.arguments.count else actual_types.count fi
748
749
for i in 0..count do
750
if i < unspecialized.count /\ _callee_parameter_slot.binds_placeholder(unspecialized[i], actual_types[i], caller) then
751
continue
752
fi
753
754
match_propagator.propagate_match(chosen.arguments[i], actual_types[i])
755
od
756
si
757
758
_find_matches(
759
group: Symbols.FUNCTION_GROUP,
760
arguments: Collections.List[Type]
761
) -> OVERLOAD_MATCHES_RESULT?
762
is
763
if group.functions.count == 0 then
764
return null
765
fi
766
767
if group.functions.count == 1 \/ arguments.count == 0 then
768
return OVERLOAD_MATCHES_RESULT(group.functions, 0, -1)
769
fi
770
771
let results = Collections.LIST[Symbols.Function]()
772
773
let best_score mut = cast int(Types.MATCH.DIFFERENT) * arguments.count
774
let best_index mut = -1
775
776
for f in group.functions do
777
if f.arguments.count >= arguments.count then
778
let score mut = cast int(Types.MATCH.SAME)
779
780
for i in 0..arguments.count do
781
let match: Types.MATCH mut
782
783
match = f.arguments[i].compare(arguments[i])
784
785
if match == Types.MATCH.DIFFERENT then
786
score = cast int(Types.MATCH.DIFFERENT)
787
fi
788
789
score = score + cast int(match)
790
od
791
792
results.add(f)
793
794
if score < best_score then
795
best_score = score
796
best_index = results.count - 1
797
fi
798
fi
799
od
800
801
return
802
OVERLOAD_MATCHES_RESULT(
803
results,
804
best_index,
805
-1
806
)
807
si
808
si
809
si