mt-lang 0.3.24 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -1
  3. data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
  4. data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
  5. data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
  6. data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
  7. data/lib/milk_tea/core/bindings.rb +4 -9
  8. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
  9. data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
  10. data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
  11. data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
  12. data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
  13. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
  14. data/lib/milk_tea/core/c_backend/statements.rb +20 -20
  15. data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
  16. data/lib/milk_tea/core/c_backend/type_system.rb +1 -1
  17. data/lib/milk_tea/core/c_backend.rb +3 -3
  18. data/lib/milk_tea/core/compile_time.rb +41 -70
  19. data/lib/milk_tea/core/control_flow/builder.rb +3 -3
  20. data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
  21. data/lib/milk_tea/core/control_flow/graph.rb +1 -1
  22. data/lib/milk_tea/core/control_flow/termination.rb +1 -1
  23. data/lib/milk_tea/core/cst.rb +16 -0
  24. data/lib/milk_tea/core/flow_scope.rb +13 -0
  25. data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
  26. data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
  27. data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
  28. data/lib/milk_tea/core/lexer/indentation.rb +4 -4
  29. data/lib/milk_tea/core/lexer/numbers.rb +1 -1
  30. data/lib/milk_tea/core/lexer/strings.rb +4 -4
  31. data/lib/milk_tea/core/lexer/symbols.rb +3 -3
  32. data/lib/milk_tea/core/lexer/trivia.rb +4 -4
  33. data/lib/milk_tea/core/lexer.rb +17 -17
  34. data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
  35. data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
  36. data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
  37. data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
  38. data/lib/milk_tea/core/lowering/block.rb +982 -980
  39. data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
  40. data/lib/milk_tea/core/lowering/declarations.rb +175 -173
  41. data/lib/milk_tea/core/lowering/dyn.rb +171 -169
  42. data/lib/milk_tea/core/lowering/events.rb +946 -944
  43. data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
  44. data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
  45. data/lib/milk_tea/core/lowering/functions.rb +186 -184
  46. data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
  47. data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
  48. data/lib/milk_tea/core/lowering/proc.rb +355 -353
  49. data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
  50. data/lib/milk_tea/core/lowering/scans.rb +162 -168
  51. data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
  52. data/lib/milk_tea/core/lowering/utils.rb +964 -963
  53. data/lib/milk_tea/core/lowering.rb +22 -22
  54. data/lib/milk_tea/core/module_binder.rb +1 -1
  55. data/lib/milk_tea/core/module_loader.rb +2 -2
  56. data/lib/milk_tea/core/parser/attributes.rb +1 -1
  57. data/lib/milk_tea/core/parser/blocks.rb +2 -2
  58. data/lib/milk_tea/core/parser/declarations.rb +15 -15
  59. data/lib/milk_tea/core/parser/expressions.rb +20 -20
  60. data/lib/milk_tea/core/parser/recovery.rb +1 -1
  61. data/lib/milk_tea/core/parser/statements.rb +3 -3
  62. data/lib/milk_tea/core/parser/types.rb +4 -4
  63. data/lib/milk_tea/core/parser.rb +39 -21
  64. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
  65. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
  66. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
  67. data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
  68. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
  69. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
  70. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
  71. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
  72. data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
  73. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
  74. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
  75. data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
  76. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
  77. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
  78. data/lib/milk_tea/core/types/layout.rb +3 -1
  79. data/lib/milk_tea/core/types/visitor.rb +351 -349
  80. data/lib/milk_tea/core.rb +0 -2
  81. data/lib/milk_tea/tooling/cli/commands/bindgen.rb +1 -1
  82. data/lib/milk_tea/tooling/cli/commands/check.rb +5 -5
  83. data/lib/milk_tea/tooling/cli/commands/debug.rb +3 -3
  84. data/lib/milk_tea/tooling/cli/commands/deps.rb +1 -1
  85. data/lib/milk_tea/tooling/cli/commands/emit_c.rb +2 -2
  86. data/lib/milk_tea/tooling/cli/commands/format.rb +6 -6
  87. data/lib/milk_tea/tooling/cli/commands/lint.rb +3 -3
  88. data/lib/milk_tea/tooling/cli/commands/lower.rb +3 -3
  89. data/lib/milk_tea/tooling/cli/commands/parse.rb +3 -3
  90. data/lib/milk_tea/tooling/cli/commands/test.rb +3 -3
  91. data/lib/milk_tea/tooling/cli/commands/toolchain.rb +1 -1
  92. data/lib/milk_tea/tooling/cli.rb +22 -34
  93. data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
  94. metadata +4 -5
  95. data/lib/milk_tea/core/cst_builder.rb +0 -19
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
  97. data/lib/milk_tea/core/token_stream.rb +0 -30
@@ -10,1140 +10,1142 @@ require_relative "declarations"
10
10
  require_relative "calls"
11
11
 
12
12
  module MilkTea
