Skip to content
← Back

src/ir/value_boxer.ghul

1
namespace IR is
2
use Semantic.Types.Type;
3
4
use IR.Values.Value;
5
6
class VALUE_BOXER(_logger: Logging.Logger) is
7
want_boxing: bool public;
8
9
super();
10
11
box_if_value(value: Value) -> Value =>
12
if want_boxing /\ value.is_value_type then
13
Values.BOX(value)
14
else
15
value
16
fi;
17
18
box_if_needed(value: Value, target_type: Type) -> Value is
19
// Try the T → T? wrap first — covers the value-type
20
// optional widening (Nullable<T>::.ctor at the slot
21
// boundary). The target stays a value type, so no
22
// subsequent BOX fires.
23
let wrapped = wrap_if_needed(value, target_type)!;
24
25
if wrapped != value then
26
return wrapped;
27
fi
28
29
if want_boxing /\ value.is_value_type /\ !target_type.is_value_type then
30
return Values.BOX(value);
31
else
32
return value;
33
fi
34
si
35
36
// Implicit T → T? widening at slot boundaries. A non-optional
37
// value-type T flowing into a value-type optional target gets
38
// wrapped via Nullable<T>::.ctor — the value-type analogue of
39
// the box-on-assignment-to-object path. Reference T → T? needs
40
// no wrap (the bits are identical, only the type-system flag
41
// differs) so this helper only fires when the target is a
42
// value-type optional.
43
wrap_if_needed(value: Value?, target_type: Type?) -> Value? is
44
if !value? \/ !target_type? \/ !value.type? then
45
return value;
46
fi
47
48
// MAYBE[T] → reference T?: read the .value property. The
49
// field already carries the reference — null when absent,
50
// the user's reference when present — so a single property
51
// load is the entire coercion.
52
if
53
value.type!.is_maybe /\
54
target_type.is_optional /\
55
!target_type.is_value_type
56
then
57
let value_member = value.type!.find_member("value");
58
59
if value_member? then
60
return value_member.load(
61
value.location,
62
value,
63
IoC.CONTAINER.instance.symbol_loader);
64
fi
65
fi
66
67
if !target_type.is_value_type \/ !target_type.is_optional then
68
return value;
69
fi
70
71
// A bare `null` flowing into a value-type optional slot is the
72
// empty optional — a zeroed value, not a null reference. Lower
73
// it like `default`, mirroring the `null`-into-NULLABLE path in
74
// compile_expressions. The constraint that would otherwise drive
75
// that lowering does not reach a call argument, so the coercion
76
// happens here at the boxing boundary instead.
77
if value.type!.is_null then
78
return Values.DEFAULT(target_type);
79
fi
80
81
if value.type!.is_optional then
82
return value;
83
fi
84
85
// Value-type optional — source-side NULLABLE or a reflected
86
// Nullable<T> wrapper; both are GENERICs carrying the inner
87
// T as their single argument.
88
if isa Semantic.Types.GENERIC(target_type) then
89
let optional = target_type;
90
91
if optional.arguments.count != 1 then
92
return value;
93
fi
94
95
if !optional.arguments[0].is_assignable_from(value.type!) then
96
return value;
97
fi
98
99
return Values.WRAP_OPTIONAL(target_type, value);
100
fi
101
102
return value;
103
si
104
105
box_arguments(arguments: Collections.List[Value], argument_types: Collections.List[Type]) -> Collections.List[Value] is
106
if arguments.count != argument_types.count then
107
_logger.poison(Source.LOCATION.internal, "boxed incomplete arguments");
108
return arguments;
109
fi
110
111
let any_need_change mut = false;
112
113
for i in 0..arguments.count do
114
115
// Apply the wrap first — a value-typed source that
116
// coerces to a reference-typed target (e.g. MAYBE[T] →
117
// T?) emerges from `wrap_if_needed` as the .value
118
// reference, so the boxing branch below must see the
119
// already-coerced value or it boxes the wrong thing.
120
let wrapped = wrap_if_needed(arguments[i], argument_types[i])!;
121
122
if wrapped != arguments[i] then
123
any_need_change = true;
124
break;
125
fi
126
127
if want_boxing /\ !argument_types[i].is_value_type /\ wrapped.type!.is_value_type then
128
any_need_change = true;
129
break;
130
fi
131
od
132
133
if !any_need_change then
134
return arguments;
135
fi
136
137
let result = Collections.LIST[Value](arguments.count);
138
139
for i in 0..arguments.count do
140
let wrapped = wrap_if_needed(arguments[i], argument_types[i])!;
141
142
if want_boxing /\ !argument_types[i].is_value_type /\ wrapped.type!.is_value_type then
143
result.add(Values.BOX(wrapped));
144
else
145
result.add(wrapped);
146
fi
147
od
148
149
return result;
150
si
151
si
152
si