mt-lang 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -3,565 +3,563 @@
3
3
  module MilkTea
4
4
  class CBackend
5
5
  module Statements
6
- private
7
-
8
- def emit_statement_sequence(statements, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil)
9
- statements.each_with_index.flat_map do |statement, index|
10
- emit_statement(
11
- statement,
12
- level,
13
- function:,
14
- used_labels:,
15
- loop_continue_label:,
16
- loop_break_label:,
17
- remaining_statements: statements[(index + 1)..] || [],
18
- )
19
- end
20
- end
21
-
22
- def emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: [])
23
- indent = INDENT * level
24
- aliases = checked_index_aliases_for_statement(statement)
25
- alias_lines = emit_checked_index_alias_declarations(aliases, indent)
26
- if statement.respond_to?(:line) && statement.line
27
- @current_line = statement.line
28
- @source_path ||= statement.source_path if statement.respond_to?(:source_path) && statement.source_path
6
+ def emit_statement_sequence(statements, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil)
7
+ statements.each_with_index.flat_map do |statement, index|
8
+ emit_statement(
9
+ statement,
10
+ level,
11
+ function:,
12
+ used_labels:,
13
+ loop_continue_label:,
14
+ loop_break_label:,
15
+ remaining_statements: statements[(index + 1)..] || [],
16
+ )
17
+ end
18
+ end
19
+
20
+ def emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: [])
21
+ indent = INDENT * level
22
+ aliases = checked_index_aliases_for_statement(statement)
23
+ alias_lines = emit_checked_index_alias_declarations(aliases, indent)
24
+ if statement.respond_to?(:line) && statement.line
25
+ @current_line = statement.line
26
+ @source_path ||= statement.path if statement.respond_to?(:path) && statement.path
27
+ end
28
+ line_directive = if @emit_line_directives && statement.respond_to?(:line) && statement.line
29
+ sp = (statement.respond_to?(:path) && statement.path) || @source_path
30
+ sp ? ["#line #{statement.line} #{sp.inspect}"] : []
31
+ else
32
+ []
33
+ end
34
+ statement_lines = with_checked_index_aliases(aliases) do
35
+ case statement
36
+ when IR::LocalDecl
37
+ if array_type?(statement.type) && statement.value.is_a?(IR::Call)
38
+ lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"]
39
+ lines << emit_array_call_statement(statement.value, emit_array_out_argument(statement.linkage_name), indent)
40
+ lines
41
+ elsif array_type?(statement.type) && !statement.value.is_a?(IR::ArrayLiteral) && !statement.value.is_a?(IR::ZeroInit)
42
+ lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"]
43
+ lines << emit_array_copy_statement(statement.linkage_name, statement.value, indent)
44
+ lines
45
+ else
46
+ [
47
+ "#{indent}#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)};",
48
+ ]
29
49
  end