13
- module LowererBlock
14
- def lower_block(statements, env:, active_defers:, return_type:, loop_flow:, allow_return: true)
15
- local_env = duplicate_env(env)
16
- lowered = []
17
- local_defers = []
18
- local_env[:return_context] = {
19
- return_type:,
20
- active_defers:,
21
- local_defers:,
22
- allow_return:,
23
- }
24
-
25
- statements.each do |statement|
26
- case statement
27
- when AST::DeferStmt
28
- local_defers << lower_defer_cleanup_body(statement.body, env: local_env, return_type:)
29
- when AST::UnsafeStmt
30
- lower_block_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
31
- when AST::LocalDecl
32
- lower_block_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
33
- when AST::Assignment
34
- lower_block_assignment_stmt(statement, lowered:, local_defers:, local_env:)
35
- when AST::IfStmt
36
- lower_block_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
37
- when AST::MatchStmt
38
- lower_block_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
39
- when AST::StaticAssert
40
- lowered << lower_static_assert(statement, env: local_env)
41
- when AST::ForStmt
42
- if statement.inline
43
- lowered.concat(lower_inline_for_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
44
- else
45
- lowered << lower_for_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
46
- end
47
- when AST::ParallelBlockStmt
48
- lowered << lower_parallel_block_stmt(statement, env: local_env, active_defers: active_defers + local_defers)
49
- when AST::GatherStmt
50
- lowered << lower_gather_stmt(statement, env: local_env)
51
- when AST::WhileStmt
52
- if statement.inline
53
- lowered.concat(lower_inline_while_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
13
+ module Lowering
14
+ module Block
15
+ def lower_block(statements, env:, active_defers:, return_type:, loop_flow:, allow_return: true)
16
+ local_env = duplicate_env(env)
17
+ lowered = []
18
+ local_defers = []
19
+ local_env[:return_context] = {
20
+ return_type:,
21
+ active_defers:,
22
+ local_defers:,
23
+ allow_return:,
24
+ }
25
+
26
+ statements.each do |statement|
27
+ case statement
28
+ when AST::DeferStmt
29
+ local_defers << lower_defer_cleanup_body(statement.body, env: local_env, return_type:)
30
+ when AST::UnsafeStmt
31
+ lower_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
32
+ when AST::LocalDecl
33
+ lower_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
34
+ when AST::Assignment
35
+ lower_assignment_stmt(statement, lowered:, local_defers:, local_env:)
36
+ when AST::IfStmt
37
+ lower_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
38
+ when AST::MatchStmt
39
+ lower_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
40
+ when AST::StaticAssert
41
+ lowered << lower_static_assert(statement, env: local_env)
42
+ when AST::ForStmt
43
+ if statement.inline
44
+ lowered.concat(lower_inline_for_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
45
+ else
46
+ lowered << lower_for_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
47
+ end
48
+ when AST::ParallelBlockStmt
49
+ lowered << lower_parallel_block_stmt(statement, env: local_env, active_defers: active_defers + local_defers)
50
+ when AST::GatherStmt
51
+ lowered << lower_gather_stmt(statement, env: local_env)
52
+ when AST::WhileStmt
53
+ if statement.inline
54
+ lowered.concat(lower_inline_while_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
55
+ else
56
+ lowered << lower_while_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
57
+ end
58
+ when AST::PassStmt
59
+ nil
60
+ when AST::BreakStmt
61
+ raise LoweringError.new("break must be inside a loop",
62
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
63
+
64
+ lowered.concat(lower_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers]))
65
+ when AST::ContinueStmt
66
+ raise LoweringError.new("continue must be inside a loop",
67
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
68
+
69
+ lowered.concat(lower_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers]))
70
+ when AST::ReturnStmt
71
+ lower_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
72
+ when AST::ExpressionStmt
73
+ lower_expression_stmt(statement, lowered:, local_defers:, local_env:)
74
+ when AST::EmitStmt
75
+ lowered.concat(lower_emit_stmt(statement, env: local_env))
76
+ when AST::WhenStmt
77
+ lower_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
54
78
  else
55
- lowered << lower_while_stmt(statement, env: local_env, active_defers: active_defers + local_defers, return_type:, allow_return:)
79
+ raise LoweringError.new("unsupported statement #{statement.class.name}",
80
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
56
81
  end
57
- when AST::PassStmt
58
- nil
59
- when AST::BreakStmt
60
- raise LoweringError.new("break must be inside a loop",
61
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
62
-
63
- lowered.concat(lower_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers]))
64
- when AST::ContinueStmt
65
- raise LoweringError.new("continue must be inside a loop",
66
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
67
-
68
- lowered.concat(lower_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers]))
69
- when AST::ReturnStmt
70
- lower_block_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
71
- when AST::ExpressionStmt
72
- lower_block_expression_stmt(statement, lowered:, local_defers:, local_env:)
73
- when AST::EmitStmt
74
- lowered.concat(lower_emit_stmt(statement, env: local_env))
75
- when AST::WhenStmt
76
- lower_block_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
77
- else
78
- raise LoweringError.new("unsupported statement #{statement.class.name}",
79
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
80
82
  end
81
- end
82
83
 
83
- unless terminating_ir_statement?(lowered.last)
84
- lowered.concat(cleanup_statements(local_defers, []))
84
+ unless terminating_ir_statement?(lowered.last)
85
+ lowered.concat(cleanup_statements(local_defers, []))
86
+ end
87
+ lowered
85
88
  end
86
- lowered
87
- end
88
89
 
89
- def lower_proc_expression_for_local(expression, env:, local_name:, proc_type:)
90
- captures = proc_capture_entries(expression, env)
91
- captures.each do |capture|
92
- if ref_type?(capture[:type]) || contains_ref_type?(capture[:type])
93
- raise LoweringError.new("proc capture #{capture[:name]} cannot use ref types",
94
- line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
90
+ def lower_proc_expression_for_local(expression, env:, local_name:, proc_type:)
91
+ captures = proc_capture_entries(expression, env)
92
+ captures.each do |capture|
93
+ if ref_type?(capture[:type]) || contains_ref_type?(capture[:type])
94
+ raise LoweringError.new("proc capture #{capture[:name]} cannot use ref types",
95
+ line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
96
+ end
95
97
  end
96
- end
97
98
 
98
- proc_id = fresh_proc_symbol
99
- invoke_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__invoke"
100
- release_linkage_name = "#{@ctx.module_prefix}__proc_#{proc_id}__release"
101
- retain_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__retain"
102
- env_struct_type = nil
103
- setup = []
104
-
105
- env_value = if captures.empty?
106
- IR::NullLiteral.new(type: proc_env_pointer_type)
107
- else
108
- env_struct_type = Types::Struct.new("#{@ctx.module_prefix}__proc_#{proc_id}__env").define_fields(
109
- { "__mt_ref_count" => @ctx.types.fetch("ptr_uint") }.merge(captures.each_with_object({}) { |capture, fields| fields[capture[:field_name]] = capture[:type] }),
110
- )
111
- @artifacts.synthetic_structs << IR::StructDecl.new(
112
- name: env_struct_type.name,
113
- linkage_name: env_struct_type.name,
114
- fields: [IR::Field.new(name: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")), *captures.map { |capture| IR::Field.new(name: capture[:field_name], type: capture[:type]) }],
115
- packed: false,
116
- alignment: nil,
117
- )
99
+ proc_id = fresh_proc_symbol
100
+ invoke_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__invoke"
101
+ release_linkage_name = "#{@ctx.module_prefix}__proc_#{proc_id}__release"
102
+ retain_c_name = "#{@ctx.module_prefix}__proc_#{proc_id}__retain"
103
+ env_struct_type = nil
104
+ setup = []
118
105
 
119
- env_pointer_type = pointer_to(env_struct_type)
120
- env_name = fresh_c_temp_name(env, "#{local_name}_env")
121
- raw_allocation = IR::Call.new(
122
- callee: "mt_async_alloc",
123
- arguments: [IR::SizeofExpr.new(target_type: env_struct_type, type: @ctx.types.fetch("ptr_uint"))],
124
- type: proc_env_pointer_type,
125
- )
126
- setup << IR::LocalDecl.new(
127
- name: env_name,
128
- linkage_name: env_name,
129
- type: env_pointer_type,
130
- value: IR::Cast.new(target_type: env_pointer_type, expression: raw_allocation, type: env_pointer_type),
131
- )
132
- env_pointer = IR::Name.new(name: env_name, type: env_pointer_type, pointer: false)
133
- setup << IR::Assignment.new(
134
- target: IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")),
135
- operator: "=",
136
- value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
137
- )
138
- captures.each do |capture|
106
+ env_value = if captures.empty?
107
+ IR::NullLiteral.new(type: proc_env_pointer_type)
108
+ else
109
+ env_struct_type = Types::Struct.new("#{@ctx.module_prefix}__proc_#{proc_id}__env").define_fields(
110
+ { "__mt_ref_count" => @ctx.types.fetch("ptr_uint") }.merge(captures.each_with_object({}) { |capture, fields| fields[capture[:field_name]] = capture[:type] }),
111
+ )
112
+ @artifacts.synthetic_structs << IR::StructDecl.new(
113
+ name: env_struct_type.name,
114
+ linkage_name: env_struct_type.name,
115
+ fields: [IR::Field.new(name: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")), *captures.map { |capture| IR::Field.new(name: capture[:field_name], type: capture[:type]) }],
116
+ packed: false,
117
+ alignment: nil,
118
+ )
119
+
120
+ env_pointer_type = pointer_to(env_struct_type)
121
+ env_name = fresh_c_temp_name(env, "#{local_name}_env")
122
+ raw_allocation = IR::Call.new(
123
+ callee: "mt_async_alloc",
124
+ arguments: [IR::SizeofExpr.new(target_type: env_struct_type, type: @ctx.types.fetch("ptr_uint"))],
125
+ type: proc_env_pointer_type,
126
+ )
127
+ setup << IR::LocalDecl.new(
128
+ name: env_name,
129
+ linkage_name: env_name,
130
+ type: env_pointer_type,
131
+ value: IR::Cast.new(target_type: env_pointer_type, expression: raw_allocation, type: env_pointer_type),
132
+ )
133
+ env_pointer = IR::Name.new(name: env_name, type: env_pointer_type, pointer: false)
139
134
  setup << IR::Assignment.new(
140
- target: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
135
+ target: IR::Member.new(receiver: env_pointer, member: "__mt_ref_count", type: @ctx.types.fetch("ptr_uint")),
141
136
  operator: "=",
142
- value: lower_expression(AST::Identifier.new(name: capture[:name]), env:, expected_type: capture[:type]),
137
+ value: IR::IntegerLiteral.new(value: 1, type: @ctx.types.fetch("ptr_uint")),
143
138
  )
144
- end
145
- captures.each do |capture|
146
- next unless contains_proc_storage_type?(capture[:type])
139
+ captures.each do |capture|
140
+ setup << IR::Assignment.new(
141
+ target: IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type]),
142
+ operator: "=",
143
+ value: lower_expression(AST::Identifier.new(name: capture[:name]), env:, expected_type: capture[:type]),
144
+ )
145
+ end
146
+ captures.each do |capture|
147
+ next unless contains_proc_storage_type?(capture[:type])
147
148
 
148
- member = IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type])
149
- setup.concat(lower_proc_contained_retain_statements(member, capture[:type]))
149
+ member = IR::Member.new(receiver: env_pointer, member: capture[:field_name], type: capture[:type])
150
+ setup.concat(lower_proc_contained_retain_statements(member, capture[:type]))
151
+ end
152
+ IR::Cast.new(target_type: proc_env_pointer_type, expression: env_pointer, type: proc_env_pointer_type)
150
153
  end
151
- IR::Cast.new(target_type: proc_env_pointer_type, expression: env_pointer, type: proc_env_pointer_type)
152
- end
153
154
 
154
- @artifacts.synthetic_functions << build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
155
- @artifacts.synthetic_functions << build_proc_release_function(release_linkage_name, env_struct_type)
156
- @artifacts.synthetic_functions << build_proc_retain_function(retain_c_name, env_struct_type)
157
-
158
- [
159
- setup,
160
- IR::AggregateLiteral.new(
161
- type: proc_type,
162
- fields: [
163
- IR::AggregateField.new(name: "env", value: env_value),
164
- IR::AggregateField.new(name: "invoke", value: IR::Name.new(name: invoke_c_name, type: proc_invoke_function_type(proc_type), pointer: false)),
165
- IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: proc_release_function_type, pointer: false)),
166
- IR::AggregateField.new(name: "retain", value: IR::Name.new(name: retain_c_name, type: proc_retain_function_type, pointer: false)),
167
- ],
168
- ),
169
- ]
170
- end
155
+ @artifacts.synthetic_functions << build_proc_invoke_function(expression, proc_type, captures, env_struct_type, invoke_c_name)
156
+ @artifacts.synthetic_functions << build_proc_release_function(release_linkage_name, env_struct_type)
157
+ @artifacts.synthetic_functions << build_proc_retain_function(retain_c_name, env_struct_type)
158
+
159
+ [
160
+ setup,
161
+ IR::AggregateLiteral.new(
162
+ type: proc_type,
163
+ fields: [
164
+ IR::AggregateField.new(name: "env", value: env_value),
165
+ IR::AggregateField.new(name: "invoke", value: IR::Name.new(name: invoke_c_name, type: proc_invoke_function_type(proc_type), pointer: false)),
166
+ IR::AggregateField.new(name: "release", value: IR::Name.new(name: release_linkage_name, type: proc_release_function_type, pointer: false)),
167
+ IR::AggregateField.new(name: "retain", value: IR::Name.new(name: retain_c_name, type: proc_retain_function_type, pointer: false)),
168
+ ],
169
+ ),
170
+ ]
171
+ end
171
172
 
172
- def lower_inline_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
173
- iterable = compile_time_const_value(statement.iterables.first, env:)
174
- return [] unless iterable.is_a?(Array) && !iterable.empty?
175
-
176
- loop_var_name = statement.bindings.first.name
177
- loop_var_type = inline_loop_element_type(iterable.first)
178
- lowered = []
179
-
180
- iterable.each do |element|
181
- iter_env = duplicate_env(env)
182
- current_actual_scope(iter_env[:scopes])[loop_var_name] = local_binding(
183
- type: loop_var_type,
184
- linkage_name: c_local_name(loop_var_name),
185
- mutable: false,
186
- pointer: false,
187
- const_value: element,
188
- )
189
- emit_stmts, other_stmts = statement.body.partition { |s| s.is_a?(AST::EmitStmt) }
190
- emit_stmts.each do |emit_stmt|
191
- lowered.concat(lower_emit_stmt(emit_stmt, env: iter_env))
192
- rescue LoweringError
193
- # emit for compile-time-only constructs — skip
194
- end
173
+ def lower_inline_for_stmt(statement, env:, active_defers:, return_type:, allow_return:)
174
+ iterable = compile_time_const_value(statement.iterables.first, env:)
175
+ return [] unless iterable.is_a?(Array) && !iterable.empty?
176
+
177
+ loop_var_name = statement.bindings.first.name
178
+ loop_var_type = inline_loop_element_type(iterable.first)
179
+ lowered = []
180
+
181
+ iterable.each do |element|
182
+ iter_env = duplicate_env(env)
183
+ current_actual_scope(iter_env[:scopes])[loop_var_name] = local_binding(
184
+ type: loop_var_type,
185
+ linkage_name: c_local_name(loop_var_name),
186
+ mutable: false,
187
+ pointer: false,
188
+ const_value: element,
189
+ )
190
+ emit_stmts, other_stmts = statement.body.partition { |s| s.is_a?(AST::EmitStmt) }
191
+ emit_stmts.each do |emit_stmt|
192
+ lowered.concat(lower_emit_stmt(emit_stmt, env: iter_env))
193
+ rescue LoweringError
194
+ # emit for compile-time-only constructs — skip
195
+ end
195
196
 
196
- unless other_stmts.empty?
197
- saved_type_env = @type_resolution_env
198
- saved_bypass = @bypass_sema_type_cache
199
- # Each unrolled iteration may bind the loop var to a different value
200
- # (e.g. a field whose `.type` differs per field), so expression types
201
- # must be re-inferred per iteration instead of reusing the single
202
- # sema-cached type computed for the representative first element.
203
- @type_resolution_env = iter_env
204
- @bypass_sema_type_cache = true
205
- begin
206
- body = lower_block(other_stmts, env: iter_env, active_defers:, return_type:, loop_flow: nil, allow_return:)
207
- ensure
208
- @type_resolution_env = saved_type_env
209
- @bypass_sema_type_cache = saved_bypass
197
+ unless other_stmts.empty?
198
+ saved_type_env = @type_resolution_env
199
+ saved_bypass = @bypass_sema_type_cache
200
+ # Each unrolled iteration may bind the loop var to a different value
201
+ # (e.g. a field whose `.type` differs per field), so expression types
202
+ # must be re-inferred per iteration instead of reusing the single
203
+ # sema-cached type computed for the representative first element.
204
+ @type_resolution_env = iter_env
205
+ @bypass_sema_type_cache = true
206
+ begin
207
+ body = lower_block(other_stmts, env: iter_env, active_defers:, return_type:, loop_flow: nil, allow_return:)
208
+ ensure
209
+ @type_resolution_env = saved_type_env
210
+ @bypass_sema_type_cache = saved_bypass
211
+ end
212
+ lowered << IR::BlockStmt.new(body:) unless body.empty?
210
213
  end
211
- lowered << IR::BlockStmt.new(body:) unless body.empty?
212
214
  end
215
+
216
+ lowered
213
217
  end
214
218
 
215
- lowered
216
- end
219
+ def lower_inline_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
220
+ condition = compile_time_const_value(statement.condition, env:)
221
+ return [] unless condition
217
222
 
218
- def lower_inline_while_stmt(statement, env:, active_defers:, return_type:, allow_return:)
219
- condition = compile_time_const_value(statement.condition, env:)
220
- return [] unless condition
223
+ iterations = 0
224
+ max_iterations = 10_000
225
+ lowered = []
221
226
 
222
- iterations = 0
223
- max_iterations = 10_000
224
- lowered = []
227
+ while compile_time_const_value(statement.condition, env:) && iterations < max_iterations
228
+ body = lower_block(statement.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
229
+ lowered << IR::BlockStmt.new(body:) unless body.empty?
230
+ iterations += 1
231
+ end
225
232
 
226
- while compile_time_const_value(statement.condition, env:) && iterations < max_iterations
227
- body = lower_block(statement.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
228
- lowered << IR::BlockStmt.new(body:) unless body.empty?
229
- iterations += 1
233
+ lowered
230
234
  end
231
235
 
232
- lowered
233
- end
234
-
235
- def lower_inline_if_stmt(statement, env:, active_defers:, return_type:, allow_return:)
236
- chosen_branch = statement.branches.find do |branch|
237
- ct_value = compile_time_const_value(branch.condition, env:)
238
- ct_value == true
239
- end
236
+ def lower_inline_if_stmt(statement, env:, active_defers:, return_type:, allow_return:)
237
+ chosen_branch = statement.branches.find do |branch|
238
+ ct_value = compile_time_const_value(branch.condition, env:)
239
+ ct_value == true
240
+ end
240
241
 
241
- if chosen_branch
242
- lower_block(chosen_branch.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
243
- elsif statement.else_body
244
- lower_block(statement.else_body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
245
- else
246
- []
242
+ if chosen_branch
243
+ lower_block(chosen_branch.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
244
+ elsif statement.else_body
245
+ lower_block(statement.else_body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
246
+ else
247
+ []
248
+ end
247
249
  end
248
- end
249
-
250
- def lower_inline_match_stmt(statement, env:, active_defers:, return_type:, allow_return:)
251
- scrutinee = compile_time_const_value(statement.expression, env:)
252
- return [] unless scrutinee
253
250
 
254
- chosen_arm = statement.arms.find do |arm|
255
- scrutinee == compile_time_const_value(arm.pattern, env:)
256
- end
251
+ def lower_inline_match_stmt(statement, env:, active_defers:, return_type:, allow_return:)
252
+ scrutinee = compile_time_const_value(statement.expression, env:)
253
+ return [] unless scrutinee
257
254
 
258
- return [] unless chosen_arm
255
+ chosen_arm = statement.arms.find do |arm|
256
+ scrutinee == compile_time_const_value(arm.pattern, env:)
257
+ end
259
258
 
260
- lower_block(chosen_arm.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
261
- end
259
+ return [] unless chosen_arm
262
260
 
263
- def inline_loop_element_type(element)
264
- if element.is_a?(Types::FieldHandle)
265
- @ctx.types.fetch("field_handle")
266
- elsif element.is_a?(Types::CallableHandle)
267
- @ctx.types.fetch("callable_handle")
268
- elsif element.is_a?(Types::AttributeHandle)
269
- @ctx.types.fetch("attribute_handle")
270
- elsif element.is_a?(Types::MemberHandle)
271
- @ctx.types.fetch("member_handle")
272
- elsif element.is_a?(Types::StructHandle)
273
- @ctx.types.fetch("struct_handle")
274
- else
275
- @ctx.types.fetch("int")
261
+ lower_block(chosen_arm.body, env:, active_defers:, return_type:, loop_flow: nil, allow_return:)
276
262
  end
277
- end
278
263
 
279
- def lower_emit_stmt(statement, env:)
280
- decl = statement.declaration
281
- case decl
282
- when AST::FunctionDef
283
- lower_emitted_function(decl, env:)
284
- when AST::StructDecl
285
- lower_emitted_struct(decl)
286
- when AST::ConstDecl
287
- lower_emitted_const(decl, env:)
288
- else
289
- raise LoweringError.new("emit is not supported for #{decl.class.name}",
290
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
264
+ def inline_loop_element_type(element)
265
+ if element.is_a?(Types::FieldHandle)
266
+ @ctx.types.fetch("field_handle")
267
+ elsif element.is_a?(Types::CallableHandle)
268
+ @ctx.types.fetch("callable_handle")
269
+ elsif element.is_a?(Types::AttributeHandle)
270
+ @ctx.types.fetch("attribute_handle")
271
+ elsif element.is_a?(Types::MemberHandle)
272
+ @ctx.types.fetch("member_handle")
273
+ elsif element.is_a?(Types::StructHandle)
274
+ @ctx.types.fetch("struct_handle")
275
+ else
276
+ @ctx.types.fetch("int")
277
+ end
291
278
  end
292
- end
293
279
 
294
- def lower_emitted_function(decl, env:)
295
- params = []
296
- param_setup = []
297
- return_type = decl.return_type ? resolve_type_ref(decl.return_type) : @ctx.types.fetch("void")
298
- linkage_name = "#{@ctx.module_prefix}#{decl.name}"
299
-
300
- decl.params.each do |param|
301
- param_type = param.type ? resolve_type_ref(param.type) : @ctx.types.fetch("int")
302
- param_linkage_name = c_local_name(param.name)
303
- params << IR::Param.new(name: param.name, linkage_name: param_linkage_name, type: param_type, pointer: false)
304
- env[:scopes].last[param.name] = local_binding(type: param_type, linkage_name: param_linkage_name, mutable: false, pointer: false)
280
+ def lower_emit_stmt(statement, env:)
281
+ decl = statement.declaration
282
+ case decl
283
+ when AST::FunctionDef
284
+ lower_emitted_function(decl, env:)
285
+ when AST::StructDecl
286
+ lower_emitted_struct(decl)
287
+ when AST::ConstDecl
288
+ lower_emitted_const(decl, env:)
289
+ else
290
+ raise LoweringError.new("emit is not supported for #{decl.class.name}",
291
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
292
+ end
305
293
  end
306
294
 
307
- body = lower_block(decl.body, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: true)
308
- body = param_setup + body
309
- func = IR::Function.new(name: decl.name, linkage_name:, params:, return_type:, body:, entry_point: false, method_receiver_param: false)
310
- @artifacts.emitted_declarations << func
311
- []
312
- end
295
+ def lower_emitted_function(decl, env:)
296
+ params = []
297
+ param_setup = []
298
+ return_type = decl.return_type ? resolve_type_ref(decl.return_type) : @ctx.types.fetch("void")
299
+ linkage_name = "#{@ctx.module_prefix}#{decl.name}"
300
+
301
+ decl.params.each do |param|
302
+ param_type = param.type ? resolve_type_ref(param.type) : @ctx.types.fetch("int")
303
+ param_linkage_name = c_local_name(param.name)
304
+ params << IR::Param.new(name: param.name, linkage_name: param_linkage_name, type: param_type, pointer: false)
305
+ env[:scopes].last[param.name] = local_binding(type: param_type, linkage_name: param_linkage_name, mutable: false, pointer: false)
306
+ end
313
307
 
314
- def lower_emitted_struct(decl)
315
- fields = decl.fields.map do |field|
316
- field_type = field.type ? resolve_type_ref(field.type) : @ctx.types.fetch("int")
317
- IR::Field.new(name: field.name, type: field_type)
308
+ body = lower_block(decl.body, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: true)
309
+ body = param_setup + body
310
+ func = IR::Function.new(name: decl.name, linkage_name:, params:, return_type:, body:, entry_point: false, method_receiver_param: false)
311
+ @artifacts.emitted_declarations << func
312
+ []
318
313
  end
319
- linkage_name = "#{@ctx.module_prefix}#{decl.name}"
320
- struct_decl = IR::StructDecl.new(name: decl.name, linkage_name:, fields:, packed: decl.respond_to?(:packed) ? decl.packed : false, alignment: nil)
321
- @artifacts.emitted_declarations << struct_decl
322
- []
323
- end
324
314
 
325
- def lower_destructure_decl(statement, env:, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
326
- value_type = infer_expression_type(statement.value, env:)
327
- setup, prepared_value, _cleanups = prepare_expression_with_cleanups(statement.value, env:, expected_type: value_type)
328
- lowered.concat(setup)
329
- value = lower_contextual_expression(prepared_value, env:, expected_type: value_type)
330
-
331
- temp_name = fresh_c_temp_name(env, "destructure_val")
332
- lowered << IR::LocalDecl.new(name: temp_name, linkage_name: temp_name, type: value_type, value:)
333
-
334
- if statement.destructure_type_name
335
- # Struct destructure: find fields by name in the struct type
336
- destructure_type = value_type
337
- fields = destructure_type.fields
338
- statement.destructure_bindings.each do |name|
339
- next if name == "_"
340
- field_type = fields[name]
341
- field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: destructure_type, pointer: false), member: name, type: field_type)
342
- decl_c_name = c_local_name(name)
343
- lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
344
- env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
345
- end
346
- else
347
- # Tuple destructure: index by position
348
- statement.destructure_bindings.each_with_index do |name, index|
349
- next if name == "_"
350
- field_name = value_type.field_names[index]
351
- field_type = value_type.element_types[index]
352
- field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: value_type, pointer: false), member: field_name, type: field_type)
353
- decl_c_name = c_local_name(name)
354
- lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
355
- env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
315
+ def lower_emitted_struct(decl)
316
+ fields = decl.fields.map do |field|
317
+ field_type = field.type ? resolve_type_ref(field.type) : @ctx.types.fetch("int")
318
+ IR::Field.new(name: field.name, type: field_type)
356
319
  end
320
+ linkage_name = "#{@ctx.module_prefix}#{decl.name}"
321
+ struct_decl = IR::StructDecl.new(name: decl.name, linkage_name:, fields:, packed: decl.respond_to?(:packed) ? decl.packed : false, alignment: nil)
322
+ @artifacts.emitted_declarations << struct_decl
323
+ []
357
324
  end
358
- end
359
-
360
- def lower_emitted_const(decl, env:)
361
- value = lower_static_storage_initializer(decl.value, env:, expected_type: decl.type ? resolve_type_ref(decl.type) : nil)
362
- type = decl.type ? resolve_type_ref(decl.type) : infer_expression_type(decl.value, env:)
363
- linkage_name = value_c_name(decl.name)
364
- constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:, line: decl.line, path: @ctx.current_analysis_path)
365
- @artifacts.emitted_declarations << constant
366
- []
367
- end
368
325
 
369
- def variant_self_referencing_field?(field_type, scrutinee_type)
370
- return false unless field_type.is_a?(Types::Variant) || field_type.is_a?(Types::VariantInstance)
371
- return false unless scrutinee_type.is_a?(Types::Variant) || scrutinee_type.is_a?(Types::VariantInstance)
372
- field_type == scrutinee_type
373
- end
326
+ def lower_destructure_decl(statement, env:, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
327
+ value_type = infer_expression_type(statement.value, env:)
328
+ setup, prepared_value, _cleanups = prepare_expression_with_cleanups(statement.value, env:, expected_type: value_type)
329
+ lowered.concat(setup)
330
+ value = lower_contextual_expression(prepared_value, env:, expected_type: value_type)
331
+
332
+ temp_name = fresh_c_temp_name(env, "destructure_val")
333
+ lowered << IR::LocalDecl.new(name: temp_name, linkage_name: temp_name, type: value_type, value:)
334
+
335
+ if statement.destructure_type_name
336
+ # Struct destructure: find fields by name in the struct type
337
+ destructure_type = value_type
338
+ fields = destructure_type.fields
339
+ statement.destructure_bindings.each do |name|
340
+ next if name == "_"
341
+ field_type = fields[name]
342
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: destructure_type, pointer: false), member: name, type: field_type)
343
+ decl_c_name = c_local_name(name)
344
+ lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
345
+ env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
346
+ end
347
+ else
348
+ # Tuple destructure: index by position
349
+ statement.destructure_bindings.each_with_index do |name, index|
350
+ next if name == "_"
351
+ field_name = value_type.field_names[index]
352
+ field_type = value_type.element_types[index]
353
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: temp_name, type: value_type, pointer: false), member: field_name, type: field_type)
354
+ decl_c_name = c_local_name(name)
355
+ lowered << IR::LocalDecl.new(name: decl_c_name, linkage_name: decl_c_name, type: field_type, value: field_expr)
356
+ env[:scopes].last[name] = local_binding(type: field_type, linkage_name: decl_c_name, mutable: false, pointer: false)
357
+ end
358
+ end
359
+ end
374
360
 
375
- def dereference_if_self_ref(field_expr, field_type, scrutinee_type)
376
- if variant_self_referencing_field?(field_type, scrutinee_type)
377
- IR::Unary.new(operator: "*", operand: field_expr, type: field_type)
378
- else
379
- field_expr
361
+ def lower_emitted_const(decl, env:)
362
+ value = lower_static_storage_initializer(decl.value, env:, expected_type: decl.type ? resolve_type_ref(decl.type) : nil)
363
+ type = decl.type ? resolve_type_ref(decl.type) : infer_expression_type(decl.value, env:)
364
+ linkage_name = value_c_name(decl.name)
365
+ constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:, line: decl.line, path: @ctx.current_analysis_path)
366
+ @artifacts.emitted_declarations << constant
367
+ []
380
368
  end
381
- end
382
369
 
383
- def lower_block_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
384
- body = lower_block(
385
- statement.body,
386
- env: local_env,
387
- active_defers: active_defers + local_defers,
388
- return_type:,
389
- loop_flow: nested_loop_flow(loop_flow, local_defers),
390
- allow_return:,
391
- )
392
- lowered << IR::BlockStmt.new(body:)
393
- end
370
+ def variant_self_referencing_field?(field_type, scrutinee_type)
371
+ return false unless field_type.is_a?(Types::Variant) || field_type.is_a?(Types::VariantInstance)
372
+ return false unless scrutinee_type.is_a?(Types::Variant) || scrutinee_type.is_a?(Types::VariantInstance)
373
+ field_type == scrutinee_type
374
+ end
394
375
 
395
- def lower_block_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
396
- discriminant = compile_time_const_value(statement.discriminant)
397
- chosen_branch = statement.branches.find do |branch|
398
- discriminant == compile_time_const_value(branch.pattern)
376
+ def dereference_if_self_ref(field_expr, field_type, scrutinee_type)
377
+ if variant_self_referencing_field?(field_type, scrutinee_type)
378
+ IR::Unary.new(operator: "*", operand: field_expr, type: field_type)
379
+ else
380
+ field_expr
381
+ end
399
382
  end
400
383
 
401
- if chosen_branch
402
- lowered.concat(lower_block(
403
- chosen_branch.body,
404
- env: local_env,
405
- active_defers:,
406
- return_type:,
407
- loop_flow:,
408
- allow_return:,
409
- ))
410
- elsif statement.else_body
411
- lowered.concat(lower_block(
412
- statement.else_body,
384
+ def lower_unsafe_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
385
+ body = lower_block(
386
+ statement.body,
413
387
  env: local_env,
414
- active_defers:,
388
+ active_defers: active_defers + local_defers,
415
389
  return_type:,
416
- loop_flow:,
390
+ loop_flow: nested_loop_flow(loop_flow, local_defers),
417
391
  allow_return:,
418
- ))
392
+ )
393
+ lowered << IR::BlockStmt.new(body:)
419
394
  end
420
- end
421
395
 
422
- def lower_block_expression_stmt(statement, lowered:, local_defers:, local_env:)
423
- if (format_sink_statements = lower_explicit_format_sink_expression_statement(statement.expression, env: local_env, line: statement.line))
424
- lowered.concat(format_sink_statements)
425
- return
426
- end
396
+ def lower_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
397
+ discriminant = compile_time_const_value(statement.discriminant)
398
+ chosen_branch = statement.branches.find do |branch|
399
+ discriminant == compile_time_const_value(branch.pattern)
400
+ end
427
401
 
428
- expression_expected_type = if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?"
429
- nil
430
- else
431
- infer_expression_type(statement.expression, env: local_env)
432
- end
433
- prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
434
- statement.expression,
435
- env: local_env,
436
- expected_type: expression_expected_type,
437
- allow_root_statement_foreign: true,
438
- allow_void_propagation: true,
439
- )
440
- lowered.concat(prepared_setup)
441
- if prepared_expression && (foreign_call = foreign_call_info(prepared_expression, local_env))
442
- setup, _value = lower_foreign_call_statement(
443
- foreign_call,
444
- env: local_env,
445
- expected_type: foreign_call[:binding].type.return_type,
446
- statement_position: true,
447
- discard_result: true,
448
- )
449
- lowered.concat(setup)
450
- lowered.concat(prepared_cleanups.flat_map(&:itself))
451
- local_env[:scopes] = scopes_with_refinements(local_env[:scopes], consuming_foreign_call_refinements(foreign_call, local_env))
452
- elsif prepared_expression
453
- lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env: local_env), line: statement.line, path: @ctx.current_analysis_path)
454
- lowered.concat(prepared_cleanups.flat_map(&:itself))
455
- else
456
- lowered.concat(prepared_cleanups.flat_map(&:itself))
402
+ if chosen_branch
403
+ lowered.concat(lower_block(
404
+ chosen_branch.body,
405
+ env: local_env,
406
+ active_defers:,
407
+ return_type:,
408
+ loop_flow:,
409
+ allow_return:,
410
+ ))
411
+ elsif statement.else_body
412
+ lowered.concat(lower_block(
413
+ statement.else_body,
414
+ env: local_env,
415
+ active_defers:,
416
+ return_type:,
417
+ loop_flow:,
418
+ allow_return:,
419
+ ))
420
+ end
457
421
  end
