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