Skip to content
← Back

src/ir/value_converter.ghul

1
namespace IR is
2
use System.Reflection.Metadata.ILOpCode
3
use IO.Std
4
5
use Semantic.Types.Type
6
use NAMED_TYPE = Semantic.Types.NAMED
7
8
// How a value becomes a decimal, or stops being one. `is_identity`
9
// is the decimal-to-decimal case, where nothing has to happen;
10
// otherwise the conversion is one static method on `System.Decimal`
11
// and these are the types it goes between.
12
class DECIMAL_CONVERSION(
13
is_identity: bool,
14
method_name: string,
15
parameter_type: Type,
16
return_type: Type
17
)
18
19
// The decimal tables are built by complete_initialization, which
20
// every entry point calls before reading them.
21
@suppress("field-definite-assignment")
22
class VALUE_CONVERTER(
23
_logger: Logging.Logger,
24
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
25
) is
26
_instructions: Collections.MAP[string, ILOpCode]?
27
28
// System.Decimal has no `conv.*` IL opcode. Conversion to or
29
// from decimal is a static call to `op_Implicit` / `op_Explicit`
30
// on the Decimal type, overloaded per neighbouring type; this
31
// is the set of neighbours those overloads cover. Populated by
32
// complete_initialization alongside _instructions.
33
_decimal_neighbours: Collections.SET[string]
34
_decimal_implicit_sources: Collections.SET[string]
35
36
super()
37
38
complete_initialization() is
39
if _instructions? then
40
return
41
fi
42
43
_instructions = Collections.MAP[string, ILOpCode]()
44
45
add_instruction(_innate_symbol_lookup.get_bool_type(), ILOpCode.CONV_U1)
46
47
add_instruction(_innate_symbol_lookup.get_ubyte_type(), ILOpCode.CONV_U1)
48
add_instruction(_innate_symbol_lookup.get_byte_type(), ILOpCode.CONV_I1)
49
50
add_instruction(_innate_symbol_lookup.get_char_type(), ILOpCode.CONV_U2)
51
52
add_instruction(_innate_symbol_lookup.get_ushort_type(), ILOpCode.CONV_U2)
53
add_instruction(_innate_symbol_lookup.get_short_type(), ILOpCode.CONV_I2)
54
55
add_instruction(_innate_symbol_lookup.get_uint_type(), ILOpCode.CONV_U4)
56
add_instruction(_innate_symbol_lookup.get_int_type(), ILOpCode.CONV_I4)
57
58
add_instruction(_innate_symbol_lookup.get_ulong_type(), ILOpCode.CONV_U8)
59
add_instruction(_innate_symbol_lookup.get_long_type(), ILOpCode.CONV_I8)
60
61
add_instruction(_innate_symbol_lookup.get_uword_type(), ILOpCode.CONV_U)
62
add_instruction(_innate_symbol_lookup.get_word_type(), ILOpCode.CONV_I)
63
64
add_instruction(_innate_symbol_lookup.get_single_type(), ILOpCode.CONV_R4)
65
add_instruction(_innate_symbol_lookup.get_double_type(), ILOpCode.CONV_R8)
66
67
_decimal_neighbours = Collections.SET[string]()
68
_decimal_implicit_sources = Collections.SET[string]()
69
70
// char is omitted intentionally — Decimal's conversion
71
// overloads here are the ones taking or returning a CIL
72
// primitive, and char is not among them.
73
// System.Decimal does declare op_Implicit(char) and
74
// an op_Explicit returning char - TYPE_CASTER.find_user_defined_conversion
75
// reaches those through the general methodref machinery
76
// instead, so `cast char(d)` and `cast decimal(c)` still
77
// work even though this fast-path table doesn't cover them.
78
register_decimal_neighbour(_innate_symbol_lookup.get_byte_type(), true)
79
register_decimal_neighbour(_innate_symbol_lookup.get_ubyte_type(), true)
80
register_decimal_neighbour(_innate_symbol_lookup.get_short_type(), true)
81
register_decimal_neighbour(_innate_symbol_lookup.get_ushort_type(), true)
82
register_decimal_neighbour(_innate_symbol_lookup.get_int_type(), true)
83
register_decimal_neighbour(_innate_symbol_lookup.get_uint_type(), true)
84
register_decimal_neighbour(_innate_symbol_lookup.get_long_type(), true)
85
register_decimal_neighbour(_innate_symbol_lookup.get_ulong_type(), true)
86
register_decimal_neighbour(_innate_symbol_lookup.get_single_type(), false)
87
register_decimal_neighbour(_innate_symbol_lookup.get_double_type(), false)
88
si
89
90
add_instruction(type: Type, op_code: ILOpCode) is
91
_instructions![type.symbol.qualified_name] = op_code
92
si
93
94
register_decimal_neighbour(type: Type, is_implicit_to_decimal: bool) is
95
let qn = type.symbol.qualified_name
96
_decimal_neighbours.add(qn)
97
if is_implicit_to_decimal then
98
_decimal_implicit_sources.add(qn)
99
fi
100
si
101
102
get_instruction(type: Type?) -> ILOpCode? is
103
complete_initialization()
104
105
if !type? then
106
return null
107
fi
108
109
let name = type.symbol.qualified_name
110
111
let instructions = _instructions
112
113
if instructions? /\ instructions.contains_key(name) then
114
return instructions[name]
115
fi
116
return null
117
si
118
119
// How a value of `source` on the stack becomes a `target`, when
120
// at least one of them is decimal. Null when neither is, or
121
// when the pair has no `Decimal::op_Implicit`/`op_Explicit`
122
// overload.
123
//
124
// `is_identity` is the decimal-to-decimal case, which is not a
125
// call: the stack stays typed as decimal either way. Otherwise
126
// the conversion is that one static method on `System.Decimal`,
127
// described by the types it goes between rather than by the IL
128
// line that calls it — the caller needs a token for it, and a
129
// rendered call is not something a token can be read out of.
130
get_decimal_conversion(source: Type?, target: Type?) -> DECIMAL_CONVERSION? is
131
complete_initialization()
132
133
if !source? \/ !target? then
134
return null
135
fi
136
137
let decimal_type = _innate_symbol_lookup.get_decimal_type()
138
139
let source_is_decimal = source.matches(decimal_type)
140
let target_is_decimal = target.matches(decimal_type)
141
142
if !source_is_decimal /\ !target_is_decimal then
143
return null
144
fi
145
146
if source_is_decimal /\ target_is_decimal then
147
return DECIMAL_CONVERSION(true, "", decimal_type, decimal_type)
148
fi
149
150
if target_is_decimal then
151
if !_decimal_neighbours.contains(source.symbol.qualified_name) then
152
return null
153
fi
154
155
return DECIMAL_CONVERSION(
156
false,
157
if _decimal_implicit_sources.contains(source.symbol.qualified_name) then
158
"op_Implicit"
159
else
160
"op_Explicit"
161
fi,
162
source,
163
decimal_type)
164
fi
165
166
if !_decimal_neighbours.contains(target.symbol.qualified_name) then
167
return null
168
fi
169
170
return DECIMAL_CONVERSION(false, "op_Explicit", decimal_type, target)
171
si
172
173
si
174
si