458
- end
459
422
 
460
- def lower_block_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
461
- raise LoweringError.new("return is not allowed inside defer blocks",
462
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless allow_return
423
+ def lower_expression_stmt(statement, lowered:, local_defers:, local_env:)
424
+ if (format_sink_statements = lower_explicit_format_sink_expression_statement(statement.expression, env: local_env, line: statement.line))
425
+ lowered.concat(format_sink_statements)
426
+ return
427
+ end
463
428
 
464
- value = nil
465
- prepared_setup = []
466
- prepared_value = statement.value
467
- prepared_cleanups = []
468
- if statement.value
469
- prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
470
- statement.value,
429
+ expression_expected_type = if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?"
430
+ nil
431
+ else
432
+ infer_expression_type(statement.expression, env: local_env)
433
+ end
434
+ prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
435
+ statement.expression,
471
436
  env: local_env,
472
- expected_type: return_type,
437
+ expected_type: expression_expected_type,
473
438
  allow_root_statement_foreign: true,
474
- materialize_array_calls: !array_type?(return_type),
439
+ allow_void_propagation: true,
475
440
  )
476
441
  lowered.concat(prepared_setup)
477
- end
478
- if prepared_value && (foreign_call = foreign_call_info(prepared_value, local_env))
479
- setup, value = lower_foreign_call_statement(foreign_call, env: local_env, expected_type: return_type, statement_position: false)
480
- lowered.concat(setup)
481
- end
482
- value ||= prepared_value ? lower_contextual_expression(
483
- prepared_value,
484
- env: local_env,
485
- expected_type: return_type,
486
- contextual_int_to_float: contextual_int_to_float_target?(return_type),
487
- ) : nil
488
- if prepared_cleanups.any? && cstr_trackable_type?(return_type)
489
- raise LoweringError.new("formatted string temporaries cannot be returned as borrowed text; use std.fmt.format(f\"...\") when ownership must escape",
490
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
442
+ if prepared_expression && (foreign_call = foreign_call_info(prepared_expression, local_env))
443
+ setup, _value = lower_foreign_call_statement(
444
+ foreign_call,
445
+ env: local_env,
446
+ expected_type: foreign_call[:binding].type.return_type,
447
+ statement_position: true,
448
+ discard_result: true,
449
+ )
450
+ lowered.concat(setup)
451
+ lowered.concat(prepared_cleanups.flat_map(&:itself))
452
+ local_env[:scopes] = scopes_with_refinements(local_env[:scopes], consuming_foreign_call_refinements(foreign_call, local_env))
453
+ elsif prepared_expression
454
+ lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env: local_env), line: statement.line, path: @ctx.current_analysis_path)
455
+ lowered.concat(prepared_cleanups.flat_map(&:itself))
456
+ else
457
+ lowered.concat(prepared_cleanups.flat_map(&:itself))
458
+ end
491
459
  end
