mt-lang 0.2.0

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,421 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
7
+
8
+ def check_definite_assignment(binding)
9
+ return unless binding.ast.respond_to?(:body)
10
+
11
+ resolution = binding_resolution_snapshot
12
+ graph = ControlFlow::Builder.new(
13
+ binding_resolution: ControlFlow::BindingResolution.new(
14
+ identifier_binding_ids: resolution.identifier_binding_ids,
15
+ declaration_binding_ids: resolution.declaration_binding_ids,
16
+ mutating_argument_identifier_ids: resolution.mutating_argument_identifier_ids,
17
+ ),
18
+ strict_binding_ids: true,
19
+ local_decl_without_initializer_writes: true,
20
+ ).build(binding.ast.body)
21
+
22
+ local_declared_ids = Set.new
23
+ graph.each_node do |node|
24
+ node.writes_info.each do |write|
25
+ origin = write[:origin]
26
+ next unless %i[declaration for_binding match_binding].include?(origin)
27
+
28
+ local_declared_ids << write[:binding_key]
29
+ end
30
+ end
31
+
32
+ initially_assigned = binding.body_params.each_with_object(Set.new) do |param, set|
33
+ set << param.id if param.id
34
+ end
35
+ # Any binding read but never defined in this function body is treated as
36
+ # preassigned (for example module-level const/var bindings).
37
+ initially_assigned.merge(graph.read_bindings - local_declared_ids)
38
+
39
+ result = ControlFlow::DefiniteAssignment.solve(graph, initially_assigned:)
40
+ first_issue = result.read_before_assignment.min_by do |issue|
41
+ [issue.line || Float::INFINITY, issue.column || Float::INFINITY, issue.node_id]
42
+ end
43
+ return unless first_issue
44
+
45
+ name = @binding_name_by_id[first_issue.binding_key] || first_issue.binding_key
46
+ raise SemanticError.new(
47
+ "read of '#{name}' before definite assignment",
48
+ line: first_issue.line,
49
+ column: first_issue.column,
50
+ length: first_issue.length || name.to_s.length,
51
+ path: @path,
52
+ )
53
+ end
54
+
55
+ # Runs a strict binding-ID nullability pass before statement checks.
56
+ # Resolution is computed with a lexical pre-check walk so shadowed names
57
+ # are disambiguated without relying on name fallback.
58
+ def run_nullability_pre_pass(binding, scopes)
59
+ return unless binding.ast.respond_to?(:body)
60
+
61
+ @nullability_flow_result = nil
62
+ resolution = precheck_binding_resolution(binding.ast.body, scopes)
63
+ graph = ControlFlow::Builder.new(
64
+ binding_resolution: ControlFlow::BindingResolution.new(
65
+ identifier_binding_ids: resolution.identifier_binding_ids,
66
+ declaration_binding_ids: resolution.declaration_binding_ids,
67
+ mutating_argument_identifier_ids: resolution.mutating_argument_identifier_ids,
68
+ ),
69
+ strict_binding_ids: true,
70
+ ).build(binding.ast.body)
71
+ @nullability_flow_result = ControlFlow::NullabilityFlow.solve(graph)
72
+ end
73
+
74
+ # After processing a statement, apply ControlFlow-derived non-null refinements to
75
+ # the scopes so the *next* statement benefits from cross-branch narrowing.
76
+ def apply_nullability_continuation_refinements!(scopes, next_stmt)
77
+ return unless @nullability_flow_result
78
+
79
+ nonnull_binding_ids = @nullability_flow_result.nonnull_before(next_stmt)
80
+ return if nonnull_binding_ids.empty?
81
+
82
+ refinements = {}
83
+ nonnull_binding_ids.each do |binding_id|
84
+ next unless binding_id.is_a?(Integer)
85
+
86
+ name = @binding_name_by_id[binding_id]
87
+ next unless name
88
+
89
+ binding = lookup_value(name, scopes)
90
+ next unless binding&.id == binding_id
91
+ next unless binding&.storage_type.is_a?(Types::Nullable)
92
+
93
+ refinements[name] = binding.storage_type.base
94
+ end
95
+ apply_continuation_refinements!(scopes, refinements) unless refinements.empty?
96
+ end
97
+
98
+ def preassign_local_binding_ids(statements)
99
+ @preassigned_local_binding_ids = {}
100
+ preassign_local_binding_ids_in_statements(statements || [])
101
+ end
102
+
103
+ def preassign_local_binding_ids_in_statements(statements)
104
+ statements.each do |statement|
105
+ case statement
106
+ when AST::ErrorBlockStmt
107
+ preassign_local_binding_ids_in_expression(statement.header_expression) if statement.header_expression
108
+ Array(statement.header_iterables).each { |iterable| preassign_local_binding_ids_in_expression(iterable) }
109
+ Array(statement.header_bindings).each do |binding|
110
+ @preassigned_local_binding_ids[binding.object_id] ||= allocate_binding_id
111
+ end if statement.header_type == :for
112
+ preassign_local_binding_ids_in_statements(statement.body || [])
113
+ when AST::LocalDecl
114
+ @preassigned_local_binding_ids[statement.object_id] ||= allocate_binding_id
115
+ preassign_local_binding_ids_in_expression(statement.value) if statement.value
116
+ if statement.else_binding && (statement.else_body || statement.recovered_else)
117
+ @preassigned_local_binding_ids[statement.else_binding.object_id] ||= allocate_binding_id
118
+ end
119
+ preassign_local_binding_ids_in_statements(statement.else_body || [])
120
+ when AST::Assignment
121
+ preassign_local_binding_ids_in_expression(statement.target)
122
+ preassign_local_binding_ids_in_expression(statement.value)
123
+ when AST::IfStmt
124
+ statement.branches.each do |branch|
125
+ preassign_local_binding_ids_in_expression(branch.condition)
126
+ preassign_local_binding_ids_in_statements(branch.body || [])
127
+ end
128
+ preassign_local_binding_ids_in_statements(statement.else_body || [])
129
+ when AST::MatchStmt
130
+ preassign_local_binding_ids_in_expression(statement.expression)
131
+ statement.arms.each do |arm|
132
+ preassign_local_binding_ids_in_expression(arm.pattern)
133
+ @preassigned_local_binding_ids[arm.object_id] ||= allocate_binding_id if arm.binding_name
134
+
135
+ # Preassign binding IDs for struct pattern bindings
136
+ if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty?
137
+ arm.pattern.arguments.each do |arg|
138
+ next if arg.name
139
+ next unless arg.value.is_a?(AST::Identifier)
140
+
141
+ @preassigned_local_binding_ids[arg.object_id] ||= allocate_binding_id
142
+ end
143
+ end
144
+
145
+ preassign_local_binding_ids_in_statements(if arm.respond_to?(:body) then (arm.body || []) else [arm.value].compact end)
146
+ end
147
+ when AST::UnsafeStmt
148
+ preassign_local_binding_ids_in_statements(statement.body || [])
149
+ when AST::WhileStmt
150
+ preassign_local_binding_ids_in_expression(statement.condition)
151
+ preassign_local_binding_ids_in_statements(statement.body || [])
152
+ when AST::ForStmt
153
+ statement.bindings.each do |binding|
154
+ @preassigned_local_binding_ids[binding.object_id] ||= allocate_binding_id
155
+ end
156
+ statement.iterables.each { |iterable| preassign_local_binding_ids_in_expression(iterable) }
157
+ preassign_local_binding_ids_in_statements(statement.body || [])
158
+ when AST::DeferStmt
159
+ preassign_local_binding_ids_in_expression(statement.expression) if statement.expression
160
+ preassign_local_binding_ids_in_statements(statement.body || []) if statement.body
161
+ when AST::ReturnStmt
162
+ preassign_local_binding_ids_in_expression(statement.value) if statement.value
163
+ when AST::StaticAssert
164
+ preassign_local_binding_ids_in_expression(statement.condition)
165
+ preassign_local_binding_ids_in_expression(statement.message)
166
+ when AST::ExpressionStmt
167
+ preassign_local_binding_ids_in_expression(statement.expression)
168
+ end
169
+ end
170
+ end
171
+
172
+ def preassign_local_binding_ids_in_expression(expression)
173
+ case expression
174
+ when nil, AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
175
+ AST::BooleanLiteral, AST::NullLiteral, AST::SizeofExpr, AST::AlignofExpr,
176
+ AST::OffsetofExpr, AST::ErrorExpr
177
+ nil
178
+ when AST::MemberAccess
179
+ preassign_local_binding_ids_in_expression(expression.receiver)
180
+ when AST::IndexAccess
181
+ preassign_local_binding_ids_in_expression(expression.receiver)
182
+ preassign_local_binding_ids_in_expression(expression.index)
183
+ when AST::Specialization, AST::Call
184
+ preassign_local_binding_ids_in_expression(expression.callee)
185
+ expression.arguments.each { |argument| preassign_local_binding_ids_in_expression(argument.value) }
186
+ when AST::UnaryOp
187
+ preassign_local_binding_ids_in_expression(expression.operand)
188
+ when AST::BinaryOp
189
+ preassign_local_binding_ids_in_expression(expression.left)
190
+ preassign_local_binding_ids_in_expression(expression.right)
191
+ when AST::RangeExpr
192
+ preassign_local_binding_ids_in_expression(expression.start_expr)
193
+ preassign_local_binding_ids_in_expression(expression.end_expr)
194
+ when AST::IfExpr
195
+ preassign_local_binding_ids_in_expression(expression.condition)
196
+ preassign_local_binding_ids_in_expression(expression.then_expression)
197
+ preassign_local_binding_ids_in_expression(expression.else_expression)
198
+ when AST::MatchExpr
199
+ preassign_local_binding_ids_in_expression(expression.expression)
200
+ expression.arms.each do |arm|
201
+ preassign_local_binding_ids_in_expression(arm.pattern)
202
+ @preassigned_local_binding_ids[arm.object_id] ||= allocate_binding_id if arm.binding_name
203
+ preassign_local_binding_ids_in_expression(if arm.respond_to?(:value) then arm.value else arm.body end)
204
+ end
205
+ when AST::UnsafeExpr
206
+ preassign_local_binding_ids_in_expression(expression.expression)
207
+ when AST::PrefixCast
208
+ preassign_local_binding_ids_in_expression(expression.expression)
209
+ when AST::AwaitExpr
210
+ preassign_local_binding_ids_in_expression(expression.expression)
211
+ when AST::FormatString
212
+ expression.parts.each do |part|
213
+ preassign_local_binding_ids_in_expression(part.expression) if part.is_a?(AST::FormatExprPart)
214
+ end
215
+ when AST::ProcExpr
216
+ preassign_local_binding_ids_in_statements(expression.body)
217
+ end
218
+ end
219
+
220
+ def precheck_binding_resolution(statements, scopes)
221
+ declaration_binding_ids = {}
222
+ identifier_binding_ids = {}
223
+
224
+ initial_scope = {}
225
+ scopes.each do |scope|
226
+ scope.each do |name, binding|
227
+ initial_scope[name] = binding.id if binding.respond_to?(:id) && binding.id
228
+ end
229
+ end
230
+
231
+ walk_statements_for_precheck_resolution(
232
+ statements || [],
233
+ [initial_scope],
234
+ declaration_binding_ids,
235
+ identifier_binding_ids,
236
+ )
237
+
238
+ BindingResolution.new(
239
+ identifier_binding_ids: identifier_binding_ids,
240
+ declaration_binding_ids: declaration_binding_ids,
241
+ mutating_argument_identifier_ids: {},
242
+ editable_receiver_expression_ids: {},
243
+ mutable_lvalue_argument_identifier_ids: {},
244
+ binding_types: {},
245
+ )
246
+ end
247
+
248
+ def walk_statements_for_precheck_resolution(statements, scopes, declaration_ids, identifier_ids)
249
+ block_scopes = scopes + [{}]
250
+ statements.each do |statement|
251
+ case statement
252
+ when AST::ErrorBlockStmt
253
+ if statement.header_type == :for
254
+ Array(statement.header_iterables).each do |iterable|
255
+ walk_expression_for_precheck_resolution(iterable, block_scopes, identifier_ids, declaration_ids)
256
+ end
257
+ for_scopes = block_scopes + [{}]
258
+ Array(statement.header_bindings).each do |binding|
259
+ binding_id = @preassigned_local_binding_ids.fetch(binding.object_id)
260
+ for_scopes.last[binding.name] = binding_id
261
+ declaration_ids[binding.object_id] = binding_id
262
+ end
263
+ walk_statements_for_precheck_resolution(statement.body || [], for_scopes, declaration_ids, identifier_ids)
264
+ else
265
+ walk_expression_for_precheck_resolution(statement.header_expression, block_scopes, identifier_ids, declaration_ids) if statement.header_expression
266
+ walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids)
267
+ end
268
+ when AST::LocalDecl
269
+ walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids) if statement.value
270
+ if statement.else_binding && (statement.else_body || statement.recovered_else)
271
+ else_scopes = block_scopes + [{}]
272
+ binding_id = @preassigned_local_binding_ids.fetch(statement.else_binding.object_id)
273
+ else_scopes.last[statement.else_binding.name] = binding_id
274
+ declaration_ids[statement.else_binding.object_id] = binding_id
275
+ walk_statements_for_precheck_resolution(statement.else_body || [], else_scopes, declaration_ids, identifier_ids)
276
+ else
277
+ walk_statements_for_precheck_resolution(statement.else_body || [], block_scopes, declaration_ids, identifier_ids)
278
+ end
279
+ binding_id = @preassigned_local_binding_ids.fetch(statement.object_id)
280
+ unless let_else_discard_binding_syntax?(statement)
281
+ block_scopes.last[statement.name] = binding_id
282
+ declaration_ids[statement.object_id] = binding_id
283
+ end
284
+ when AST::Assignment
285
+ walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids)
286
+ walk_assignment_target_reads_for_precheck_resolution(statement.target, statement.operator, block_scopes, identifier_ids, declaration_ids)
287
+ if statement.target.is_a?(AST::Identifier)
288
+ if (binding_id = resolve_name_in_precheck_scopes(statement.target.name, block_scopes))
289
+ identifier_ids[statement.target.object_id] = binding_id
290
+ end
291
+ end
292
+ when AST::IfStmt
293
+ statement.branches.each do |branch|
294
+ walk_expression_for_precheck_resolution(branch.condition, block_scopes, identifier_ids, declaration_ids)
295
+ walk_statements_for_precheck_resolution(branch.body || [], block_scopes, declaration_ids, identifier_ids)
296
+ end
297
+ walk_statements_for_precheck_resolution(statement.else_body || [], block_scopes, declaration_ids, identifier_ids)
298
+ when AST::MatchStmt
299
+ walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids)
300
+ statement.arms.each do |arm|
301
+ arm_scopes = block_scopes + [{}]
302
+ if arm.binding_name
303
+ binding_id = @preassigned_local_binding_ids.fetch(arm.object_id)
304
+ arm_scopes.last[arm.binding_name] = binding_id
305
+ declaration_ids[arm.object_id] = binding_id
306
+ end
307
+ walk_statements_for_precheck_resolution(if arm.respond_to?(:body) then (arm.body || []) else [arm.value].compact end, arm_scopes, declaration_ids, identifier_ids)
308
+ end
309
+ when AST::UnsafeStmt, AST::WhileStmt
310
+ walk_expression_for_precheck_resolution(statement.condition, block_scopes, identifier_ids, declaration_ids) if statement.is_a?(AST::WhileStmt)
311
+ walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids)
312
+ when AST::ForStmt
313
+ statement.iterables.each do |iterable|
314
+ walk_expression_for_precheck_resolution(iterable, block_scopes, identifier_ids, declaration_ids)
315
+ end
316
+ for_scopes = block_scopes + [{}]
317
+ statement.bindings.each do |binding|
318
+ binding_id = @preassigned_local_binding_ids.fetch(binding.object_id)
319
+ for_scopes.last[binding.name] = binding_id
320
+ declaration_ids[binding.object_id] = binding_id
321
+ end
322
+ walk_statements_for_precheck_resolution(statement.body || [], for_scopes, declaration_ids, identifier_ids)
323
+ when AST::DeferStmt
324
+ walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids) if statement.expression
325
+ walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids) if statement.body
326
+ when AST::ExpressionStmt
327
+ walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids)
328
+ when AST::ReturnStmt
329
+ walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids) if statement.value
330
+ when AST::StaticAssert
331
+ walk_expression_for_precheck_resolution(statement.condition, block_scopes, identifier_ids, declaration_ids)
332
+ end
333
+ end
334
+ end
335
+
336
+ def walk_expression_for_precheck_resolution(expression, scopes, identifier_ids, declaration_ids = nil)
337
+ case expression
338
+ when nil
339
+ nil
340
+ when AST::Identifier
341
+ if (binding_id = resolve_name_in_precheck_scopes(expression.name, scopes))
342
+ identifier_ids[expression.object_id] = binding_id
343
+ end
344
+ when AST::MemberAccess
345
+ walk_expression_for_precheck_resolution(expression.receiver, scopes, identifier_ids, declaration_ids)
346
+ when AST::IndexAccess
347
+ walk_expression_for_precheck_resolution(expression.receiver, scopes, identifier_ids, declaration_ids)
348
+ walk_expression_for_precheck_resolution(expression.index, scopes, identifier_ids, declaration_ids)
349
+ when AST::Specialization
350
+ walk_expression_for_precheck_resolution(expression.callee, scopes, identifier_ids, declaration_ids)
351
+ when AST::Call
352
+ walk_expression_for_precheck_resolution(expression.callee, scopes, identifier_ids, declaration_ids)
353
+ expression.arguments.each { |argument| walk_expression_for_precheck_resolution(argument.value, scopes, identifier_ids, declaration_ids) }
354
+ when AST::UnaryOp
355
+ walk_expression_for_precheck_resolution(expression.operand, scopes, identifier_ids, declaration_ids)
356
+ when AST::BinaryOp
357
+ walk_expression_for_precheck_resolution(expression.left, scopes, identifier_ids, declaration_ids)
358
+ walk_expression_for_precheck_resolution(expression.right, scopes, identifier_ids, declaration_ids)
359
+ when AST::RangeExpr
360
+ walk_expression_for_precheck_resolution(expression.start_expr, scopes, identifier_ids, declaration_ids)
361
+ walk_expression_for_precheck_resolution(expression.end_expr, scopes, identifier_ids, declaration_ids)
362
+ when AST::IfExpr
363
+ walk_expression_for_precheck_resolution(expression.condition, scopes, identifier_ids, declaration_ids)
364
+ walk_expression_for_precheck_resolution(expression.then_expression, scopes, identifier_ids, declaration_ids)
365
+ walk_expression_for_precheck_resolution(expression.else_expression, scopes, identifier_ids, declaration_ids)
366
+ when AST::MatchExpr
367
+ walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids)
368
+ expression.arms.each do |arm|
369
+ walk_expression_for_precheck_resolution(arm.pattern, scopes, identifier_ids, declaration_ids)
370
+ arm_scopes = scopes
371
+ if arm.binding_name
372
+ binding_id = @preassigned_local_binding_ids.fetch(arm.object_id)
373
+ arm_scopes = scopes + [{ arm.binding_name => binding_id }]
374
+ declaration_ids[arm.object_id] = binding_id if declaration_ids
375
+ end
376
+ walk_expression_for_precheck_resolution(if arm.respond_to?(:value) then arm.value else arm.body end, arm_scopes, identifier_ids, declaration_ids)
377
+ end
378
+ when AST::UnsafeExpr
379
+ walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids)
380
+ when AST::AwaitExpr
381
+ walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids)
382
+ when AST::FormatString
383
+ expression.parts.each do |part|
384
+ next unless part.is_a?(AST::FormatExprPart)
385
+
386
+ walk_expression_for_precheck_resolution(part.expression, scopes, identifier_ids, declaration_ids)
387
+ end
388
+ end
389
+ end
390
+
391
+ def walk_assignment_target_reads_for_precheck_resolution(target, operator, scopes, identifier_ids, declaration_ids = nil)
392
+ if operator != "=" && target.is_a?(AST::Identifier)
393
+ if (binding_id = resolve_name_in_precheck_scopes(target.name, scopes))
394
+ identifier_ids[target.object_id] = binding_id
395
+ end
396
+ end
397
+
398
+ case target
399
+ when AST::Identifier
400
+ nil
401
+ when AST::MemberAccess
402
+ walk_expression_for_precheck_resolution(target.receiver, scopes, identifier_ids, declaration_ids)
403
+ when AST::IndexAccess
404
+ walk_expression_for_precheck_resolution(target.receiver, scopes, identifier_ids, declaration_ids)
405
+ walk_expression_for_precheck_resolution(target.index, scopes, identifier_ids, declaration_ids)
406
+ else
407
+ walk_expression_for_precheck_resolution(target, scopes, identifier_ids, declaration_ids)
408
+ end
409
+ end
410
+
411
+ def resolve_name_in_precheck_scopes(name, scopes)
412
+ scopes.reverse_each do |scope|
413
+ return scope[name] if scope.key?(name)
414
+ end
415
+
416
+ nil
417
+ end
418
+
419
+ end
420
+ end
421
+ end