30
- line_directive = if @emit_line_directives && statement.respond_to?(:line) && statement.line
31
- sp = (statement.respond_to?(:source_path) && statement.source_path) || @source_path
32
- sp ? ["#line #{statement.line} #{sp.inspect}"] : []
33
- else
34
- []
35
- end
36
- statement_lines = with_checked_index_aliases(aliases) do
37
- case statement
38
- when IR::LocalDecl
39
- if array_type?(statement.type) && statement.value.is_a?(IR::Call)
40
- lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"]
41
- lines << emit_array_call_statement(statement.value, emit_array_out_argument(statement.linkage_name), indent)
42
- lines
43
- elsif array_type?(statement.type) && !statement.value.is_a?(IR::ArrayLiteral) && !statement.value.is_a?(IR::ZeroInit)
44
- lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"]
45
- lines << emit_array_copy_statement(statement.linkage_name, statement.value, indent)
46
- lines
47
- else
48
- [
49
- "#{indent}#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)};",
50
- ]
51
- end
52
- when IR::Assignment
53
- if array_type?(statement.target.type) && statement.operator == "=" && statement.value.is_a?(IR::Call)
54
- [emit_array_call_statement(statement.value, emit_array_out_argument(emit_expression(statement.target)), indent)]
55
- elsif array_type?(statement.target.type) && statement.operator == "="
56
- [emit_array_copy_statement(emit_expression(statement.target), statement.value, indent)]
57
- else
58
- ["#{indent}#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)};"]
59
- end
60
- when IR::BlockStmt
61
- if block_requires_scope?(statement.body)
62
- lines = ["#{indent}{"]
63
- lines.concat(emit_statement_sequence(statement.body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
64
- lines << "#{indent}}"
65
- lines
66
- else
67
- emit_statement_sequence(statement.body, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
68
- end
69
- when IR::ExpressionStmt
70
- if statement.expression.is_a?(IR::Assignment)
71
- emit_assignment_expression(statement.expression, indent)
72
- elsif statement.expression.is_a?(IR::Call)
73
- ["#{indent}#{emit_expression(statement.expression)};"]
74
- else
75
- # A pure-value expression discarded as a statement (e.g. the
76
- # `T<-x` touch idiom) needs a void cast, otherwise the value
77
- # is unused and triggers -Wunused-value.
78
- ["#{indent}(void)(#{emit_expression(statement.expression)});"]
79
- end
80
- when IR::ReturnStmt
81
- if statement.value
82
- if array_type?(function.return_type)
83
- emit_array_return(statement.value, indent)
84
- else
85
- ["#{indent}return #{emit_expression(statement.value)};"]
86
- end
87
- else
88
- ["#{indent}return;"]
89
- end
90
- when IR::WhileStmt
91
- body_continue_label = loop_continue_label_name(statement.body)
92
- body = body_continue_label ? statement.body[0...-1] : statement.body
93
- body_break_label = loop_break_label_name(body, remaining_statements)
94
- @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
95
- if @debug_guards
96
- @loop_guard_id += 1
97
- guard_name = "__mt_loop_#{@loop_guard_id}"
98
- lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"]
99
- lines << "#{indent}while (#{emit_expression(statement.condition)}) {"
100
- guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");"
101
- lines << guard
102
- lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
103
- lines << "#{indent}}"
104
- lines << "#{indent}}"
105
- lines
106
- else
107
- lines = ["#{indent}while (#{emit_expression(statement.condition)}) {"]
108
- lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
109
- lines << "#{indent}}"
110
- lines
111
- end
112
- when IR::ForStmt
113
- body_continue_label = loop_continue_label_name(statement.body)
114
- body = body_continue_label ? statement.body[0...-1] : statement.body
115
- body_break_label = loop_break_label_name(body, remaining_statements)
116
- @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
117
- if @debug_guards
118
- @loop_guard_id += 1
119
- guard_name = "__mt_loop_#{@loop_guard_id}"
120
- lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"]
121
- lines << "#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"
122
- guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");"
123
- lines << guard
124
- lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
125
- lines << "#{indent}}"
126
- lines << "#{indent}}"
127
- lines
128
- else
129
- lines = ["#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"]
130
- lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
131
- lines << "#{indent}}"
132
- lines
133
- end
134
- when IR::BreakStmt
135
- ["#{indent}break;"]
136
- when IR::ContinueStmt
137
- ["#{indent}continue;"]
138
- when IR::GotoStmt
139
- return ["#{indent}continue;"] if loop_continue_label && statement.label == loop_continue_label
140
- return ["#{indent}break;"] if loop_break_label && statement.label == loop_break_label
141
-
142
- ["#{indent}goto #{statement.label};"]
143
- when IR::LabelStmt
144
- return [] if @suppressed_labels.include?(statement.name)
145
- return [] if loop_continue_label && statement.name == loop_continue_label
146
- return [] unless used_labels.include?(statement.name)
147
-
148
- ["#{indent}#{statement.name}:;"]
149
- when IR::StaticAssert
150
- ["#{indent}#{emit_static_assert(statement)}"]
151
- when IR::IfStmt
152
- emit_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
153
- when IR::SwitchStmt
154
- if switch_emittable_as_if?(statement, loop_break_label:)
155
- emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
156
- else
157
- lines = ["#{indent}switch (#{emit_expression(statement.expression)}) {"]
158
- has_default = false
159
- statement.cases.each do |switch_case|
160
- if switch_case.is_a?(IR::SwitchDefaultCase)
161
- has_default = true
162
- lines << "#{indent}#{INDENT}default: {"
163
- else
164
- lines << "#{indent}#{INDENT}case #{emit_expression(switch_case.value)}: {"
165
- end
166
- lines.concat(emit_statement_sequence(switch_case.body, level + 2, function:, used_labels:, loop_continue_label:))
167
- lines << "#{indent}#{INDENT}#{INDENT}break;" unless body_terminates?(switch_case.body)
168
- lines << "#{indent}#{INDENT}}"
169
- end
170
- # An exhaustive enum/variant match covers every arm, so the C
171
- # `default` path is unreachable. Emitting it lets the compiler
172
- # prove non-void functions always return, avoiding spurious
173
- # -Wreturn-type warnings on the implicit no-match path.
174
- if statement.exhaustive && !has_default
175
- lines << "#{indent}#{INDENT}default: __builtin_unreachable();"
176
- end
177
- lines << "#{indent}}"
178
- lines
179
- end
180
- else
181
- raise CBackendError.new("unsupported IR statement #{statement.class.name}", line: 0, column: 0, path: @path)
182
- end
50
+ when IR::Assignment
51
+ if array_type?(statement.target.type) && statement.operator == "=" && statement.value.is_a?(IR::Call)
52
+ [emit_array_call_statement(statement.value, emit_array_out_argument(emit_expression(statement.target)), indent)]
53
+ elsif array_type?(statement.target.type) && statement.operator == "="
54
+ [emit_array_copy_statement(emit_expression(statement.target), statement.value, indent)]
55
+ else
56
+ ["#{indent}#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)};"]
183
57
  end
184
-
185
- alias_lines + line_directive + statement_lines
186
- end
187
-
188
- def compact_generated_statement_sequence(statements)
189
- transformed = statements.map { |statement| transform_compactable_nested_bodies(statement) }
190
- compacted = []
191
- index = 0
192
- reachable = true
193
-
194
- while index < transformed.length
195
- current = transformed[index]
196
-
197
- unless reachable
198
- if current.is_a?(IR::LabelStmt)
199
- compacted << current
200
- reachable = true
201
- end
202
- index += 1
203
- next
204
- end
205
-
206
- following = transformed[index + 1]
207
- remaining = transformed[(index + 2)..] || []
208
-
209
- if following && (folded_local_alias = fold_single_use_local_alias(current, following, remaining))
210
- compacted << folded_local_alias
211
- reachable = !statement_prevents_sequential_fallthrough?(folded_local_alias)
212
- index += 2
213
- next
214
- end
215
-
216
- if following && (folded_if = fold_single_use_bool_if_temp(current, following, remaining))
217
- compacted << folded_if
218
- reachable = !statement_prevents_sequential_fallthrough?(folded_if)
219
- index += 2
220
- next
221
- end
222
-
223
- compacted << current
224
- reachable = !statement_prevents_sequential_fallthrough?(current)
225
- index += 1
58
+ when IR::BlockStmt
59
+ if block_requires_scope?(statement.body)
60
+ lines = ["#{indent}{"]
61
+ lines.concat(emit_statement_sequence(statement.body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
62
+ lines << "#{indent}}"
63
+ lines
64
+ else
65
+ emit_statement_sequence(statement.body, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
226
66
  end
227
-
228
- compacted
229
- end
230
-
231
- def transform_compactable_nested_bodies(statement)
232
- case statement
233
- when IR::BlockStmt
234
- IR::BlockStmt.new(body: compact_generated_statement_sequence(statement.body))
235
- when IR::WhileStmt
236
- canonicalize_top_guarded_while(
237
- IR::WhileStmt.new(condition: statement.condition, body: compact_generated_statement_sequence(statement.body))
238
- )
239
- when IR::ForStmt
240
- IR::ForStmt.new(
241
- init: statement.init,
242
- condition: statement.condition,
243
- post: statement.post,
244
- body: compact_generated_statement_sequence(statement.body),
245
- )
246
- when IR::IfStmt
247
- IR::IfStmt.new(
248
- condition: statement.condition,
249
- then_body: compact_generated_statement_sequence(statement.then_body),
250
- else_body: statement.else_body ? compact_generated_statement_sequence(statement.else_body) : nil,
251
- )
252
- when IR::SwitchStmt
253
- IR::SwitchStmt.new(
254
- expression: statement.expression,
255
- exhaustive: statement.exhaustive,
256
- cases: statement.cases.map do |switch_case|
257
- if switch_case.is_a?(IR::SwitchDefaultCase)
258
- IR::SwitchDefaultCase.new(body: compact_generated_statement_sequence(switch_case.body))
259
- else
260
- IR::SwitchCase.new(value: switch_case.value, body: compact_generated_statement_sequence(switch_case.body))
261
- end
262
- end,
263
- )
67
+ when IR::ExpressionStmt
68
+ if statement.expression.is_a?(IR::Assignment)
69
+ emit_assignment_expression(statement.expression, indent)
70
+ elsif statement.expression.is_a?(IR::Call)
71
+ ["#{indent}#{emit_expression(statement.expression)};"]
264
72
  else
265
- statement
73
+ # A pure-value expression discarded as a statement (e.g. the
74
+ # `T<-x` touch idiom) needs a void cast, otherwise the value
75
+ # is unused and triggers -Wunused-value.
76
+ ["#{indent}(void)(#{emit_expression(statement.expression)});"]
266
77
  end
267
- end
268
-
269
- def fold_single_use_local_alias(source_decl, alias_decl, remaining_statements)
270
- return unless source_decl.is_a?(IR::LocalDecl)
271
- return unless alias_decl.is_a?(IR::LocalDecl)
272
- return unless compiler_generated_local_name?(source_decl.linkage_name)
273
- return if array_type?(source_decl.type) || array_type?(alias_decl.type)
274
- return unless source_decl.type == alias_decl.type
275
- return unless alias_decl.value.is_a?(IR::Name) && alias_decl.value.name == source_decl.linkage_name
276
- return unless name_reference_count_in_statements(remaining_statements, source_decl.linkage_name).zero?
277
-
278
- IR::LocalDecl.new(name: alias_decl.name, linkage_name: alias_decl.linkage_name, type: alias_decl.type, value: source_decl.value)
279
- end
280
-
281
- def compiler_generated_local_name?(name)
282
- name.start_with?("__mt_")
283
- end
284
-
285
- def fold_single_use_bool_if_temp(local_decl, if_stmt, remaining_statements)
286
- return unless local_decl.is_a?(IR::LocalDecl)
287
- return unless if_stmt.is_a?(IR::IfStmt)
288
- return unless bool_type?(local_decl.type)
289
-
290
- condition_kind = single_use_bool_if_condition_kind(if_stmt.condition, local_decl.linkage_name)
291
- return unless condition_kind
292
- return unless name_reference_count_in_statements(if_stmt.then_body, local_decl.linkage_name).zero?
293
- return unless name_reference_count_in_statements(if_stmt.else_body || [], local_decl.linkage_name).zero?
294
- return unless name_reference_count_in_statements(remaining_statements, local_decl.linkage_name).zero?
295
-
296
- condition = if condition_kind == :direct
297
- local_decl.value
298
- else
299
- IR::Unary.new(operator: "not", operand: local_decl.value, type: local_decl.type)
300
- end
301
-
302
- IR::IfStmt.new(condition:, then_body: if_stmt.then_body, else_body: if_stmt.else_body)
303
- end
304
-
305
- def single_use_bool_if_condition_kind(condition, temp_name)
306
- return :direct if condition.is_a?(IR::Name) && condition.name == temp_name
307
-
308
- if condition.is_a?(IR::Unary) && condition.operator == "not" && condition.operand.is_a?(IR::Name) && condition.operand.name == temp_name
309
- return :negated
78
+ when IR::ReturnStmt
79
+ if statement.value
80
+ if array_type?(function.return_type)
81
+ emit_array_return(statement.value, indent)
82
+ else
83
+ ["#{indent}return #{emit_expression(statement.value)};"]
84
+ end
85
+ else
86
+ ["#{indent}return;"]
310
87
  end
311
-
312
- nil
313
- end
314
-
315
- def name_reference_count_in_statements(statements, name)
316
- statements.sum { |statement| name_reference_count_in_statement(statement, name) }
317
- end
318
-
319
- def name_reference_count_in_statement(statement, name)
320
- case statement
321
- when IR::LocalDecl
322
- name_reference_count_in_expression(statement.value, name)
323
- when IR::Assignment
324
- name_reference_count_in_expression(statement.target, name) + name_reference_count_in_expression(statement.value, name)
325
- when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
326
- count = name_reference_count_in_statements(statement.body, name)
327
- return count unless statement.is_a?(IR::WhileStmt) || statement.is_a?(IR::ForStmt)
328
-
329
- count += name_reference_count_in_expression(statement.condition, name)
330
- count += name_reference_count_in_statement(statement.init, name) if statement.is_a?(IR::ForStmt)
331
- count += name_reference_count_in_statement(statement.post, name) if statement.is_a?(IR::ForStmt)
332
- count
333
- when IR::IfStmt
334
- name_reference_count_in_expression(statement.condition, name) +
335
- name_reference_count_in_statements(statement.then_body, name) +
336
- name_reference_count_in_statements(statement.else_body || [], name)
337
- when IR::SwitchStmt
338
- name_reference_count_in_expression(statement.expression, name) +
339
- statement.cases.sum { |switch_case| (switch_case.is_a?(IR::SwitchCase) ? name_reference_count_in_expression(switch_case.value, name) : 0) + name_reference_count_in_statements(switch_case.body, name) }
340
- when IR::StaticAssert
341
- name_reference_count_in_expression(statement.condition, name) + name_reference_count_in_expression(statement.message, name)
342
- when IR::ReturnStmt
343
- statement.value ? name_reference_count_in_expression(statement.value, name) : 0
344
- when IR::ExpressionStmt
345
- name_reference_count_in_expression(statement.expression, name)
88
+ when IR::WhileStmt
89
+ body_continue_label = loop_continue_label_name(statement.body)
90
+ body = body_continue_label ? statement.body[0...-1] : statement.body
91
+ body_break_label = loop_break_label_name(body, remaining_statements)
92
+ @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
93
+ if @debug_guards
94
+ @loop_guard_id += 1
95
+ guard_name = "__mt_loop_#{@loop_guard_id}"
96
+ lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"]
97
+ lines << "#{indent}while (#{emit_expression(statement.condition)}) {"
98
+ guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");"
99
+ lines << guard
100
+ lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
101
+ lines << "#{indent}}"
102
+ lines << "#{indent}}"
103
+ lines
346
104
  else
347
- 0
105
+ lines = ["#{indent}while (#{emit_expression(statement.condition)}) {"]
106
+ lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
107
+ lines << "#{indent}}"
108
+ lines
348
109
  end
349
- end
350
-
351
- def name_reference_count_in_expression(expression, name)
352
- case expression
353
- when IR::Name
354
- expression.name == name ? 1 : 0
355
- when IR::Member
356
- name_reference_count_in_expression(expression.receiver, name)
357
- when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
358
- name_reference_count_in_expression(expression.receiver, name) + name_reference_count_in_expression(expression.index, name)
359
- when IR::Call
360
- callee_count = expression.callee.is_a?(String) ? 0 : name_reference_count_in_expression(expression.callee, name)
361
- callee_count + expression.arguments.sum { |argument| name_reference_count_in_expression(argument, name) }
362
- when IR::Unary
363
- name_reference_count_in_expression(expression.operand, name)
364
- when IR::Binary
365
- name_reference_count_in_expression(expression.left, name) + name_reference_count_in_expression(expression.right, name)
366
- when IR::Conditional
367
- name_reference_count_in_expression(expression.condition, name) +
368
- name_reference_count_in_expression(expression.then_expression, name) +
369
- name_reference_count_in_expression(expression.else_expression, name)
370
- when IR::ReinterpretExpr
371
- name_reference_count_in_expression(expression.expression, name)
372
- when IR::AddressOf, IR::Cast
373
- name_reference_count_in_expression(expression.expression, name)
374
- when IR::AggregateLiteral
375
- expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) }
376
- when IR::ArrayLiteral
377
- expression.elements.sum { |element| name_reference_count_in_expression(element, name) }
378
- when IR::VariantLiteral
379
- expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) }
110
+ when IR::ForStmt
111
+ body_continue_label = loop_continue_label_name(statement.body)
112
+ body = body_continue_label ? statement.body[0...-1] : statement.body
113
+ body_break_label = loop_break_label_name(body, remaining_statements)
114
+ @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
115
+ if @debug_guards
116
+ @loop_guard_id += 1
117
+ guard_name = "__mt_loop_#{@loop_guard_id}"
118
+ lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"]
119
+ lines << "#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"
120
+ guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");"
121
+ lines << guard
122
+ lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
123
+ lines << "#{indent}}"
124
+ lines << "#{indent}}"
125
+ lines
380
126
  else
381
- 0
127
+ lines = ["#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"]
128
+ lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
129
+ lines << "#{indent}}"
130
+ lines
382
131
  end
383
- end
384
-
385
- def bool_type?(type)
386
- type.is_a?(Types::Primitive) && type.name == "bool"
387
- end
388
-
389
- def checked_index_aliases_for_statement(statement)
390
- expressions = case statement
391
- when IR::LocalDecl
392
- [statement.value]
393
- when IR::Assignment
394
- [statement.target, statement.value]
395
- when IR::ExpressionStmt
396
- [statement.expression]
397
- when IR::ReturnStmt
398
- statement.value ? [statement.value] : []
399
- else
400
- []
401
- end
402
-
403
- collect_checked_index_aliases(expressions.compact)
404
- end
405
-
406
- def collect_checked_index_aliases(expressions)
407
- counts = Hash.new(0)
408
- order = []
409
- expressions.each do |expression|
410
- collect_checked_index_alias_candidates(expression, counts, order)
132
+ when IR::BreakStmt
133
+ ["#{indent}break;"]
134
+ when IR::ContinueStmt
135
+ ["#{indent}continue;"]
136
+ when IR::GotoStmt
137
+ return ["#{indent}continue;"] if loop_continue_label && statement.label == loop_continue_label
138
+ return ["#{indent}break;"] if loop_break_label && statement.label == loop_break_label
139
+
140
+ ["#{indent}goto #{statement.label};"]
141
+ when IR::LabelStmt
142
+ return [] if @suppressed_labels.include?(statement.name)
143
+ return [] if loop_continue_label && statement.name == loop_continue_label
144
+ return [] unless used_labels.include?(statement.name)
145
+
146
+ ["#{indent}#{statement.name}:;"]
147
+ when IR::StaticAssert
148
+ ["#{indent}#{emit_static_assert(statement)}"]
149
+ when IR::IfStmt
150
+ emit_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
151
+ when IR::SwitchStmt
152
+ if switch_emittable_as_if?(statement, loop_break_label:)
153
+ emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
154
+ else
155
+ lines = ["#{indent}switch (#{emit_expression(statement.expression)}) {"]
156
+ has_default = false
157
+ statement.cases.each do |switch_case|
158
+ if switch_case.is_a?(IR::SwitchDefaultCase)
159
+ has_default = true
160
+ lines << "#{indent}#{INDENT}default: {"
161
+ else
162
+ lines << "#{indent}#{INDENT}case #{emit_expression(switch_case.value)}: {"
163
+ end
164
+ lines.concat(emit_statement_sequence(switch_case.body, level + 2, function:, used_labels:, loop_continue_label:))
165
+ lines << "#{indent}#{INDENT}#{INDENT}break;" unless body_terminates?(switch_case.body)
166
+ lines << "#{indent}#{INDENT}}"
411
167
  end
412
-
413
- order.each_with_object({}) do |expression, aliases|
414
- next unless counts[expression] > 1
415
- next unless hoistable_checked_index_alias?(expression)
416
-
417
- aliases[expression] = fresh_checked_index_alias_name
168
+ # An exhaustive enum/variant match covers every arm, so the C
169
+ # `default` path is unreachable. Emitting it lets the compiler
170
+ # prove non-void functions always return, avoiding spurious
171
+ # -Wreturn-type warnings on the implicit no-match path.
172
+ if statement.exhaustive && !has_default
173
+ lines << "#{indent}#{INDENT}default: __builtin_unreachable();"
418
174
  end
419
- end
420
-
421
- def collect_checked_index_alias_candidates(expression, counts, order)
422
- case expression
423
- when IR::Member
424
- collect_checked_index_alias_candidates(expression.receiver, counts, order)
425
- when IR::Index
426
- collect_checked_index_alias_candidates(expression.receiver, counts, order)
427
- collect_checked_index_alias_candidates(expression.index, counts, order)
428
- when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
429
- order << expression unless counts.key?(expression)
430
- counts[expression] += 1
431
- collect_checked_index_alias_candidates(expression.receiver, counts, order)
432
- collect_checked_index_alias_candidates(expression.index, counts, order)
433
- when IR::Call
434
- collect_checked_index_alias_candidates(expression.callee, counts, order) unless expression.callee.is_a?(String)
435
- expression.arguments.each { |argument| collect_checked_index_alias_candidates(argument, counts, order) }
436
- when IR::Unary
437
- collect_checked_index_alias_candidates(expression.operand, counts, order)
438
- when IR::Binary
439
- collect_checked_index_alias_candidates(expression.left, counts, order)
440
- collect_checked_index_alias_candidates(expression.right, counts, order)
441
- when IR::Conditional
442
- collect_checked_index_alias_candidates(expression.condition, counts, order)
443
- collect_checked_index_alias_candidates(expression.then_expression, counts, order)
444
- collect_checked_index_alias_candidates(expression.else_expression, counts, order)
445
- when IR::ReinterpretExpr, IR::AddressOf, IR::Cast
446
- collect_checked_index_alias_candidates(expression.expression, counts, order)
447
- when IR::AggregateLiteral
448
- expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) }
449
- when IR::ArrayLiteral
450
- expression.elements.each { |element| collect_checked_index_alias_candidates(element, counts, order) }
451
- when IR::VariantLiteral
452
- expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) }
175
+ lines << "#{indent}}"
176
+ lines
453
177
  end