492
460
 
493
- prepared_cleanup_list = prepared_cleanups.flat_map(&:itself)
494
- if prepared_cleanup_list.any? && return_type.is_a?(Types::Struct) && struct_contains_string_field?(return_type)
495
- prepared_cleanup_list = prepared_cleanup_list.reject { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
496
- end
497
- cleanup = prepared_cleanup_list + cleanup_statements(local_defers, active_defers)
498
- needs_proc_retain = value && contains_proc_storage_type?(return_type) && !local_defers.empty? && !expression_contains_proc_expr?(prepared_value)
499
- if value && (!cleanup.empty? && !cleanup_safe_return_expression?(prepared_value) || needs_proc_retain)
500
- return_value_name = fresh_c_temp_name(local_env, "return_value")
501
- lowered << IR::LocalDecl.new(name: return_value_name, linkage_name: return_value_name, type: return_type, value:)
502
- value = IR::Name.new(name: return_value_name, type: return_type, pointer: false)
503
- end
504
- lowered.concat(lower_proc_contained_retain_statements(value, return_type)) if needs_proc_retain
505
- lowered.concat(cleanup)
506
- lowered << IR::ReturnStmt.new(value:, line: statement.line, path: @ctx.current_analysis_path)
507
- end
461
+ def lower_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
462
+ raise LoweringError.new("return is not allowed inside defer blocks",
463
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless allow_return
464
+
465
+ value = nil
466
+ prepared_setup = []
467
+ prepared_value = statement.value
468
+ prepared_cleanups = []
469
+ if statement.value
470
+ prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
471
+ statement.value,
472
+ env: local_env,
473
+ expected_type: return_type,
474
+ allow_root_statement_foreign: true,
475
+ materialize_array_calls: !array_type?(return_type),
476
+ )
477
+ lowered.concat(prepared_setup)
478
+ end
479
+ if prepared_value && (foreign_call = foreign_call_info(prepared_value, local_env))
480
+ setup, value = lower_foreign_call_statement(foreign_call, env: local_env, expected_type: return_type, statement_position: false)
481
+ lowered.concat(setup)
482
+ end
483
+ value ||= prepared_value ? lower_contextual_expression(
484
+ prepared_value,
485
+ env: local_env,
486
+ expected_type: return_type,
487
+ contextual_int_to_float: contextual_int_to_float_target?(return_type),
488
+ ) : nil
489
+ if prepared_cleanups.any? && cstr_trackable_type?(return_type)
490
+ raise LoweringError.new("formatted string temporaries cannot be returned as borrowed text; use std.fmt.format(f\"...\") when ownership must escape",
491
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
492
+ end
508
493
 
509
- def lower_block_assignment_stmt(statement, lowered:, local_defers:, local_env:)
510
- if statement.operator == "=" &&
511
- statement.target.is_a?(AST::IndexAccess) &&
512
- statement.target.index.is_a?(AST::RangeExpr) &&
513
- statement.value.is_a?(AST::ExpressionList)
514
- lowered.concat(lower_range_index_assignment(statement, env: local_env))
515
- return
494
+ prepared_cleanup_list = prepared_cleanups.flat_map(&:itself)
495
+ if prepared_cleanup_list.any? && return_type.is_a?(Types::Struct) && struct_contains_string_field?(return_type)
496
+ prepared_cleanup_list = prepared_cleanup_list.reject { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
497
+ end
498
+ cleanup = prepared_cleanup_list + cleanup_statements(local_defers, active_defers)
499
+ needs_proc_retain = value && contains_proc_storage_type?(return_type) && !local_defers.empty? && !expression_contains_proc_expr?(prepared_value)
500
+ if value && (!cleanup.empty? && !cleanup_safe_return_expression?(prepared_value) || needs_proc_retain)
501
+ return_value_name = fresh_c_temp_name(local_env, "return_value")
502
+ lowered << IR::LocalDecl.new(name: return_value_name, linkage_name: return_value_name, type: return_type, value:)
503
+ value = IR::Name.new(name: return_value_name, type: return_type, pointer: false)
504
+ end
505
+ lowered.concat(lower_proc_contained_retain_statements(value, return_type)) if needs_proc_retain
506
+ lowered.concat(cleanup)
507
+ lowered << IR::ReturnStmt.new(value:, line: statement.line, path: @ctx.current_analysis_path)
516
508
  end
517
- target = lower_assignment_target(statement.target, env: local_env)
518
- prepared_cleanups = []
519
- prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
520
- statement.value,
521
- env: local_env,
522
- expected_type: target.type,
523
- allow_root_statement_foreign: true,
524
- materialize_array_calls: !array_type?(target.type),
525
- )
526
- lowered.concat(prepared_setup)
527
- if (foreign_call = foreign_call_info(prepared_value, local_env))
528
- setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
529
- foreign_call,
509
+
510
+ def lower_assignment_stmt(statement, lowered:, local_defers:, local_env:)
511
+ if statement.operator == "=" &&
512
+ statement.target.is_a?(AST::IndexAccess) &&
513
+ statement.target.index.is_a?(AST::RangeExpr) &&
514
+ statement.value.is_a?(AST::ExpressionList)
515
+ lowered.concat(lower_range_index_assignment(statement, env: local_env))
516
+ return
517
+ end
518
+ target = lower_assignment_target(statement.target, env: local_env)
519
+ prepared_cleanups = []
520
+ prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
521
+ statement.value,
530
522
  env: local_env,
531
523
  expected_type: target.type,
532
- statement_position: false,
524
+ allow_root_statement_foreign: true,
525
+ materialize_array_calls: !array_type?(target.type),
533
526
  )
534
- lowered.concat(setup)
535
- raise LoweringError.new("foreign call used in assignment must return a value",
536
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
537
- raise LoweringError.new("consuming foreign calls must return void",
538
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
539
-
540
- lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
541
- lowered.concat(cleanup_statements)
542
- update_cstr_metadata_for_assignment!(statement, prepared_value, local_env)
543
- local_defers.concat(suppress_format_releases_for_assignment(prepared_cleanups, target.type))
544
- return
545
- else
546
- value = if statement.operator == "="
547
- lower_contextual_expression(
548
- prepared_value,
549
- env: local_env,
550
- expected_type: target.type,
551
- external_numeric: external_numeric_assignment_target?(statement.target, env: local_env),
552
- contextual_int_to_float: contextual_int_to_float_target?(target.type),
553
- )
554
- elsif ["+=", "-=", "*=", "/="].include?(statement.operator)
555
- lower_contextual_expression(
556
- prepared_value,
527
+ lowered.concat(prepared_setup)
528
+ if (foreign_call = foreign_call_info(prepared_value, local_env))
529
+ setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
530
+ foreign_call,
557
531
  env: local_env,
558
532
  expected_type: target.type,
559
- contextual_int_to_float: contextual_int_to_float_target?(target.type),
533
+ statement_position: false,
560
534
  )
535
+ lowered.concat(setup)
536
+ raise LoweringError.new("foreign call used in assignment must return a value",
537
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
538
+ raise LoweringError.new("consuming foreign calls must return void",
539
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
540
+
541
+ lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
542
+ lowered.concat(cleanup_statements)
543
+ update_cstr_metadata_for_assignment!(statement, prepared_value, local_env)
544
+ local_defers.concat(reject_format_releases_for_assignment(prepared_cleanups, target.type))
545
+ return
561
546
  else
562
- lower_expression(prepared_value, env: local_env, expected_type: target.type)
547
+ value = if statement.operator == "="
548
+ lower_contextual_expression(
549
+ prepared_value,
550
+ env: local_env,
551
+ expected_type: target.type,
552
+ external_numeric: external_numeric_assignment_target?(statement.target, env: local_env),
553
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
554
+ )
555
+ elsif ["+=", "-=", "*=", "/="].include?(statement.operator)
556
+ lower_contextual_expression(
557
+ prepared_value,
558
+ env: local_env,
559
+ expected_type: target.type,
560
+ contextual_int_to_float: contextual_int_to_float_target?(target.type),
561
+ )
562
+ else
563
+ lower_expression(prepared_value, env: local_env, expected_type: target.type)
564
+ end
565
+ end
566
+ if target.type.is_a?(Types::Nullable) && value && !value.type.is_a?(Types::Nullable) && !pointer_like_type?(target.type.base)
567
+ value = nullable_some_literal(target.type, value)
568
+ end
569
+ update_cstr_metadata_for_assignment!(statement, prepared_value, local_env)
570
+ local_defers.concat(reject_format_releases_for_assignment(prepared_cleanups, target.type))
571
+ operator = statement.operator
572
+ if ["+=", "-=", "*=", "/="].include?(operator) &&
573
+ (target.type.is_a?(Types::Vector) || target.type.is_a?(Types::Matrix) || target.type.is_a?(Types::Quaternion))
574
+ binary_op = operator[0...-1]
575
+ expanded = lower_vector_binary_operation(binary_op, target, target.type, value, value.type, target.type)
576
+ value = expanded || IR::Binary.new(operator: binary_op, left: target, right: value, type: target.type)
577
+ operator = "="
578
+ end
579
+ if operator == "=" && contains_proc_storage_type?(target.type)
580
+ rhs_name = fresh_c_temp_name(local_env, "proc_assign")
581
+ lowered << IR::LocalDecl.new(name: rhs_name, linkage_name: rhs_name, type: target.type, value:)
582
+ rhs = IR::Name.new(name: rhs_name, type: target.type, pointer: false)
583
+ lowered.concat(lower_proc_selective_retain_statements(rhs, statement.value, target.type))
584
+ lowered.concat(lower_proc_nullable_release_statements(target, target.type))
585
+ lowered << IR::Assignment.new(target:, operator: "=", value: rhs)
586
+ else
587
+ lowered << IR::Assignment.new(target:, operator:, value:)
563
588
  end
564
589
  end
565
- if target.type.is_a?(Types::Nullable) && value && !value.type.is_a?(Types::Nullable) && !pointer_like_type?(target.type.base)
566
- value = nullable_some_literal(target.type, value)
567
- end
568
- update_cstr_metadata_for_assignment!(statement, prepared_value, local_env)
569
- local_defers.concat(suppress_format_releases_for_assignment(prepared_cleanups, target.type))
570
- operator = statement.operator
571
- if ["+=", "-=", "*=", "/="].include?(operator) &&
572
- (target.type.is_a?(Types::Vector) || target.type.is_a?(Types::Matrix) || target.type.is_a?(Types::Quaternion))
573
- binary_op = operator[0...-1]
574
- expanded = lower_vector_binary_op(binary_op, target, target.type, value, value.type, target.type)
575
- value = expanded || IR::Binary.new(operator: binary_op, left: target, right: value, type: target.type)
576
- operator = "="
577
- end
578
- if operator == "=" && contains_proc_storage_type?(target.type)
579
- rhs_name = fresh_c_temp_name(local_env, "proc_assign")
580
- lowered << IR::LocalDecl.new(name: rhs_name, linkage_name: rhs_name, type: target.type, value:)
581
- rhs = IR::Name.new(name: rhs_name, type: target.type, pointer: false)
582
- lowered.concat(lower_proc_selective_retain_statements(rhs, statement.value, target.type))
583
- lowered.concat(lower_proc_contained_guarded_release_statements(target, target.type))
584
- lowered << IR::Assignment.new(target:, operator: "=", value: rhs)
585
- else
586
- lowered << IR::Assignment.new(target:, operator:, value:)
587
- end
588
- end
589
590
 
590
- def lower_block_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
591
- if statement.inline
592
- lowered.concat(lower_inline_if_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
593
- return
594
- end
591
+ def lower_if_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
592
+ if statement.inline
593
+ lowered.concat(lower_inline_if_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
594
+ return
595
+ end
595
596
 
596
- false_refinements = {}
597
- branch_entries = []
597
+ false_refinements = {}
598
+ branch_entries = []
598
599
 
599
- unless statement.branches.empty?
600
- if @bypass_sema_type_cache
601
- ct_cond = compile_time_const_value(statement.branches.first.condition, env: local_env)
602
- if ct_cond == true
603
- lowered.concat(lower_block(
604
- statement.branches.first.body,
605
- env: local_env,
606
- active_defers: active_defers + local_defers,
607
- return_type:,
608
- loop_flow: nested_loop_flow(loop_flow, local_defers),
609
- allow_return:,
610
- ))
611
- return
612
- elsif ct_cond == false
613
- if statement.else_body
600
+ unless statement.branches.empty?
601
+ if @bypass_sema_type_cache
602
+ ct_cond = compile_time_const_value(statement.branches.first.condition, env: local_env)
603
+ if ct_cond == true
614
604
  lowered.concat(lower_block(
615
- statement.else_body,
605
+ statement.branches.first.body,
616
606
  env: local_env,
617
607
  active_defers: active_defers + local_defers,
618
608
  return_type:,
619
609
  loop_flow: nested_loop_flow(loop_flow, local_defers),
620
610
  allow_return:,
621
611
  ))
612
+ return
613
+ elsif ct_cond == false
614
+ if statement.else_body
615
+ lowered.concat(lower_block(
616
+ statement.else_body,
617
+ env: local_env,
618
+ active_defers: active_defers + local_defers,
619
+ return_type:,
620
+ loop_flow: nested_loop_flow(loop_flow, local_defers),
621
+ allow_return:,
622
+ ))
623
+ end
624
+ return
622
625
  end
623
- return
624
626
  end
625
627
  end
626
- end
627
-
628
- statement.branches.each do |branch|
629
- branch_env = env_with_refinements(local_env, false_refinements)
630
- condition_setup, prepared_condition, condition_cleanups = prepare_expression_with_cleanups(
631
- branch.condition,
632
- env: branch_env,
633
- expected_type: @ctx.types.fetch("bool"),
634
- )
635
- true_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: true, env: branch_env))
636
-
637
- branch_entries << [
638
- condition_setup,
639
- condition_cleanups,
640
- lower_expression(prepared_condition, env: branch_env, expected_type: @ctx.types.fetch("bool")),
641
- lower_block(
642
- branch.body,
643
- env: env_with_refinements(local_env, true_refinements),
644
- active_defers: active_defers + local_defers,
645
- return_type:,
646
- loop_flow: nested_loop_flow(loop_flow, local_defers),
647
- allow_return:,
648
- ),
649
- ]
650
-
651
- false_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: false, env: branch_env))
652
- end
653
-
654
- nested_else_body = statement.else_body ? lower_block(
655
- statement.else_body,
656
- env: env_with_refinements(local_env, false_refinements),
657
- active_defers: active_defers + local_defers,
658
- return_type:,
659
- loop_flow: nested_loop_flow(loop_flow, local_defers),
660
- allow_return:,
661
- ) : []
662
-
663
- nested_if = nested_else_body
664
- branch_entries.reverse_each do |condition_setup, condition_cleanups, condition, then_body|
665
- condition_cleanup_statements = condition_cleanups.flat_map(&:itself)
666
- nested_if = [
667
- *condition_setup,
668
- IR::IfStmt.new(
669
- condition:,
670
- then_body: condition_cleanup_statements + then_body,
671
- else_body: condition_cleanup_statements + nested_if,
672
- ),
673
- ]
674
- end
675
- lowered.concat(nested_if)
676
-
677
- merge_cstr_metadata_after_if_statement!(statement, local_env)
678
628
 
679
- if statement.else_body.nil? && statement.branches.all? { |branch| cfg_block_always_terminates?(branch.body) }
680
- local_env[:scopes] = scopes_with_refinements(local_env[:scopes], false_refinements)
681
- end
682
- end
683
-
684
- def lower_block_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
685
- if statement.destructure_bindings
686
- lower_destructure_decl(statement, env: local_env, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
687
- return
688
- end
629
+ statement.branches.each do |branch|
630
+ branch_env = env_with_refinements(local_env, false_refinements)
631
+ condition_setup, prepared_condition, condition_cleanups = prepare_expression_with_cleanups(
632
+ branch.condition,
633
+ env: branch_env,
634
+ expected_type: @ctx.types.fetch("bool"),
635
+ )
636
+ true_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: true, env: branch_env))
637
+
638
+ branch_entries << [
639
+ condition_setup,
640
+ condition_cleanups,
641
+ lower_expression(prepared_condition, env: branch_env, expected_type: @ctx.types.fetch("bool")),
642
+ lower_block(
643
+ branch.body,
644
+ env: env_with_refinements(local_env, true_refinements),
645
+ active_defers: active_defers + local_defers,
646
+ return_type:,
647
+ loop_flow: nested_loop_flow(loop_flow, local_defers),
648
+ allow_return:,
649
+ ),
650
+ ]
689
651
 
690
- storage_type = if statement.else_body
691
- infer_expression_type(statement.value, env: local_env)
692
- elsif statement.type
693
- resolve_type_ref(statement.type)
694
- else
695
- infer_expression_type(statement.value, env: local_env)
696
- end
697
- type = if statement.else_body
698
- statement.type ? resolve_type_ref(statement.type) : let_else_success_type(storage_type)
699
- else
700
- storage_type
701
- end
702
- linkage_name = let_else_storage_c_name(statement, local_env)
703
- decl_name = bind_let_else_local?(statement) ? statement.name : linkage_name
704
- prepared_setup = []
705
- prepared_value = statement.value
706
- prepared_cleanups = []
707
- emitted_decl = false
708
- if statement.value
709
- local_env[:current_local_name] = linkage_name
710
- prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
711
- statement.value,
712
- env: local_env,
713
- expected_type: storage_type,
714
- allow_root_statement_foreign: true,
715
- materialize_array_calls: !array_type?(storage_type),
716
- )
717
- lowered.concat(prepared_setup)
718
- end
719
- if prepared_value && (foreign_call = foreign_call_info(prepared_value, local_env))
720
- setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
721
- foreign_call,
722
- env: local_env,
723
- expected_type: storage_type,
724
- statement_position: false,
725
- )
726
- lowered.concat(setup)
727
- raise LoweringError.new("foreign call used to initialize #{statement.name} must return a value",
728
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
729
- raise LoweringError.new("consuming foreign calls must return void",
730
- line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
731
-
732
- lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path)
733
- lowered.concat(cleanup_statements)
734
- emitted_decl = true
735
- elsif prepared_value.is_a?(AST::ProcExpr)
736
- setup, value = lower_proc_expression_for_local(prepared_value, env: local_env, local_name: statement.name, proc_type: storage_type)
737
- lowered.concat(setup)
738
- elsif prepared_value
739
- value = lower_contextual_expression(
740
- prepared_value,
741
- env: local_env,
742
- expected_type: storage_type,
743
- contextual_int_to_float: statement.type && contextual_int_to_float_target?(type),
744
- )
745
- else
746
- value = IR::ZeroInit.new(type: storage_type)
747
- end
748
- if value && storage_type.is_a?(Types::Nullable) && !value.type.is_a?(Types::Nullable) && !pointer_like_type?(storage_type.base)
749
- value = nullable_some_literal(storage_type, value)
750
- end
751
- if bind_let_else_local?(statement)
752
- current_actual_scope(local_env[:scopes])[statement.name] = local_binding(
753
- type:,
754
- storage_type:,
755
- linkage_name:,
756
- mutable: statement.kind == :var,
757
- pointer: false,
758
- projection: statement.else_body ? let_else_binding_projection(storage_type) : nil,
759
- cstr_backed: cstr_backed_storage_value?(storage_type, prepared_value, local_env),
760
- cstr_list_backed: cstr_list_backed_storage_value?(storage_type, prepared_value, local_env),
761
- const_value: statement.else_body ? nil : statement.kind == :let && prepared_value ? compile_time_const_value(prepared_value, env: local_env) : nil,
762
- )
763
- end
764
- lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path) unless emitted_decl
765
- if statement.else_body
766
- else_env = if statement.else_binding
767
- duplicate_env(local_env).tap do |env_with_error|
768
- current_actual_scope(env_with_error[:scopes])[statement.else_binding.name] = local_binding(
769
- type: let_else_error_type(storage_type),
770
- storage_type:,
771
- linkage_name:,
772
- mutable: false,
773
- pointer: false,
774
- projection: :result_failure_error,
775
- )
776
- end
777
- else
778
- local_env
652
+ false_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: false, env: branch_env))
779
653
  end
