Skip to content
← Back

src/ir/values/null_safe_equality.ghul

1
namespace IR.Values is
2
use Semantic.Types.Type
3
4
// `a =~ b` where either operand is optional.
5
//
6
// The left operand is always tested when it is optional, because an
7
// absent one has no receiver to dispatch on: absent on the left
8
// answers whether the right is absent too, so two absent values are
9
// equal.
10
//
11
// The right operand is tested here only when the operator declares
12
// its parameter non-optional, which is `guards_argument`. An
13
// operator declaring `T?` is written to answer for an absent
14
// argument itself, so it is handed one and decides.
15
//
16
// Presence arrives as a bool-valued child rather than being tested
17
// here, so the three optional lowerings all reach the same shape: a
18
// reference optional compares itself against null, a `NULLABLE[T]`
19
// or `MAYBE[T]` reads its `has_value` member. A null child means the
20
// operand is not optional, and so is always present.
21
//
22
// `!~` wraps the whole thing in a NOT, so it negates these arms
23
// along with the body's answer.
24
//
25
// <left presence> // each presence child is read up to twice,
26
// brtrue <present> // so the caller hands over repeatable
27
// // loads rather than the expressions
28
// <right presence>
29
// ldc.i4.0
30
// ceq // both absent
31
// br <end>
32
// <present>:
33
// <right presence> // only when guards_argument
34
// brtrue <both>
35
// ldc.i4.0 // left present, right absent
36
// br <end>
37
// <both>:
38
// <equals call>
39
// <end>:
40
//
41
// Built by COMPILE_OPERATORS. The branch offsets are written long
42
// rather than short: the call arm carries an arbitrary user body.
43
class NULL_SAFE_EQUALITY: Value is
44
left_presence: Value?
45
right_presence: Value?
46
equals_call: Value
47
guards_argument: bool
48
_result_type: Type
49
50
type: Type => _result_type
51
is_lightweight_pure: bool => false
52
53
init(
54
left_presence: Value?,
55
right_presence: Value?,
56
equals_call: Value,
57
guards_argument: bool,
58
result_type: Type
59
) is
60
super.init()
61
62
self.left_presence = left_presence
63
self.right_presence = right_presence
64
self.equals_call = equals_call
65
self.guards_argument = guards_argument
66
self._result_type = result_type
67
si
68
69
gen(context: IR.CONTEXT) is
70
let left_present = IR.LABEL()
71
let both_present = IR.LABEL()
72
let end_label = IR.LABEL()
73
74
let body = context.current_srm_body_emitter!
75
76
if left_presence? then
77
gen(left_presence, context)
78
body.branch(System.Reflection.Metadata.ILOpCode.BRTRUE, left_present)
79
80
if right_presence? then
81
gen(right_presence, context)
82
body.ldc_i4(0)
83
body.op(System.Reflection.Metadata.ILOpCode.CEQ)
84
else
85
// a right operand that can never be absent does not
86
// match an absent left one
87
body.ldc_i4(0)
88
fi
89
90
body.branch(System.Reflection.Metadata.ILOpCode.BR, end_label)
91
body.mark_label(left_present)
92
fi
93
94
if guards_argument /\ right_presence? then
95
gen(right_presence, context)
96
body.branch(System.Reflection.Metadata.ILOpCode.BRTRUE, both_present)
97
body.ldc_i4(0)
98
body.branch(System.Reflection.Metadata.ILOpCode.BR, end_label)
99
body.mark_label(both_present)
100
fi
101
102
gen(equals_call, context)
103
104
body.mark_label(end_label)
105
si
106
107
to_string() -> string =>
108
"null-safe-equality:[{type}]({left_presence},{right_presence},{equals_call},guards-argument={guards_argument})"
109
si
110
si