178
+ else
179
+ raise CBackendError.new("unsupported IR statement #{statement.class.name}", line: 0, column: 0, path: @path)
454
180
  end
181
+ end
455
182
 
456
- def hoistable_checked_index_alias?(expression)
457
- side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index)
458
- end
183
+ alias_lines + line_directive + statement_lines
184
+ end
459
185
 
460
- def side_effect_free_expression?(expression)
461
- case expression
462
- when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr
463
- true
464
- when IR::Member
465
- side_effect_free_expression?(expression.receiver)
466
- when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
467
- side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index)
468
- when IR::Unary
469
- side_effect_free_expression?(expression.operand)
470
- when IR::Binary
471
- side_effect_free_expression?(expression.left) && side_effect_free_expression?(expression.right)
472
- when IR::Conditional
473
- side_effect_free_expression?(expression.condition) &&
474
- side_effect_free_expression?(expression.then_expression) &&
475
- side_effect_free_expression?(expression.else_expression)
476
- when IR::ReinterpretExpr, IR::AddressOf, IR::Cast
477
- side_effect_free_expression?(expression.expression)
478
- when IR::AggregateLiteral
479
- expression.fields.all? { |field| side_effect_free_expression?(field.value) }
480
- when IR::VariantLiteral
481
- expression.fields.all? { |field| side_effect_free_expression?(field.value) }
482
- when IR::ArrayLiteral
483
- expression.elements.all? { |element| side_effect_free_expression?(element) }
484
- when IR::Call
485
- false
486
- else
487
- false
488
- end
489
- end
186
+ def compact_generated_statement_sequence(statements)
187
+ transformed = statements.map { |statement| transform_compactable_nested_bodies(statement) }
188
+ compacted = []
189
+ index = 0
190
+ reachable = true
490
191
 