780
- else_body = lower_block(
654
+
655
+ nested_else_body = statement.else_body ? lower_block(
781
656
  statement.else_body,
782
- env: else_env,
783
- active_defers: active_defers + local_defers + prepared_cleanups,
657
+ env: env_with_refinements(local_env, false_refinements),
658
+ active_defers: active_defers + local_defers,
784
659
  return_type:,
785
660
  loop_flow: nested_loop_flow(loop_flow, local_defers),
786
661
  allow_return:,
787
- )
788
- local_ref = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
789
- lowered << IR::IfStmt.new(
790
- condition: let_else_failure_condition(local_ref, storage_type),
791
- then_body: else_body,
792
- else_body: nil,
793
- )
794
- end
795
- local_defers.concat(prepared_cleanups)
796
- if contains_proc_storage_type?(storage_type)
797
- local_value = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
798
- local_defers << lower_proc_contained_guarded_release_statements(local_value, storage_type)
799
- if statement.value && !expression_contains_proc_expr?(statement.value)
800
- lowered.concat(lower_proc_contained_retain_statements(local_value, storage_type))
662
+ ) : []
663
+
664
+ nested_if = nested_else_body
665
+ branch_entries.reverse_each do |condition_setup, condition_cleanups, condition, then_body|
666
+ condition_cleanup_statements = condition_cleanups.flat_map(&:itself)
667
+ nested_if = [
668
+ *condition_setup,
669
+ IR::IfStmt.new(
670
+ condition:,
671
+ then_body: condition_cleanup_statements + then_body,
672
+ else_body: condition_cleanup_statements + nested_if,
673
+ ),
674
+ ]
801
675
  end
