Appearance
| 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 |