491
- def emit_checked_index_alias_declarations(aliases, indent)
492
- aliases.map do |expression, alias_name|
493
- "#{indent}#{c_declaration(pointer_to(expression.type), alias_name)} = #{emit_checked_index_pointer(expression)};"
494
- end
495
- end
192
+ while index < transformed.length
193
+ current = transformed[index]
496
194
 
497
- def emit_checked_index_pointer(expression)
498
- case expression
499
- when IR::CheckedIndex
500
- "#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)})"
501
- when IR::CheckedSpanIndex
502
- "#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
503
- else
504
- raise CBackendError.new("unsupported checked index alias expression #{expression.class.name}", line: 0, column: 0, path: @path)
195
+ unless reachable
196
+ if current.is_a?(IR::LabelStmt)
197
+ compacted << current
198
+ reachable = true
505
199
  end
200
+ index += 1
201
+ next
506
202
  end
507
203
 
508
- def fresh_checked_index_alias_name
509
- @checked_index_alias_id += 1
510
- "__mt_checked_index_ptr_#{@checked_index_alias_id}"
511
- end
512
-
513
- def with_checked_index_aliases(aliases)
514
- @checked_index_alias_stack << aliases
515
- yield
516
- ensure
517
- @checked_index_alias_stack.pop
518
- end
519
-
520
- def checked_index_alias(expression)
521
- @checked_index_alias_stack.reverse_each do |aliases|
522
- alias_name = aliases[expression]
523
- return alias_name if alias_name
524
- end
204
+ following = transformed[index + 1]
205
+ remaining = transformed[(index + 2)..] || []
525
206
 