802
- end
803
- end
676
+ lowered.concat(nested_if)
804
677
 
805
- def lower_block_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
806
- if statement.inline
807
- lowered.concat(lower_inline_match_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
808
- return
809
- end
810
- scrutinee_type = infer_expression_type(statement.expression, env: local_env)
811
- expression_setup, prepared_expression, expression_cleanups = prepare_expression_with_cleanups(
812
- statement.expression,
813
- env: local_env,
814
- expected_type: scrutinee_type,
815
- )
816
- lowered.concat(expression_setup)
817
- expression = lower_expression(prepared_expression, env: local_env, expected_type: scrutinee_type)
818
-
819
- if scrutinee_type.is_a?(Types::Variant) &&
820
- statement.arms.any? { |arm| arm.binding_name && !wildcard_arm_pattern?(arm.pattern) } &&
821
- !duplicable_foreign_argument_expression?(expression)
822
- scrutinee_linkage_name = fresh_c_temp_name(local_env, "match_value")
823
- lowered << IR::LocalDecl.new(name: scrutinee_linkage_name, linkage_name: scrutinee_linkage_name, type: scrutinee_type, value: expression)
824
- expression = IR::Name.new(name: scrutinee_linkage_name, type: scrutinee_type, pointer: false)
678
+ merge_cstr_metadata_after_if_statement!(statement, local_env)
679
+
680
+ if statement.else_body.nil? && statement.branches.all? { |branch| cfg_block_always_terminates?(branch.body) }
681
+ local_env[:scopes] = scopes_with_refinements(local_env[:scopes], false_refinements)
682
+ end
825
683
  end
826
684
 
827
- if scrutinee_type.is_a?(Types::Variant)
828
- if statement.arms.any? { |arm| arm.pattern.is_a?(AST::Call) }
829
- lower_block_variant_struct_pattern_match(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
685
+ def lower_local_decl_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
686
+ if statement.destructure_bindings
687
+ lower_destructure_decl(statement, env: local_env, lowered:, local_defers:, active_defers:, return_type:, loop_flow:, allow_return:)
688
+ return
689
+ end
690
+
691
+ storage_type = if statement.else_body
692
+ infer_expression_type(statement.value, env: local_env)
693
+ elsif statement.type
694
+ resolve_type_ref(statement.type)
830
695
  else
831
- kind_type = @ctx.types.fetch("int")
832
- kind_expr = IR::Member.new(receiver: expression, member: "kind", type: kind_type)
833
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
834
- cases = statement.arms.map do |arm|
835
- arm_local_env = duplicate_env(local_env)
836
- binding_decl = if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
837
- arm_name = variant_match_arm_name_from_pattern(arm.pattern)
838
- if arm_name && scrutinee_type.has_payload?(arm_name)
839
- fields = scrutinee_type.arm(arm_name)
840
- payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
841
- data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
842
- arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
843
- binding_c = c_local_name(arm.binding_name)
844
- arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: true, pointer: false)
845
- IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
846
- end
847
- end
848
- body = lower_block(
849
- arm.body,
850
- env: arm_local_env,
851
- active_defers: active_defers + local_defers,
852
- return_type:,
853
- loop_flow: arm_loop_flow,
854
- allow_return:,
855
- )
856
- body = [binding_decl, *body].compact if binding_decl
857
- if wildcard_arm_pattern?(arm.pattern)
858
- IR::SwitchDefaultCase.new(body:)
859
- else
860
- arm_name = variant_match_arm_name_from_pattern(arm.pattern)
861
- IR::SwitchCase.new(value: IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false), body:)
862
- end
863
- end
864
- lowered << IR::SwitchStmt.new(expression: kind_expr, cases:, exhaustive: true)
696
+ infer_expression_type(statement.value, env: local_env)
865
697
  end
866
- elsif scrutinee_type.is_a?(Types::StringView)
867
- lower_block_string_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
868
- elsif scrutinee_type.is_a?(Types::Tuple)
869
- lower_block_tuple_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
870
- else
871
- has_range_arms = statement.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
872
-
873
- if has_range_arms
874
- lower_block_range_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
698
+ type = if statement.else_body
699
+ statement.type ? resolve_type_ref(statement.type) : let_else_success_type(storage_type)
875
700
  else
876
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
877
- cases = statement.arms.map do |arm|
878
- body = lower_block(
879
- arm.body,
880
- env: local_env,
881
- active_defers: active_defers + local_defers,
882
- return_type:,
883
- loop_flow: arm_loop_flow,
884
- allow_return:,
885
- )
886
- if wildcard_arm_pattern?(arm.pattern)
887
- IR::SwitchDefaultCase.new(body:)
888
- else
889
- value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
890
- IR::SwitchCase.new(value:, body:)
701
+ storage_type
702
+ end
703
+ linkage_name = let_else_storage_c_name(statement, local_env)
704
+ decl_name = bind_let_else_local?(statement) ? statement.name : linkage_name
705
+ prepared_setup = []
706
+ prepared_value = statement.value
707
+ prepared_cleanups = []
708
+ emitted_decl = false
709
+ if statement.value
710
+ local_env[:current_local_name] = linkage_name
711
+ prepared_setup, prepared_value, prepared_cleanups = prepare_expression_with_cleanups(
712
+ statement.value,
713
+ env: local_env,
714
+ expected_type: storage_type,
715
+ allow_root_statement_foreign: true,
716
+ materialize_array_calls: !array_type?(storage_type),
717
+ )
718
+ lowered.concat(prepared_setup)
719
+ end
720
+ if prepared_value && (foreign_call = foreign_call_info(prepared_value, local_env))
721
+ setup, value, call_type, release_assignments, cleanup_statements = lower_foreign_call_components(
722
+ foreign_call,
723
+ env: local_env,
724
+ expected_type: storage_type,
725
+ statement_position: false,
726
+ )
727
+ lowered.concat(setup)
728
+ raise LoweringError.new("foreign call used to initialize #{statement.name} must return a value",
729
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
730
+ raise LoweringError.new("consuming foreign calls must return void",
731
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
732
+
733
+ lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path)
734
+ lowered.concat(cleanup_statements)
735
+ emitted_decl = true
736
+ elsif prepared_value.is_a?(AST::ProcExpr)
737
+ setup, value = lower_proc_expression_for_local(prepared_value, env: local_env, local_name: statement.name, proc_type: storage_type)
738
+ lowered.concat(setup)
739
+ elsif prepared_value
740
+ value = lower_contextual_expression(
741
+ prepared_value,
742
+ env: local_env,
743
+ expected_type: storage_type,
744
+ contextual_int_to_float: statement.type && contextual_int_to_float_target?(type),
745
+ )
746
+ else
747
+ value = IR::ZeroInit.new(type: storage_type)
748
+ end
749
+ if value && storage_type.is_a?(Types::Nullable) && !value.type.is_a?(Types::Nullable) && !pointer_like_type?(storage_type.base)
750
+ value = nullable_some_literal(storage_type, value)
751
+ end
752
+ if bind_let_else_local?(statement)
753
+ current_actual_scope(local_env[:scopes])[statement.name] = local_binding(
754
+ type:,
755
+ storage_type:,
756
+ linkage_name:,
757
+ mutable: statement.kind == :var,
758
+ pointer: false,
759
+ projection: statement.else_body ? let_else_binding_projection(storage_type) : nil,
760
+ cstr_backed: cstr_backed_storage_value?(storage_type, prepared_value, local_env),
761
+ cstr_list_backed: cstr_list_backed_storage_value?(storage_type, prepared_value, local_env),
762
+ const_value: statement.else_body ? nil : statement.kind == :let && prepared_value ? compile_time_const_value(prepared_value, env: local_env) : nil,
763
+ )
764
+ end
765
+ lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, path: @ctx.current_analysis_path) unless emitted_decl
766
+ if statement.else_body
767
+ else_env = if statement.else_binding
768
+ duplicate_env(local_env).tap do |env_with_error|
769
+ current_actual_scope(env_with_error[:scopes])[statement.else_binding.name] = local_binding(
770
+ type: let_else_error_type(storage_type),
771
+ storage_type:,
772
+ linkage_name:,
773
+ mutable: false,
774
+ pointer: false,
775
+ projection: :result_failure_error,
776
+ )
891
777
  end
778
+ else
779
+ local_env
780
+ end
781
+ else_body = lower_block(
782
+ statement.else_body,
783
+ env: else_env,
784
+ active_defers: active_defers + local_defers + prepared_cleanups,
785
+ return_type:,
786
+ loop_flow: nested_loop_flow(loop_flow, local_defers),
787
+ allow_return:,
788
+ )
789
+ local_ref = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
790
+ lowered << IR::IfStmt.new(
791
+ condition: let_else_failure_condition(local_ref, storage_type),
792
+ then_body: else_body,
793
+ else_body: nil,
794
+ )
795
+ end
796
+ local_defers.concat(prepared_cleanups)
797
+ if contains_proc_storage_type?(storage_type)
798
+ local_value = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
799
+ local_defers << lower_proc_nullable_release_statements(local_value, storage_type)
800
+ if statement.value && !expression_contains_proc_expr?(statement.value)
801
+ lowered.concat(lower_proc_contained_retain_statements(local_value, storage_type))
892
802
  end
893
- lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
894
803
  end
895
804
  end
896
- lowered.concat(expression_cleanups.flat_map(&:itself))
897
- end
898
805
 
