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,398 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module MilkTea
6
+ class CBackend
7
+ module Reachability
8
+ private
9
+
10
+ def emitted_constants
11
+ @emitted_constants ||= begin
12
+ constants_by_name = @program.constants.each_with_object({}) do |constant, result|
13
+ result[constant.linkage_name] = constant
14
+ end
15
+ referenced_names = {}
16
+ root_module_prefix = "#{module_c_prefix(@program.module_name)}_"
17
+
18
+ @program.constants.each do |constant|
19
+ next unless constant.linkage_name.start_with?(root_module_prefix)
20
+
21
+ referenced_names[constant.linkage_name] = true
22
+ collect_referenced_constant_names_from_expression(constant.value, constants_by_name, referenced_names)
23
+ end
24
+
25
+ @program.globals.each do |global|
26
+ collect_referenced_constant_names_from_expression(global.value, constants_by_name, referenced_names)
27
+ end
28
+
29
+ @program.static_asserts.each do |statement|
30
+ collect_referenced_constant_names_from_expression(statement.condition, constants_by_name, referenced_names)
31
+ collect_referenced_constant_names_from_expression(statement.message, constants_by_name, referenced_names)
32
+ end
33
+
34
+ emitted_functions.each do |function|
35
+ collect_referenced_constant_names_from_statements(function.body, constants_by_name, referenced_names)
36
+ end
37
+
38
+ @program.constants.select { |constant| referenced_names[constant.linkage_name] }
39
+ end
40
+ end
41
+
42
+ def emitted_functions
43
+ @emitted_functions ||= begin
44
+ functions_by_name = @program.functions.each_with_object({}) do |function, result|
45
+ result[function.linkage_name] = function
46
+ end
47
+
48
+ seeds = @program.functions.select(&:entry_point)
49
+ if seeds.empty?
50
+ root_module_prefix = "#{module_c_prefix(@program.module_name)}_"
51
+ seeds = @program.functions.select { |function| function.linkage_name.start_with?(root_module_prefix) }
52
+ end
53
+
54
+ reachable_names = {}
55
+ worklist = seeds.dup
56
+
57
+ until worklist.empty?
58
+ function = worklist.shift
59
+ next if reachable_names[function.linkage_name]
60
+
61
+ reachable_names[function.linkage_name] = true
62
+ collect_called_function_names_from_statements(function.body, functions_by_name, reachable_names, worklist)
63
+ end
64
+
65
+ (@program.constants + @program.globals).each do |value|
66
+ collect_called_function_names_from_expression(value.value, functions_by_name, reachable_names, worklist)
67
+ end
68
+
69
+ until worklist.empty?
70
+ function = worklist.shift
71
+ next if reachable_names[function.linkage_name]
72
+
73
+ reachable_names[function.linkage_name] = true
74
+ collect_called_function_names_from_statements(function.body, functions_by_name, reachable_names, worklist)
75
+ end
76
+
77
+ @program.functions.select { |function| reachable_names[function.linkage_name] }
78
+ end
79
+ end
80
+
81
+ def all_emitted_top_level_values
82
+ emitted_constants + emitted_globals
83
+ end
84
+
85
+ def collect_active_module_names
86
+ active = Set.new
87
+ active << @program.module_name
88
+
89
+ collect_type_referenced_module_names.each { |mod| active << mod }
90
+
91
+ emitted_functions.each do |fn|
92
+ prefix = module_c_prefix(@program.module_name)
93
+ if fn.linkage_name != prefix && !fn.linkage_name.start_with?("#{prefix}_")
94
+ candidate = fn.linkage_name.split("_")
95
+ candidate.pop
96
+ active << candidate.join("_")
97
+ end
98
+ end
99
+
100
+ (@program.structs + @program.unions + @program.variants + @program.opaques).each do |decl|
101
+ active << decl.source_module if decl.source_module
102
+ end
103
+
104
+ loop do
105
+ size_before = active.size
106
+ (@program.structs + @program.unions).each do |decl|
107
+ next unless decl.source_module
108
+ next unless active.include?(decl.source_module)
109
+
110
+ decl.fields.each { |f| add_type_module(f.type, active) }
111
+ end
112
+ @program.variants.each do |decl|
113
+ next unless decl.source_module
114
+ next unless active.include?(decl.source_module)
115
+
116
+ decl.arms.each { |arm| arm.fields.each { |f| add_type_module(f.type, active) } }
117
+ end
118
+ break if active.size == size_before
119
+ end
120
+
121
+ active
122
+ end
123
+
124
+ def collect_type_referenced_module_names
125
+ modules = Set.new
126
+ emitted_functions.each do |fn|
127
+ fn.params.each { |p| add_type_module(p.type, modules) }
128
+ add_type_module(fn.return_type, modules)
129
+ fn.body.each { |stmt| add_statement_type_modules(stmt, modules) }
130
+ end
131
+ modules
132
+ end
133
+
134
+ def add_statement_type_modules(stmt, modules)
135
+ case stmt
136
+ when IR::LocalDecl
137
+ add_type_module(stmt.type, modules)
138
+ when IR::ExpressionStmt
139
+ add_expr_type_module(stmt.expression, modules)
140
+ when IR::Assignment
141
+ add_expr_type_module(stmt.value, modules)
142
+ when IR::ReturnStmt
143
+ add_expr_type_module(stmt.value, modules) if stmt.value
144
+ when IR::IfStmt
145
+ stmt.then_body.each { |s| add_statement_type_modules(s, modules) }
146
+ stmt.else_body&.each { |s| add_statement_type_modules(s, modules) }
147
+ when IR::WhileStmt
148
+ stmt.body.each { |s| add_statement_type_modules(s, modules) }
149
+ when IR::ForStmt
150
+ stmt.body.each { |s| add_statement_type_modules(s, modules) }
151
+ end
152
+ end
153
+
154
+ def add_expr_type_module(expr, modules)
155
+ return unless expr
156
+ add_type_module(expr.type, modules) if expr.respond_to?(:type)
157
+ case expr
158
+ when IR::Call
159
+ expr.arguments.each { |a| add_expr_type_module(a, modules) }
160
+ when IR::Binary
161
+ add_expr_type_module(expr.left, modules)
162
+ add_expr_type_module(expr.right, modules)
163
+ when IR::AggregateLiteral
164
+ expr.fields.each { |f| add_expr_type_module(f.value, modules) }
165
+ when IR::VariantLiteral
166
+ expr.fields.each { |f| add_expr_type_module(f.value, modules) }
167
+ when IR::Conditional
168
+ add_expr_type_module(expr.then_expression, modules)
169
+ add_expr_type_module(expr.else_expression, modules)
170
+ when IR::Cast, IR::ReinterpretExpr, IR::AddressOf
171
+ add_expr_type_module(expr.expression, modules)
172
+ end
173
+ end
174
+
175
+ def add_type_module(type, modules)
176
+ return unless type
177
+ modules << type.module_name if type.respond_to?(:module_name) && type.module_name
178
+
179
+ case type
180
+ when Types::Nullable
181
+ add_type_module(type.base, modules)
182
+ when Types::Span
183
+ add_type_module(type.element_type, modules)
184
+ when Types::GenericInstance
185
+ type.arguments.each { |a| add_type_module(a, modules) unless a.is_a?(Types::LiteralTypeArg) }
186
+ when Types::Task
187
+ add_type_module(type.result_type, modules)
188
+ end
189
+ end
190
+
191
+
192
+ def emitted_aggregate_structs
193
+ @emitted_aggregate_structs ||= filter_by_type_reachability(@program.structs)
194
+ end
195
+
196
+ def emitted_aggregate_unions
197
+ @emitted_aggregate_unions ||= filter_by_type_reachability(@program.unions)
198
+ end
199
+
200
+ def emitted_aggregate_variants
201
+ @emitted_aggregate_variants ||= filter_by_type_reachability(@program.variants)
202
+ end
203
+
204
+ def filter_by_type_reachability(decls)
205
+ return decls if decls.empty?
206
+ return decls if decls.all? { |d| d.source_module.nil? }
207
+
208
+ active_modules = collect_active_module_names
209
+ by_c_name = decls.each_with_object({}) { |d, h| h[d.linkage_name] = d }
210
+
211
+ reachable = Set.new
212
+ decls.each do |decl|
213
+ next unless decl.source_module.nil? || active_modules.include?(decl.source_module)
214
+ reachable << decl.linkage_name
215
+ end
216
+
217
+ worklist = reachable.to_a
218
+ until worklist.empty?
219
+ linkage_name = worklist.shift
220
+ decl = by_c_name[linkage_name]
221
+ next unless decl
222
+
223
+ deps = aggregate_decl_dependencies(decl)
224
+ deps.each do |dep_name|
225
+ next unless by_c_name.key?(dep_name)
226
+ next if reachable.include?(dep_name)
227
+ reachable << dep_name
228
+ worklist << dep_name
229
+ end
230
+ end
231
+
232
+ decls.select { |d| reachable.include?(d.linkage_name) }
233
+ end
234
+
235
+ def emitted_globals
236
+ @emitted_globals ||= begin
237
+ root_module_prefix = "#{module_c_prefix(@program.module_name)}_"
238
+ reachable_names = {}
239
+ @program.globals.select { |g| g.linkage_name.start_with?(root_module_prefix) }.each { |g| reachable_names[g.linkage_name] = true }
240
+
241
+ emitted_functions.each do |function|
242
+ traverse_ir_statements(function.body) do |expression|
243
+ next unless expression.is_a?(IR::Name)
244
+ global = @program.globals.find { |g| g.linkage_name == expression.name }
245
+ next unless global
246
+ next if reachable_names[global.linkage_name]
247
+ reachable_names[global.linkage_name] = true
248
+ traverse_ir_expression(global.value) do |inner|
249
+ next unless inner.is_a?(IR::Name)
250
+ dep = @program.globals.find { |g| g.linkage_name == inner.name }
251
+ reachable_names[dep.linkage_name] = true if dep
252
+ end
253
+ end
254
+ end
255
+
256
+ @program.globals.each do |global|
257
+ next unless reachable_names[global.linkage_name]
258
+ traverse_ir_expression(global.value) do |inner|
259
+ next unless inner.is_a?(IR::Name)
260
+ dep = @program.globals.find { |g| g.linkage_name == inner.name }
261
+ reachable_names[dep.linkage_name] = true if dep
262
+ end
263
+ end
264
+
265
+ @program.globals.select { |g| reachable_names[g.linkage_name] }
266
+ end
267
+ end
268
+
269
+ def collect_called_function_names_from_statements(statements, functions_by_name, reachable_names, worklist)
270
+ traverse_ir_statements(statements) do |expression|
271
+ case expression
272
+ when IR::Name
273
+ callee = functions_by_name[expression.name]
274
+ worklist << callee if callee && !reachable_names[callee.linkage_name]
275
+ when IR::Call
276
+ next unless expression.callee.is_a?(String)
277
+
278
+ callee = functions_by_name[expression.callee]
279
+ worklist << callee if callee && !reachable_names[callee.linkage_name]
280
+ end
281
+ end
282
+ end
283
+
284
+ def collect_called_function_names_from_expression(expression, functions_by_name, reachable_names, worklist)
285
+ traverse_ir_expression(expression) do |candidate|
286
+ case candidate
287
+ when IR::Name
288
+ callee = functions_by_name[candidate.name]
289
+ worklist << callee if callee && !reachable_names[callee.linkage_name]
290
+ when IR::Call
291
+ next unless candidate.callee.is_a?(String)
292
+
293
+ callee = functions_by_name[candidate.callee]
294
+ worklist << callee if callee && !reachable_names[callee.linkage_name]
295
+ end
296
+ end
297
+ end
298
+
299
+ def collect_referenced_constant_names_from_statements(statements, constants_by_name, referenced_names)
300
+ visitor = constant_reference_visitor(constants_by_name, referenced_names)
301
+ traverse_ir_statements(statements, visit_switch_case_values: true, &visitor)
302
+ end
303
+
304
+ def collect_referenced_constant_names_from_expression(expression, constants_by_name, referenced_names)
305
+ visitor = constant_reference_visitor(constants_by_name, referenced_names)
306
+ traverse_ir_expression(expression, &visitor)
307
+ end
308
+
309
+ def constant_reference_visitor(constants_by_name, referenced_names)
310
+ lambda do |expression|
311
+ next unless expression.is_a?(IR::Name)
312
+
313
+ constant = constants_by_name[expression.name]
314
+ next unless constant
315
+ next if referenced_names[constant.linkage_name]
316
+
317
+ referenced_names[constant.linkage_name] = true
318
+ traverse_ir_expression(constant.value, &constant_reference_visitor(constants_by_name, referenced_names))
319
+ end
320
+ end
321
+
322
+ def traverse_ir_statements(statements, visit_switch_case_values: false, &expression_visitor)
323
+ Array(statements).compact.each do |statement|
324
+ case statement
325
+ when IR::LocalDecl
326
+ traverse_ir_expression(statement.value, &expression_visitor)
327
+ when IR::Assignment
328
+ traverse_ir_expression(statement.target, &expression_visitor)
329
+ traverse_ir_expression(statement.value, &expression_visitor)
330
+ when IR::BlockStmt
331
+ traverse_ir_statements(statement.body, visit_switch_case_values:, &expression_visitor)
332
+ when IR::WhileStmt
333
+ traverse_ir_expression(statement.condition, &expression_visitor)
334
+ traverse_ir_statements(statement.body, visit_switch_case_values:, &expression_visitor)
335
+ when IR::ForStmt
336
+ traverse_ir_statements([statement.init], visit_switch_case_values:, &expression_visitor)
337
+ traverse_ir_expression(statement.condition, &expression_visitor)
338
+ traverse_ir_statements(statement.body, visit_switch_case_values:, &expression_visitor)
339
+ traverse_ir_statements([statement.post], visit_switch_case_values:, &expression_visitor)
340
+ when IR::IfStmt
341
+ traverse_ir_expression(statement.condition, &expression_visitor)
342
+ traverse_ir_statements(statement.then_body, visit_switch_case_values:, &expression_visitor)
343
+ traverse_ir_statements(statement.else_body, visit_switch_case_values:, &expression_visitor) if statement.else_body
344
+ when IR::SwitchStmt
345
+ traverse_ir_expression(statement.expression, &expression_visitor)
346
+ statement.cases.each do |switch_case|
347
+ if visit_switch_case_values && switch_case.is_a?(IR::SwitchCase)
348
+ traverse_ir_expression(switch_case.value, &expression_visitor)
349
+ end
350
+ traverse_ir_statements(switch_case.body, visit_switch_case_values:, &expression_visitor)
351
+ end
352
+ when IR::StaticAssert
353
+ traverse_ir_expression(statement.condition, &expression_visitor)
354
+ traverse_ir_expression(statement.message, &expression_visitor)
355
+ when IR::ReturnStmt
356
+ traverse_ir_expression(statement.value, &expression_visitor) if statement.value
357
+ when IR::ExpressionStmt
358
+ traverse_ir_expression(statement.expression, &expression_visitor)
359
+ end
360
+ end
361
+ end
362
+
363
+ def traverse_ir_expression(expression, &expression_visitor)
364
+ return if expression.nil?
365
+
366
+ expression_visitor.call(expression) if expression_visitor
367
+
368
+ case expression
369
+ when IR::Member
370
+ traverse_ir_expression(expression.receiver, &expression_visitor)
371
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
372
+ traverse_ir_expression(expression.receiver, &expression_visitor)
373
+ traverse_ir_expression(expression.index, &expression_visitor)
374
+ when IR::Call
375
+ traverse_ir_expression(expression.callee, &expression_visitor) unless expression.callee.is_a?(String)
376
+ expression.arguments.each { |argument| traverse_ir_expression(argument, &expression_visitor) }
377
+ when IR::Unary
378
+ traverse_ir_expression(expression.operand, &expression_visitor)
379
+ when IR::Binary
380
+ traverse_ir_expression(expression.left, &expression_visitor)
381
+ traverse_ir_expression(expression.right, &expression_visitor)
382
+ when IR::Conditional
383
+ traverse_ir_expression(expression.condition, &expression_visitor)
384
+ traverse_ir_expression(expression.then_expression, &expression_visitor)
385
+ traverse_ir_expression(expression.else_expression, &expression_visitor)
386
+ when IR::ReinterpretExpr, IR::AddressOf, IR::Cast
387
+ traverse_ir_expression(expression.expression, &expression_visitor)
388
+ when IR::AggregateLiteral
389
+ expression.fields.each { |field| traverse_ir_expression(field.value, &expression_visitor) }
390
+ when IR::ArrayLiteral
391
+ expression.elements.each { |element| traverse_ir_expression(element, &expression_visitor) }
392
+ when IR::VariantLiteral
393
+ expression.fields.each { |field| traverse_ir_expression(field.value, &expression_visitor) }
394
+ end
395
+ end
396
+ end
397
+ end
398
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module Reinterpret
6
+ private
7
+
8
+
9
+ def collect_reinterpret_helpers
10
+ helpers = []
11
+ seen = {}
12
+
13
+ emitted_functions.each do |function|
14
+ collect_reinterpret_helpers_from_statements(function.body, helpers, seen)
15
+ end
16
+
17
+ helpers
18
+ end
19
+
20
+ def collect_reinterpret_helpers_from_statements(statements, helpers, seen)
21
+ statements.each do |statement|
22
+ case statement
23
+ when IR::LocalDecl
24
+ collect_reinterpret_helpers_from_expression(statement.value, helpers, seen)
25
+ when IR::Assignment
26
+ collect_reinterpret_helpers_from_expression(statement.target, helpers, seen)
27
+ collect_reinterpret_helpers_from_expression(statement.value, helpers, seen)
28
+ when IR::BlockStmt
29
+ collect_reinterpret_helpers_from_statements(statement.body, helpers, seen)
30
+ when IR::WhileStmt
31
+ collect_reinterpret_helpers_from_expression(statement.condition, helpers, seen)
32
+ collect_reinterpret_helpers_from_statements(statement.body, helpers, seen)
33
+ when IR::ForStmt
34
+ collect_reinterpret_helpers_from_statements([statement.init], helpers, seen)
35
+ collect_reinterpret_helpers_from_expression(statement.condition, helpers, seen)
36
+ collect_reinterpret_helpers_from_statements(statement.body, helpers, seen)
37
+ collect_reinterpret_helpers_from_statements([statement.post], helpers, seen)
38
+ when IR::IfStmt
39
+ collect_reinterpret_helpers_from_expression(statement.condition, helpers, seen)
40
+ collect_reinterpret_helpers_from_statements(statement.then_body, helpers, seen)
41
+ collect_reinterpret_helpers_from_statements(statement.else_body, helpers, seen) if statement.else_body
42
+ when IR::SwitchStmt
43
+ collect_reinterpret_helpers_from_expression(statement.expression, helpers, seen)
44
+ statement.cases.each do |switch_case|
45
+ collect_reinterpret_helpers_from_statements(switch_case.body, helpers, seen)
46
+ end
47
+ when IR::StaticAssert
48
+ collect_reinterpret_helpers_from_expression(statement.condition, helpers, seen)
49
+ collect_reinterpret_helpers_from_expression(statement.message, helpers, seen)
50
+ when IR::ReturnStmt
51
+ collect_reinterpret_helpers_from_expression(statement.value, helpers, seen) if statement.value
52
+ when IR::ExpressionStmt
53
+ collect_reinterpret_helpers_from_expression(statement.expression, helpers, seen)
54
+ end
55
+ end
56
+ end
57
+
58
+ def collect_reinterpret_helpers_from_expression(expression, helpers, seen)
59
+ case expression
60
+ when IR::Member
61
+ collect_reinterpret_helpers_from_expression(expression.receiver, helpers, seen)
62
+ when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
63
+ collect_reinterpret_helpers_from_expression(expression.receiver, helpers, seen)
64
+ collect_reinterpret_helpers_from_expression(expression.index, helpers, seen)
65
+ when IR::Call
66
+ collect_reinterpret_helpers_from_expression(expression.callee, helpers, seen) unless expression.callee.is_a?(String)
67
+ expression.arguments.each { |argument| collect_reinterpret_helpers_from_expression(argument, helpers, seen) }
68
+ when IR::Unary
69
+ collect_reinterpret_helpers_from_expression(expression.operand, helpers, seen)
70
+ when IR::Binary
71
+ collect_reinterpret_helpers_from_expression(expression.left, helpers, seen)
72
+ collect_reinterpret_helpers_from_expression(expression.right, helpers, seen)
73
+ when IR::Conditional
74
+ collect_reinterpret_helpers_from_expression(expression.condition, helpers, seen)
75
+ collect_reinterpret_helpers_from_expression(expression.then_expression, helpers, seen)
76
+ collect_reinterpret_helpers_from_expression(expression.else_expression, helpers, seen)
77
+ when IR::ReinterpretExpr
78
+ return if no_op_reinterpret?(expression.target_type, expression.source_type)
79
+
80
+ key = [expression.target_type, expression.source_type]
81
+ unless seen[key]
82
+ helpers << expression
83
+ seen[key] = true
84
+ end
85
+ collect_reinterpret_helpers_from_expression(expression.expression, helpers, seen)
86
+ when IR::AddressOf
87
+ collect_reinterpret_helpers_from_expression(expression.expression, helpers, seen)
88
+ when IR::Cast
89
+ collect_reinterpret_helpers_from_expression(expression.expression, helpers, seen)
90
+ when IR::AggregateLiteral
91
+ expression.fields.each { |field| collect_reinterpret_helpers_from_expression(field.value, helpers, seen) }
92
+ when IR::ArrayLiteral
93
+ expression.elements.each { |element| collect_reinterpret_helpers_from_expression(element, helpers, seen) }
94
+ when IR::VariantLiteral
95
+ expression.fields.each { |field| collect_reinterpret_helpers_from_expression(field.value, helpers, seen) }
96
+ when IR::ZeroInit, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::Name, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr
97
+ nil
98
+ end
99
+ end
100
+
101
+ def emit_reinterpret_helper(expression)
102
+ helper_name = reinterpret_helper_name(expression.target_type, expression.source_type)
103
+ params = c_declaration(expression.source_type, 'value')
104
+ [
105
+ "static inline #{c_function_declaration(expression.target_type, helper_name, params)} {",
106
+ "#{INDENT}_Static_assert(sizeof(#{layout_type_expression(expression.target_type)}) == sizeof(#{layout_type_expression(expression.source_type)}), \"reinterpret requires equal sizes\");",
107
+ "#{INDENT}#{c_declaration(expression.target_type, 'result')};",
108
+ "#{INDENT}memcpy(&result, &value, sizeof(result));",
109
+ "#{INDENT}return result;",
110
+ "}",
111
+ ]
112
+ end
113
+
114
+ def reinterpret_helper_name(target_type, source_type)
115
+ "mt_reinterpret_#{sanitize_identifier(target_type.to_s)}_from_#{sanitize_identifier(source_type.to_s)}"
116
+ end
117
+
118
+ def no_op_cast?(expression)
119
+ return false if expression.expression.type.is_a?(Types::Null)
120
+
121
+ c_type(expression.target_type) == c_type(expression.expression.type)
122
+ rescue StandardError
123
+ false
124
+ end
125
+
126
+ def no_op_reinterpret?(target_type, source_type)
127
+ c_type(target_type) == c_type(source_type)
128
+ rescue StandardError
129
+ false
130
+ end
131
+
132
+ def checked_array_index_helper_name(type)
133
+ return "mt_checked_index_array_#{sanitize_identifier(c_declaration(array_element_type(type), 'value'))}_#{array_length(type)}" if array_type?(type) && callable_container_element_type?(array_element_type(type))
134
+
135
+ "mt_checked_index_#{sanitize_identifier(type.to_s)}"
136
+ end
137
+
138
+ def checked_span_index_helper_name(type)
139
+ return "mt_checked_span_index_#{sanitize_identifier(c_declaration(type.element_type, 'value'))}" if callable_container_element_type?(type.element_type)
140
+
141
+ "mt_checked_span_index_#{sanitize_identifier(type.to_s)}"
142
+ end
143
+
144
+ def nullable_array_index_helper_name(type)
145
+ return "mt_nullable_index_array_#{sanitize_identifier(c_declaration(array_element_type(type), 'value'))}_#{array_length(type)}" if array_type?(type) && callable_container_element_type?(array_element_type(type))
146
+
147
+ "mt_nullable_index_#{sanitize_identifier(type.to_s)}"
148
+ end
149
+
150
+ def nullable_span_index_helper_name(type)
151
+ return "mt_nullable_span_index_#{sanitize_identifier(c_declaration(type.element_type, 'value'))}" if callable_container_element_type?(type.element_type)
152
+
153
+ "mt_nullable_span_index_#{sanitize_identifier(type.to_s)}"
154
+ end
155
+
156
+ def callable_container_element_type?(type)
157
+ type.is_a?(Types::Function) || type.is_a?(Types::Proc)
158
+ end
159
+
160
+ def wrap_member_receiver(expression)
161
+ case expression
162
+ when IR::Unary
163
+ if expression.operator == "*"
164
+ wrap_pointer_member_receiver(expression.operand)
165
+ else
166
+ "(#{emit_expression(expression)})"
167
+ end
168
+ when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
169
+ checked_index_alias(expression) || emit_expression(expression)
170
+ when IR::Name, IR::Member, IR::Index
171
+ emit_expression(expression)
172
+ else
173
+ "(#{emit_expression(expression)})"
174
+ end
175
+ end
176
+
177
+ def pointer_member_receiver?(expression)
178
+ return true if checked_index_alias(expression)
179
+ return pointer_member_receiver?(expression.operand) if expression.is_a?(IR::Unary) && expression.operator == "*"
180
+
181
+ (expression.is_a?(IR::Name) && expression.pointer) ||
182
+ (expression.respond_to?(:type) && (raw_pointer_type?(expression.type) || ref_type?(expression.type) || nullable_pointer_like_ir_type?(expression.type)))
183
+ end
184
+
185
+ def nullable_pointer_like_ir_type?(type)
186
+ return false unless type.is_a?(Types::Nullable)
187
+
188
+ c_backend_pointer_like_type?(type.base)
189
+ end
190
+
191
+ def wrap_pointer_member_receiver(expression)
192
+ case expression
193
+ when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
194
+ checked_index_alias(expression) || emit_expression(expression)
195
+ when IR::Name, IR::Member, IR::Index, IR::Call
196
+ emit_expression(expression)
197
+ else
198
+ "(#{emit_expression(expression)})"
199
+ end
200
+ end
201
+
202
+ def wrap_index_receiver(expression)
203
+ case expression
204
+ when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
205
+ if (alias_name = checked_index_alias(expression))
206
+ "(*#{alias_name})"
207
+ else
208
+ emit_expression(expression)
209
+ end
210
+ when IR::Name, IR::Member, IR::Index, IR::Call
211
+ emit_expression(expression)
212
+ else
213
+ "(#{emit_expression(expression)})"
214
+ end
215
+ end
216
+
217
+ def c_operator(operator)
218
+ operator == "and" ? "&&" : operator == "or" ? "||" : operator
219
+ end
220
+
221
+ def void_type
222
+ @void_type ||= Types::Registry.primitive("void")
223
+ end
224
+ end
225
+ end
226
+ end