526
- nil
207
+ if following && (folded_local_alias = fold_single_use_local_alias(current, following, remaining))
208
+ compacted << folded_local_alias
209
+ reachable = !statement_prevents_sequential_fallthrough?(folded_local_alias)
210
+ index += 2
211
+ next
527
212
  end
528
213
 
529
- def block_requires_scope?(statements)
530
- statements.any? { |statement| statement.is_a?(IR::LocalDecl) }
214
+ if following && (folded_if = fold_single_use_bool_if_temp(current, following, remaining))
215
+ compacted << folded_if
216
+ reachable = !statement_prevents_sequential_fallthrough?(folded_if)
217
+ index += 2
218
+ next
531
219
  end
532
220
 
533
- def emit_for_clause_statement(statement)
534
- case statement
535
- when IR::LocalDecl
536
- raise CBackendError.new("array for-loop init declarations are unsupported", line: 0, column: 0, path: @path) if array_type?(statement.type)
537
-
538
- "#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)}"
539
- when IR::Assignment
540
- if array_type?(statement.target.type) && statement.operator == "="
541
- raise CBackendError.new("array for-loop assignment clauses are unsupported", line: 0, column: 0, path: @path)
221
+ compacted << current
222
+ reachable = !statement_prevents_sequential_fallthrough?(current)
223
+ index += 1
224
+ end
225
+
226
+ compacted
227
+ end
228
+
229
+ def transform_compactable_nested_bodies(statement)
230
+ case statement
231
+ when IR::BlockStmt
232
+ IR::BlockStmt.new(body: compact_generated_statement_sequence(statement.body))
233
+ when IR::WhileStmt
234
+ canonicalize_top_guarded_while(
235
+ IR::WhileStmt.new(condition: statement.condition, body: compact_generated_statement_sequence(statement.body))
236
+ )
237
+ when IR::ForStmt
238
+ IR::ForStmt.new(
239
+ init: statement.init,
240
+ condition: statement.condition,
241
+ post: statement.post,
242
+ body: compact_generated_statement_sequence(statement.body),
243
+ )
244
+ when IR::IfStmt
245
+ IR::IfStmt.new(
246
+ condition: statement.condition,
247
+ then_body: compact_generated_statement_sequence(statement.then_body),
248
+ else_body: statement.else_body ? compact_generated_statement_sequence(statement.else_body) : nil,
249
+ )
250
+ when IR::SwitchStmt
251
+ IR::SwitchStmt.new(
252
+ expression: statement.expression,
253
+ exhaustive: statement.exhaustive,
254
+ cases: statement.cases.map do |switch_case|
255
+ if switch_case.is_a?(IR::SwitchDefaultCase)
256
+ IR::SwitchDefaultCase.new(body: compact_generated_statement_sequence(switch_case.body))
257
+ else
258
+ IR::SwitchCase.new(value: switch_case.value, body: compact_generated_statement_sequence(switch_case.body))
542
259
  end
543
-
544
- "#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)}"
545
- when IR::ExpressionStmt
546
- emit_expression(statement.expression)
547
- else
548
- raise CBackendError.new("unsupported for-loop clause #{statement.class.name}", line: 0, column: 0, path: @path)
549
- end
260
+ end,
261
+ )
262
+ else
263
+ statement
264
+ end
265
+ end
266
+
267
+ def fold_single_use_local_alias(source_decl, alias_decl, remaining_statements)
268
+ return unless source_decl.is_a?(IR::LocalDecl)
269
+ return unless alias_decl.is_a?(IR::LocalDecl)
270
+ return unless compiler_generated_local_name?(source_decl.linkage_name)
271
+ return if array_type?(source_decl.type) || array_type?(alias_decl.type)
272
+ return unless source_decl.type == alias_decl.type
273
+ return unless alias_decl.value.is_a?(IR::Name) && alias_decl.value.name == source_decl.linkage_name
274
+ return unless name_reference_count_in_statements(remaining_statements, source_decl.linkage_name).zero?
275
+
276
+ IR::LocalDecl.new(name: alias_decl.name, linkage_name: alias_decl.linkage_name, type: alias_decl.type, value: source_decl.value)
277
+ end
278
+
279
+ def compiler_generated_local_name?(name)
280
+ name.start_with?("__mt_")
281
+ end
282
+
283
+ def fold_single_use_bool_if_temp(local_decl, if_stmt, remaining_statements)
284
+ return unless local_decl.is_a?(IR::LocalDecl)
285
+ return unless if_stmt.is_a?(IR::IfStmt)
286
+ return unless bool_type?(local_decl.type)
287
+
288
+ condition_kind = single_use_bool_if_condition_kind(if_stmt.condition, local_decl.linkage_name)
289
+ return unless condition_kind
290
+ return unless name_reference_count_in_statements(if_stmt.then_body, local_decl.linkage_name).zero?
291
+ return unless name_reference_count_in_statements(if_stmt.else_body || [], local_decl.linkage_name).zero?
292
+ return unless name_reference_count_in_statements(remaining_statements, local_decl.linkage_name).zero?
293
+
294
+ condition = if condition_kind == :direct
295
+ local_decl.value
296
+ else
297
+ IR::Unary.new(operator: "not", operand: local_decl.value, type: local_decl.type)
298
+ end
299
+
300
+ IR::IfStmt.new(condition:, then_body: if_stmt.then_body, else_body: if_stmt.else_body)
301
+ end
302
+
303
+ def single_use_bool_if_condition_kind(condition, temp_name)
304
+ return :direct if condition.is_a?(IR::Name) && condition.name == temp_name
305
+
306
+ if condition.is_a?(IR::Unary) && condition.operator == "not" && condition.operand.is_a?(IR::Name) && condition.operand.name == temp_name
307
+ return :negated
308
+ end
309
+
310
+ nil
311
+ end
312
+
313
+ def name_reference_count_in_statements(statements, name)
314
+ statements.sum { |statement| name_reference_count_in_statement(statement, name) }
315
+ end
316
+
317
+ def name_reference_count_in_statement(statement, name)
318
+ case statement
319
+ when IR::LocalDecl
320
+ name_reference_count_in_expression(statement.value, name)
321
+ when IR::Assignment
322
+ name_reference_count_in_expression(statement.target, name) + name_reference_count_in_expression(statement.value, name)
323
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
324
+ count = name_reference_count_in_statements(statement.body, name)
325
+ return count unless statement.is_a?(IR::WhileStmt) || statement.is_a?(IR::ForStmt)
326
+
327
+ count += name_reference_count_in_expression(statement.condition, name)
328
+ count += name_reference_count_in_statement(statement.init, name) if statement.is_a?(IR::ForStmt)
329
+ count += name_reference_count_in_statement(statement.post, name) if statement.is_a?(IR::ForStmt)
330
+ count
331
+ when IR::IfStmt
332
+ name_reference_count_in_expression(statement.condition, name) +
333
+ name_reference_count_in_statements(statement.then_body, name) +
334
+ name_reference_count_in_statements(statement.else_body || [], name)
335
+ when IR::SwitchStmt
336
+ name_reference_count_in_expression(statement.expression, name) +
337
+ statement.cases.sum { |switch_case| (switch_case.is_a?(IR::SwitchCase) ? name_reference_count_in_expression(switch_case.value, name) : 0) + name_reference_count_in_statements(switch_case.body, name) }
338
+ when IR::StaticAssert
339
+ name_reference_count_in_expression(statement.condition, name) + name_reference_count_in_expression(statement.message, name)
340
+ when IR::ReturnStmt
341
+ statement.value ? name_reference_count_in_expression(statement.value, name) : 0
342
+ when IR::ExpressionStmt
343
+ name_reference_count_in_expression(statement.expression, name)
344
+ else
345
+ 0
346
+ end
347
+ end
348
+
349
+ def name_reference_count_in_expression(expression, name)
350
+ case expression
351
+ when IR::Name
352
+ expression.name == name ? 1 : 0
353
+ when IR::Member
354
+ name_reference_count_in_expression(expression.receiver, name)
355
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
356
+ name_reference_count_in_expression(expression.receiver, name) + name_reference_count_in_expression(expression.index, name)
357
+ when IR::Call
358
+ callee_count = expression.callee.is_a?(String) ? 0 : name_reference_count_in_expression(expression.callee, name)
359
+ callee_count + expression.arguments.sum { |argument| name_reference_count_in_expression(argument, name) }
360
+ when IR::Unary
361
+ name_reference_count_in_expression(expression.operand, name)
362
+ when IR::Binary
363
+ name_reference_count_in_expression(expression.left, name) + name_reference_count_in_expression(expression.right, name)
364
+ when IR::Conditional
365
+ name_reference_count_in_expression(expression.condition, name) +
366
+ name_reference_count_in_expression(expression.then_expression, name) +
367
+ name_reference_count_in_expression(expression.else_expression, name)
368
+ when IR::ReinterpretExpr
369
+ name_reference_count_in_expression(expression.expression, name)
370
+ when IR::AddressOf, IR::Cast
371
+ name_reference_count_in_expression(expression.expression, name)
372
+ when IR::AggregateLiteral
373
+ expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) }
374
+ when IR::ArrayLiteral
375
+ expression.elements.sum { |element| name_reference_count_in_expression(element, name) }
376
+ when IR::VariantLiteral
377
+ expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) }
378
+ else
379
+ 0
380
+ end
381
+ end
382
+
383
+ def bool_type?(type)
384
+ type.is_a?(Types::Primitive) && type.name == "bool"
385
+ end
386
+
387
+ def checked_index_aliases_for_statement(statement)
388
+ expressions = case statement
389
+ when IR::LocalDecl
390
+ [statement.value]
391
+ when IR::Assignment
392
+ [statement.target, statement.value]
393
+ when IR::ExpressionStmt
394
+ [statement.expression]
395
+ when IR::ReturnStmt
396
+ statement.value ? [statement.value] : []
397
+ else
398
+ []
399
+ end
400
+
401
+ collect_checked_index_aliases(expressions.compact)
402
+ end
403
+
404
+ def collect_checked_index_aliases(expressions)
405
+ counts = Hash.new(0)
406
+ order = []
407
+ expressions.each do |expression|
408
+ collect_checked_index_alias_candidates(expression, counts, order)
409
+ end
410
+
411
+ order.each_with_object({}) do |expression, aliases|
412
+ next unless counts[expression] > 1
413
+ next unless hoistable_checked_index_alias?(expression)
414
+
415
+ aliases[expression] = fresh_checked_index_alias_name
416
+ end
417
+ end
418
+
419
+ def collect_checked_index_alias_candidates(expression, counts, order)
420
+ case expression
421
+ when IR::Member
422
+ collect_checked_index_alias_candidates(expression.receiver, counts, order)
423
+ when IR::Index
424
+ collect_checked_index_alias_candidates(expression.receiver, counts, order)
425
+ collect_checked_index_alias_candidates(expression.index, counts, order)
426
+ when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
427
+ order << expression unless counts.key?(expression)
428
+ counts[expression] += 1
429
+ collect_checked_index_alias_candidates(expression.receiver, counts, order)
430
+ collect_checked_index_alias_candidates(expression.index, counts, order)
431
+ when IR::Call
432
+ collect_checked_index_alias_candidates(expression.callee, counts, order) unless expression.callee.is_a?(String)
433
+ expression.arguments.each { |argument| collect_checked_index_alias_candidates(argument, counts, order) }
434
+ when IR::Unary
435
+ collect_checked_index_alias_candidates(expression.operand, counts, order)
436
+ when IR::Binary
437
+ collect_checked_index_alias_candidates(expression.left, counts, order)
438
+ collect_checked_index_alias_candidates(expression.right, counts, order)
439
+ when IR::Conditional
440
+ collect_checked_index_alias_candidates(expression.condition, counts, order)
441
+ collect_checked_index_alias_candidates(expression.then_expression, counts, order)
442
+ collect_checked_index_alias_candidates(expression.else_expression, counts, order)
443
+ when IR::ReinterpretExpr, IR::AddressOf, IR::Cast
444
+ collect_checked_index_alias_candidates(expression.expression, counts, order)
445
+ when IR::AggregateLiteral
446
+ expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) }
447
+ when IR::ArrayLiteral
448
+ expression.elements.each { |element| collect_checked_index_alias_candidates(element, counts, order) }
449
+ when IR::VariantLiteral
450
+ expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) }
451
+ end
452
+ end
453
+
454
+ def hoistable_checked_index_alias?(expression)
455
+ side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index)
456
+ end
457
+
458
+ def side_effect_free_expression?(expression)
459
+ case expression
460
+ when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr
461
+ true
462
+ when IR::Member
463
+ side_effect_free_expression?(expression.receiver)
464
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
465
+ side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index)
466
+ when IR::Unary
467
+ side_effect_free_expression?(expression.operand)
468
+ when IR::Binary
469
+ side_effect_free_expression?(expression.left) && side_effect_free_expression?(expression.right)
470
+ when IR::Conditional
471
+ side_effect_free_expression?(expression.condition) &&
472
+ side_effect_free_expression?(expression.then_expression) &&
473
+ side_effect_free_expression?(expression.else_expression)
474
+ when IR::ReinterpretExpr, IR::AddressOf, IR::Cast
475
+ side_effect_free_expression?(expression.expression)
476
+ when IR::AggregateLiteral
477
+ expression.fields.all? { |field| side_effect_free_expression?(field.value) }
478
+ when IR::VariantLiteral
479
+ expression.fields.all? { |field| side_effect_free_expression?(field.value) }
480
+ when IR::ArrayLiteral
481
+ expression.elements.all? { |element| side_effect_free_expression?(element) }
482
+ when IR::Call
483
+ false
484
+ else
485
+ false
486
+ end
487
+ end
488
+
489
+ def emit_checked_index_alias_declarations(aliases, indent)
490
+ aliases.map do |expression, alias_name|
491
+ "#{indent}#{c_declaration(pointer_to(expression.type), alias_name)} = #{emit_checked_index_pointer(expression)};"
492
+ end
493
+ end
494
+
495
+ def emit_checked_index_pointer(expression)
496
+ case expression
497
+ when IR::CheckedIndex
498
+ "#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)})"
499
+ when IR::CheckedSpanIndex
500
+ "#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
501
+ else
502
+ raise CBackendError.new("unsupported checked index alias expression #{expression.class.name}", line: 0, column: 0, path: @path)
503
+ end
504
+ end
505
+
506
+ def fresh_checked_index_alias_name
507
+ @checked_index_alias_id += 1
508
+ "__mt_checked_index_ptr_#{@checked_index_alias_id}"
509
+ end
510
+
511
+ def with_checked_index_aliases(aliases)
512
+ @checked_index_alias_stack << aliases
513
+ yield
514
+ ensure
515
+ @checked_index_alias_stack.pop
516
+ end
517
+
518
+ def checked_index_alias(expression)
519
+ @checked_index_alias_stack.reverse_each do |aliases|
520
+ alias_name = aliases[expression]
521
+ return alias_name if alias_name
522
+ end
523
+
524
+ nil
525
+ end
526
+
527
+ def block_requires_scope?(statements)
528
+ statements.any? { |statement| statement.is_a?(IR::LocalDecl) }
529
+ end
530
+
531
+ def emit_for_clause_statement(statement)
532
+ case statement
533
+ when IR::LocalDecl
534
+ raise CBackendError.new("array for-loop init declarations are unsupported", line: 0, column: 0, path: @path) if array_type?(statement.type)
535
+
536
+ "#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)}"
537
+ when IR::Assignment
538
+ if array_type?(statement.target.type) && statement.operator == "="
539
+ raise CBackendError.new("array for-loop assignment clauses are unsupported", line: 0, column: 0, path: @path)
550
540
  end