899
- def lower_block_variant_struct_pattern_match(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
900
- kind_type = @ctx.types.fetch("int")
901
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
902
- @match_label_counter ||= 0
903
- @match_label_counter += 1
904
- m = @match_label_counter
905
- match_end_label = "__mt_match_#{m}_end"
906
- arm_next_label = "__mt_match_#{m}_arm_next"
907
-
908
- statement.arms.each_with_index do |arm, arm_index|
909
- arm_local_env = duplicate_env(local_env)
910
- arm_name = variant_match_arm_name_from_pattern(arm.pattern) unless wildcard_arm_pattern?(arm.pattern)
911
-
912
- if arm_index > 0
913
- lowered << IR::LabelStmt.new(name: "__mt_match_#{m}_arm_#{arm_index}")
806
+ def lower_match_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
807
+ if statement.inline
808
+ lowered.concat(lower_inline_match_stmt(statement, env: local_env, active_defers:, return_type:, allow_return:))
809
+ return
810
+ end
811
+ scrutinee_type = infer_expression_type(statement.expression, env: local_env)
812
+ expression_setup, prepared_expression, expression_cleanups = prepare_expression_with_cleanups(
813
+ statement.expression,
814
+ env: local_env,
815
+ expected_type: scrutinee_type,
816
+ )
817
+ lowered.concat(expression_setup)
818
+ expression = lower_expression(prepared_expression, env: local_env, expected_type: scrutinee_type)
819
+
820
+ if scrutinee_type.is_a?(Types::Variant) &&
821
+ statement.arms.any? { |arm| arm.binding_name && !wildcard_arm_pattern?(arm.pattern) } &&
822
+ !duplicable_foreign_argument_expression?(expression)
823
+ scrutinee_linkage_name = fresh_c_temp_name(local_env, "match_value")
824
+ lowered << IR::LocalDecl.new(name: scrutinee_linkage_name, linkage_name: scrutinee_linkage_name, type: scrutinee_type, value: expression)
825
+ expression = IR::Name.new(name: scrutinee_linkage_name, type: scrutinee_type, pointer: false)
914
826
  end
915
827
 
916
- if arm_name && !wildcard_arm_pattern?(arm.pattern)
917
- tag_value = IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false)
918
- tag_expr = IR::Member.new(receiver: expression, member: "kind", type: kind_type)
919
- goto_label = if arm_index < statement.arms.length - 1
920
- "__mt_match_#{m}_arm_#{arm_index + 1}"
828
+ if scrutinee_type.is_a?(Types::Variant)
829
+ if statement.arms.any? { |arm| arm.pattern.is_a?(AST::Call) }
830
+ lower_variant_struct_pattern_match(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
921
831
  else
922
- arm_next_label
832
+ kind_type = @ctx.types.fetch("int")
833
+ kind_expr = IR::Member.new(receiver: expression, member: "kind", type: kind_type)
834
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
835
+ cases = statement.arms.map do |arm|
836
+ arm_local_env = duplicate_env(local_env)
837
+ binding_decl = if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
838
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
839
+ if arm_name && scrutinee_type.has_payload?(arm_name)
840
+ fields = scrutinee_type.arm(arm_name)
841
+ payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
842
+ data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
843
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
844
+ binding_c = c_local_name(arm.binding_name)
845
+ arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: true, pointer: false)
846
+ IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
847
+ end
848
+ end
849
+ body = lower_block(
850
+ arm.body,
851
+ env: arm_local_env,
852
+ active_defers: active_defers + local_defers,
853
+ return_type:,
854
+ loop_flow: arm_loop_flow,
855
+ allow_return:,
856
+ )
857
+ body = [binding_decl, *body].compact if binding_decl
858
+ if wildcard_arm_pattern?(arm.pattern)
859
+ IR::SwitchDefaultCase.new(body:)
860
+ else
861
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern)
862
+ IR::SwitchCase.new(value: IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false), body:)
863
+ end
864
+ end
865
+ lowered << IR::SwitchStmt.new(expression: kind_expr, cases:, exhaustive: true)
923
866
  end
924
- tag_check = IR::Binary.new(operator: "!=", left: tag_expr, right: tag_value, type: @ctx.types.fetch("bool"))
925
- lowered << IR::IfStmt.new(condition: tag_check, then_body: [IR::GotoStmt.new(label: goto_label)], else_body: [])
926
- end
867
+ elsif scrutinee_type.is_a?(Types::StringView)
868
+ lower_string_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
869
+ elsif scrutinee_type.is_a?(Types::Tuple)
870
+ lower_tuple_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
871
+ else
872
+ has_range_arms = statement.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
927
873
 
928
- if arm_name && !wildcard_arm_pattern?(arm.pattern) && scrutinee_type.has_payload?(arm_name)
929
- fields = scrutinee_type.arm(arm_name)
930
- payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
931
- data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
932
- arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
933
- payload_c_name = fresh_c_temp_name(arm_local_env, "match_payload")
934
- arm_local_env[:scopes].last["__mt_payload"] = local_binding(type: payload_type, linkage_name: payload_c_name, mutable: true, pointer: false)
935
-
936
- arm_body_ir = []
937
- arm_body_ir << IR::LocalDecl.new(name: payload_c_name, linkage_name: payload_c_name, type: payload_type, value: arm_expr)
938
-
939
- nested_struct_fields = nil
940
- nested_struct_c_name = nil
941
- nested_struct_type = nil
942
- if fields.size == 1 && fields.values.first.is_a?(Types::Struct)
943
- native_field = fields.keys.first
944
- has_native_reference = arm.pattern.arguments.any? do |arg|
945
- next false if !arg.name && arg.value.is_a?(AST::Identifier) && arg.value.name == "_"
946
-
947
- name = arg.name || (arg.value.is_a?(AST::Identifier) ? arg.value.name : nil)
948
- name == native_field
949
- end
950
- unless has_native_reference
951
- nested_struct_type = fields.values.first
952
- nested_struct_fields = nested_struct_type.fields
953
- single_field_name = fields.keys.first
954
- nested_struct_c_name = fresh_c_temp_name(arm_local_env, "match_struct")
955
- struct_expr = IR::Member.new(
956
- receiver: IR::Name.new(name: payload_c_name, type: payload_type, pointer: false),
957
- member: single_field_name,
958
- type: nested_struct_type,
874
+ if has_range_arms
875
+ lower_range_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
876
+ else
877
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
878
+ cases = statement.arms.map do |arm|
879
+ body = lower_block(
880
+ arm.body,
881
+ env: local_env,
882
+ active_defers: active_defers + local_defers,
883
+ return_type:,
884
+ loop_flow: arm_loop_flow,
885
+ allow_return:,
959
886
  )
960
- arm_body_ir << IR::LocalDecl.new(name: nested_struct_c_name, linkage_name: nested_struct_c_name, type: nested_struct_type, value: struct_expr)
887
+ if wildcard_arm_pattern?(arm.pattern)
888
+ IR::SwitchDefaultCase.new(body:)
889
+ else
890
+ value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
891
+ IR::SwitchCase.new(value:, body:)
892
+ end
961
893
  end
894
+ lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
962
895
  end
896
+ end
897
+ lowered.concat(expression_cleanups.flat_map(&:itself))
898
+ end
963
899
 
964
- if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty?
965
- pattern_fields = nested_struct_fields || fields
966
- pattern_receiver_name = nested_struct_c_name || payload_c_name
967
- pattern_receiver_type = nested_struct_type || payload_type
900
+ def lower_variant_struct_pattern_match(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
901
+ kind_type = @ctx.types.fetch("int")
902
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
903
+ @match_label_counter ||= 0
904
+ @match_label_counter += 1
905
+ m = @match_label_counter
906
+ match_end_label = "__mt_match_#{m}_end"
907
+ arm_next_label = "__mt_match_#{m}_arm_next"
908
+
909
+ statement.arms.each_with_index do |arm, arm_index|
910
+ arm_local_env = duplicate_env(local_env)
911
+ arm_name = variant_match_arm_name_from_pattern(arm.pattern) unless wildcard_arm_pattern?(arm.pattern)
912
+
913
+ if arm_index > 0
914
+ lowered << IR::LabelStmt.new(name: "__mt_match_#{m}_arm_#{arm_index}")
915
+ end
968
916
 
969
- arm.pattern.arguments.each do |arg|
970
- if !arg.name && arg.value.is_a?(AST::BinaryOp) && arg.value.left.is_a?(AST::Identifier)
971
- field_name = arg.value.left.name
972
- next unless pattern_fields.key?(field_name)
917
+ if arm_name && !wildcard_arm_pattern?(arm.pattern)
918
+ tag_value = IR::Name.new(name: enum_member_c_name(scrutinee_type, "kind_#{arm_name}"), type: kind_type, pointer: false)
919
+ tag_expr = IR::Member.new(receiver: expression, member: "kind", type: kind_type)
920
+ goto_label = if arm_index < statement.arms.length - 1
921
+ "__mt_match_#{m}_arm_#{arm_index + 1}"
922
+ else
923
+ arm_next_label
924
+ end
925
+ tag_check = IR::Binary.new(operator: "!=", left: tag_expr, right: tag_value, type: @ctx.types.fetch("bool"))
926
+ lowered << IR::IfStmt.new(condition: tag_check, then_body: [IR::GotoStmt.new(label: goto_label)], else_body: [])
927
+ end
973
928
 
974
- field_type = pattern_fields[field_name]
975
- field_expr = IR::Member.new(receiver: IR::Name.new(name: pattern_receiver_name, type: pattern_receiver_type, pointer: false), member: field_name, type: field_type)
976
- field_expr = dereference_if_self_ref(field_expr, field_type, scrutinee_type)
977
- rhs_expr = lower_expression(arg.value.right, env: arm_local_env, expected_type: field_type)
978
- guard_condition = IR::Binary.new(operator: arg.value.operator, left: field_expr, right: rhs_expr, type: @ctx.types.fetch("bool"))
979
- arm_body_ir << IR::IfStmt.new(condition: guard_condition, then_body: [], else_body: [IR::GotoStmt.new(label: goto_label)])
929
+ if arm_name && !wildcard_arm_pattern?(arm.pattern) && scrutinee_type.has_payload?(arm_name)
930
+ fields = scrutinee_type.arm(arm_name)
931
+ payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
932
+ data_expr = IR::Member.new(receiver: expression, member: "data", type: nil)
933
+ arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
934
+ payload_c_name = fresh_c_temp_name(arm_local_env, "match_payload")
935
+ arm_local_env[:scopes].last["__mt_payload"] = local_binding(type: payload_type, linkage_name: payload_c_name, mutable: true, pointer: false)
936
+
937
+ arm_body_ir = []
938
+ arm_body_ir << IR::LocalDecl.new(name: payload_c_name, linkage_name: payload_c_name, type: payload_type, value: arm_expr)
939
+
940
+ nested_struct_fields = nil
941
+ nested_struct_c_name = nil
942
+ nested_struct_type = nil
943
+ if fields.size == 1 && fields.values.first.is_a?(Types::Struct)
944
+ native_field = fields.keys.first
945
+ has_native_reference = arm.pattern.arguments.any? do |arg|
946
+ next false if !arg.name && arg.value.is_a?(AST::Identifier) && arg.value.name == "_"
947
+
948
+ name = arg.name || (arg.value.is_a?(AST::Identifier) ? arg.value.name : nil)
949
+ name == native_field
980
950
  end
951
+ unless has_native_reference
952
+ nested_struct_type = fields.values.first
953
+ nested_struct_fields = nested_struct_type.fields
954
+ single_field_name = fields.keys.first
955
+ nested_struct_c_name = fresh_c_temp_name(arm_local_env, "match_struct")
956
+ struct_expr = IR::Member.new(
957
+ receiver: IR::Name.new(name: payload_c_name, type: payload_type, pointer: false),
958
+ member: single_field_name,
959
+ type: nested_struct_type,
960
+ )
961
+ arm_body_ir << IR::LocalDecl.new(name: nested_struct_c_name, linkage_name: nested_struct_c_name, type: nested_struct_type, value: struct_expr)
962
+ end
963
+ end
964
+
965
+ if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty?
966
+ pattern_fields = nested_struct_fields || fields
967
+ pattern_receiver_name = nested_struct_c_name || payload_c_name
968
+ pattern_receiver_type = nested_struct_type || payload_type
969
+
970
+ arm.pattern.arguments.each do |arg|
971
+ if !arg.name && arg.value.is_a?(AST::BinaryOp) && arg.value.left.is_a?(AST::Identifier)
972
+ field_name = arg.value.left.name
973
+ next unless pattern_fields.key?(field_name)
974
+
975
+ field_type = pattern_fields[field_name]
976
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: pattern_receiver_name, type: pattern_receiver_type, pointer: false), member: field_name, type: field_type)
977
+ field_expr = dereference_if_self_ref(field_expr, field_type, scrutinee_type)
978
+ rhs_expr = lower_expression(arg.value.right, env: arm_local_env, expected_type: field_type)
979
+ guard_condition = IR::Binary.new(operator: arg.value.operator, left: field_expr, right: rhs_expr, type: @ctx.types.fetch("bool"))
980
+ arm_body_ir << IR::IfStmt.new(condition: guard_condition, then_body: [], else_body: [IR::GotoStmt.new(label: goto_label)])
981
+ end
982
+
983
+ if arg.name
984
+ field_name = arg.name
985
+ next unless pattern_fields.key?(field_name)
986
+
987
+ field_type = pattern_fields[field_name]
988
+ field_expr = IR::Member.new(receiver: IR::Name.new(name: pattern_receiver_name, type: pattern_receiver_type, pointer: false), member: field_name, type: field_type)
989
+ field_expr = dereference_if_self_ref(field_expr, field_type, scrutinee_type)
990
+ value_expr = lower_expression(arg.value, env: arm_local_env, expected_type: field_type)
991
+ eq_check = IR::Binary.new(operator: "!=", left: field_expr, right: value_expr, type: @ctx.types.fetch("bool"))
992
+ arm_body_ir << IR::IfStmt.new(condition: eq_check, then_body: [IR::GotoStmt.new(label: goto_label)], else_body: [])
993
+ end
994
+ end
995
+
996
+ arm.pattern.arguments.each do |arg|
997
+ next if arg.name
998
+ next unless arg.value.is_a?(AST::Identifier)
999
+ next if arg.value.name == "_"
981
1000
 
