Skip to content
← Back

src/ir/value_converter.ghul

1
namespace IR is
2
use IO.Std;
3
4
use Semantic.Types.Type;
5
use NAMED_TYPE = Semantic.Types.NAMED;
6
7
class VALUE_CONVERTER(
8
_logger: Logging.Logger,
9
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
10
) is
11
_instructions: Collections.MAP[string, string]?;
12
13
// System.Decimal has no `conv.*` IL opcode. Conversion to or
14
// from decimal is a static call to `op_Implicit` / `op_Explicit`
15
// on the Decimal type; the overload is keyed by the other type.
16
// Populated by complete_initialization alongside _instructions.
17
_decimal_to_il: Collections.MAP[string, string];
18
_decimal_implicit_sources: Collections.SET[string];
19
20
super();
21
22
complete_initialization() is
23
if _instructions? then
24
return;
25
fi
26
27
_instructions = Collections.MAP[string, string]();
28
29
add_instruction(_innate_symbol_lookup.get_bool_type(), "conv.u1");
30
31
add_instruction(_innate_symbol_lookup.get_ubyte_type(), "conv.u1");
32
add_instruction(_innate_symbol_lookup.get_byte_type(), "conv.i1");
33
34
add_instruction(_innate_symbol_lookup.get_char_type(), "conv.u2");
35
36
add_instruction(_innate_symbol_lookup.get_ushort_type(), "conv.u2");
37
add_instruction(_innate_symbol_lookup.get_short_type(), "conv.i2");
38
39
add_instruction(_innate_symbol_lookup.get_uint_type(), "conv.u4");
40
add_instruction(_innate_symbol_lookup.get_int_type(), "conv.i4");
41
42
add_instruction(_innate_symbol_lookup.get_ulong_type(), "conv.u8");
43
add_instruction(_innate_symbol_lookup.get_long_type(), "conv.i8");
44
45
add_instruction(_innate_symbol_lookup.get_single_type(), "conv.r4");
46
add_instruction(_innate_symbol_lookup.get_double_type(), "conv.r8");
47
48
_decimal_to_il = Collections.MAP[string, string]();
49
_decimal_implicit_sources = Collections.SET[string]();
50
51
// char is omitted intentionally — this table hard-codes a
52
// CIL primitive keyword per neighbour ("int8", "float32", …)
53
// for a direct `call ... op_Implicit(<keyword>)` /
54
// `op_Explicit() -> <keyword>`, and char has no such
55
// keyword. System.Decimal does declare op_Implicit(char) and
56
// an op_Explicit returning char - TYPE_CASTER.find_user_defined_conversion
57
// reaches those through the general methodref machinery
58
// instead, so `cast char(d)` and `cast decimal(c)` still
59
// work even though this fast-path table doesn't cover them.
60
register_decimal_neighbour(_innate_symbol_lookup.get_byte_type(), "int8", true);
61
register_decimal_neighbour(_innate_symbol_lookup.get_ubyte_type(), "unsigned int8", true);
62
register_decimal_neighbour(_innate_symbol_lookup.get_short_type(), "int16", true);
63
register_decimal_neighbour(_innate_symbol_lookup.get_ushort_type(), "unsigned int16", true);
64
register_decimal_neighbour(_innate_symbol_lookup.get_int_type(), "int32", true);
65
register_decimal_neighbour(_innate_symbol_lookup.get_uint_type(), "unsigned int32", true);
66
register_decimal_neighbour(_innate_symbol_lookup.get_long_type(), "int64", true);
67
register_decimal_neighbour(_innate_symbol_lookup.get_ulong_type(), "unsigned int64", true);
68
register_decimal_neighbour(_innate_symbol_lookup.get_single_type(), "float32", false);
69
register_decimal_neighbour(_innate_symbol_lookup.get_double_type(), "float64", false);
70
si
71
72
add_instruction(type: Type, instruction: string) is
73
_instructions![type.symbol.qualified_name] = instruction;
74
si
75
76
register_decimal_neighbour(type: Type, il_name: string, is_implicit_to_decimal: bool) is
77
let qn = type.symbol.qualified_name;
78
_decimal_to_il[qn] = il_name;
79
if is_implicit_to_decimal then
80
_decimal_implicit_sources.add(qn);
81
fi
82
si
83
84
get_instruction(type: Type?) -> string? is
85
complete_initialization();
86
87
if !type? then
88
return null;
89
fi
90
91
let name = type.symbol.qualified_name;
92
93
let instructions = _instructions;
94
95
if instructions? /\ instructions.contains_key(name) then
96
return instructions[name];
97
fi
98
return null;
99
si
100
101
// Returns the IL line that converts a value of `source` on the
102
// stack to `target`, when at least one of them is decimal.
103
// Returns null if neither is decimal, or if the requested
104
// conversion has no Decimal::op_Implicit/op_Explicit overload.
105
// Returns "nop" for the decimal -> decimal identity case so
106
// callers can still emit a non-empty CONVERT (the stack stays
107
// typed as decimal either way).
108
get_decimal_conversion_instruction(source: Type?, target: Type?) -> string? is
109
complete_initialization();
110
111
if !source? \/ !target? then
112
return null;
113
fi
114
115
let decimal_type = _innate_symbol_lookup.get_decimal_type();
116
117
let source_is_decimal = source.matches(decimal_type);
118
let target_is_decimal = target.matches(decimal_type);
119
120
if !source_is_decimal /\ !target_is_decimal then
121
return null;
122
fi
123
124
if source_is_decimal /\ target_is_decimal then
125
return "nop";
126
fi
127
128
let decimal_il = "valuetype ['System.Runtime']'System'.'Decimal'";
129
130
if target_is_decimal then
131
if !_decimal_to_il.contains_key(source.symbol.qualified_name) then
132
return null;
133
fi
134
135
let source_il = _decimal_to_il[source.symbol.qualified_name];
136
137
let method_name =
138
if _decimal_implicit_sources.contains(source.symbol.qualified_name) then
139
"op_Implicit"
140
else
141
"op_Explicit"
142
fi;
143
144
return "call {decimal_il} {decimal_il}::{method_name}({source_il})";
145
fi
146
147
if !_decimal_to_il.contains_key(target.symbol.qualified_name) then
148
return null;
149
fi
150
151
let target_il = _decimal_to_il[target.symbol.qualified_name];
152
153
return "call {target_il} {decimal_il}::op_Explicit({decimal_il})";
154
si
155
si
156
si