Skip to content
← Back

src/syntax/process/optional_operand.ghul

1
namespace Syntax.Process is
2
use Semantic.Types.Type
3
use IR.Values.Value
4
5
// Splits an operand into the two things a presence test needs: is
6
// it here, and what is the value if it is.
7
//
8
// The three optional lowerings answer both differently. A reference
9
// optional is the value, tested against null. A `NULLABLE[T]` or
10
// `MAYBE[T]` carries presence in a `has_value` member and the value
11
// in a `value` member, both reached through the operand's address -
12
// which is why the operand has to be something addressable, a
13
// spilled temp rather than an expression. A non-optional operand is
14
// simply always here.
15
class OPTIONAL_OPERAND(_symbol_loader: Semantic.SYMBOL_LOADER, _bool_type: Type) is
16
// Answers null when the operand is not optional at all, which
17
// the caller reads as always present rather than as a failure.
18
presence(location: Source.LOCATION, operand: Value) -> Value? is
19
let type = operand.type
20
21
if !type? \/ !type.is_optional then
22
return null
23
fi
24
25
if !type.is_value_type then
26
return IR.Values.HAS_VALUE(operand, _bool_type)
27
fi
28
29
let has_value_member = type.find_member("has_value")
30
31
if !has_value_member? \/ !isa Semantic.Symbols.Property(has_value_member) then
32
return null
33
fi
34
35
return has_value_member.load(location, operand, _symbol_loader)
36
si
37
38
// The value to hand the operator. A reference optional is
39
// already it; a value-shape one is projected out of its
40
// carrier, which is only sound once presence is established -
41
// `Nullable[T].Value` throws when absent.
42
//
43
// The carrier cannot reliably go across instead. A reflected
44
// `Ghul.Equatable[T].=~` takes a bare `T` at IL whatever its
45
// optionality says, so handing it one produces an invalid
46
// program; a user-declared `=~(other: T?)` on a struct does
47
// take the carrier. Rather than have the answer turn on which,
48
// the caller settles presence for every value-shape operand.
49
payload(location: Source.LOCATION, operand: Value) -> Value? is
50
let type = operand.type
51
52
if !type? \/ !type.is_optional \/ !type.is_value_type then
53
return operand
54
fi
55
56
let value_member = type.find_member("value")
57
58
if !value_member? then
59
return null
60
fi
61
62
return value_member.load(location, operand, _symbol_loader)
63
si
64
65
// Whether an operand has to have its presence settled here
66
// rather than being handed to the operator absent. A value-
67
// shape one always does, per `payload` above - and always,
68
// rather than only when the other operand happens to be
69
// optional, so one comparison does not answer two ways.
70
// Reads nothing but the operand, so it needs no instance: a
71
// caller deciding whether to build one at all can ask first.
72
must_be_guarded(operand: Value) -> bool static is
73
let type = operand.type
74
75
return type? /\ type.is_optional /\ type.is_value_type
76
si
77
78
// Whether both halves can be built for this operand. A
79
// value-shape optional missing either member is not one this
80
// can drive, so the caller leaves the comparison alone.
81
can_split(location: Source.LOCATION, operand: Value) -> bool is
82
let type = operand.type
83
84
if !type? then
85
return false
86
fi
87
88
if !type.is_optional \/ !type.is_value_type then
89
return true
90
fi
91
92
return presence(location, operand)? /\ payload(location, operand)?
93
si
94
si
95
si