Skip to content
← Back

src/semantic/tuple_element_lub.ghul

1
namespace Semantic is
2
use Collections
3
use Semantic.Types.Type
4
use Semantic.Lookups.InnateSymbolLookup
5
6
use Ghul.Pipes
7
8
// Builds a tuple type whose element types are the per-position least
9
// upper bounds of a set of tuple-literal value types — used when
10
// inferring the type of a list literal or a set of if-branch values
11
// whose elements are themselves tuple literals.
12
class TUPLE_ELEMENT_LUB(_innate_symbol_lookup: InnateSymbolLookup) is
13
14
// A tuple-literal element position widens to its optional
15
// carrier the same way an `if`/`case` expression's result does
16
// when one arm is null: a value contributed at that position by
17
// one tuple literal and `null` contributed by another must
18
// settle at the value's optional type, not at the `null`
19
// literal type itself — which no back end can encode as a
20
// field or local's static type. Handled per position via
21
// ARM_NULL_JOIN, the same decision the whole-arm case uses in
22
// Syntax.Process.COMPILE_CONDITIONALS.
23
combine(tuple_types: Iterable[Type], element_names: LIST[string?]?) -> Type is
24
let lubs = LIST[LEAST_UPPER_BOUND_MAP]()
25
let seen_null = LIST[bool]()
26
let seen_genuine_null = LIST[bool]()
27
28
let arm_null_join = ARM_NULL_JOIN()
29
30
for tuple_type in tuple_types do
31
for (index, element_type) in tuple_type.arguments |> index() do
32
if index >= lubs.count then
33
lubs.add(LEAST_UPPER_BOUND_MAP())
34
seen_null.add(false)
35
seen_genuine_null.add(false)
36
fi
37
38
// A placeholder answers `is_null`, so it is added to the
39
// join here, ahead of the null arm's own handling: a
40
// position still being inferred is not an absent one.
41
if element_type.is_inferred then
42
lubs[index].add(element_type)
43
elif element_type.is_null then
44
seen_null[index] = true
45
46
if arm_null_join.is_genuine_null(element_type) then
47
seen_genuine_null[index] = true
48
fi
49
else
50
lubs[index].add(element_type)
51
fi
52
od
53
od
54
55
let element_types = LIST[Type](lubs.count)
56
57
for i in 0..lubs.count do
58
let base_type = lubs[i].get_result() ?? _innate_symbol_lookup.get_object_type()
59
60
let decision = arm_null_join.decide(base_type, seen_genuine_null[i], seen_null[i])
61
62
element_types.add(
63
case decision
64
when ArmNullJoinDecision.WIDEN_VALUE_OPTIONAL then _innate_symbol_lookup.get_optional_type(base_type)
65
when ArmNullJoinDecision.WIDEN_REFERENCE_OPTIONAL then base_type.as_optional()
66
else base_type
67
esac
68
)
69
od
70
71
return _innate_symbol_lookup.get_tuple_type(element_types, element_names)
72
si
73
si
74
si