mt-lang 0.3.24 → 0.3.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
- data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
- data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
- data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
- data/lib/milk_tea/core/bindings.rb +4 -9
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
- data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
- data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
- data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/statements.rb +20 -20
- data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
- data/lib/milk_tea/core/c_backend/type_system.rb +1 -1
- data/lib/milk_tea/core/c_backend.rb +3 -3
- data/lib/milk_tea/core/compile_time.rb +41 -70
- data/lib/milk_tea/core/control_flow/builder.rb +3 -3
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
- data/lib/milk_tea/core/control_flow/graph.rb +1 -1
- data/lib/milk_tea/core/control_flow/termination.rb +1 -1
- data/lib/milk_tea/core/cst.rb +16 -0
- data/lib/milk_tea/core/flow_scope.rb +13 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
- data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
- data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
- data/lib/milk_tea/core/lexer/indentation.rb +4 -4
- data/lib/milk_tea/core/lexer/numbers.rb +1 -1
- data/lib/milk_tea/core/lexer/strings.rb +4 -4
- data/lib/milk_tea/core/lexer/symbols.rb +3 -3
- data/lib/milk_tea/core/lexer/trivia.rb +4 -4
- data/lib/milk_tea/core/lexer.rb +17 -17
- data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
- data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
- data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
- data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
- data/lib/milk_tea/core/lowering/block.rb +982 -980
- data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
- data/lib/milk_tea/core/lowering/declarations.rb +175 -173
- data/lib/milk_tea/core/lowering/dyn.rb +171 -169
- data/lib/milk_tea/core/lowering/events.rb +946 -944
- data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
- data/lib/milk_tea/core/lowering/functions.rb +186 -184
- data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
- data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
- data/lib/milk_tea/core/lowering/proc.rb +355 -353
- data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
- data/lib/milk_tea/core/lowering/scans.rb +162 -168
- data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
- data/lib/milk_tea/core/lowering/utils.rb +964 -963
- data/lib/milk_tea/core/lowering.rb +22 -22
- data/lib/milk_tea/core/module_binder.rb +1 -1
- data/lib/milk_tea/core/module_loader.rb +2 -2
- data/lib/milk_tea/core/parser/attributes.rb +1 -1
- data/lib/milk_tea/core/parser/blocks.rb +2 -2
- data/lib/milk_tea/core/parser/declarations.rb +15 -15
- data/lib/milk_tea/core/parser/expressions.rb +20 -20
- data/lib/milk_tea/core/parser/recovery.rb +1 -1
- data/lib/milk_tea/core/parser/statements.rb +3 -3
- data/lib/milk_tea/core/parser/types.rb +4 -4
- data/lib/milk_tea/core/parser.rb +39 -21
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
- data/lib/milk_tea/core/types/layout.rb +3 -1
- data/lib/milk_tea/core/types/visitor.rb +351 -349
- data/lib/milk_tea/core.rb +0 -2
- data/lib/milk_tea/tooling/cli/commands/bindgen.rb +1 -1
- data/lib/milk_tea/tooling/cli/commands/check.rb +5 -5
- data/lib/milk_tea/tooling/cli/commands/debug.rb +3 -3
- data/lib/milk_tea/tooling/cli/commands/deps.rb +1 -1
- data/lib/milk_tea/tooling/cli/commands/emit_c.rb +2 -2
- data/lib/milk_tea/tooling/cli/commands/format.rb +6 -6
- data/lib/milk_tea/tooling/cli/commands/lint.rb +3 -3
- data/lib/milk_tea/tooling/cli/commands/lower.rb +3 -3
- data/lib/milk_tea/tooling/cli/commands/parse.rb +3 -3
- data/lib/milk_tea/tooling/cli/commands/test.rb +3 -3
- data/lib/milk_tea/tooling/cli/commands/toolchain.rb +1 -1
- data/lib/milk_tea/tooling/cli.rb +22 -34
- data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
- metadata +4 -5
- data/lib/milk_tea/core/cst_builder.rb +0 -19
- data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
- data/lib/milk_tea/core/token_stream.rb +0 -30
|
@@ -1,1232 +1,1233 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MilkTea
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def lower_range_match_condition(range_pattern, scrutinee_ir, bool_type, env:)
|
|
10
|
-
start_ir = lower_expression(range_pattern.start_expr, env:, expected_type: nil)
|
|
11
|
-
end_ir = lower_expression(range_pattern.end_expr, env:, expected_type: nil)
|
|
12
|
-
ge = IR::Binary.new(operator: ">=", left: scrutinee_ir, right: start_ir, type: bool_type)
|
|
13
|
-
le = IR::Binary.new(operator: "<=", left: scrutinee_ir, right: end_ir, type: bool_type)
|
|
14
|
-
IR::Binary.new(operator: "and", left: ge, right: le, type: bool_type)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def range_start_of(iterable)
|
|
18
|
-
iterable.start_expr
|
|
19
|
-
end
|
|
4
|
+
module Lowering
|
|
5
|
+
module Utils
|
|
6
|
+
def range_iterable?(expression)
|
|
7
|
+
range_expr?(expression)
|
|
8
|
+
end
|
|
20
9
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
def lower_range_match_condition(range_pattern, scrutinee_ir, bool_type, env:)
|
|
11
|
+
start_ir = lower_expression(range_pattern.start_expr, env:, expected_type: nil)
|
|
12
|
+
end_ir = lower_expression(range_pattern.end_expr, env:, expected_type: nil)
|
|
13
|
+
ge = IR::Binary.new(operator: ">=", left: scrutinee_ir, right: start_ir, type: bool_type)
|
|
14
|
+
le = IR::Binary.new(operator: "<=", left: scrutinee_ir, right: end_ir, type: bool_type)
|
|
15
|
+
IR::Binary.new(operator: "and", left: ge, right: le, type: bool_type)
|
|
16
|
+
end
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
def range_start_of(iterable)
|
|
19
|
+
iterable.start_expr
|
|
20
|
+
end
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
when AST::Call
|
|
32
|
-
pattern.callee
|
|
33
|
-
else
|
|
34
|
-
pattern
|
|
22
|
+
def range_end_of(iterable)
|
|
23
|
+
iterable.end_expr
|
|
35
24
|
end
|
|
36
|
-
callee.is_a?(AST::MemberAccess) ? callee.member : nil
|
|
37
|
-
end
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
26
|
+
def wildcard_arm_pattern?(expression)
|
|
27
|
+
expression.is_a?(AST::Identifier) && expression.name == "_"
|
|
28
|
+
end
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
def variant_match_arm_name_from_pattern(pattern)
|
|
31
|
+
callee = case pattern
|
|
32
|
+
when AST::Call
|
|
33
|
+
pattern.callee
|
|
34
|
+
else
|
|
35
|
+
pattern
|
|
36
|
+
end
|
|
37
|
+
callee.is_a?(AST::MemberAccess) ? callee.member : nil
|
|
38
|
+
end
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
def array_type?(type)
|
|
41
|
+
type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
|
|
42
|
+
type.arguments[1].is_a?(Types::LiteralTypeArg)
|
|
43
|
+
end
|
|
50
44
|
|
|
51
|
-
type
|
|
52
|
-
|
|
45
|
+
def simd_type?(type)
|
|
46
|
+
type.is_a?(Types::Simd)
|
|
47
|
+
end
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
end
|
|
49
|
+
def array_element_type(type)
|
|
50
|
+
return unless array_type?(type)
|
|
57
51
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
52
|
+
type.arguments.first
|
|
53
|
+
end
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
def array_to_span_compatible?(actual_type, expected_type)
|
|
56
|
+
array_type?(actual_type) && expected_type.is_a?(Types::Span) && array_element_type(actual_type) == expected_type.element_type
|
|
57
|
+
end
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
def cstr_trackable_type?(type)
|
|
60
|
+
type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
|
|
61
|
+
end
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
def struct_contains_string_field?(type)
|
|
64
|
+
return false unless type.is_a?(Types::Struct)
|
|
70
65
|
|
|
71
|
-
|
|
72
|
-
items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
|
|
66
|
+
type.fields.any? { |_name, field_type| cstr_trackable_type?(field_type) || struct_contains_string_field?(field_type) }
|
|
73
67
|
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def cstr_list_trackable_type?(type)
|
|
77
|
-
return false unless array_type?(type)
|
|
78
68
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
end
|
|
69
|
+
def reject_format_releases_for_assignment(cleanups, target_type)
|
|
70
|
+
return cleanups unless cstr_trackable_type?(target_type) || struct_contains_string_field?(target_type)
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
cleanups.reject do |items|
|
|
73
|
+
items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
def cstr_list_trackable_type?(type)
|
|
78
|
+
return false unless array_type?(type)
|
|
89
79
|
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
element_type = array_element_type(type)
|
|
81
|
+
element_type == @ctx.types.fetch("str") || element_type == @ctx.types.fetch("cstr")
|
|
82
|
+
end
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
def str_buffer_to_span_compatible?(actual_type, expected_type)
|
|
85
|
+
str_buffer_type?(actual_type) && expected_type.is_a?(Types::Span) && expected_type.element_type == @ctx.types.fetch("char")
|
|
86
|
+
end
|
|
96
87
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
|
|
100
|
-
end
|
|
88
|
+
def array_length(type)
|
|
89
|
+
return unless array_type?(type)
|
|
101
90
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
91
|
+
type.arguments[1].value
|
|
92
|
+
end
|
|
105
93
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
def char_array_text_type?(type)
|
|
95
|
+
array_type?(type) && array_element_type(type) == @ctx.types.fetch("char")
|
|
96
|
+
end
|
|
109
97
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
true
|
|
114
|
-
when AST::MemberAccess, AST::IndexAccess
|
|
115
|
-
addressable_storage_expression?(expression.receiver)
|
|
116
|
-
when AST::Call
|
|
117
|
-
read_call?(expression)
|
|
118
|
-
else
|
|
119
|
-
false
|
|
98
|
+
def str_buffer_type?(type)
|
|
99
|
+
type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
|
|
100
|
+
type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
|
|
120
101
|
end
|
|
121
|
-
end
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
103
|
+
def str_buffer_capacity(type)
|
|
104
|
+
type.arguments.first.value
|
|
105
|
+
end
|
|
126
106
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return pointee_type(handle_type) if pointer_type?(handle_type)
|
|
107
|
+
def str_buffer_storage_capacity(type)
|
|
108
|
+
str_buffer_capacity(type) + 1
|
|
109
|
+
end
|
|
131
110
|
|
|
132
|
-
|
|
133
|
-
|
|
111
|
+
def addressable_storage_expression?(expression)
|
|
112
|
+
case expression
|
|
113
|
+
when AST::Identifier
|
|
114
|
+
true
|
|
115
|
+
when AST::MemberAccess, AST::IndexAccess
|
|
116
|
+
addressable_storage_expression?(expression.receiver)
|
|
117
|
+
when AST::Call
|
|
118
|
+
read_call?(expression)
|
|
119
|
+
else
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
end
|
|
134
123
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
def read_call?(expression)
|
|
125
|
+
expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read"
|
|
126
|
+
end
|
|
138
127
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return
|
|
128
|
+
def infer_value_type(handle_expression, env:)
|
|
129
|
+
handle_type = infer_expression_type(handle_expression, env:)
|
|
130
|
+
return referenced_type(handle_type) if ref_type?(handle_type)
|
|
131
|
+
return pointee_type(handle_type) if pointer_type?(handle_type)
|
|
142
132
|
|
|
143
|
-
|
|
133
|
+
raise LoweringError.new("read expects ref[...] or ptr[...], got #{handle_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
144
134
|
end
|
|
145
135
|
|
|
146
|
-
|
|
147
|
-
|
|
136
|
+
def infer_method_receiver_type(receiver_expression, env:, member_name: nil)
|
|
137
|
+
receiver_type = infer_expression_type(receiver_expression, env:)
|
|
138
|
+
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
148
139
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return pointee_type(receiver_type) if pointer_type?(receiver_type)
|
|
140
|
+
if pointer_type?(receiver_type)
|
|
141
|
+
dispatch_receiver_type = method_dispatch_receiver_type(receiver_type)
|
|
142
|
+
return receiver_type if member_name && (@method_definitions.key?([receiver_type, member_name]) || @method_definitions.key?([dispatch_receiver_type, member_name]) || @method_definitions.key?([receiver_type, "static:#{member_name}"]) || @method_definitions.key?([dispatch_receiver_type, "static:#{member_name}"]))
|
|
153
143
|
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
return pointee_type(receiver_type)
|
|
145
|
+
end
|
|
156
146
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
end
|
|
147
|
+
receiver_type
|
|
148
|
+
end
|
|
160
149
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
150
|
+
def infer_field_receiver_type(receiver_expression, env:)
|
|
151
|
+
receiver_type = infer_expression_type(receiver_expression, env:)
|
|
152
|
+
return referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
153
|
+
return pointee_type(receiver_type) if pointer_type?(receiver_type)
|
|
164
154
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
end
|
|
155
|
+
receiver_type
|
|
156
|
+
end
|
|
168
157
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
|
|
172
|
-
else
|
|
173
|
-
data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
|
|
174
|
-
IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
|
|
158
|
+
def collection_loop_type(type)
|
|
159
|
+
super
|
|
175
160
|
end
|
|
176
|
-
end
|
|
177
161
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
|
|
181
|
-
else
|
|
182
|
-
IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
|
|
162
|
+
def collection_loop_binding_type(iterable_type, element_type)
|
|
163
|
+
super
|
|
183
164
|
end
|
|
184
|
-
end
|
|
185
165
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
AST::Call.new(
|
|
190
|
-
callee: AST::Identifier.new(name: "fatal"),
|
|
191
|
-
arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
|
|
192
|
-
),
|
|
193
|
-
env:,
|
|
194
|
-
expected_type: @ctx.types.fetch("void"),
|
|
195
|
-
),
|
|
196
|
-
)
|
|
197
|
-
end
|
|
166
|
+
def collection_loop_ref_element_type?(type)
|
|
167
|
+
super
|
|
168
|
+
end
|
|
198
169
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if start_type != stop_type
|
|
206
|
-
if start_expr.is_a?(AST::IntegerLiteral)
|
|
207
|
-
start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
|
|
208
|
-
elsif stop_expr.is_a?(AST::IntegerLiteral)
|
|
209
|
-
stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
|
|
170
|
+
def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
|
|
171
|
+
if array_type?(iterable_type)
|
|
172
|
+
IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
|
|
173
|
+
else
|
|
174
|
+
data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
|
|
175
|
+
IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
|
|
210
176
|
end
|
|
211
177
|
end
|
|
212
178
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
type.is_a?(Types::Primitive) && type.integer?
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def infer_index_result_type(receiver_type, index_type)
|
|
223
|
-
raise LoweringError.new("index must be an integer type, got #{index_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless integer_type?(index_type)
|
|
224
|
-
|
|
225
|
-
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
226
|
-
|
|
227
|
-
if array_type?(receiver_type)
|
|
228
|
-
return array_element_type(receiver_type)
|
|
179
|
+
def collection_loop_stop_value(iterable_ref, iterable_type)
|
|
180
|
+
if array_type?(iterable_type)
|
|
181
|
+
IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
|
|
182
|
+
else
|
|
183
|
+
IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
|
|
184
|
+
end
|
|
229
185
|
end
|
|
230
186
|
|
|
231
|
-
|
|
232
|
-
|
|
187
|
+
def lower_fatal_statement(message, env:)
|
|
188
|
+
IR::ExpressionStmt.new(
|
|
189
|
+
expression: lower_expression(
|
|
190
|
+
AST::Call.new(
|
|
191
|
+
callee: AST::Identifier.new(name: "fatal"),
|
|
192
|
+
arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
|
|
193
|
+
),
|
|
194
|
+
env:,
|
|
195
|
+
expected_type: @ctx.types.fetch("void"),
|
|
196
|
+
),
|
|
197
|
+
)
|
|
233
198
|
end
|
|
234
199
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
200
|
+
def infer_range_loop_type(expression, env:)
|
|
201
|
+
start_expr = range_start_of(expression)
|
|
202
|
+
stop_expr = range_end_of(expression)
|
|
203
|
+
start_type = infer_expression_type(start_expr, env:)
|
|
204
|
+
stop_type = infer_expression_type(stop_expr, env:)
|
|
205
|
+
|
|
206
|
+
if start_type != stop_type
|
|
207
|
+
if start_expr.is_a?(AST::IntegerLiteral)
|
|
208
|
+
start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
|
|
209
|
+
elsif stop_expr.is_a?(AST::IntegerLiteral)
|
|
210
|
+
stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
raise LoweringError.new("range bounds must use matching integer types, got #{start_type} and #{stop_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless start_type == stop_type
|
|
238
215
|
|
|
239
|
-
|
|
240
|
-
return receiver_type.element_type
|
|
216
|
+
start_type
|
|
241
217
|
end
|
|
242
218
|
|
|
243
|
-
|
|
244
|
-
|
|
219
|
+
def integer_type?(type)
|
|
220
|
+
type.is_a?(Types::Primitive) && type.integer?
|
|
245
221
|
end
|
|
246
222
|
|
|
247
|
-
|
|
248
|
-
|
|
223
|
+
def infer_index_result_type(receiver_type, index_type)
|
|
224
|
+
raise LoweringError.new("index must be an integer type, got #{index_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless integer_type?(index_type)
|
|
249
225
|
|
|
250
|
-
|
|
251
|
-
super
|
|
252
|
-
end
|
|
226
|
+
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
253
227
|
|
|
228
|
+
if array_type?(receiver_type)
|
|
229
|
+
return array_element_type(receiver_type)
|
|
230
|
+
end
|
|
254
231
|
|
|
255
|
-
|
|
256
|
-
|
|
232
|
+
if receiver_type.is_a?(Types::Span)
|
|
233
|
+
return receiver_type.element_type
|
|
234
|
+
end
|
|
257
235
|
|
|
258
|
-
|
|
259
|
-
|
|
236
|
+
if receiver_type.is_a?(Types::SoA)
|
|
237
|
+
return receiver_type.element_type
|
|
238
|
+
end
|
|
260
239
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
when Types::Nullable
|
|
264
|
-
stored_ref_supported_type?(type.base, visited)
|
|
265
|
-
when Types::GenericInstance
|
|
266
|
-
if ref_type?(type)
|
|
267
|
-
lt = ref_lifetime(type)
|
|
268
|
-
return !!lt # lifetime-ref is supported by default; bare ref is not
|
|
240
|
+
if simd_type?(receiver_type)
|
|
241
|
+
return receiver_type.element_type
|
|
269
242
|
end
|
|
270
243
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
when Types::StructInstance, Types::VariantInstance
|
|
277
|
-
type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
|
|
278
|
-
when Types::Proc, Types::Function
|
|
279
|
-
callable_param_ref_supported?(type)
|
|
280
|
-
else
|
|
281
|
-
!contains_ref_type?(type)
|
|
244
|
+
if pointer_type?(receiver_type)
|
|
245
|
+
return pointee_type(receiver_type)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
282
249
|
end
|
|
283
|
-
end
|
|
284
250
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
251
|
+
def contains_type_var?(type)
|
|
252
|
+
super
|
|
253
|
+
end
|
|
288
254
|
|
|
289
|
-
def with_analysis_context(analysis)
|
|
290
|
-
saved = @ctx.save
|
|
291
|
-
@ctx.install(analysis)
|
|
292
|
-
@ctx.module_prefix = module_c_prefix(@ctx.module_name)
|
|
293
|
-
yield
|
|
294
|
-
ensure
|
|
295
|
-
@ctx.restore(saved)
|
|
296
|
-
end
|
|
297
255
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
256
|
+
def stored_ref_supported_type?(type, visited = {})
|
|
257
|
+
return true unless type
|
|
258
|
+
|
|
259
|
+
visit_key = [type.class, type.object_id]
|
|
260
|
+
return true if visited[visit_key]
|
|
261
|
+
|
|
262
|
+
visited[visit_key] = true
|
|
263
|
+
case type
|
|
264
|
+
when Types::Nullable
|
|
265
|
+
stored_ref_supported_type?(type.base, visited)
|
|
266
|
+
when Types::GenericInstance
|
|
267
|
+
if ref_type?(type)
|
|
268
|
+
lt = ref_lifetime(type)
|
|
269
|
+
return !!lt # lifetime-ref is supported by default; bare ref is not
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
type.arguments.all? { |argument| argument.is_a?(Types::LiteralTypeArg) || stored_ref_supported_type?(argument, visited) }
|
|
273
|
+
when Types::Span
|
|
274
|
+
stored_ref_supported_type?(type.element_type, visited)
|
|
275
|
+
when Types::Task
|
|
276
|
+
stored_ref_supported_type?(type.result_type, visited)
|
|
277
|
+
when Types::StructInstance, Types::VariantInstance
|
|
278
|
+
type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
|
|
279
|
+
when Types::Proc, Types::Function
|
|
280
|
+
callable_param_ref_supported?(type)
|
|
281
|
+
else
|
|
282
|
+
!contains_ref_type?(type)
|
|
283
|
+
end
|
|
301
284
|
end
|
|
302
285
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{
|
|
306
|
-
type: binding.type,
|
|
307
|
-
storage_type: binding.storage_type,
|
|
308
|
-
linkage_name: value_c_name(name),
|
|
309
|
-
mutable: binding.mutable,
|
|
310
|
-
pointer: false,
|
|
311
|
-
cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
|
|
312
|
-
cstr_list_backed: false,
|
|
313
|
-
const_value: binding.const_value,
|
|
314
|
-
}
|
|
286
|
+
def pointer_to(type)
|
|
287
|
+
Types::Registry.generic_instance("ptr", [type])
|
|
315
288
|
end
|
|
316
|
-
end
|
|
317
289
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
290
|
+
def with_analysis_context(analysis)
|
|
291
|
+
saved = @ctx.save
|
|
292
|
+
@ctx.install(analysis)
|
|
293
|
+
@ctx.module_prefix = module_c_prefix(@ctx.module_name)
|
|
294
|
+
yield
|
|
295
|
+
ensure
|
|
296
|
+
@ctx.restore(saved)
|
|
321
297
|
end
|
|
322
298
|
|
|
323
|
-
|
|
324
|
-
|
|
299
|
+
def lookup_value(name, env)
|
|
300
|
+
env[:scopes].reverse_each do |scope|
|
|
301
|
+
return scope[name] if scope.key?(name)
|
|
302
|
+
end
|
|
325
303
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
304
|
+
if @ctx.values.key?(name)
|
|
305
|
+
binding = @ctx.values.fetch(name)
|
|
306
|
+
{
|
|
307
|
+
type: binding.type,
|
|
308
|
+
storage_type: binding.storage_type,
|
|
309
|
+
linkage_name: value_c_name(name),
|
|
310
|
+
mutable: binding.mutable,
|
|
311
|
+
pointer: false,
|
|
312
|
+
cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
|
|
313
|
+
cstr_list_backed: false,
|
|
314
|
+
const_value: binding.const_value,
|
|
315
|
+
}
|
|
338
316
|
end
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return
|
|
344
|
-
lowered = lower_compile_time_literal(field_value, field_type)
|
|
345
|
-
return nil unless lowered
|
|
346
|
-
IR::AggregateField.new(name:, value: lowered)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def lower_static_storage_initializer(expression, env:, expected_type: nil)
|
|
320
|
+
if expected_type && (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), expected_type))
|
|
321
|
+
return literal
|
|
347
322
|
end
|
|
348
|
-
|
|
323
|
+
|
|
324
|
+
lower_expression(rewrite_static_storage_initializer(expression), env:, expected_type: expected_type)
|
|
349
325
|
end
|
|
350
326
|
|
|
351
|
-
|
|
352
|
-
|
|
327
|
+
def lower_compile_time_literal(value, type)
|
|
328
|
+
case value
|
|
329
|
+
when true, false
|
|
330
|
+
return IR::BooleanLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.boolean?
|
|
331
|
+
when Integer
|
|
332
|
+
return IR::IntegerLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.integer?
|
|
333
|
+
return IR::FloatLiteral.new(value: value.to_f, type:) if type.is_a?(Types::Primitive) && type.float?
|
|
334
|
+
when Float
|
|
335
|
+
return IR::FloatLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.float?
|
|
336
|
+
when String
|
|
337
|
+
if type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
|
|
338
|
+
return IR::StringLiteral.new(value:, type:, cstring: type == @ctx.types.fetch("cstr"))
|
|
339
|
+
end
|
|
340
|
+
when Hash
|
|
341
|
+
return nil unless type.is_a?(Types::Struct)
|
|
342
|
+
fields = value.map do |name, field_value|
|
|
343
|
+
field_type = type.field(name)
|
|
344
|
+
return nil unless field_type
|
|
345
|
+
lowered = lower_compile_time_literal(field_value, field_type)
|
|
346
|
+
return nil unless lowered
|
|
347
|
+
IR::AggregateField.new(name:, value: lowered)
|
|
348
|
+
end
|
|
349
|
+
return IR::AggregateLiteral.new(type:, fields:)
|
|
350
|
+
end
|
|
353
351
|
|
|
354
|
-
def compile_time_builtin_function_type(name, arguments, env)
|
|
355
|
-
return_type = case name
|
|
356
|
-
when "field_of"
|
|
357
|
-
@ctx.types.fetch("field_handle")
|
|
358
|
-
when "callable_of"
|
|
359
|
-
@ctx.types.fetch("callable_handle")
|
|
360
|
-
when "has_attribute"
|
|
361
|
-
@ctx.types.fetch("bool")
|
|
362
|
-
when "attribute_of"
|
|
363
|
-
@ctx.types.fetch("attribute_handle")
|
|
364
|
-
else
|
|
365
352
|
nil
|
|
366
353
|
end
|
|
367
|
-
raise LoweringError.new("unsupported compile-time builtin #{name}", line: 0, column: 0, path: @ctx.current_analysis_path) unless return_type
|
|
368
354
|
|
|
369
|
-
|
|
370
|
-
|
|
355
|
+
def compile_time_builtin_function_type(name, arguments, env)
|
|
356
|
+
return_type = case name
|
|
357
|
+
when "field_of"
|
|
358
|
+
@ctx.types.fetch("field_handle")
|
|
359
|
+
when "callable_of"
|
|
360
|
+
@ctx.types.fetch("callable_handle")
|
|
361
|
+
when "has_attribute"
|
|
362
|
+
@ctx.types.fetch("bool")
|
|
363
|
+
when "attribute_of"
|
|
364
|
+
@ctx.types.fetch("attribute_handle")
|
|
365
|
+
else
|
|
366
|
+
nil
|
|
367
|
+
end
|
|
368
|
+
raise LoweringError.new("unsupported compile-time builtin #{name}", line: 0, column: 0, path: @ctx.current_analysis_path) unless return_type
|
|
371
369
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
end
|
|
370
|
+
Types::Registry.function(name, params: [], return_type: return_type)
|
|
371
|
+
end
|
|
375
372
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
binding = @ctx.values[expression.name]
|
|
380
|
-
if binding&.kind == :const
|
|
381
|
-
declaration = const_declaration_for_module(@ctx.module_name, expression.name)
|
|
382
|
-
return rewrite_static_storage_initializer(declaration.value)
|
|
383
|
-
end
|
|
373
|
+
def compile_time_builtin_specialization_function_type(callee)
|
|
374
|
+
Types::Registry.function("attribute_arg", params: [], return_type: resolve_type_ref(callee.arguments.fetch(0).value))
|
|
375
|
+
end
|
|
384
376
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
377
|
+
def rewrite_static_storage_initializer(expression)
|
|
378
|
+
case expression
|
|
379
|
+
when AST::Identifier
|
|
380
|
+
binding = @ctx.values[expression.name]
|
|
381
|
+
if binding&.kind == :const
|
|
382
|
+
declaration = const_declaration_for_module(@ctx.module_name, expression.name)
|
|
383
|
+
return rewrite_static_storage_initializer(declaration.value)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
expression
|
|
387
|
+
when AST::MemberAccess
|
|
388
|
+
if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
|
|
389
|
+
imported_module = @ctx.imports.fetch(expression.receiver.name)
|
|
390
|
+
if (binding = imported_module.values[expression.member])&.kind == :const
|
|
391
|
+
imported_analysis = analysis_for_module(imported_module.name)
|
|
392
|
+
declaration = const_declaration_for_module(imported_module.name, expression.member)
|
|
393
|
+
return with_analysis_context(imported_analysis) do
|
|
394
|
+
rewrite_static_storage_initializer(declaration.value)
|
|
395
|
+
end
|
|
394
396
|
end
|
|
395
397
|
end
|
|
396
|
-
end
|
|
397
398
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
399
|
+
AST::MemberAccess.new(
|
|
400
|
+
receiver: rewrite_static_storage_initializer(expression.receiver),
|
|
401
|
+
member: expression.member,
|
|
402
|
+
)
|
|
403
|
+
when AST::UnaryOp
|
|
404
|
+
AST::UnaryOp.new(operator: expression.operator, operand: rewrite_static_storage_initializer(expression.operand))
|
|
405
|
+
when AST::BinaryOp
|
|
406
|
+
AST::BinaryOp.new(
|
|
407
|
+
operator: expression.operator,
|
|
408
|
+
left: rewrite_static_storage_initializer(expression.left),
|
|
409
|
+
right: rewrite_static_storage_initializer(expression.right),
|
|
410
|
+
)
|
|
411
|
+
when AST::IfExpr
|
|
412
|
+
AST::IfExpr.new(
|
|
413
|
+
condition: rewrite_static_storage_initializer(expression.condition),
|
|
414
|
+
then_expression: rewrite_static_storage_initializer(expression.then_expression),
|
|
415
|
+
else_expression: rewrite_static_storage_initializer(expression.else_expression),
|
|
416
|
+
)
|
|
417
|
+
when AST::UnsafeExpr
|
|
418
|
+
AST::UnsafeExpr.new(expression: rewrite_static_storage_initializer(expression.expression))
|
|
419
|
+
when AST::Call
|
|
420
|
+
AST::Call.new(
|
|
421
|
+
callee: rewrite_static_storage_initializer(expression.callee),
|
|
422
|
+
arguments: expression.arguments.map do |argument|
|
|
423
|
+
AST::Argument.new(name: argument.name, value: rewrite_static_storage_initializer(argument.value))
|
|
424
|
+
end,
|
|
425
|
+
)
|
|
426
|
+
when AST::Specialization
|
|
427
|
+
AST::Specialization.new(
|
|
428
|
+
callee: rewrite_static_storage_initializer(expression.callee),
|
|
429
|
+
arguments: expression.arguments.map { |argument| AST::TypeArgument.new(value: argument.value) },
|
|
430
|
+
)
|
|
431
|
+
else
|
|
432
|
+
expression
|
|
433
|
+
end
|
|
432
434
|
end
|
|
433
|
-
end
|
|
434
435
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
def callable_type?(type)
|
|
440
|
-
type.is_a?(Types::Function) || proc_type?(type)
|
|
441
|
-
end
|
|
436
|
+
def local_binding(type:, linkage_name:, mutable:, pointer:, storage_type: nil, projection: nil, cstr_backed: false, cstr_list_backed: false, const_value: nil)
|
|
437
|
+
{ type:, storage_type: storage_type || type, linkage_name:, mutable:, pointer:, projection:, cstr_backed:, cstr_list_backed:, const_value: }
|
|
438
|
+
end
|
|
442
439
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
case type
|
|
447
|
-
when Types::Proc
|
|
448
|
-
true
|
|
449
|
-
when Types::Struct, Types::StructInstance
|
|
450
|
-
visited.add(type.object_id)
|
|
451
|
-
type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
|
|
452
|
-
when Types::Nullable
|
|
453
|
-
contains_proc_storage_type?(type.base, visited)
|
|
454
|
-
else
|
|
455
|
-
false
|
|
440
|
+
def callable_type?(type)
|
|
441
|
+
type.is_a?(Types::Function) || proc_type?(type)
|
|
456
442
|
end
|
|
457
|
-
end
|
|
458
443
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
type.arms.each_value.any? do |arm_fields|
|
|
473
|
-
arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
|
|
444
|
+
def contains_proc_storage_type?(type, visited = Set.new)
|
|
445
|
+
return false if visited.include?(type.object_id)
|
|
446
|
+
|
|
447
|
+
case type
|
|
448
|
+
when Types::Proc
|
|
449
|
+
true
|
|
450
|
+
when Types::Struct, Types::StructInstance
|
|
451
|
+
visited.add(type.object_id)
|
|
452
|
+
type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
|
|
453
|
+
when Types::Nullable
|
|
454
|
+
contains_proc_storage_type?(type.base, visited)
|
|
455
|
+
else
|
|
456
|
+
false
|
|
474
457
|
end
|
|
475
|
-
when Types::GenericInstance
|
|
476
|
-
type.arguments.any? { |arg| contains_task_type?(arg, visited) }
|
|
477
|
-
when Types::Nullable
|
|
478
|
-
contains_task_type?(type.base, visited)
|
|
479
|
-
else
|
|
480
|
-
false
|
|
481
458
|
end
|
|
482
|
-
end
|
|
483
459
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
460
|
+
def contains_task_type?(type, visited = Set.new)
|
|
461
|
+
return false if visited.include?(type.object_id)
|
|
462
|
+
|
|
463
|
+
case type
|
|
464
|
+
when Types::Task
|
|
465
|
+
true
|
|
466
|
+
when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
|
|
467
|
+
visited.add(type.object_id)
|
|
468
|
+
type.fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
|
|
469
|
+
when Types::VariantInstance
|
|
470
|
+
type.arguments.any? { |arg| contains_task_type?(arg, visited) }
|
|
471
|
+
when Types::Variant, Types::GenericVariantDefinition
|
|
472
|
+
visited.add(type.object_id)
|
|
473
|
+
type.arms.each_value.any? do |arm_fields|
|
|
474
|
+
arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
|
|
475
|
+
end
|
|
476
|
+
when Types::GenericInstance
|
|
477
|
+
type.arguments.any? { |arg| contains_task_type?(arg, visited) }
|
|
478
|
+
when Types::Nullable
|
|
479
|
+
contains_task_type?(type.base, visited)
|
|
480
|
+
else
|
|
481
|
+
false
|
|
482
|
+
end
|
|
483
|
+
end
|
|
487
484
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
|
|
492
|
-
return_type: proc_type.return_type,
|
|
493
|
-
)
|
|
494
|
-
end
|
|
485
|
+
def proc_env_pointer_type
|
|
486
|
+
@proc_env_pointer_type ||= pointer_to(@ctx.types.fetch("void"))
|
|
487
|
+
end
|
|
495
488
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
489
|
+
def proc_invoke_function_type(proc_type)
|
|
490
|
+
Types::Registry.function(
|
|
491
|
+
nil,
|
|
492
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
|
|
493
|
+
return_type: proc_type.return_type,
|
|
494
|
+
)
|
|
495
|
+
end
|
|
503
496
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
497
|
+
def proc_release_function_type
|
|
498
|
+
@proc_release_function_type ||= Types::Registry.function(
|
|
499
|
+
nil,
|
|
500
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type)],
|
|
501
|
+
return_type: @ctx.types.fetch("void"),
|
|
502
|
+
)
|
|
503
|
+
end
|
|
511
504
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
505
|
+
def proc_retain_function_type
|
|
506
|
+
@proc_retain_function_type ||= Types::Registry.function(
|
|
507
|
+
nil,
|
|
508
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type)],
|
|
509
|
+
return_type: @ctx.types.fetch("void"),
|
|
510
|
+
)
|
|
511
|
+
end
|
|
515
512
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return scope unless scope.is_a?(FlowScope)
|
|
513
|
+
def fresh_proc_symbol
|
|
514
|
+
@synthetic_proc_counter += 1
|
|
519
515
|
end
|
|
520
516
|
|
|
521
|
-
|
|
522
|
-
|
|
517
|
+
def current_actual_scope(scopes)
|
|
518
|
+
scopes.reverse_each do |scope|
|
|
519
|
+
return scope unless scope.is_a?(FlowScope)
|
|
520
|
+
end
|
|
523
521
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
|
|
527
|
-
updated
|
|
528
|
-
end
|
|
522
|
+
raise LoweringError.new("missing lexical scope", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
523
|
+
end
|
|
529
524
|
|
|
530
|
-
|
|
531
|
-
|
|
525
|
+
def env_with_refinements(env, refinements)
|
|
526
|
+
updated = env.dup
|
|
527
|
+
updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
|
|
528
|
+
updated
|
|
529
|
+
end
|
|
532
530
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
merged_refinements = merge_refinements(merged_refinements, refinements)
|
|
536
|
-
flow_scope = FlowScope.new
|
|
531
|
+
def scopes_with_refinements(scopes, refinements)
|
|
532
|
+
return scopes if refinements.nil? || refinements.empty?
|
|
537
533
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
534
|
+
base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
|
|
535
|
+
merged_refinements = scopes.last.is_a?(FlowScope) ? scopes.last.each_with_object({}) { |(name, binding), result| result[name] = binding[:type] } : {}
|
|
536
|
+
merged_refinements = merge_refinements(merged_refinements, refinements)
|
|
537
|
+
flow_scope = FlowScope.new
|
|
541
538
|
|
|
542
|
-
|
|
543
|
-
|
|
539
|
+
merged_refinements.each do |name, refined_type|
|
|
540
|
+
binding = lookup_value(name, { scopes: base_scopes })
|
|
541
|
+
next unless binding
|
|
544
542
|
|
|
545
|
-
|
|
543
|
+
flow_scope[name] = binding.merge(type: refined_type)
|
|
544
|
+
end
|
|
546
545
|
|
|
547
|
-
|
|
548
|
-
end
|
|
546
|
+
return base_scopes if flow_scope.empty?
|
|
549
547
|
|
|
550
|
-
|
|
551
|
-
merged = existing.dup
|
|
552
|
-
incoming.each do |name, refined_type|
|
|
553
|
-
if merged.key?(name) && merged[name] != refined_type
|
|
554
|
-
merged.delete(name)
|
|
555
|
-
else
|
|
556
|
-
merged[name] = refined_type
|
|
557
|
-
end
|
|
548
|
+
base_scopes + [flow_scope]
|
|
558
549
|
end
|
|
559
550
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
when AST::BinaryOp
|
|
568
|
-
case expression.operator
|
|
569
|
-
when "and"
|
|
570
|
-
if truthy
|
|
571
|
-
left_truthy = flow_refinements(expression.left, truthy: true, env:)
|
|
572
|
-
right_env = env_with_refinements(env, left_truthy)
|
|
573
|
-
right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
|
|
574
|
-
return merge_refinements(left_truthy, right_truthy)
|
|
551
|
+
def merge_refinements(existing, incoming)
|
|
552
|
+
merged = existing.dup
|
|
553
|
+
incoming.each do |name, refined_type|
|
|
554
|
+
if merged.key?(name) && merged[name] != refined_type
|
|
555
|
+
merged.delete(name)
|
|
556
|
+
else
|
|
557
|
+
merged[name] = refined_type
|
|
575
558
|
end
|
|
576
|
-
when "or"
|
|
577
|
-
unless truthy
|
|
578
|
-
left_falsy = flow_refinements(expression.left, truthy: false, env:)
|
|
579
|
-
right_env = env_with_refinements(env, left_falsy)
|
|
580
|
-
right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
|
|
581
|
-
return merge_refinements(left_falsy, right_falsy)
|
|
582
|
-
end
|
|
583
|
-
when "==", "!="
|
|
584
|
-
return null_test_refinements(expression, truthy:, env:)
|
|
585
559
|
end
|
|
586
|
-
end
|
|
587
|
-
|
|
588
|
-
{}
|
|
589
|
-
end
|
|
590
560
|
|
|
591
|
-
|
|
592
|
-
identifier_expression = nil
|
|
593
|
-
if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
|
|
594
|
-
identifier_expression = expression.left
|
|
595
|
-
elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
|
|
596
|
-
identifier_expression = expression.right
|
|
597
|
-
else
|
|
598
|
-
return {}
|
|
561
|
+
merged
|
|
599
562
|
end
|
|
600
563
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
564
|
+
def flow_refinements(expression, truthy:, env:)
|
|
565
|
+
case expression
|
|
566
|
+
when AST::UnaryOp
|
|
567
|
+
return flow_refinements(expression.operand, truthy: !truthy, env:) if expression.operator == "not"
|
|
568
|
+
when AST::BinaryOp
|
|
569
|
+
case expression.operator
|
|
570
|
+
when "and"
|
|
571
|
+
if truthy
|
|
572
|
+
left_truthy = flow_refinements(expression.left, truthy: true, env:)
|
|
573
|
+
right_env = env_with_refinements(env, left_truthy)
|
|
574
|
+
right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
|
|
575
|
+
return merge_refinements(left_truthy, right_truthy)
|
|
576
|
+
end
|
|
577
|
+
when "or"
|
|
578
|
+
unless truthy
|
|
579
|
+
left_falsy = flow_refinements(expression.left, truthy: false, env:)
|
|
580
|
+
right_env = env_with_refinements(env, left_falsy)
|
|
581
|
+
right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
|
|
582
|
+
return merge_refinements(left_falsy, right_falsy)
|
|
583
|
+
end
|
|
584
|
+
when "==", "!="
|
|
585
|
+
return null_test_refinements(expression, truthy:, env:)
|
|
586
|
+
end
|
|
587
|
+
end
|
|
608
588
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
end
|
|
589
|
+
{}
|
|
590
|
+
end
|
|
612
591
|
|
|
613
|
-
|
|
614
|
-
|
|
592
|
+
def null_test_refinements(expression, truthy:, env:)
|
|
593
|
+
identifier_expression = nil
|
|
594
|
+
if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
|
|
595
|
+
identifier_expression = expression.left
|
|
596
|
+
elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
|
|
597
|
+
identifier_expression = expression.right
|
|
598
|
+
else
|
|
599
|
+
return {}
|
|
600
|
+
end
|
|
615
601
|
|
|
616
|
-
|
|
617
|
-
|
|
602
|
+
binding = lookup_value(identifier_expression.name, env)
|
|
603
|
+
return {} unless binding && binding[:storage_type].is_a?(Types::Nullable)
|
|
618
604
|
|
|
619
|
-
|
|
620
|
-
|
|
605
|
+
null_result = expression.operator == "==" ? truthy : !truthy
|
|
606
|
+
refined_type = null_result ? null_type : binding[:storage_type].base
|
|
607
|
+
{ identifier_expression.name => refined_type }
|
|
621
608
|
end
|
|
622
609
|
|
|
623
|
-
|
|
624
|
-
|
|
610
|
+
def cfg_block_always_terminates?(statements)
|
|
611
|
+
ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
|
|
625
612
|
end
|
|
626
613
|
|
|
627
|
-
|
|
628
|
-
|
|
614
|
+
def conditional_common_type(then_type, else_type)
|
|
615
|
+
return then_type if then_type == else_type
|
|
629
616
|
|
|
630
|
-
|
|
631
|
-
|
|
617
|
+
numeric_type = common_numeric_type(then_type, else_type)
|
|
618
|
+
return numeric_type if numeric_type
|
|
632
619
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
|
|
637
|
-
return true if common_numeric_type(actual_type, expected_type) == expected_type
|
|
620
|
+
if (nullable_type = conditional_null_common_type(then_type, else_type))
|
|
621
|
+
return nullable_type
|
|
622
|
+
end
|
|
638
623
|
|
|
639
|
-
|
|
640
|
-
|
|
624
|
+
if (nullable_type = conditional_null_common_type(else_type, then_type))
|
|
625
|
+
return nullable_type
|
|
626
|
+
end
|
|
641
627
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
end
|
|
628
|
+
return then_type if then_type.is_a?(Types::Nullable) && else_type == then_type.base
|
|
629
|
+
return else_type if else_type.is_a?(Types::Nullable) && then_type == else_type.base
|
|
645
630
|
|
|
646
|
-
|
|
647
|
-
|
|
631
|
+
nil
|
|
632
|
+
end
|
|
648
633
|
|
|
649
|
-
|
|
650
|
-
return
|
|
634
|
+
def if_expression_branch_compatible?(actual_type, expected_type)
|
|
635
|
+
return true if actual_type == expected_type
|
|
636
|
+
return true if null_assignable_to?(actual_type, expected_type)
|
|
637
|
+
return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
|
|
638
|
+
return true if common_numeric_type(actual_type, expected_type) == expected_type
|
|
651
639
|
|
|
652
|
-
|
|
640
|
+
false
|
|
653
641
|
end
|
|
654
642
|
|
|
655
|
-
|
|
656
|
-
|
|
643
|
+
def nullable_candidate?(type)
|
|
644
|
+
!ref_type?(type) && type != @ctx.types.fetch("void")
|
|
645
|
+
end
|
|
657
646
|
|
|
658
|
-
|
|
659
|
-
|
|
647
|
+
def conditional_null_common_type(null_type, other_type)
|
|
648
|
+
return unless null_type.is_a?(Types::Null)
|
|
660
649
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
end
|
|
650
|
+
if other_type.is_a?(Types::Nullable)
|
|
651
|
+
return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
|
|
664
652
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
break_target:,
|
|
668
|
-
continue_target:,
|
|
669
|
-
break_defers:,
|
|
670
|
-
continue_defers:,
|
|
671
|
-
}
|
|
672
|
-
end
|
|
653
|
+
return nil
|
|
654
|
+
end
|
|
673
655
|
|
|
674
|
-
|
|
675
|
-
|
|
656
|
+
return unless nullable_candidate?(other_type)
|
|
657
|
+
return if null_type.target_type && null_type.target_type != other_type
|
|
676
658
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
continue_target: current_loop_flow[:continue_target],
|
|
680
|
-
break_defers: current_loop_flow[:break_defers] + local_defers,
|
|
681
|
-
continue_defers: current_loop_flow[:continue_defers] + local_defers,
|
|
682
|
-
)
|
|
683
|
-
end
|
|
659
|
+
Types::Registry.nullable(other_type)
|
|
660
|
+
end
|
|
684
661
|
|
|
685
|
-
|
|
686
|
-
|
|
662
|
+
def null_type
|
|
663
|
+
@null_type ||= Types::Null.new
|
|
664
|
+
end
|
|
687
665
|
|
|
688
|
-
|
|
689
|
-
|
|
666
|
+
def loop_flow(break_target:, continue_target:, break_defers: [], continue_defers: [])
|
|
667
|
+
{
|
|
668
|
+
break_target:,
|
|
669
|
+
continue_target:,
|
|
670
|
+
break_defers:,
|
|
671
|
+
continue_defers:,
|
|
672
|
+
}
|
|
673
|
+
end
|
|
690
674
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
return nil unless nested
|
|
675
|
+
def nested_loop_flow(current_loop_flow, local_defers)
|
|
676
|
+
return nil unless current_loop_flow
|
|
694
677
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
678
|
+
loop_flow(
|
|
679
|
+
break_target: current_loop_flow[:break_target],
|
|
680
|
+
continue_target: current_loop_flow[:continue_target],
|
|
681
|
+
break_defers: current_loop_flow[:break_defers] + local_defers,
|
|
682
|
+
continue_defers: current_loop_flow[:continue_defers] + local_defers,
|
|
683
|
+
)
|
|
684
|
+
end
|
|
702
685
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
end
|
|
686
|
+
def switch_loop_target(target)
|
|
687
|
+
return target unless target && target[:label]
|
|
706
688
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
end
|
|
689
|
+
loop_exit_label(target[:label])
|
|
690
|
+
end
|
|
710
691
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
692
|
+
def switch_loop_flow(current_loop_flow, local_defers)
|
|
693
|
+
nested = nested_loop_flow(current_loop_flow, local_defers)
|
|
694
|
+
return nil unless nested
|
|
714
695
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
696
|
+
loop_flow(
|
|
697
|
+
break_target: switch_loop_target(nested[:break_target]),
|
|
698
|
+
continue_target: switch_loop_target(nested[:continue_target]),
|
|
699
|
+
break_defers: nested[:break_defers],
|
|
700
|
+
continue_defers: nested[:continue_defers],
|
|
701
|
+
)
|
|
702
|
+
end
|
|
718
703
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
IR::BreakStmt.new
|
|
723
|
-
when :continue
|
|
724
|
-
IR::ContinueStmt.new
|
|
725
|
-
when :label
|
|
726
|
-
return IR::GotoStmt.new(label: target[:label]) if target[:label]
|
|
704
|
+
def cleanup_statements(local_defers, outer_defers)
|
|
705
|
+
local_defers.reverse.flat_map(&:itself) + outer_defers.reverse.flat_map(&:itself)
|
|
706
|
+
end
|
|
727
707
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
raise LoweringError.new("unsupported loop exit target #{target.inspect}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
708
|
+
def loop_exit_break(label = nil)
|
|
709
|
+
{ kind: :break, label: }
|
|
731
710
|
end
|
|
732
|
-
end
|
|
733
711
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
[loop_exit_statement(target, local_defers:, outer_defers:)]
|
|
738
|
-
else
|
|
739
|
-
label = target[:label]
|
|
740
|
-
raise LoweringError.new("structured loop exits with cleanup are unsupported", line: 0, column: 0, path: @ctx.current_analysis_path) unless label
|
|
712
|
+
def loop_exit_continue(label = nil)
|
|
713
|
+
{ kind: :continue, label: }
|
|
714
|
+
end
|
|
741
715
|
|
|
742
|
-
|
|
716
|
+
def loop_exit_label(label)
|
|
717
|
+
{ kind: :label, label: }
|
|
743
718
|
end
|
|
744
|
-
end
|
|
745
719
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
|
|
720
|
+
def loop_exit_statement(target, local_defers:, outer_defers:)
|
|
721
|
+
case target[:kind]
|
|
722
|
+
when :break
|
|
723
|
+
IR::BreakStmt.new
|
|
724
|
+
when :continue
|
|
725
|
+
IR::ContinueStmt.new
|
|
726
|
+
when :label
|
|
727
|
+
return IR::GotoStmt.new(label: target[:label]) if target[:label]
|
|
728
|
+
|
|
729
|
+
IR::GotoStmt.new(label: target[:label])
|
|
757
730
|
else
|
|
758
|
-
|
|
731
|
+
raise LoweringError.new("unsupported loop exit target #{target.inspect}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
759
732
|
end
|
|
760
733
|
end
|
|
761
|
-
end
|
|
762
734
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
735
|
+
def lower_loop_exit(target, local_defers, outer_defers)
|
|
736
|
+
cleanup = cleanup_statements(local_defers, outer_defers)
|
|
737
|
+
if cleanup.empty?
|
|
738
|
+
[loop_exit_statement(target, local_defers:, outer_defers:)]
|
|
739
|
+
else
|
|
740
|
+
label = target[:label]
|
|
741
|
+
raise LoweringError.new("structured loop exits with cleanup are unsupported", line: 0, column: 0, path: @ctx.current_analysis_path) unless label
|
|
766
742
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
743
|
+
cleanup + [IR::GotoStmt.new(label:)]
|
|
744
|
+
end
|
|
745
|
+
end
|
|
770
746
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
747
|
+
def contains_label_target?(statements, label)
|
|
748
|
+
statements.any? do |statement|
|
|
749
|
+
case statement
|
|
750
|
+
when IR::GotoStmt
|
|
751
|
+
statement.label == label
|
|
752
|
+
when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
|
|
753
|
+
contains_label_target?(statement.body, label)
|
|
754
|
+
when IR::IfStmt
|
|
755
|
+
contains_label_target?(statement.then_body, label) || (statement.else_body && contains_label_target?(statement.else_body, label))
|
|
756
|
+
when IR::SwitchStmt
|
|
757
|
+
statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
|
|
758
|
+
else
|
|
759
|
+
false
|
|
760
|
+
end
|
|
761
|
+
end
|
|
762
|
+
end
|
|
774
763
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
764
|
+
def lower_defer_cleanup_body(statements, env:, return_type:)
|
|
765
|
+
lower_block(statements, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: false)
|
|
766
|
+
end
|
|
778
767
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
duplicated[:counter] = env[:counter]
|
|
783
|
-
duplicated.delete(:prepared_expression_cleanups)
|
|
784
|
-
duplicated
|
|
785
|
-
end
|
|
768
|
+
def terminating_ir_statement?(statement)
|
|
769
|
+
statement.is_a?(IR::ReturnStmt) || statement.is_a?(IR::GotoStmt)
|
|
770
|
+
end
|
|
786
771
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
772
|
+
def empty_env
|
|
773
|
+
{ scopes: [{}], counter: { value: 0 } }
|
|
774
|
+
end
|
|
790
775
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
776
|
+
def snapshot_env(env)
|
|
777
|
+
{ scopes: env[:scopes].map(&:dup), counter: env[:counter] }
|
|
778
|
+
end
|
|
794
779
|
|
|
795
|
-
|
|
796
|
-
|
|
780
|
+
def duplicate_env(env)
|
|
781
|
+
duplicated = env.dup
|
|
782
|
+
duplicated[:scopes] = env[:scopes].map(&:dup) + [{}]
|
|
783
|
+
duplicated[:counter] = env[:counter]
|
|
784
|
+
duplicated.delete(:prepared_expression_cleanups)
|
|
785
|
+
duplicated
|
|
786
|
+
end
|
|
797
787
|
|
|
798
|
-
|
|
788
|
+
def let_else_discards_binding?(statement)
|
|
789
|
+
statement.is_a?(AST::LocalDecl) && statement.else_body && statement.name == "_"
|
|
790
|
+
end
|
|
799
791
|
|
|
800
|
-
|
|
801
|
-
|
|
792
|
+
def bind_let_else_local?(statement)
|
|
793
|
+
!let_else_discards_binding?(statement)
|
|
794
|
+
end
|
|
802
795
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
return type.arm("some").fetch("value") if option_let_else_type?(type)
|
|
806
|
-
return unless result_let_else_type?(type)
|
|
796
|
+
def let_else_storage_c_name(statement, env)
|
|
797
|
+
return fresh_c_temp_name(env, "let_else_discard") if let_else_discards_binding?(statement)
|
|
807
798
|
|
|
808
|
-
|
|
809
|
-
end
|
|
799
|
+
return fresh_c_temp_name(env, "_") if statement.name == "_"
|
|
810
800
|
|
|
811
|
-
|
|
812
|
-
|
|
801
|
+
c_local_name(statement.name)
|
|
802
|
+
end
|
|
813
803
|
|
|
814
|
-
type
|
|
815
|
-
|
|
804
|
+
def let_else_success_type(type)
|
|
805
|
+
return type.base if type.is_a?(Types::Nullable)
|
|
806
|
+
return type.arm("some").fetch("value") if option_let_else_type?(type)
|
|
807
|
+
return unless result_let_else_type?(type)
|
|
816
808
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
return :option_some_value if option_let_else_type?(type)
|
|
809
|
+
type.arm("success").fetch("value")
|
|
810
|
+
end
|
|
820
811
|
|
|
821
|
-
|
|
822
|
-
|
|
812
|
+
def let_else_error_type(type)
|
|
813
|
+
return unless result_let_else_type?(type)
|
|
823
814
|
|
|
824
|
-
|
|
825
|
-
|
|
815
|
+
type.arm("failure").fetch("error")
|
|
816
|
+
end
|
|
826
817
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
none_fields && none_fields.empty?
|
|
831
|
-
end
|
|
818
|
+
def let_else_binding_projection(type)
|
|
819
|
+
return :result_success_value if result_let_else_type?(type)
|
|
820
|
+
return :option_some_value if option_let_else_type?(type)
|
|
832
821
|
|
|
833
|
-
|
|
834
|
-
|
|
822
|
+
nil
|
|
823
|
+
end
|
|
835
824
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
success_fields && success_fields.length == 1 && success_fields.key?("value") &&
|
|
839
|
-
failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
|
|
840
|
-
end
|
|
825
|
+
def option_let_else_type?(type)
|
|
826
|
+
return false unless type.is_a?(Types::Variant)
|
|
841
827
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
left: storage_expr,
|
|
847
|
-
right: IR::NullLiteral.new(type: storage_type),
|
|
848
|
-
type: @ctx.types.fetch("bool"),
|
|
849
|
-
)
|
|
828
|
+
some_fields = type.arm("some")
|
|
829
|
+
none_fields = type.arm("none")
|
|
830
|
+
some_fields && some_fields.length == 1 && some_fields.key?("value") &&
|
|
831
|
+
none_fields && none_fields.empty?
|
|
850
832
|
end
|
|
851
833
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return IR::Binary.new(
|
|
855
|
-
operator: "==",
|
|
856
|
-
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
857
|
-
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
|
|
858
|
-
type: @ctx.types.fetch("bool"),
|
|
859
|
-
)
|
|
860
|
-
end
|
|
834
|
+
def result_let_else_type?(type)
|
|
835
|
+
return false unless type.is_a?(Types::Variant)
|
|
861
836
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
867
|
-
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
|
|
868
|
-
type: @ctx.types.fetch("bool"),
|
|
869
|
-
)
|
|
837
|
+
success_fields = type.arm("success")
|
|
838
|
+
failure_fields = type.arm("failure")
|
|
839
|
+
success_fields && success_fields.length == 1 && success_fields.key?("value") &&
|
|
840
|
+
failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
|
|
870
841
|
end
|
|
871
842
|
|
|
872
|
-
|
|
873
|
-
|
|
843
|
+
def let_else_failure_condition(storage_expr, storage_type)
|
|
844
|
+
if storage_type.is_a?(Types::Nullable)
|
|
845
|
+
return IR::Binary.new(
|
|
846
|
+
operator: "==",
|
|
847
|
+
left: storage_expr,
|
|
848
|
+
right: IR::NullLiteral.new(type: storage_type),
|
|
849
|
+
type: @ctx.types.fetch("bool"),
|
|
850
|
+
)
|
|
851
|
+
end
|
|
874
852
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
853
|
+
if result_let_else_type?(storage_type)
|
|
854
|
+
kind_type = @ctx.types.fetch("int")
|
|
855
|
+
return IR::Binary.new(
|
|
856
|
+
operator: "==",
|
|
857
|
+
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
858
|
+
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
|
|
859
|
+
type: @ctx.types.fetch("bool"),
|
|
860
|
+
)
|
|
861
|
+
end
|
|
879
862
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
863
|
+
if option_let_else_type?(storage_type)
|
|
864
|
+
kind_type = @ctx.types.fetch("int")
|
|
865
|
+
return IR::Binary.new(
|
|
866
|
+
operator: "==",
|
|
867
|
+
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
868
|
+
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
|
|
869
|
+
type: @ctx.types.fetch("bool"),
|
|
870
|
+
)
|
|
871
|
+
end
|
|
884
872
|
|
|
885
|
-
|
|
886
|
-
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
887
|
-
return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
|
|
873
|
+
raise LoweringError.new("unsupported let-else storage type #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
888
874
|
end
|
|
889
875
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
876
|
+
def lower_bound_identifier(binding, expected_type: nil)
|
|
877
|
+
storage_type = binding[:storage_type]
|
|
878
|
+
visible_type = binding[:type]
|
|
879
|
+
projection = binding[:projection]
|
|
894
880
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
return name if expected_type == storage_type
|
|
900
|
-
return IR::Member.new(receiver: name, member: "value", type: visible_type)
|
|
901
|
-
end
|
|
881
|
+
if projection == :result_success_value
|
|
882
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
883
|
+
return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
|
|
884
|
+
end
|
|
902
885
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
886
|
+
if projection == :result_failure_error
|
|
887
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
888
|
+
return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
|
|
889
|
+
end
|
|
907
890
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
891
|
+
if projection == :option_some_value
|
|
892
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
893
|
+
return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
|
|
894
|
+
end
|
|
912
895
|
|
|
913
|
-
|
|
914
|
-
|
|
896
|
+
return IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer]) if visible_type == storage_type
|
|
897
|
+
if storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type
|
|
898
|
+
name = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
899
|
+
return name if pointer_like_type?(storage_type.base)
|
|
900
|
+
return name if expected_type == storage_type
|
|
901
|
+
return IR::Member.new(receiver: name, member: "value", type: visible_type)
|
|
902
|
+
end
|
|
915
903
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
member_expr = IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
|
|
921
|
-
if type_reaches_target?(field_type, payload_type.variant_type)
|
|
922
|
-
return IR::Unary.new(operator: "*", operand: member_expr, type: field_type)
|
|
923
|
-
end
|
|
904
|
+
if result_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
|
|
905
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
906
|
+
return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
|
|
907
|
+
end
|
|
924
908
|
|
|
925
|
-
|
|
926
|
-
|
|
909
|
+
if option_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
|
|
910
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
911
|
+
return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
|
|
912
|
+
end
|
|
927
913
|
|
|
928
|
-
|
|
929
|
-
|
|
914
|
+
IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
|
|
915
|
+
end
|
|
930
916
|
|
|
931
|
-
|
|
932
|
-
|
|
917
|
+
def variant_binding_projection_expression(storage_expr, storage_type, arm_name, field_name, field_type)
|
|
918
|
+
payload_type = Types::VariantArmPayload.new(storage_type, arm_name, storage_type.arm(arm_name))
|
|
919
|
+
data_expr = IR::Member.new(receiver: storage_expr, member: "data", type: nil)
|
|
920
|
+
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
921
|
+
member_expr = IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
|
|
922
|
+
if type_reaches_target?(field_type, payload_type.variant_type)
|
|
923
|
+
return IR::Unary.new(operator: "*", operand: member_expr, type: field_type)
|
|
924
|
+
end
|
|
933
925
|
|
|
934
|
-
|
|
935
|
-
storage_type = infer_expression_type(expression.operand, env:)
|
|
936
|
-
if result_let_else_type?(storage_type)
|
|
937
|
-
infer_result_propagation_details(storage_type, env:, allow_void_success:)
|
|
938
|
-
elsif option_let_else_type?(storage_type)
|
|
939
|
-
infer_option_propagation_details(storage_type, env:, allow_void_success:)
|
|
940
|
-
else
|
|
941
|
-
raise LoweringError.new("propagation expects Result[T, E] or Option[T], got #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
926
|
+
member_expr
|
|
942
927
|
end
|
|
943
|
-
end
|
|
944
928
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
error_type = let_else_error_type(storage_type)
|
|
948
|
-
raise LoweringError.new("propagation requires a non-void Result success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
|
|
929
|
+
def infer_result_propagation_type(expression, env:)
|
|
930
|
+
_storage_type, success_type, = infer_result_propagation_types(expression, env:)
|
|
949
931
|
|
|
950
|
-
|
|
951
|
-
raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
|
|
952
|
-
raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
|
|
953
|
-
|
|
954
|
-
return_type = context[:return_type]
|
|
955
|
-
unless result_let_else_type?(return_type)
|
|
956
|
-
raise LoweringError.new("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
932
|
+
success_type
|
|
957
933
|
end
|
|
958
934
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
935
|
+
def infer_result_propagation_types(expression, env:, allow_void_success: false)
|
|
936
|
+
storage_type = infer_expression_type(expression.operand, env:)
|
|
937
|
+
if result_let_else_type?(storage_type)
|
|
938
|
+
infer_result_propagation_details(storage_type, env:, allow_void_success:)
|
|
939
|
+
elsif option_let_else_type?(storage_type)
|
|
940
|
+
infer_option_propagation_details(storage_type, env:, allow_void_success:)
|
|
941
|
+
else
|
|
942
|
+
raise LoweringError.new("propagation expects Result[T, E] or Option[T], got #{storage_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
943
|
+
end
|
|
962
944
|
end
|
|
963
945
|
|
|
964
|
-
|
|
965
|
-
|
|
946
|
+
def infer_result_propagation_details(storage_type, env:, allow_void_success:)
|
|
947
|
+
success_type = let_else_success_type(storage_type)
|
|
948
|
+
error_type = let_else_error_type(storage_type)
|
|
949
|
+
raise LoweringError.new("propagation requires a non-void Result success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
|
|
966
950
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
951
|
+
context = env[:return_context]
|
|
952
|
+
raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
|
|
953
|
+
raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
|
|
970
954
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
955
|
+
return_type = context[:return_type]
|
|
956
|
+
unless result_let_else_type?(return_type)
|
|
957
|
+
raise LoweringError.new("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
958
|
+
end
|
|
974
959
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
960
|
+
return_error_type = let_else_error_type(return_type)
|
|
961
|
+
unless return_error_type == error_type
|
|
962
|
+
raise LoweringError.new("propagation error type #{error_type} must match enclosing Result error type #{return_error_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
[storage_type, success_type, return_type, error_type]
|
|
978
966
|
end
|
|
979
967
|
|
|
980
|
-
|
|
981
|
-
|
|
968
|
+
def infer_option_propagation_details(storage_type, env:, allow_void_success:)
|
|
969
|
+
success_type = let_else_success_type(storage_type)
|
|
970
|
+
raise LoweringError.new("propagation requires a non-void Option success type", line: 0, column: 0, path: @ctx.current_analysis_path) if success_type == @ctx.types.fetch("void") && !allow_void_success
|
|
982
971
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
end
|
|
972
|
+
context = env[:return_context]
|
|
973
|
+
raise LoweringError.new("propagation is only allowed inside function and proc bodies", line: 0, column: 0, path: @ctx.current_analysis_path) unless context
|
|
974
|
+
raise LoweringError.new("propagation is not allowed inside defer blocks", line: 0, column: 0, path: @ctx.current_analysis_path) unless context[:allow_return]
|
|
987
975
|
|
|
988
|
-
|
|
989
|
-
|
|
976
|
+
return_type = context[:return_type]
|
|
977
|
+
unless option_let_else_type?(return_type)
|
|
978
|
+
raise LoweringError.new("propagation requires enclosing function/proc to return Option[_], got #{return_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
[storage_type, success_type, return_type, nil]
|
|
990
982
|
end
|
|
991
983
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
type.
|
|
995
|
-
elsif type.respond_to?(:module_name) && !type.module_name.nil?
|
|
996
|
-
"#{module_c_prefix(type.module_name)}_#{type.name}"
|
|
997
|
-
else
|
|
998
|
-
type.name
|
|
984
|
+
def c_type_name(type)
|
|
985
|
+
if type.is_a?(Types::Nullable)
|
|
986
|
+
return "nullable_#{c_type_name(type.base)}"
|
|
999
987
|
end
|
|
1000
988
|
|
|
1001
|
-
|
|
1002
|
-
|
|
989
|
+
if type.respond_to?(:linkage_name) && type.linkage_name
|
|
990
|
+
return type.linkage_name
|
|
991
|
+
end
|
|
1003
992
|
|
|
1004
|
-
|
|
993
|
+
if type.is_a?(Types::GenericInstance)
|
|
994
|
+
base = if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
|
|
995
|
+
type.name
|
|
996
|
+
elsif type.respond_to?(:module_name) && !type.module_name.nil?
|
|
997
|
+
"#{module_c_prefix(type.module_name)}_#{type.name}"
|
|
998
|
+
else
|
|
999
|
+
type.name
|
|
1000
|
+
end
|
|
1005
1001
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1002
|
+
return "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
|
|
1003
|
+
end
|
|
1008
1004
|
|
|
1009
|
-
|
|
1010
|
-
end
|
|
1005
|
+
return type.name if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
|
|
1011
1006
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
end
|
|
1007
|
+
base = (type.respond_to?(:module_name) && type.module_name) ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
|
|
1008
|
+
return base unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
|
|
1015
1009
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1010
|
+
"#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
|
|
1011
|
+
end
|
|
1018
1012
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1013
|
+
def opaque_c_type_name(type)
|
|
1014
|
+
type.linkage_name || c_type_name(type)
|
|
1015
|
+
end
|
|
1021
1016
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
end
|
|
1017
|
+
def opaque_forward_declarable?(type)
|
|
1018
|
+
return false unless opaque_c_type_name(type).match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
1025
1019
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
end
|
|
1020
|
+
!type.external || type.linkage_name.nil?
|
|
1021
|
+
end
|
|
1029
1022
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1023
|
+
def forward_declarable_external_opaque?(type)
|
|
1024
|
+
type.external && opaque_forward_declarable?(type)
|
|
1025
|
+
end
|
|
1033
1026
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1027
|
+
def validate_generic_type!(name, arguments)
|
|
1028
|
+
super(name, arguments) { |msg| raise LoweringError.new(msg, line: 0, column: 0, path: @ctx.current_analysis_path) }
|
|
1029
|
+
end
|
|
1037
1030
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1031
|
+
def integer_type_argument?(argument)
|
|
1032
|
+
argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
|
|
1033
|
+
end
|
|
1041
1034
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
|
|
1035
|
+
def generic_integer_type_argument?(argument)
|
|
1036
|
+
integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
|
|
1045
1037
|
end
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
|
|
1038
|
+
|
|
1039
|
+
def enum_member_c_name(type, member_name)
|
|
1040
|
+
"#{c_type_name(type)}_#{member_name}"
|
|
1050
1041
|
end
|
|
1051
1042
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1043
|
+
def function_binding_c_name(binding, module_name:, receiver_type: nil)
|
|
1044
|
+
if receiver_type.nil? && binding.name == "main" && binding.type_arguments.empty?
|
|
1045
|
+
return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
|
|
1046
|
+
end
|
|
1047
|
+
if receiver_type
|
|
1048
|
+
base = "#{c_type_name(receiver_type)}_#{binding.name}"
|
|
1049
|
+
base = "#{base}_static" if binding.type.receiver_type.nil?
|
|
1050
|
+
return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
|
|
1051
|
+
end
|
|
1054
1052
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1053
|
+
module_function_c_name(module_name, binding.name, type_arguments: binding.type_arguments)
|
|
1054
|
+
end
|
|
1057
1055
|
|
|
1058
|
-
binding
|
|
1059
|
-
|
|
1056
|
+
def external_function_c_name(binding)
|
|
1057
|
+
return binding.ast.mapping.value if binding.external && binding.ast.is_a?(AST::ExternFunctionDecl) && binding.ast.mapping
|
|
1060
1058
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1059
|
+
binding.name
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
def value_c_name(name)
|
|
1063
|
+
module_value_c_name(@ctx.module_name, name)
|
|
1064
|
+
end
|
|
1064
1065
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1066
|
+
def imported_value_c_name(imported_module, name)
|
|
1067
|
+
imported_analysis = analysis_for_module(imported_module.name)
|
|
1068
|
+
return name if imported_analysis.module_kind == :raw_module
|
|
1068
1069
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1070
|
+
module_value_c_name(imported_module.name, name)
|
|
1071
|
+
end
|
|
1071
1072
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1073
|
+
def module_function_c_name(module_name, name, type_arguments: [])
|
|
1074
|
+
base = "#{module_c_prefix(module_name)}_#{name}"
|
|
1075
|
+
return base if type_arguments.empty?
|
|
1075
1076
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1077
|
+
# A double-underscore separates a generic function instance's type
|
|
1078
|
+
# arguments so it cannot collide with a distinct regular function whose
|
|
1079
|
+
# name happens to be `<name>_<typearg>` (e.g. the instance
|
|
1080
|
+
# `expect_equal[str]` vs the function `expect_equal_str`). The method path
|
|
1081
|
+
# in `function_binding_c_name` uses the same scheme for consistency.
|
|
1082
|
+
"#{base}__#{generic_type_argument_suffix(type_arguments)}"
|
|
1083
|
+
end
|
|
1083
1084
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1085
|
+
# Joins resolved generic type arguments into the instance-name suffix used
|
|
1086
|
+
# by both the free-function (`module_function_c_name`) and method
|
|
1087
|
+
# (`function_binding_c_name`) paths, keeping the scheme consistent.
|
|
1088
|
+
def generic_type_argument_suffix(type_arguments)
|
|
1089
|
+
sanitize_identifier(type_arguments.join('_'))
|
|
1090
|
+
end
|
|
1090
1091
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1092
|
+
def module_value_c_name(module_name, name)
|
|
1093
|
+
"#{module_c_prefix(module_name)}_#{name}"
|
|
1094
|
+
end
|
|
1094
1095
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1096
|
+
def module_c_prefix(module_name)
|
|
1097
|
+
sanitize_identifier(module_name.to_s.tr('.', '_'))
|
|
1098
|
+
end
|
|
1098
1099
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
return "#{identifier}_" if c_reserved_identifier?(identifier)
|
|
1100
|
+
def c_local_name(name)
|
|
1101
|
+
return "value" unless name
|
|
1102
|
+
return "#{name}_" if c_reserved_identifier?(name)
|
|
1103
1103
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1104
|
+
name
|
|
1105
|
+
end
|
|
1106
1106
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1107
|
+
def c_reserved_identifier?(identifier)
|
|
1108
|
+
%w[
|
|
1109
|
+
auto break case char const continue default do double else enum extern
|
|
1110
|
+
float for goto if inline int long register restrict return short signed
|
|
1111
|
+
sizeof static struct switch typedef union unsigned void volatile while
|
|
1112
|
+
_Alignas _Alignof _Atomic _Bool _Complex _Generic _Imaginary _Noreturn
|
|
1113
|
+
_Static_assert _Thread_local
|
|
1114
|
+
].include?(identifier)
|
|
1115
|
+
end
|
|
1116
1116
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1117
|
+
def fresh_c_temp_name(env, prefix)
|
|
1118
|
+
env[:counter][:value] += 1
|
|
1119
|
+
"__mt_#{prefix}_#{env[:counter][:value]}"
|
|
1120
|
+
end
|
|
1121
1121
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1122
|
+
def cleanup_safe_return_expression?(expression)
|
|
1123
|
+
case expression
|
|
1124
|
+
when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
|
|
1125
|
+
true
|
|
1126
|
+
else
|
|
1127
|
+
false
|
|
1128
|
+
end
|
|
1128
1129
|
end
|
|
1129
|
-
end
|
|
1130
1130
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1131
|
+
def sanitize_identifier(text)
|
|
1132
|
+
return "value" unless text
|
|
1133
1133
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1134
|
+
cache = (@sanitize_identifier_cache ||= {})
|
|
1135
|
+
cached = cache[text]
|
|
1136
|
+
return cached if cached
|
|
1137
1137
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1138
|
+
identifier = text.gsub(/[^A-Za-z0-9_]+/, "_").gsub(/_+/, "_").sub(/_+$/, "").sub(/^_+/, "")
|
|
1139
|
+
cache[text] = identifier.empty? ? "value" : identifier
|
|
1140
|
+
end
|
|
1141
1141
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1142
|
+
def lower_assignment_target(expression, env:)
|
|
1143
|
+
case expression
|
|
1144
|
+
when AST::Identifier
|
|
1145
|
+
binding = lookup_value(expression.name, env)
|
|
1146
|
+
lower_assignment_binding_target(binding)
|
|
1147
|
+
when AST::MemberAccess
|
|
1148
|
+
if expression.receiver.is_a?(AST::IndexAccess)
|
|
1149
|
+
base_type = infer_expression_type(expression.receiver.receiver, env:)
|
|
1150
|
+
if base_type.is_a?(Types::SoA)
|
|
1151
|
+
soa_base = lower_expression(expression.receiver.receiver, env:)
|
|
1152
|
+
index = lower_expression(expression.receiver.index, env:)
|
|
1153
|
+
field_type = base_type.fields[expression.member]
|
|
1154
|
+
target_type = infer_expression_type(expression, env:)
|
|
1155
|
+
return IR::Index.new(
|
|
1156
|
+
receiver: IR::Member.new(receiver: soa_base, member: expression.member, type: field_type),
|
|
1157
|
+
index:,
|
|
1158
|
+
type: target_type,
|
|
1159
|
+
)
|
|
1160
|
+
end
|
|
1160
1161
|
end
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
receiver = lower_expression(expression.receiver, env:)
|
|
1164
|
-
type = infer_expression_type(expression, env:)
|
|
1165
|
-
IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
|
|
1166
|
-
when AST::IndexAccess
|
|
1167
|
-
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1168
|
-
receiver = lower_expression(expression.receiver, env:)
|
|
1169
|
-
index = lower_expression(expression.index, env:)
|
|
1170
|
-
type = infer_expression_type(expression, env:)
|
|
1171
|
-
if array_type?(receiver_type)
|
|
1172
|
-
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1173
|
-
elsif receiver_type.is_a?(Types::Span)
|
|
1174
|
-
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1175
|
-
else
|
|
1176
|
-
IR::Index.new(receiver:, index:, type:)
|
|
1177
|
-
end
|
|
1178
|
-
when AST::Call
|
|
1179
|
-
if read_call?(expression)
|
|
1162
|
+
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1163
|
+
receiver = lower_expression(expression.receiver, env:)
|
|
1180
1164
|
type = infer_expression_type(expression, env:)
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1165
|
+
IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
|
|
1166
|
+
when AST::IndexAccess
|
|
1167
|
+
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1168
|
+
receiver = lower_expression(expression.receiver, env:)
|
|
1169
|
+
index = lower_expression(expression.index, env:)
|
|
1170
|
+
type = infer_expression_type(expression, env:)
|
|
1171
|
+
if array_type?(receiver_type)
|
|
1172
|
+
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1173
|
+
elsif receiver_type.is_a?(Types::Span)
|
|
1174
|
+
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1175
|
+
else
|
|
1176
|
+
IR::Index.new(receiver:, index:, type:)
|
|
1177
|
+
end
|
|
1178
|
+
when AST::Call
|
|
1179
|
+
if read_call?(expression)
|
|
1180
|
+
type = infer_expression_type(expression, env:)
|
|
1181
|
+
operand = lower_expression(expression.arguments.first.value, env:)
|
|
1182
|
+
return IR::Unary.new(operator: "*", operand:, type:)
|
|
1183
|
+
end
|
|
1184
1184
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1185
|
+
raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
1186
|
+
else
|
|
1187
|
+
raise LoweringError.new("unsupported assignment target #{expression.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
1188
|
+
end
|
|
1188
1189
|
end
|
|
1189
|
-
end
|
|
1190
1190
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
else
|
|
1202
|
-
if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
|
|
1203
|
-
IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
|
|
1191
|
+
def lower_assignment_binding_target(binding)
|
|
1192
|
+
storage_type = binding[:storage_type]
|
|
1193
|
+
visible_type = binding[:type]
|
|
1194
|
+
storage_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1195
|
+
|
|
1196
|
+
case binding[:projection]
|
|
1197
|
+
when :result_success_value
|
|
1198
|
+
variant_binding_projection_expression(storage_ref, storage_type, "success", "value", visible_type)
|
|
1199
|
+
when :option_some_value
|
|
1200
|
+
variant_binding_projection_expression(storage_ref, storage_type, "some", "value", visible_type)
|
|
1204
1201
|
else
|
|
1205
|
-
|
|
1202
|
+
if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
|
|
1203
|
+
IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
|
|
1204
|
+
else
|
|
1205
|
+
storage_ref
|
|
1206
|
+
end
|
|
1206
1207
|
end
|
|
1207
1208
|
end
|
|
1208
|
-
end
|
|
1209
1209
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1210
|
+
def wrap_nullable_field_value(field_type, lowered_value, env)
|
|
1211
|
+
return lowered_value unless field_type.is_a?(Types::Nullable)
|
|
1212
|
+
return lowered_value if pointer_like_type?(field_type.base)
|
|
1213
|
+
return lowered_value if lowered_value.type.is_a?(Types::Nullable)
|
|
1214
1214
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1215
|
+
nullable_some_literal(field_type, lowered_value)
|
|
1216
|
+
end
|
|
1217
1217
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1218
|
+
def nullable_some_literal(nullable_type, value)
|
|
1219
|
+
IR::AggregateLiteral.new(
|
|
1220
|
+
type: nullable_type,
|
|
1221
|
+
fields: [
|
|
1222
|
+
IR::AggregateField.new(name: "has_value", value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool"))),
|
|
1223
|
+
IR::AggregateField.new(name: "value", value:),
|
|
1224
|
+
],
|
|
1225
|
+
)
|
|
1226
|
+
end
|
|
1227
1227
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1228
|
+
def pointer_like_type?(type)
|
|
1229
|
+
pointer_type?(type) || own_type?(type) || (type.is_a?(Types::Primitive) && type.name == "cstr") || type.is_a?(Types::Function) || type.is_a?(Types::Proc) || type.is_a?(Types::Opaque)
|
|
1230
|
+
end
|
|
1230
1231
|
end
|
|
1231
1232
|
end
|
|
1232
1233
|
end
|