Skip to content
← Back

src/semantic/least_upper_bound_map.ghul

1
namespace Semantic is
2
use Collections
3
use Semantic.Symbols.Symbol
4
use Semantic.Types.Type
5
6
use Ghul.Pipes
7
8
use Logging
9
10
enum LubMode is
11
ONLY_ELEMENT_TYPES,
12
ANY_CONCRETE,
13
ANY_TRAIT
14
si
15
16
class LEAST_UPPER_BOUND_MAP is
17
_any_unsafe_constraints: bool
18
_mode: LubMode
19
20
types: LIST[Type]
21
element_names: LIST[string?]?
22
23
result: MAP[Symbol,LIST[Type]]?
24
current: MAP[Symbol,LIST[Type]]?
25
26
init() is
27
types = LIST[Type]()
28
_mode = LubMode.ONLY_ELEMENT_TYPES
29
si
30
31
add(type: Type) is
32
// Per-position structural merge: if an already-collected
33
// constraint shares a head with `type` and they're each
34
// concrete in slots the other isn't, collapse them into a
35
// single refined entry instead of leaving both in the LUB
36
// pool where one would out-rank the other and lose the
37
// complementary information. Required for iterative
38
// inference cases where two match propagation channels each pin a
39
// different position of the same Function shape.
40
//
41
// Only useful when at least one side is a structurally-
42
// recursable GENERIC carrying placeholder slots — for the
43
// common (and very hot) flat-type case (int + int, bool +
44
// bool, ...) the existing LUB picker does the right thing
45
// and the merge would be pure overhead.
46
if isa Types.GENERIC(type) /\ type.contains_inferred then
47
for i in 0..types.count do
48
let merged = _try_merge_per_position(types[i], type)
49
50
if merged? then
51
types[i] = merged
52
53
if type.is_unsafe_constraints then
54
_any_unsafe_constraints = true
55
fi
56
57
_update_element_names_from(merged)
58
return
59
fi
60
od
61
else
62
for i in 0..types.count do
63
let existing = types[i]
64
65
if isa Types.GENERIC(existing) /\ existing.contains_inferred then
66
let merged = _try_merge_per_position(existing, type)
67
68
if merged? then
69
types[i] = merged
70
71
if type.is_unsafe_constraints then
72
_any_unsafe_constraints = true
73
fi
74
75
_update_element_names_from(merged)
76
return
77
fi
78
fi
79
od
80
fi
81
82
types.add(type)
83
84
if type.is_unsafe_constraints then
85
_any_unsafe_constraints = true
86
fi
87
88
_update_element_names_from(type)
89
si
90
91
// When the per-position merge defers a placeholder slot to a
92
// concrete one, push the concrete back to the placeholder's
93
// origin so iterative-inference resolves the phantom to the
94
// same concrete type. Without this the LUB result picks the
95
// right concrete at the top level but the AST nodes that
96
// produced the placeholder side keep their placeholder-bearing
97
// value type — surfaces in analyse-mode HOVER as `***` and in
98
// ancestor LUB merges as the placeholder collapsing to object.
99
//
100
// Only fires for INFERRED_VARIABLE_TYPE (the placeholder kind
101
// that carries a back-pointer origin). INFERRED_RETURN_TYPE is
102
// a singleton sentinel with no origin to push into; ERROR
103
// similarly. Concrete must itself not be a sentinel / inferred
104
// — pushing one placeholder into another's LUB would just
105
// propagate uncertainty.
106
//
107
// Identical in spirit to `Semantic.MATCH_PROPAGATOR.pair`'s
108
// placeholder-formal branch, but reached from inside the LUB
109
// merge — the LUB has no Logger field and isn't constructed
110
// with one (callers create instances ad-hoc), so the retry
111
// signal is sent via the IoC-resolved logger rather than a
112
// dependency-injected reference. Calling through MATCH_PROPAGATOR
113
// would have required threading a Logger through every LUB
114
// construction site.
115
_propagate_to_placeholder(placeholder: Type, concrete: Type?) is
116
if !isa Types.INFERRED_VARIABLE_TYPE(placeholder) then
117
return
118
fi
119
120
if !concrete? \/ concrete.is_sentinel \/ concrete.is_inferred \/ concrete.is_error then
121
return
122
fi
123
124
let p = cast Types.INFERRED_VARIABLE_TYPE(placeholder)
125
126
IoC.CONTAINER.instance.logger.mark_consumed_any_if(Semantic.INFERENCE_TRACE.add_lower_bound("lub.propagate_to_placeholder", p.origin, concrete))
127
si
128
129
// Returns a per-position structural refinement of a and b, or
130
// null if the two aren't shape-compatible. At each position the
131
// non-placeholder side wins; recurses through generic args to
132
// handle nested cases like Function[Function[?, int], bool].
133
_try_merge_per_position(a: Type?, b: Type?) -> Type? is
134
if !a? then return b; fi
135
if !b? then return a; fi
136
137
// Inference placeholder OR ERROR at the top — defer to the
138
// other. ERROR is the "this slot failed" sentinel; an
139
// inference placeholder is "still working on it". Either
140
// way the other side, however it's resolved, is strictly
141
// more informative than this one. Without the is_error leg
142
// the tuple LUB from `let v18; v18 = (delayed_lambda([…]),
143
// 5);` froze at `(ERROR, int)` on iter 1 (call to the
144
// not-yet-resolved lambda returned DUMMY(ERROR)) and out-
145
// ranked iter 2's refined `(placeholder, int)` because the
146
// top of the latter was treated as is_inferred and got
147
// discarded in favour of ERROR.
148
if a.is_inferred \/ a.is_error then
149
_propagate_to_placeholder(a, b)
150
return b
151
fi
152
if b.is_inferred \/ b.is_error then
153
_propagate_to_placeholder(b, a)
154
return a
155
fi
156
157
// Otherwise both must be GENERIC with the same head and arity.
158
if !isa Types.GENERIC(a) \/ !isa Types.GENERIC(b) then
159
if a.matches(b) then return a; fi
160
return null
161
fi
162
163
let ga = a
164
let gb = b
165
166
let sa = cast Symbols.GENERIC?(ga.symbol)
167
let sb = cast Symbols.GENERIC?(gb.symbol)
168
169
if !sa? \/ !sb? then return null; fi
170
171
// Heads differ — try promoting both to a shared generic
172
// ancestor (variant -> union being the motivating case:
173
// `DONE[<phantom>]` and `YIELD[int]` share `STEP[…]`).
174
// The non-merge fallback in get_result walks ancestors too
175
// but intersects via `matches` on the specialised ancestor; a
176
// phantom slot vs concrete won't equate, so that path
177
// drops to `object`. Merging through the ancestor here
178
// lets the per-position rule (placeholder slot defers to
179
// concrete) fire on the slots that need it.
180
if sa.symbol != sb.symbol then
181
return _try_merge_through_shared_ancestor(ga, gb, sa, sb)
182
fi
183
184
if ga.arguments.count != gb.arguments.count then return null; fi
185
186
let merged_args = LIST[Types.Type](ga.arguments.count)
187
188
for j in 0..ga.arguments.count do
189
let merged_arg = _try_merge_per_position(ga.arguments[j], gb.arguments[j])
190
191
if !merged_arg? then return null; fi
192
193
merged_args.add(merged_arg)
194
od
195
196
// Tuples carry element names alongside element types.
197
// Reconciliation rules: both unnamed → unnamed; one named one
198
// unnamed → take the name; both named the same → that name;
199
// both named differently → take the name from the side whose
200
// arg is concrete, or fail the merge if both are concrete.
201
if a.is_value_tuple /\ b.is_value_tuple then
202
let merged_names = LIST[string](ga.arguments.count)
203
let any_name mut = false
204
205
for j in 0..ga.arguments.count do
206
let name_a = ga.symbol.get_element_name(j)
207
let name_b = gb.symbol.get_element_name(j)
208
209
let name = _try_merge_element_name(name_a, name_b, ga.arguments[j], gb.arguments[j])
210
211
if name_a? /\ name_b? /\ name_a !~ name_b /\ !name? then
212
return null
213
fi
214
215
if name? then any_name = true; fi
216
217
// Positional placeholder for an unnamed slot, per the
218
// tuple-construction convention.
219
merged_names.add(if name? then name else "{j}" fi)
220
od
221
222
let names: Collections.List[string] = if any_name then merged_names else LIST[string]() fi
223
224
return IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(merged_args, names)
225
fi
226
227
return ga.create(ga.symbol.location, sa.symbol, merged_args)
228
si
229
230
// For two GENERICs with different head symbols, look for a
231
// shared generic ancestor with matching head and arity, and
232
// attempt the per-position merge on the specialised ancestor
233
// pair. Returns the merged ancestor type, or null if no
234
// ancestor pair lined up.
235
//
236
// Specialised ancestors come from `Symbols.GENERIC.get_ancestor`,
237
// which substitutes the GENERIC's type_map — so for `DONE[?p_T]`
238
// the STEP ancestor is `STEP[?p_T]`, and the recursive merge
239
// can defer the placeholder slot to the concrete slot of
240
// `STEP[int]` coming from a sibling `YIELD[int]`.
241
//
242
// Walks `sa`'s ancestor list outermost-first (push_ancestor
243
// puts direct parents at index 0, so STEP appears before
244
// object for a variant), so the closest shared ancestor wins.
245
_try_merge_through_shared_ancestor(
246
ga: Types.GENERIC, gb: Types.GENERIC,
247
sa: Symbols.GENERIC, sb: Symbols.GENERIC
248
) -> Types.Type? is
249
for i in 0..sa.ancestors.count do
250
let a_anc = sa.get_ancestor(i)
251
252
if !isa Types.GENERIC(a_anc) then
253
continue
254
fi
255
256
let a_anc_gen = a_anc
257
let a_anc_sym = cast Symbols.GENERIC?(a_anc_gen.symbol)
258
259
if !a_anc_sym? then
260
continue
261
fi
262
263
for j in 0..sb.ancestors.count do
264
let b_anc = sb.get_ancestor(j)
265
266
if !isa Types.GENERIC(b_anc) then
267
continue
268
fi
269
270
let b_anc_gen = b_anc
271
let b_anc_sym = cast Symbols.GENERIC?(b_anc_gen.symbol)
272
273
if !b_anc_sym? then
274
continue
275
fi
276
277
if
278
a_anc_sym.symbol =~ b_anc_sym.symbol /\
279
a_anc_gen.arguments.count == b_anc_gen.arguments.count
280
then
281
let merged = _try_merge_per_position(a_anc, b_anc)
282
283
if merged? then
284
return merged
285
fi
286
fi
287
od
288
od
289
290
return null
291
si
292
293
_try_merge_element_name(name_a: string? mut, name_b: string? mut, arg_a: Types.Type, arg_b: Types.Type) -> string? is
294
// Auto-generated positional names like 0 / 1 don't carry
295
// user intent; treat them as absent.
296
if name_a? /\ Symbols.Symbol.is_positional_member_name(name_a) then name_a = null; fi
297
if name_b? /\ Symbols.Symbol.is_positional_member_name(name_b) then name_b = null; fi
298
299
if !name_a? /\ !name_b? then return null; fi
300
if !name_a? then return name_b; fi
301
if !name_b? then return name_a; fi
302
if name_a =~ name_b then return name_a; fi
303
304
// Names differ. If one side's element type is still a
305
// placeholder we trust the named side's intent more than
306
// the placeholder's; otherwise the conflict is real.
307
if !arg_a.contains_inferred /\ arg_b.contains_inferred then return name_a; fi
308
if arg_a.contains_inferred /\ !arg_b.contains_inferred then return name_b; fi
309
310
return null
311
si
312
313
_add(type: Type) is
314
if !result? then
315
result = MAP[Symbol,LIST[Type]]()
316
_add_all_first(type)
317
else
318
current = MAP[Symbol,LIST[Type]]()
319
320
_add_all_subsequent(type)
321
322
_apply_intersection()
323
fi
324
si
325
326
// The join is optional when any joined reference type is: the
327
// ancestor searches below compare the types' shapes and keep
328
// optionality only where one side is assignable to the other,
329
// so a join of `CAT?` and `DOG?` would otherwise be a bare
330
// `Animal` over a value that can be absent.
331
get_result() -> Type? is
332
let result = _get_result_ignoring_optionality()
333
334
if
335
result? /\
336
!result.is_optional /\
337
!result.is_value_type /\
338
!result.is_type_variable /\
339
types |> any(t => t.is_optional /\ !t.is_value_type /\ !t.is_type_variable)
340
then
341
return result.as_optional()
342
fi
343
344
return result
345
si
346
347
_get_result_ignoring_optionality() -> Type? is
348
if types.count == 0 then
349
return null
350
fi
351
352
// A join over an operand whose type is still being inferred
353
// is not yet decidable: an inference placeholder matches
354
// every type, so the settled operands would decide it, and
355
// whatever they decided the placeholder's slot would have
356
// to fit later. The join waits for the operand instead. A
357
// single provisional operand is handed back as it is, since
358
// there is nothing to decide and its shape may already be
359
// useful to a consumer. A bare type parameter among the
360
// operands is a binding still to be made rather than a type
361
// to decide between, as when a candidate's own parameter
362
// reaches its binder unbound, so it neither counts as a
363
// second operand nor is handed back in the provisional one's
364
// place.
365
let provisional = types |> filter(t => t.contains_inferred) |> collect_list()
366
367
if provisional.count > 0 then
368
let decisive = types |> filter(t => !t.contains_inferred /\ !t.is_type_variable) |> count()
369
370
if decisive > 0 \/ provisional.count > 1 then
371
let awaited = _awaited_placeholder(provisional[0])
372
373
OBLIGATIONS.defer("join", awaited, null)
374
375
return Types.INFERRED_JOIN_TYPE(awaited)
376
elif types.count > 1 then
377
return provisional[0]
378
fi
379
fi
380
381
// this was the original common element type
382
// inference heuristic
383
let best_assignable = _try_get_best_assignable()
384
if best_assignable? then
385
return best_assignable
386
fi
387
388
let best_array = _try_get_best_array()
389
if best_array? then
390
return best_array
391
fi
392
393
// this is LUB applied to only element types, but
394
// it's actually worse that the original heuristic
395
// in some cases because it doesn't handle type variance
396
// TODO consider removing
397
let best_element_type = _try_get_best_element_type()
398
if best_element_type? then
399
return best_element_type
400
fi
401
402
// this is LUB applied to all elements and all concrete
403
// element ancestor types. If all the types are concrete
404
// then this will always return a result. In the worse
405
// case it will return object, which may still be better
406
// than a fairly random choice of interface type
407
let best_concrete_type = _try_get_best_concrete()
408
409
// this is LUB applied to all elements and all traits
410
// they implement. It has a tendency to select unhelpful
411
// traits, and needs tuning somehow to prioritize
412
// traits that are relevant based on context
413
let best_trait_type = _try_get_best_trait()
414
415
// this still might not be enough - we might need to record the
416
// average depth difference (='specificity') per type, but that
417
// could be slow
418
if best_concrete_type? /\ best_trait_type? then
419
let total_concrete_depth_difference =
420
types |> reduce(0, (d, t) => d + (t.depth - best_concrete_type.depth))
421
422
let total_trait_depth_difference =
423
types |> reduce(0, (d, t) => d + (t.depth - best_trait_type.depth))
424
425
if total_trait_depth_difference < total_concrete_depth_difference then
426
return best_trait_type
427
else
428
return best_concrete_type
429
fi
430
elif best_concrete_type? then
431
return best_concrete_type
432
else
433
return best_trait_type
434
fi
435
si
436
437
// The first placeholder inside a provisional type, which is the
438
// one an obligation on the type names.
439
_awaited_placeholder(type: Type) -> Type is
440
if type.is_inferred then
441
return type
442
fi
443
444
if let generic: Types.GENERIC = type then
445
for argument in generic.arguments do
446
if argument.contains_inferred then
447
return _awaited_placeholder(argument)
448
fi
449
od
450
fi
451
452
return type
453
si
454
455
_try_get_best_assignable() -> Type? is
456
let best: Type? mut = null
457
458
for type in types do
459
if !best? then
460
best = type
461
elif type.is_assignable_from(best) then
462
best = type
463
elif !best.is_assignable_from(type) then
464
return null
465
fi
466
od
467
468
return _with_element_names(best)
469
si
470
471
// An array of a reference type is covariant in its element, so
472
// arrays whose elements join to a reference type join to an
473
// array of that type. Their ancestors meet only at the
474
// non-generic iterable.
475
_try_get_best_array() -> Type? is
476
let first = cast Types.ARRAY?(types[0])
477
478
if !first? then
479
return null
480
fi
481
482
let elements = LEAST_UPPER_BOUND_MAP()
483
484
for type in types do
485
if let array: Types.ARRAY = type /\ !array.arguments[0].is_value_type then
486
elements.add(array.arguments[0])
487
else
488
return null
489
fi
490
od
491
492
if let element = elements.get_result() /\ !element.is_value_type /\ element.is_settled then
493
let generic_symbol = cast Symbols.GENERIC?(first.symbol)!
494
495
return first.create(generic_symbol.location, generic_symbol.symbol, LIST[Type]([element]))
496
fi
497
498
return null
499
si
500
501
_try_get_best_element_type() -> Type? is
502
_mode = LubMode.ONLY_ELEMENT_TYPES
503
504
for type in types do
505
_add(type)
506
od
507
508
return
509
let best = _get_best() in
510
if best? /\ !best.is_object then
511
best
512
else
513
null
514
fi
515
si
516
517
_try_get_best_concrete() -> Type? is
518
result = null
519
current = null
520
_mode = LubMode.ANY_CONCRETE
521
522
for type in types do
523
_add(type)
524
od
525
526
return _get_best()
527
si
528
529
_try_get_best_trait() -> Type? is
530
result = null
531
current = null
532
_mode = LubMode.ANY_TRAIT
533
534
for type in types do
535
_add(type)
536
od
537
538
return _get_best()
539
si
540
541
_get_best() -> Type? is
542
let result = self.result!
543
544
let best: Type? mut = null
545
let is_ambiguous mut = false
546
547
for list in result.values do
548
for type in list do
549
if !_any_unsafe_constraints /\ type.is_unsafe_constraints then
550
// only allow unsafe constraints if some of the element
551
// types have unsafe constraints:
552
continue
553
fi
554
555
if !best? then
556
best = type
557
is_ambiguous = false
558
elif type.depth > best.depth then
559
best = type
560
is_ambiguous = false
561
elif type.depth == best.depth then
562
is_ambiguous = true
563
fi
564
od
565
od
566
567
if !is_ambiguous then
568
return _with_element_names(best)
569
fi
570
return null
571
si
572
573
_apply_intersection() is
574
let result = self.result!
575
let current = self.current!
576
577
let new_result = MAP[Symbol,LIST[Type]]()
578
579
for kv in result do
580
let rsf = kv.key
581
let result_list = kv.value
582
583
let current_list: LIST[Type] mut
584
585
if !current.try_get_value(rsf, current_list ref) then
586
continue
587
fi
588
589
// TODO this could be slow, although in practice for non-generic types
590
// there will only ever be a single type in the list
591
let new_result_list = result_list |> filter(t => current_list |> any(u => t.matches(u))) |> collect_list()
592
593
if new_result_list.count > 0 then
594
new_result.add(rsf, new_result_list)
595
fi
596
od
597
598
self.result = new_result
599
si
600
601
// Yield ancestors of `type` for the LUB walk, specialised via
602
// the type's type-map when the symbol is generic. Without
603
// specialisation, the ancestors come back with each class's
604
// OWN type-variable symbols intact — so two siblings sharing
605
// an `Iterable[T]` ancestor look distinct to the intersection
606
// step because their T's are different symbols. Specialising
607
// collapses `array_class[int].Iterable[T]` and
608
// `list_class[int].Iterable[T]` to a common `Iterable[int]`,
609
// which is the only way the trait-walk can find them
610
// intersecting.
611
_specialised_ancestor(type: Type, i: int) -> Type is
612
if let symbol: Symbols.GENERIC = type.symbol then
613
return symbol.get_ancestor(i)
614
fi
615
616
return type.ancestors[i]
617
si
618
619
// populate the result set from type and all
620
// its ancestor types
621
_add_all_first(type: Type) is
622
if
623
_mode == LubMode.ONLY_ELEMENT_TYPES \/
624
(_mode == LubMode.ANY_CONCRETE /\ !type.is_trait) \/
625
(_mode == LubMode.ANY_TRAIT /\ type.is_trait)
626
then
627
_add_single_first(type)
628
fi
629
630
if _mode == LubMode.ANY_CONCRETE /\ type.ancestors.count > 0 then
631
_add_all_first(_specialised_ancestor(type, 0))
632
elif _mode == LubMode.ANY_TRAIT then
633
for i in 0..type.ancestors.count do
634
_add_all_first(_specialised_ancestor(type, i))
635
od
636
fi
637
si
638
639
_add_all_subsequent(type: Type) is
640
if
641
_mode == LubMode.ONLY_ELEMENT_TYPES \/
642
(_mode == LubMode.ANY_CONCRETE /\ !type.is_trait) \/
643
(_mode == LubMode.ANY_TRAIT /\ type.is_trait)
644
then
645
_add_single_subsequent(type)
646
fi
647
648
if _mode == LubMode.ANY_CONCRETE /\ type.ancestors.count > 0 then
649
_add_all_subsequent(_specialised_ancestor(type, 0))
650
elif _mode == LubMode.ANY_TRAIT then
651
for i in 0..type.ancestors.count do
652
_add_all_subsequent(_specialised_ancestor(type, i))
653
od
654
fi
655
si
656
657
// add a single type to the result set
658
_add_single_first(type: Type) is
659
if type.is_root_value_type then
660
// System.ValueType is not a real type for
661
// our purposes here, so exclude it:
662
return
663
fi
664
665
// what unspecialized type was this type ultimately
666
// specialized from. If the type is not generic
667
// then this is the classy type that represents it
668
let rsf = type.symbol.root_unspecialized_symbol
669
670
// we can't put types in a set directly, because types
671
// are not interned. However we can reduce the amount
672
// of linear searching we need to do by partitioning
673
// by root-specialized-from symbols, which are unique
674
// per unspecialized generic type. we then only need
675
// to search under the matching root specialized from symbol
676
677
let result = self.result!
678
679
let list: LIST[Type] mut
680
681
if !result.try_get_value(rsf, list ref) then
682
list = LIST[Type]()
683
result.add(rsf, list)
684
list.add(type)
685
elif list |> all(t => !t.matches(type)) then
686
list.add(type)
687
fi
688
689
assert result.contains_key(rsf) else "somehow haven't added {rsf} to {result.keys |> join(", ")}"
690
si
691
692
// add a single type to the current set
693
_add_single_subsequent(type: Type) is
694
let rsf = type.symbol.root_unspecialized_symbol
695
696
let result = self.result!
697
let current = self.current!
698
699
if !result.contains_key(rsf) then
700
// cannot intersect
701
return
702
fi
703
704
let list: LIST[Type] mut
705
706
if !current.try_get_value(rsf, list ref) then
707
list = LIST[Type]()
708
current.add(rsf, list)
709
list.add(type)
710
elif list |> all(t => !t.matches(type)) then
711
list.add(type)
712
fi
713
si
714
715
_update_element_names_from(type: Type) is
716
if type.is_value_tuple then
717
if !element_names? then
718
element_names = LIST[string?]()
719
fi
720
721
let names = element_names!
722
723
for i in 0..type.arguments.count do
724
if i >= names.count then
725
names.add(null)
726
fi
727
728
let element_name = type.symbol.get_element_name(i)
729
730
if element_name? /\ ! Symbols.Symbol.is_positional_member_name(element_name) then
731
names[i] = element_name
732
fi
733
od
734
fi
735
si
736
737
_with_element_names(type: Type?) -> Type? =>
738
if !type? then
739
null
740
elif type.is_value_tuple /\ element_names? /\ element_names.count == type.arguments.count then
741
IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(type.arguments, element_names)
742
else
743
type
744
fi
745
si
746
si