551
541
 
552
- def emit_static_assert(statement)
553
- message = if statement.message.is_a?(IR::StringLiteral)
554
- statement.message.value.inspect
555
- else
556
- emit_expression(statement.message)
557
- end
558
-
559
- "_Static_assert(#{emit_expression(statement.condition)}, #{message});"
560
- end
561
-
562
- def emit_assignment_expression(assignment, indent)
563
- ["#{indent}#{emit_expression(assignment.target)} #{assignment.operator} #{emit_expression(assignment.value)};"]
564
- end
542
+ "#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)}"
543
+ when IR::ExpressionStmt
544
+ emit_expression(statement.expression)
545
+ else
546
+ raise CBackendError.new("unsupported for-loop clause #{statement.class.name}", line: 0, column: 0, path: @path)
547
+ end
548
+ end
549
+
550
+ def emit_static_assert(statement)
551
+ message = if statement.message.is_a?(IR::StringLiteral)
552
+ statement.message.value.inspect
553
+ else
554
+ emit_expression(statement.message)
555
+ end
556
+
557
+ "_Static_assert(#{emit_expression(statement.condition)}, #{message});"
558
+ end
559
+
560
+ def emit_assignment_expression(assignment, indent)
561
+ ["#{indent}#{emit_expression(assignment.target)} #{assignment.operator} #{emit_expression(assignment.value)};"]
562
+ end
565
563
  end
566
564
  end
567
565
  end