982
- if arg.name
983
- field_name = arg.name
1001
+ field_name = arg.value.name
984
1002
  next unless pattern_fields.key?(field_name)
985
1003
 
986
1004
  field_type = pattern_fields[field_name]
1005
+ binding_c = c_local_name(field_name)
987
1006
  field_expr = IR::Member.new(receiver: IR::Name.new(name: pattern_receiver_name, type: pattern_receiver_type, pointer: false), member: field_name, type: field_type)
988
1007
  field_expr = dereference_if_self_ref(field_expr, field_type, scrutinee_type)
989
- value_expr = lower_expression(arg.value, env: arm_local_env, expected_type: field_type)
990
- eq_check = IR::Binary.new(operator: "!=", left: field_expr, right: value_expr, type: @ctx.types.fetch("bool"))
991
- arm_body_ir << IR::IfStmt.new(condition: eq_check, then_body: [IR::GotoStmt.new(label: goto_label)], else_body: [])
1008
+ arm_body_ir << IR::LocalDecl.new(name: binding_c, linkage_name: binding_c, type: field_type, value: field_expr)
1009
+ arm_local_env[:scopes].last[field_name] = local_binding(type: field_type, linkage_name: binding_c, mutable: false, pointer: false)
992
1010
  end
993
1011
  end
994
1012
 
995
- arm.pattern.arguments.each do |arg|
996
- next if arg.name
997
- next unless arg.value.is_a?(AST::Identifier)
998
- next if arg.value.name == "_"
1013
+ if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
1014
+ binding_c = c_local_name(arm.binding_name)
1015
+ if arm_name && scrutinee_type.has_payload?(arm_name)
1016
+ payload_key = "__mt_payload"
1017
+ if arm_local_env[:scopes].last.key?(payload_key)
1018
+ payload_binding = arm_local_env[:scopes].last[payload_key]
1019
+ arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_binding[:type], linkage_name: binding_c, mutable: true, pointer: false)
1020
+ payload_ref = IR::Name.new(name: payload_binding[:linkage_name], type: payload_binding[:type], pointer: false)
1021
+ arm_body_ir << IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_binding[:type], value: payload_ref)
1022
+ end
1023
+ end
1024
+ end
999
1025
 
1000
- field_name = arg.value.name
1001
- next unless pattern_fields.key?(field_name)
1026
+ body = lower_block(
1027
+ arm.body,
1028
+ env: arm_local_env,
1029
+ active_defers: active_defers + local_defers,
1030
+ return_type:,
1031
+ loop_flow: arm_loop_flow,
1032
+ allow_return:,
1033
+ )
1034
+ arm_body_ir.concat(body)
1035
+ arm_body_ir << IR::GotoStmt.new(label: match_end_label)
1002
1036
 
1003
- field_type = pattern_fields[field_name]
1004
- binding_c = c_local_name(field_name)
1005
- field_expr = IR::Member.new(receiver: IR::Name.new(name: pattern_receiver_name, type: pattern_receiver_type, pointer: false), member: field_name, type: field_type)
1006
- field_expr = dereference_if_self_ref(field_expr, field_type, scrutinee_type)
1007
- arm_body_ir << IR::LocalDecl.new(name: binding_c, linkage_name: binding_c, type: field_type, value: field_expr)
1008
- arm_local_env[:scopes].last[field_name] = local_binding(type: field_type, linkage_name: binding_c, mutable: false, pointer: false)
1009
- end
1037
+ lowered << IR::BlockStmt.new(body: arm_body_ir)
1038
+ else
1039
+ body = lower_block(
1040
+ arm.body,
1041
+ env: arm_local_env,
1042
+ active_defers: active_defers + local_defers,
1043
+ return_type:,
1044
+ loop_flow: arm_loop_flow,
1045
+ allow_return:,
1046
+ )
1047
+ lowered << IR::BlockStmt.new(body: body + [IR::GotoStmt.new(label: match_end_label)])
1010
1048
  end
1049
+ end
1011
1050
 
1012
- if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
1013
- binding_c = c_local_name(arm.binding_name)
1014
- if arm_name && scrutinee_type.has_payload?(arm_name)
1015
- payload_key = "__mt_payload"
1016
- if arm_local_env[:scopes].last.key?(payload_key)
1017
- payload_binding = arm_local_env[:scopes].last[payload_key]
1018
- arm_local_env[:scopes].last[arm.binding_name] = local_binding(type: payload_binding[:type], linkage_name: binding_c, mutable: true, pointer: false)
1019
- payload_ref = IR::Name.new(name: payload_binding[:linkage_name], type: payload_binding[:type], pointer: false)
1020
- arm_body_ir << IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_binding[:type], value: payload_ref)
1021
- end
1022
- end
1023
- end
1051
+ lowered << IR::LabelStmt.new(name: arm_next_label)
1052
+ lowered << IR::LabelStmt.new(name: match_end_label)
1053
+ end
1024
1054
 
1025
- body = lower_block(
1026
- arm.body,
1027
- env: arm_local_env,
1055
+ def lower_string_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1056
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1057
+ bool_type = @ctx.types.fetch("bool")
1058
+ non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1059
+ wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1060
+ else_body = if wildcard
1061
+ lower_block(
1062
+ wildcard.body,
1063
+ env: local_env,
1028
1064
  active_defers: active_defers + local_defers,
1029
1065
  return_type:,
1030
1066
  loop_flow: arm_loop_flow,
1031
1067
  allow_return:,
1032
1068
  )
1033
- arm_body_ir.concat(body)
1034
- arm_body_ir << IR::GotoStmt.new(label: match_end_label)
1035
-
1036
- lowered << IR::BlockStmt.new(body: arm_body_ir)
1037
1069
  else
1038
- body = lower_block(
1070
+ []
1071
+ end
1072
+ non_wildcard.reverse_each do |arm|
1073
+ arm_body = lower_block(
1039
1074
  arm.body,
1040
- env: arm_local_env,
1075
+ env: local_env,
1041
1076
  active_defers: active_defers + local_defers,
1042
1077
  return_type:,
1043
1078
  loop_flow: arm_loop_flow,
1044
1079
  allow_return:,
1045
1080
  )
1046
- lowered << IR::BlockStmt.new(body: body + [IR::GotoStmt.new(label: match_end_label)])
1081
+ lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1082
+ cond = IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1083
+ else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1047
1084
  end
1085
+ lowered.concat(else_body)
1048
1086
  end
1049
1087
 
1050
- lowered << IR::LabelStmt.new(name: arm_next_label)
1051
- lowered << IR::LabelStmt.new(name: match_end_label)
1052
- end
1053
-
1054
- def lower_block_string_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1055
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1056
- bool_type = @ctx.types.fetch("bool")
1057
- non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1058
- wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1059
- else_body = if wildcard
1060
- lower_block(
1061
- wildcard.body,
1062
- env: local_env,
1063
- active_defers: active_defers + local_defers,
1064
- return_type:,
1065
- loop_flow: arm_loop_flow,
1066
- allow_return:,
1067
- )
1068
- else
1069
- []
1070
- end
1071
- non_wildcard.reverse_each do |arm|
1072
- arm_body = lower_block(
1073
- arm.body,
1074
- env: local_env,
1075
- active_defers: active_defers + local_defers,
1076
- return_type:,
1077
- loop_flow: arm_loop_flow,
1078
- allow_return:,
1079
- )
1080
- lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1081
- cond = IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1082
- else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1083
- end
1084
- lowered.concat(else_body)
1085
- end
1086
-
1087
- def lower_block_tuple_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1088
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1089
- bool_type = @ctx.types.fetch("bool")
1090
- non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1091
- wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1092
- else_body = if wildcard
1093
- lower_block(
1094
- wildcard.body,
1095
- env: local_env,
1096
- active_defers: active_defers + local_defers,
1097
- return_type:,
1098
- loop_flow: arm_loop_flow,
1099
- allow_return:,
1100
- )
1101
- else
1102
- []
1103
- end
1104
- non_wildcard.reverse_each do |arm|
1105
- arm_body = lower_block(
1106
- arm.body,
1107
- env: local_env,
1108
- active_defers: active_defers + local_defers,
1109
- return_type:,
1110
- loop_flow: arm_loop_flow,
1111
- allow_return:,
1112
- )
1113
- cond = tuple_arm_condition(arm, expression, scrutinee_type, bool_type, env: local_env)
1114
- else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1088
+ def lower_tuple_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1089
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1090
+ bool_type = @ctx.types.fetch("bool")
1091
+ non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1092
+ wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1093
+ else_body = if wildcard
1094
+ lower_block(
1095
+ wildcard.body,
1096
+ env: local_env,
1097
+ active_defers: active_defers + local_defers,
1098
+ return_type:,
1099
+ loop_flow: arm_loop_flow,
1100
+ allow_return:,
1101
+ )
1102
+ else
1103
+ []
1104
+ end
1105
+ non_wildcard.reverse_each do |arm|
1106
+ arm_body = lower_block(
1107
+ arm.body,
1108
+ env: local_env,
1109
+ active_defers: active_defers + local_defers,
1110
+ return_type:,
1111
+ loop_flow: arm_loop_flow,
1112
+ allow_return:,
1113
+ )
1114
+ cond = tuple_arm_condition(arm, expression, scrutinee_type, bool_type, env: local_env)
1115
+ else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1116
+ end
1117
+ lowered.concat(else_body)
1115
1118
  end
1116
- lowered.concat(else_body)
1117
- end
1118
1119
 
1119
- def lower_block_range_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1120
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1121
- bool_type = @ctx.types.fetch("bool")
1122
- non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1123
- wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1124
- else_body = if wildcard
1125
- lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
1126
- else
1127
- []
1128
- end
1129
- non_wildcard.reverse_each do |arm|
1130
- arm_body = lower_block(
1131
- arm.body,
1132
- env: local_env,
1133
- active_defers: active_defers + local_defers,
1134
- return_type:,
1135
- loop_flow: arm_loop_flow,
1136
- allow_return:,
1137
- )
1138
- cond = if arm.pattern.is_a?(AST::RangeExpr)
1139
- lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
1120
+ def lower_range_match_stmt(statement, scrutinee_type:, expression:, lowered:, local_defers:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
1121
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
1122
+ bool_type = @ctx.types.fetch("bool")
1123
+ non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1124
+ wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1125
+ else_body = if wildcard
1126
+ lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
1140
1127
  else
1141
- lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1142
- IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1128
+ []
1129
+ end
1130
+ non_wildcard.reverse_each do |arm|
1131
+ arm_body = lower_block(
1132
+ arm.body,
1133
+ env: local_env,
1134
+ active_defers: active_defers + local_defers,
1135
+ return_type:,
1136
+ loop_flow: arm_loop_flow,
1137
+ allow_return:,
1138
+ )
1139
+ cond = if arm.pattern.is_a?(AST::RangeExpr)
1140
+ lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
1141
+ else
1142
+ lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
1143
+ IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
1144
+ end
1145
+ else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1143
1146
  end
1144
- else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
1147
+ lowered.concat(else_body)
1145
1148
  end
1146
- lowered.concat(else_body)
1147
1149
  end
1148
1150
  end
1149
1151
  end