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,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
7
+
8
+ def with_unsafe
9
+ @unsafe_depth += 1
10
+ yield
11
+ ensure
12
+ @unsafe_depth -= 1
13
+ end
14
+
15
+ def mark_current_unsafe_required!
16
+ current_line = @unsafe_statement_lines.last
17
+ return unless current_line
18
+
19
+ @required_unsafe_lines << current_line
20
+ end
21
+
22
+ def require_unsafe!(message, line: nil, column: nil)
23
+ if unsafe_context?
24
+ mark_current_unsafe_required!
25
+ return
26
+ end
27
+
28
+ suggestion = "wrap in an unsafe block: `unsafe: <expression>`"
29
+ if line || column
30
+ raise SemanticError.new(message, line:, column:, path: @path, suggestion:)
31
+ end
32
+
33
+ raise_sema_error(message, suggestion:)
34
+ end
35
+
36
+ def with_foreign_mapping_context
37
+ @foreign_mapping_depth += 1
38
+ yield
39
+ ensure
40
+ @foreign_mapping_depth -= 1
41
+ end
42
+
43
+ def with_async_function
44
+ @async_function_depth += 1
45
+ yield
46
+ ensure
47
+ @async_function_depth -= 1
48
+ end
49
+
50
+ def with_loop
51
+ @loop_depth += 1
52
+ yield
53
+ ensure
54
+ @loop_depth -= 1
55
+ end
56
+
57
+ def with_compile_time
58
+ @compile_time_depth += 1
59
+ yield
60
+ ensure
61
+ @compile_time_depth -= 1
62
+ end
63
+
64
+ # Tracks the innermost value scopes during body checking so that
65
+ # `resolve_type_ref` can resolve a compile-time reflection type expression
66
+ # (e.g. `field.type` inside an `inline for`) by evaluating it against the
67
+ # local bindings in scope. Nil outside body checking.
68
+ def with_type_resolution_scopes(scopes)
69
+ saved = @type_resolution_scopes
70
+ @type_resolution_scopes = scopes
71
+ yield
72
+ ensure
73
+ @type_resolution_scopes = saved
74
+ end
75
+
76
+ def with_loop_barrier
77
+ previous_loop_depth = @loop_depth
78
+ @loop_depth = 0
79
+ yield
80
+ ensure
81
+ @loop_depth = previous_loop_depth
82
+ end
83
+
84
+ def unsafe_context?
85
+ @unsafe_depth.positive?
86
+ end
87
+
88
+ def inside_async_function?
89
+ @async_function_depth.positive?
90
+ end
91
+
92
+ def inside_loop?
93
+ @loop_depth.positive?
94
+ end
95
+
96
+ def foreign_mapping_context?
97
+ @foreign_mapping_depth.positive?
98
+ end
99
+
100
+ def with_return_context(return_type, allow_return:)
101
+ @return_context_stack << { return_type:, allow_return: }
102
+ yield
103
+ ensure
104
+ @return_context_stack.pop
105
+ end
106
+
107
+ def current_return_context
108
+ @return_context_stack.last
109
+ end
110
+
111
+ def with_scope(bindings)
112
+ scope = {}
113
+ bindings.each do |binding|
114
+ raise_sema_error("duplicate local #{binding.name}") if scope.key?(binding.name)
115
+
116
+ scope[binding.name] = binding
117
+ end
118
+
119
+ yield([scope])
120
+ end
121
+
122
+ def with_nested_scope(scopes)
123
+ nested_scopes = scopes + [{}]
124
+ yield(nested_scopes)
125
+ end
126
+
127
+ def validate_async_function_body!(statements)
128
+ statements.each { |statement| validate_async_statement!(statement) }
129
+ end
130
+
131
+ def validate_async_statement!(statement)
132
+ case statement
133
+ when AST::ErrorBlockStmt
134
+ if statement.header_expression
135
+ context = case statement.header_type
136
+ when :if then "if conditions"
137
+ when :while then "while conditions"
138
+ end
139
+ validate_async_expression_support!(statement.header_expression, context:) if context
140
+ end
141
+ if statement.header_type == :for
142
+ Array(statement.header_iterables).each do |iterable|
143
+ validate_async_expression_support!(iterable, context: "for iterables")
144
+ end
145
+ end
146
+ statement.body.each { |s| validate_async_statement!(s) }
147
+ when AST::ErrorStmt
148
+ nil
149
+ when AST::LocalDecl
150
+ validate_async_expression_support!(statement.value, context: "local initializer") if statement.value
151
+ statement.else_body&.each { |s| validate_async_statement!(s) }
152
+ when AST::Assignment
153
+ validate_async_expression_support!(statement.target, context: "assignment target")
154
+ validate_async_expression_support!(statement.value, context: "assignment")
155
+ when AST::ExpressionStmt
156
+ validate_async_expression_support!(statement.expression, context: "expression statement")
157
+ when AST::ReturnStmt
158
+ return unless statement.value
159
+
160
+ validate_async_expression_support!(statement.value, context: "return statement")
161
+ when AST::IfStmt
162
+ statement.branches.each do |branch|
163
+ validate_async_expression_support!(branch.condition, context: "if conditions")
164
+
165
+ branch.body.each { |s| validate_async_statement!(s) }
166
+ end
167
+ statement.else_body&.each { |s| validate_async_statement!(s) }
168
+ when AST::WhileStmt
169
+ validate_async_expression_support!(statement.condition, context: "while conditions")
170
+
171
+ statement.body.each { |s| validate_async_statement!(s) }
172
+ when AST::ForStmt
173
+ statement.iterables.each do |iterable|
174
+ validate_async_expression_support!(iterable, context: "for iterables")
175
+ end
176
+
177
+ statement.body.each { |s| validate_async_statement!(s) }
178
+ when AST::MatchStmt
179
+ validate_async_expression_support!(statement.expression, context: "match discriminants")
180
+
181
+ statement.arms.each { |arm| arm.body.each { |s| validate_async_statement!(s) } }
182
+ when AST::UnsafeStmt
183
+ statement.body.each { |s| validate_async_statement!(s) }
184
+ when AST::DeferStmt
185
+ validate_async_expression_support!(statement.expression, context: "defer cleanup") if statement.expression
186
+ statement.body&.each { |s| validate_async_statement!(s) }
187
+ when AST::WhenStmt
188
+ statement.branches.each { |branch| branch.body.each { |s| validate_async_statement!(s) } }
189
+ statement.else_body&.each { |s| validate_async_statement!(s) }
190
+ when AST::BreakStmt, AST::ContinueStmt, AST::StaticAssert, AST::PassStmt
191
+ nil
192
+ else
193
+ raise_sema_error("async functions currently only support straight-line local declarations, assignments, expression statements, and return statements")
194
+ end
195
+ end
196
+
197
+ def validate_async_expression_support!(expression, context:)
198
+ unsupported_context = unsupported_async_await_context(expression)
199
+ return unless unsupported_context
200
+
201
+ raise_sema_error("await in async functions is not supported inside #{unsupported_context} yet")
202
+ end
203
+
204
+ def unsupported_async_await_context(expression)
205
+ nil
206
+ end
207
+
208
+ def statement_contains_await?(statement)
209
+ case statement
210
+ when AST::ErrorBlockStmt
211
+ (statement.header_expression && expression_contains_await?(statement.header_expression)) ||
212
+ Array(statement.header_iterables).any? { |iterable| expression_contains_await?(iterable) } ||
213
+ statements_contain_await?(statement.body)
214
+ when AST::LocalDecl
215
+ (statement.value && expression_contains_await?(statement.value)) ||
216
+ (statement.else_body && statements_contain_await?(statement.else_body))
217
+ when AST::Assignment
218
+ expression_contains_await?(statement.target) || expression_contains_await?(statement.value)
219
+ when AST::IfStmt
220
+ statement.branches.any? { |branch| expression_contains_await?(branch.condition) || statements_contain_await?(branch.body) } ||
221
+ (statement.else_body && statements_contain_await?(statement.else_body))
222
+ when AST::MatchStmt
223
+ expression_contains_await?(statement.expression) || statement.arms.any? { |arm| expression_contains_await?(arm.pattern) || statements_contain_await?(arm.body) }
224
+ when AST::UnsafeStmt
225
+ statements_contain_await?(statement.body)
226
+ when AST::StaticAssert
227
+ expression_contains_await?(statement.condition) || expression_contains_await?(statement.message)
228
+ when AST::ForStmt
229
+ statement.iterables.any? { |iterable| expression_contains_await?(iterable) } || statements_contain_await?(statement.body)
230
+ when AST::WhileStmt
231
+ expression_contains_await?(statement.condition) || statements_contain_await?(statement.body)
232
+ when AST::ReturnStmt
233
+ statement.value && expression_contains_await?(statement.value)
234
+ when AST::DeferStmt
235
+ (statement.expression && expression_contains_await?(statement.expression)) || (statement.body && statements_contain_await?(statement.body))
236
+ when AST::ExpressionStmt
237
+ expression_contains_await?(statement.expression)
238
+ else
239
+ false
240
+ end
241
+ end
242
+
243
+ def statements_contain_await?(statements)
244
+ statements.any? { |statement| statement_contains_await?(statement) }
245
+ end
246
+
247
+ def expression_contains_await?(expression)
248
+ case expression
249
+ when AST::AwaitExpr
250
+ true
251
+ when AST::Call, AST::Specialization
252
+ expression_contains_await?(expression.callee) || expression.arguments.any? { |argument| expression_contains_await?(argument.value) }
253
+ when AST::UnaryOp
254
+ expression_contains_await?(expression.operand)
255
+ when AST::BinaryOp
256
+ expression_contains_await?(expression.left) || expression_contains_await?(expression.right)
257
+ when AST::IfExpr
258
+ expression_contains_await?(expression.condition) || expression_contains_await?(expression.then_expression) || expression_contains_await?(expression.else_expression)
259
+ when AST::MatchExpr
260
+ expression_contains_await?(expression.expression) || expression.arms.any? { |arm| expression_contains_await?(arm.pattern) || expression_contains_await?(arm.value) }
261
+ when AST::UnsafeExpr
262
+ expression_contains_await?(expression.expression)
263
+ when AST::PrefixCast
264
+ expression_contains_await?(expression.expression)
265
+ when AST::MemberAccess
266
+ expression_contains_await?(expression.receiver)
267
+ when AST::IndexAccess
268
+ expression_contains_await?(expression.receiver) || expression_contains_await?(expression.index)
269
+ when AST::FormatString
270
+ expression.parts.any? { |part| part.is_a?(AST::FormatExprPart) && expression_contains_await?(part.expression) }
271
+ else
272
+ false
273
+ end
274
+ end
275
+
276
+ def suggest_name(wrong, candidates, max_distance: 2)
277
+ return nil if wrong.nil? || wrong.to_s.empty? || candidates.nil? || candidates.empty?
278
+
279
+ wrong_str = wrong.to_s
280
+ closest = nil
281
+ closest_dist = max_distance + 1
282
+ candidates.each do |candidate|
283
+ next if candidate.nil?
284
+ cand_str = candidate.to_s
285
+ next if cand_str.empty? || cand_str == wrong_str
286
+ dist = levenshtein(wrong_str, cand_str)
287
+ if dist < closest_dist
288
+ closest_dist = dist
289
+ closest = cand_str
290
+ end
291
+ end
292
+ closest_dist <= max_distance ? closest : nil
293
+ end
294
+
295
+ def levenshtein(s, t)
296
+ m = s.length
297
+ n = t.length
298
+ return m if n.zero?
299
+ return n if m.zero?
300
+ d = Array.new(m + 1) { Array.new(n + 1, 0) }
301
+ (0..m).each { |i| d[i][0] = i }
302
+ (0..n).each { |j| d[0][j] = j }
303
+ (1..m).each do |i|
304
+ (1..n).each do |j|
305
+ cost = s[i - 1] == t[j - 1] ? 0 : 1
306
+ d[i][j] = [d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost].min
307
+ end
308
+ end
309
+ d[m][n]
310
+ end
311
+
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
7
+
8
+ def check_attribute_applications
9
+ expanded_declarations.each do |decl|
10
+ with_error_node(decl) do
11
+ case decl
12
+ when AST::StructDecl
13
+ packed, alignment = check_decl_attribute_applications!(decl.attributes, target_kind: :struct, target_label: "struct #{decl.name}", target_node: decl)
14
+ @ctx.types.fetch(decl.name).set_layout(packed:, alignment:)
15
+
16
+ decl.fields.each do |field|
17
+ with_error_node(field) do
18
+ if raw_module? && field.attributes.any?
19
+ raise_sema_error("attributes are not allowed on fields in external files")
20
+ end
21
+
22
+ check_decl_attribute_applications!(field.attributes, target_kind: :field, target_label: "field #{decl.name}.#{field.name}", target_node: field)
23
+ end
24
+ end
25
+ when AST::FunctionDef, AST::ExternFunctionDecl, AST::ForeignFunctionDecl
26
+ check_decl_attribute_applications!(decl.attributes, target_kind: :callable, target_label: "callable #{decl.name}", target_node: decl)
27
+ when AST::InterfaceDecl
28
+ decl.methods.each do |method|
29
+ with_error_node(method) do
30
+ check_decl_attribute_applications!(method.attributes, target_kind: :callable, target_label: "callable #{decl.name}.#{method.name}", target_node: method)
31
+ end
32
+ end
33
+ when AST::ExtendingBlock
34
+ decl.methods.each do |method|
35
+ with_error_node(method) do
36
+ check_decl_attribute_applications!(method.attributes, target_kind: :callable, target_label: "callable #{decl.type_name}.#{method.name}", target_node: method)
37
+ end
38
+ end
39
+ when AST::ConstDecl
40
+ check_decl_attribute_applications!(decl.attributes, target_kind: :const, target_label: "const #{decl.name}", target_node: decl)
41
+ when AST::EventDecl
42
+ check_decl_attribute_applications!(decl.attributes, target_kind: :event, target_label: "event #{decl.name}", target_node: decl)
43
+ when AST::UnionDecl
44
+ check_decl_attribute_applications!(decl.attributes, target_kind: :union, target_label: "union #{decl.name}", target_node: decl)
45
+ when AST::EnumDecl
46
+ check_decl_attribute_applications!(decl.attributes, target_kind: :enum, target_label: "enum #{decl.name}", target_node: decl)
47
+ when AST::FlagsDecl
48
+ check_decl_attribute_applications!(decl.attributes, target_kind: :flags, target_label: "flags #{decl.name}", target_node: decl)
49
+ when AST::VariantDecl
50
+ check_decl_attribute_applications!(decl.attributes, target_kind: :variant, target_label: "variant #{decl.name}", target_node: decl)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def check_decl_attribute_applications!(applications, target_kind:, target_label:, target_node:)
57
+ seen = {}
58
+ packed = false
59
+ alignment = nil
60
+ resolved_applications = []
61
+
62
+ applications.each do |application|
63
+ with_error_node(application) do
64
+ binding = resolve_attribute_binding(application.name)
65
+
66
+ if raw_module?
67
+ raise_sema_error("only built-in struct attributes are allowed in external files") unless binding.builtin && target_kind == :struct
68
+ end
69
+
70
+ unless binding.targets.include?(target_kind)
71
+ raise_sema_error("attribute #{binding.name} cannot target #{target_kind}")
72
+ end
73
+
74
+ binding_key = [binding.module_name, binding.name]
75
+ raise_sema_error("duplicate attribute #{binding.name} on #{target_label}") if seen.key?(binding_key)
76
+
77
+ seen[binding_key] = true
78
+ argument_values = check_attribute_arguments!(binding, application)
79
+ argument_values = argument_values.freeze
80
+ @ctx.attribute_application_bindings[application.object_id] = binding
81
+ @ctx.validated_attribute_arguments[application.object_id] = argument_values
82
+ resolved_applications << ResolvedAttributeApplication.new(binding:, argument_values:)
83
+
84
+ case binding.name
85
+ when "packed"
86
+ packed = true
87
+ when "align"
88
+ bytes = argument_values.fetch("bytes")
89
+ raise_sema_error("align(...) requires a positive alignment") unless bytes.is_a?(Integer) && bytes.positive?
90
+ raise_sema_error("align(...) requires a power-of-two alignment, got #{bytes}") unless power_of_two?(bytes)
91
+
92
+ alignment = bytes
93
+ end
94
+ end
95
+ end
96
+
97
+ @ctx.resolved_attribute_applications[target_node.object_id] = resolved_applications.freeze
98
+
99
+ [packed, alignment]
100
+ end
101
+
102
+ def check_attribute_arguments!(binding, application)
103
+ params = binding.params
104
+ arguments = application.arguments
105
+
106
+ if params.empty?
107
+ raise_sema_error("attribute #{binding.name} does not take arguments") if arguments.any?
108
+
109
+ return {}
110
+ end
111
+
112
+ bound_arguments = {}
113
+ next_position = 0
114
+
115
+ arguments.each do |argument|
116
+ param = if argument.name
117
+ params.find { |candidate| candidate.name == argument.name }.tap do |candidate|
118
+ raise_sema_error("unknown attribute argument #{binding.name}.#{argument.name}") unless candidate
119
+ end
120
+ else
121
+ while next_position < params.length && bound_arguments.key?(params[next_position].name)
122
+ next_position += 1
123
+ end
124
+
125
+ raise_sema_error("attribute #{binding.name} expects #{params.length} arguments, got #{arguments.length}") if next_position >= params.length
126
+
127
+ param = params[next_position]
128
+ next_position += 1
129
+ param
130
+ end
131
+
132
+ raise_sema_error("duplicate attribute argument #{binding.name}.#{param.name}") if bound_arguments.key?(param.name)
133
+
134
+ bound_arguments[param.name] = argument.value
135
+ end
136
+
137
+ missing = params.reject { |param| bound_arguments.key?(param.name) }
138
+ unless missing.empty?
139
+ names = missing.map(&:name).join(", ")
140
+ raise_sema_error("attribute #{binding.name} is missing required arguments: #{names}")
141
+ end
142
+
143
+ params.each_with_object({}) do |param, values|
144
+ argument_expression = bound_arguments.fetch(param.name)
145
+ actual_type = infer_expression(argument_expression, scopes: [], expected_type: param.type)
146
+ ensure_assignable!(
147
+ actual_type,
148
+ param.type,
149
+ "attribute #{binding.name} argument #{param.name} expects #{param.type}, got #{actual_type}",
150
+ expression: argument_expression,
151
+ )
152
+
153
+ const_value = evaluate_compile_time_const_value(argument_expression)
154
+ raise_sema_error("attribute #{binding.name} argument #{param.name} must be a compile-time constant") if const_value.nil?
155
+
156
+ values[param.name] = const_value
157
+ end
158
+ end
159
+
160
+ def resolve_attribute_binding(name)
161
+ parts = name.parts
162
+
163
+ if parts.length == 1
164
+ binding = @ctx.attributes[parts.first]
165
+ raise_sema_error("unknown attribute #{name}") unless binding
166
+
167
+ return binding
168
+ end
169
+
170
+ if parts.length == 2 && @ctx.imports.key?(parts.first)
171
+ imported_module = @ctx.imports.fetch(parts.first)
172
+ raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}") if imported_module.private_attribute?(parts.last)
173
+
174
+ binding = imported_module.attributes[parts.last]
175
+ raise_sema_error("unknown attribute #{name}") unless binding
176
+
177
+ return binding
178
+ end
179
+
180
+ raise_sema_error("unknown attribute #{name}")
181
+ end
182
+ end
183
+ end
184
+ end