Skip to content
← Back

src/semantic/types/nullable.ghul

1
namespace Semantic.Types is
2
3
use Source.LOCATION;
4
5
// The value-type spelling of `T?` — `System.Nullable[T]`,
6
// surfaced under the ghūl-side compiler name NULLABLE. A reference
7
// type's `T?` is a flagged NAMED instead (see NAMED.as_optional);
8
// NULLABLE is value-type only, and the unconstrained-T case is
9
// `Ghul.MAYBE[T]` (Types.MAYBE).
10
class NULLABLE: GENERIC is
11
short_description: string => get_short_description(self);
12
13
get_short_description(reference: GENERIC) -> string static => "{reference.arguments[0].short_description}?";
14
15
is_optional: bool => true;
16
17
optional_inner_type: Type? => arguments[0];
18
19
init(
20
location: LOCATION,
21
symbol: Symbols.Classy,
22
arguments: Collections.List[Type]
23
) is
24
super.init(location, symbol, arguments);
25
si
26
27
create(
28
location: LOCATION,
29
symbol: Symbols.Classy,
30
arguments: Collections.List[Type]
31
) -> GENERIC =>
32
Types.NULLABLE(location, symbol, arguments);
33
34
compare(other: Type) -> Types.MATCH =>
35
compare_optional(self, other, super.compare(other));
36
37
// NULLABLE[T] accepts a value of T via the implicit T → T?
38
// widening: the boxer emits a Nullable<T>::.ctor wrap at the
39
// slot boundary (parallel to box-on-assignment-to-object).
40
// Reported as ASSIGNABLE, not SAME, so an overload resolver
41
// with both f(T) and f(T?) candidates and a T argument still
42
// prefers f(T). Shared with the reflected Nullable<T> wrapper
43
// so a source-side and a reflected value optional compare
44
// identically — `direct` is the underlying GENERIC.compare
45
// result the caller already computed.
46
compare_optional(reference: GENERIC, other: Type?, direct: Types.MATCH) -> Types.MATCH static is
47
if cast int(direct) <= cast int(Types.MATCH.ASSIGNABLE) then
48
return direct;
49
fi
50
51
if !other? \/ other.is_null \/ other.is_error then
52
return direct;
53
fi
54
55
if reference.arguments.count == 1 /\ !other.is_optional then
56
if reference.arguments[0].is_assignable_from(other) then
57
return Types.MATCH.ASSIGNABLE;
58
fi
59
fi
60
61
// Ghul.MAYBE[T] -> value-type T?. Both are sequential-layout
62
// structs with declaration order (bool, T) — see the struct
63
// emitter in classy.ghul and Ghul.MAYBE in ghul-runtime —
64
// so the slot boundary needs no IL coercion beyond the
65
// implicit struct copy. Reported as ASSIGNABLE so an
66
// overload resolver still prefers an exact-typed candidate.
67
if reference.arguments.count == 1 /\ other.is_maybe then
68
let other_inner = other.optional_inner_type;
69
70
if other_inner? /\ reference.arguments[0].is_assignable_from(other_inner) then
71
return Types.MATCH.ASSIGNABLE;
72
fi
73
fi
74
75
return direct;
76
si
77
78
to_string() -> string => "{arguments[0]}?";
79
si
80
si