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,1534 @@
1
+ # Milk Tea Language Manual
2
+
3
+ This manual documents the Milk Tea language as implemented today in the lexer, parser, semantic checker, and compiler tests.
4
+
5
+ Package manifests and build or run workflow are documented separately in `docs/build-guide.md`.
6
+
7
+ ## 1. Source Files And Modules
8
+
9
+ Milk Tea source files use the `.mt` extension.
10
+
11
+ A file can be either:
12
+
13
+ - an ordinary source file (the normal source form; no header; module name comes from the path)
14
+ - an external file (`external`) for raw ABI bindings
15
+
16
+ ### 1.1 Ordinary file
17
+
18
+ ```mt
19
+ function main() -> int:
20
+ return 0
21
+ ```
22
+
23
+ ### 1.2 External file
24
+
25
+ ```mt
26
+ external
27
+
28
+ include "raylib.h"
29
+ link "raylib"
30
+
31
+ struct Color:
32
+ r: ubyte
33
+ g: ubyte
34
+ b: ubyte
35
+ a: ubyte
36
+
37
+ external function InitWindow(width: int, height: int, title: cstr) -> void
38
+ ```
39
+
40
+ Rules:
41
+
42
+ - In ordinary files, `import` statements are parsed only at the top.
43
+ - In external files, leading `import` statements are allowed after `external`.
44
+ - Only external files accept `include`, `link`, and `compiler_flag` directives.
45
+ - External files are the dedicated raw ABI surface, usually for `std.c.*` bindings and bindgen output.
46
+ - After leading imports and directives, external files are intentionally narrow: they contain raw ABI declarations, not ordinary module logic.
47
+ - Module lookup resolves `a.b.c` to `a/b/c.mt`.
48
+ - Module identity is inferred from the resolved source path.
49
+ - Platform-specific file variants are a compiler resolution rule, not a source-language import feature.
50
+ - For an active target platform `P`, the compiler resolves `a.b.c` by preferring `a/b/c.P.mt` and falling back to `a/b/c.mt`.
51
+ - Valid platform filename suffixes are `linux`, `windows`, and `wasm`.
52
+ - The platform suffix is not part of the module name. `import a.b.c` stays the same on every target.
53
+ - Milk Tea does not have a source-level conditional compilation syntax such as `#if`, `#ifdef`, or per-declaration platform attributes.
54
+
55
+ ## 2. Lexical Rules
56
+
57
+ ### 2.1 Indentation and newlines
58
+
59
+ - Blocks are indentation-based.
60
+ - `:` starts a block.
61
+ - Indentation must be spaces only.
62
+ - Tabs are rejected.
63
+ - Indentation must be a multiple of 4 spaces.
64
+ - Indentation can increase by only one level (4 spaces) at a time.
65
+ - Newlines end statements, except while inside `()` or `[]`, or when the previous physical line ends with a binary operator.
66
+
67
+ For long expressions, prefer delimiter-based wrapping:
68
+
69
+ ```mt
70
+ let total = (
71
+ subtotal
72
+ + tax
73
+ - discount
74
+ )
75
+ ```
76
+
77
+ Milk Tea also accepts continuation after a line-ending binary operator:
78
+
79
+ ```mt
80
+ let total = subtotal +
81
+ tax -
82
+ discount
83
+ ```
84
+
85
+ Starting a new physical line with the operator is not part of the supported source contract; wrap in `()` instead if that layout is clearer.
86
+
87
+ ### 2.2 Comments
88
+
89
+ - `#` starts a line comment.
90
+
91
+ Documentation comments use `##` and attach to the nearest next declaration
92
+ without a blank line in between.
93
+
94
+ ```mt
95
+ ## Draws a colorful triangle strip.
96
+ ## Values are normalized to screen center.
97
+ function draw_strip() -> void:
98
+ return
99
+ ```
100
+
101
+ Rules for documentation comments:
102
+
103
+ - Only lines that start with `##` are documentation.
104
+ - Contiguous `##` lines form one markdown block.
105
+ - A blank line breaks attachment.
106
+ - Plain `#` comments are ignored by hover documentation.
107
+ - Documentation attaches only to declarations (`function`, `struct`, `union`, `enum`, `flags`, `variant`, `type`, `const`, `var`, `let`, `extending`, `opaque`, `interface`).
108
+
109
+ ### 2.3 Literals
110
+
111
+ Supported literals:
112
+
113
+ - integer: `42`, `0xff`, `0b1010`, with `_` separators. Integer type suffixes: `42u` (`uint`), `0xFFub` (`ubyte`), `100z` (`ptr_uint`), `7i` (`int`), `-1l` (`long`), etc.
114
+ - float: `3.14`, `1.2e-3`, `1.1920929E-7`, `1.0f` (float suffix), `1.0d` (double suffix)
115
+ - character: `'a'`, `'\n'`, `'\t'`, `'\\'`, `'\''`, `'\0'`, `'\x41'`. Type is `ubyte`. Escape sequences: `\n`, `\r`, `\t`, `\\`, `\'`, `\"`, `\0` (null), `\xNN` (hex byte).
116
+ - string: `"hello"` (`str`). Supported string escapes are `\n`, `\r`, `\t`, `\0` (null), `\"`, `\'`, and `\\`; any other `\x` sequence is taken literally. Hex byte escapes (`\xNN`) are character-literal only, not string-literal.
117
+ - cstring: `c"hello"` (`cstr`)
118
+ - heredoc string: `<<-TAG ... TAG` (`str`)
119
+ - heredoc cstring: `c<<-TAG ... TAG` (`cstr`)
120
+ - format string: `f"count=#{count}"`
121
+ - booleans: `true`, `false`
122
+ - null: `null`, typed null `null[ptr[char]]`
123
+
124
+ ### 2.4 Operators and punctuation
125
+
126
+ Symbols:
127
+
128
+ - delimiters: `(` `)` `[` `]`
129
+ - separators/access: `:` `,` `.`
130
+ - type markers: `->` `?`
131
+ - arithmetic: `+ - * / %`
132
+ - bitwise: `~ & | ^ << >>`
133
+ - comparison: `== != < <= > >=`
134
+ - assignment: `= += -= *= /= %= &= |= ^= <<= >>=`
135
+ - variadic marker: `...`
136
+
137
+ Word operators:
138
+
139
+ - `and`, `or`, `not`
140
+ - `is` — variant arm membership test (`expr is Variant.Arm` returns `bool`; desugars to a `match` expression)
141
+ - `in`, `out`, `inout` (reserved for `foreign function` parameter modes; legacy call-site forms are rejected semantically)
142
+
143
+ ## 3. Declarations
144
+
145
+ Top-level declarations:
146
+
147
+ - `const`
148
+ - `const function` — compile-time-evaluable function
149
+ - `var`
150
+ - `type`
151
+ - `attribute`
152
+ - `interface`
153
+ - `struct`
154
+ - `union`
155
+ - `variant`
156
+ - `enum`
157
+ - `flags`
158
+ - `opaque`
159
+ - `extending`
160
+ - `function`
161
+ - `async function`
162
+ - `external function`
163
+ - `foreign function`
164
+ - `event`
165
+ - `static_assert(...)`
166
+ - `emit` — compile-time code generation inside `const function` or `inline` bodies
167
+ - `when` — compile-time conditional; may appear at module level or inside function bodies
168
+
169
+ File-kind note:
170
+
171
+ - Ordinary files may use the full declaration surface above.
172
+ - External files use a restricted declaration surface: `const`, `type`, `struct`, `union`, `enum`, `flags`, `opaque`, and `external function`. External files cannot declare new attributes, but supported attribute applications such as `@[packed]` and `@[align(...)]` may still appear on declarations that accept them.
173
+ - `event` declarations are not allowed in external files.
174
+
175
+ ### 3.1 Visibility
176
+
177
+ - `public` is supported for exportable ordinary declarations.
178
+ - `public` is rejected on `extending` blocks.
179
+ - `public` is rejected on ordinary `external` declarations and `static_assert`.
180
+ - In external files, declarations are implicitly exported and `public` is rejected.
181
+
182
+ ### 3.2 Constants and variables
183
+
184
+ ```mt
185
+ const WIDTH: int = 1280
186
+ var counter: int = 0
187
+ var scratch: array[ubyte, 256]
188
+ ```
189
+
190
+ Block-bodied `const` uses `->` instead of `:`:
191
+
192
+ ```mt
193
+ const NEXT_POW2 -> int:
194
+ var n: int = 1
195
+ while n < 1024:
196
+ n = n * 2
197
+ return n
198
+ ```
199
+
200
+ The block body is evaluated at compile time. Allowed inside the block: literals, names of other `const` values, arithmetic, control flow (`if`/`else if`/`else`, `while`, `for`), `let` and `var` declarations, calls to other compile-time functions, and calls to whitelisted builtins (`size_of`, `align_of`, `offset_of`, `fields_of`, `members_of`, `attributes_of`).
201
+
202
+ Rules:
203
+
204
+ - `const` requires explicit type and initializer.
205
+ - A block-bodied form `const NAME -> TYPE:` followed by an indented block is also supported. The block is evaluated at compile time and must end with a `return` on every code path.
206
+ - Top-level `var` requires explicit type; initializer is optional.
207
+ - Top-level `var` initializer must be static-storage-safe.
208
+ - Local declarations:
209
+ - `let` is immutable
210
+ - `var` is mutable
211
+ - A local declaration without initializer requires explicit type and must be zero-initializable.
212
+ - `let` and `var` declarations may use a guard form over nullable values, `Option[T]`, and `Result[T, E]`:
213
+
214
+ ```mt
215
+ let window = maybe_window else:
216
+ return 1
217
+
218
+ let image = load_image(path) else:
219
+ return 1
220
+
221
+ var runtime = maybe_runtime else:
222
+ return 1
223
+ ```
224
+
225
+ Rules for `let ... else:` and `var ... else:`:
226
+
227
+ - both `let` and `var` support an `else` block
228
+ - the initializer must have type `T?`, `Option[T]`, or `Result[T, E]`
229
+ - an explicit type annotation, if present, must name the success type `T`
230
+ - for `Option[T]`, the bound name is the `some.value`
231
+ - for `Result[T, E]`, the bound name is the `success.value`
232
+ - `let _ = expr else:` discards the success value and does not introduce a local binding
233
+ - for `Result[T, E]`, `else as error:` optionally binds the `failure.error` value inside the `else` block
234
+ - `Result[void, E]` uses the same surface via `let _ = expr else:`
235
+ - the `else` block must exit control flow (`return`, `break`, `continue`, or another terminating path)
236
+
237
+ Tuple and struct destructuring:
238
+
239
+ ```mt
240
+ let (a, b) = pair()
241
+ let Vec2(x, y) = get_position()
242
+ let (_, _, title) = triple # _ discard may repeat
243
+ ```
244
+
245
+ Tuple destructuring binds each element by position. Struct destructuring binds each field by name against the source struct's declared field order.
246
+
247
+ Postfix Result/Option propagation:
248
+
249
+ ```mt
250
+ let parsed = parse(input)?
251
+ let lowered = lower(parsed)?
252
+ return Result[Output, Error].success(value= lowered)
253
+ ```
254
+
255
+ - `expr?` requires `Option[T]` or `Result[T, E]` with a non-`void` success type. Any variant with matching arm structure (`some(value: T)`/`none` or `success(value: T)`/`failure(error: E)`) also works.
256
+ - on success, `expr?` evaluates to the unwrapped `T`
257
+ - on failure, `expr?` returns `Option[_].none` or `Result[_, E].failure(error= ...)` from the enclosing function or proc
258
+ - as an expression statement, `expr?` also accepts `Option[void]` or `Result[void, E]`; success continues and failure returns early
259
+ - `expr?` is only allowed inside function and proc bodies
260
+ - inside `async` functions, failure completes the task early
261
+ - `expr?` is not allowed inside `defer` blocks
262
+ - the enclosing function or proc must return a compatible type — `Option[_]` or `Result[_, E]` with the same error type `E`
263
+ - `let _ = expr else:` is still useful when you need an explicit `else` block or `else as error:` binding
264
+
265
+ ### 3.3 Type aliases
266
+
267
+ ```mt
268
+ type Seconds = float
269
+ type Callback = fn(level: int, message: cstr) -> void
270
+ ```
271
+
272
+ Callable and `ref[...]` rules:
273
+
274
+ - Plain stored `ref[T]` values are rejected in constants, module variables, and nested local storage such as arrays or other generic containers.
275
+ - In struct or union fields, bare `ref[T]` auto-generates an implicit lifetime parameter. The struct becomes non-owning and inherits ref-like restrictions: allowed as function params and local `let` variables, rejected as returns and module storage. Explicit lifetimes (`ref[@a, T]`) are still supported when the lifetime needs to be shared across fields.
276
+ - Ordinary local bindings may still hold a direct `ref[T]` value, for example `let handle = ref_of(counter)`.
277
+ - `fn(...)` and `proc(...)` parameter types may use `ref[...]` directly in parameter position.
278
+ - Stored callable values may use `ref[...]` only in direct callable parameter positions. This includes `fn(...)` values and `proc(...)` closure values stored in locals, struct fields, and generic containers such as `array[...]`.
279
+ - Stored callable values may not use `ref[...]` in return types.
280
+ - Stored callable values may not nest `ref[...]` anywhere except direct callable parameter positions.
281
+ - External functions still cannot take `ref[...]` parameters, and ordinary functions still cannot return `ref[...]`.
282
+ - `proc` captures are value captures. A captured local is not a mutable alias back to the outer binding. Any storable type may be captured, including scalars, arrays, structs, and other `proc` values. Captured `proc` values participate in the ref-counted lifecycle: the capturing proc retains the captured proc on creation and releases it when the env is freed.
283
+ - `ref[T]` values are not capturable by design since they are non-owning.
284
+ - Shared mutable proc state should use explicit storage such as `std.cell.alloc[T](...)` or other explicit pointer-backed state, not implicit mutable capture.
285
+
286
+ ### 3.4 Struct, union, enum, flags, opaque
287
+
288
+ ```mt
289
+ struct Vec2:
290
+ x: float
291
+ y: float
292
+
293
+ union Number:
294
+ i: int
295
+ f: float
296
+
297
+ enum State: ubyte
298
+ idle = 0
299
+ running = 1
300
+
301
+ # Backing type defaults to int; values auto-increment from 0:
302
+ enum Color:
303
+ red
304
+ green
305
+ blue
306
+
307
+ flags Mask: uint
308
+ a = 1 << 0
309
+ b = 1 << 1
310
+
311
+ opaque SDL_Window
312
+
313
+ variant Token:
314
+ ident(text: str)
315
+ number(value: int)
316
+ eof
317
+ ```
318
+
319
+ `struct` and `opaque` declarations may add nominal interface conformance with `implements`:
320
+
321
+ ```mt
322
+ struct NPC implements Damageable, Named:
323
+ hp: int
324
+
325
+ opaque SDL_Window implements Closable
326
+ ```
327
+
328
+ `variant` is a tagged union. Each arm may optionally carry named payload fields. Generic variants are supported via type arguments, for example `Option[int]`:
329
+
330
+ ```mt
331
+ variant Option[T]:
332
+ some(value: T)
333
+ none
334
+
335
+ variant Result[T, E]:
336
+ success(value: T)
337
+ failure(error: E)
338
+ ```
339
+
340
+ Arm constructors:
341
+
342
+ - Payload arm: `Token.ident(text = "hello")` — field names with `=`.
343
+ - No-payload arm: `Token.eof` — accessed as a bare member expression.
344
+
345
+ Declaration attributes use a leading `@[name(...)]` surface. User-defined attributes are declared with explicit targets such as `attribute[field] rename(name: str)` or `attribute[const, event] trace(name: str)`. Built-in `packed` and `align(bytes)` are predefined struct attributes; `deprecated(message: str)` is predefined and targets function, struct, const, enum, flags, union, variant, and event:
346
+
347
+ ```mt
348
+ @[packed]
349
+ struct Header:
350
+ tag: ubyte
351
+
352
+ @[align(16)]
353
+ struct Mat4:
354
+ data: array[float, 16]
355
+ ```
356
+
357
+ `align(...)` must be a positive power of two.
358
+
359
+ Compile-time reflection over validated attributes uses `has_attribute`, `attribute_of`, `attribute_arg[T]`, `field_of`, and `callable_of`.
360
+
361
+ The current C backend lowers `packed` / `align(...)` attributes with GNU-style `__attribute__((...))`, so these layout controls currently require a Clang/GCC-family compiler. On Windows that means Clang or GCC-family toolchains such as MinGW; `cl.exe` is not a supported backend for these attributes today. On wasm/browser targets the same feature works through Emscripten `emcc`, which is Clang-based.
362
+
363
+ ### 3.4a Enum and flags operators
364
+
365
+ Enum and flags values support the full set of comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`) against values of the same enum type and against their backing integer type. Comparisons use the underlying integer backing values.
366
+
367
+ ```mt
368
+ const IS_RUNNING: bool = State.running == State.running
369
+ const IS_AFTER: bool = State.running > State.idle
370
+ ```
371
+
372
+ Flags values also support bitwise operators (`|`, `&`, `^`, `~`) where both operands share the same flags type.
373
+
374
+ ### 3.5 Interfaces
375
+
376
+ ```mt
377
+ public interface Damageable:
378
+ editable function take_damage(amount: int) -> void
379
+ function is_alive() -> bool
380
+ ```
381
+
382
+ Rules:
383
+
384
+ - Interface bodies contain `function`, `editable function`, or `static function` signatures.
385
+ - Generic interfaces are supported: `interface Mapper[T]: function map(x: T) -> T`.
386
+ - Interface methods may not declare their own type params — the type params come from the interface.
387
+ - Interface methods may not be `async`.
388
+ - Interface methods do not have bodies.
389
+ - `public` is allowed on the interface declaration, not on individual interface methods.
390
+ - Bare interface names are not runtime storage types; they are contracts used by `implements` and constrained generics.
391
+ - Runtime interface values use the `dyn[InterfaceName]` type constructor. A `dyn[I]` value is a fat pointer carrying a data pointer and a vtable pointer. Construct with `adapt[I](value: ref[T])` which verifies `T implements I` at compile time.
392
+ - Conformance uses type substitution: `struct Foo implements Mapper[int]` checks methods against `Mapper` with `T = int`.
393
+ - Generic interfaces instantiated through `dyn` must be fully specified: `dyn[Mapper[int]]` is valid; `dyn[Mapper]` is rejected.
394
+
395
+ ### 3.6 Methods
396
+
397
+ ```mt
398
+ extending Counter:
399
+ function read() -> int:
400
+ return this.value
401
+
402
+ editable function bump() -> void:
403
+ this.value += 1
404
+
405
+ static function zero() -> Counter:
406
+ return Counter(value = 0)
407
+ ```
408
+
409
+ Kinds:
410
+
411
+ - `function` (value receiver)
412
+ - `editable function` (editable receiver)
413
+ - `static function` (no receiver)
414
+
415
+ Names such as `init` and `default` are ordinary static functions. There is no constructor keyword or hidden initializer syntax.
416
+
417
+ Method capabilities:
418
+
419
+ - async methods are supported
420
+ - generic methods are supported
421
+
422
+ ### 3.7 Functions
423
+
424
+ ```mt
425
+ function add(a: int, b: int) -> int:
426
+ return a + b
427
+ ```
428
+
429
+ Rules:
430
+
431
+ - Parameters must be typed.
432
+ - Parameters are non-rebindable; copy into a local `var` to mutate by-value data.
433
+ - Mutation through referent surfaces (for example span element writes, `read(ref_value) = ...`, and pointer writes in `unsafe`) is allowed.
434
+ - Return type defaults to `void` if omitted.
435
+ - Generic functions are supported.
436
+ - Generic function and method type parameters may declare constraints with `implements`.
437
+
438
+ Examples:
439
+
440
+ ```mt
441
+ function damage_one[T implements Damageable](target: ref[T], amount: int) -> void:
442
+ if target.is_alive():
443
+ target.take_damage(amount)
444
+
445
+ function tag[T implements Damageable and Named](target: ref[T]) -> str:
446
+ return target.name()
447
+
448
+ function make_default[T]() -> T:
449
+ return default[T]
450
+
451
+ function boot_screen[T implements ScreenState]() -> T:
452
+ return default[T]
453
+ ```
454
+
455
+ ### 3.7a `const function`
456
+
457
+ A `const function` is evaluable at compile time and follows the same body restrictions as a block-bodied `const`. When called from a compile-time context, the call is constant-folded to its computed value:
458
+
459
+ ```mt
460
+ const function square(x: int) -> int:
461
+ return x * x
462
+
463
+ const SQUARE_5: int = square(5) # folded to 25 at compile time
464
+ ```
465
+
466
+ Rules:
467
+
468
+ - The body must be evaluable at compile time (literals, `const` values, arithmetic, `if`/`else`, `while`, `for`, `let`/`var`, calls to other `const` functions, and whitelisted builtins).
469
+ - Generates a normal runtime function as well — callable from ordinary runtime code.
470
+ - Called from `const` initializers, `when` discriminants, `inline for` bodies, and other compile-time contexts.
471
+ - Recursive calls between `const` functions are supported.
472
+
473
+ ### 3.8 External functions
474
+
475
+ ```mt
476
+ external function printf(format: cstr, ...) -> int
477
+ ```
478
+
479
+ Raw `std.c.*` modules usually group many `external function` declarations inside an external file, but `external function` is also allowed in ordinary files for small manual ABI bridges.
480
+
481
+ Rules:
482
+
483
+ - no body
484
+ - variadic `...` supported
485
+ - cannot be generic
486
+ - cannot be async
487
+ - cannot take `ref` parameters
488
+ - cannot take `proc` parameters
489
+ - cannot take or return arrays
490
+ - cannot use a non-pointer value nullable (such as `int?`) as a parameter or return type; only pointer-like `T?` may cross the boundary
491
+
492
+ ### 3.9 Foreign functions
493
+
494
+ ```mt
495
+ foreign function init_window(width: int, height: int, title: str as cstr) -> void = c.InitWindow
496
+ foreign function load_file_data(file_name: str as cstr, out data_size: int) -> ptr[ubyte]? = c.LoadFileData
497
+ foreign function close_window(consuming window: Window) -> void = c.CloseWindow
498
+ ```
499
+
500
+ Parameter modes:
501
+
502
+ - plain
503
+ - `in`
504
+ - `out`
505
+ - `inout`
506
+ - `consuming`
507
+
508
+ Boundary projections:
509
+
510
+ - `name: PublicType as BoundaryType`
511
+
512
+ Rules:
513
+
514
+ - `as` is only allowed on plain and `in` params.
515
+ - `in`, `out`, and `inout` are declared on the `foreign function` parameter; callers pass ordinary expressions or lvalues at those argument positions.
516
+ - Legacy imported-call syntax such as `load_file_data(path, out size)` or `set_shader_value(shader, loc, in value, kind)` is rejected semantically.
517
+ - consuming foreign calls must appear as top-level expression statements.
518
+ - foreign functions with consuming params must return `void`.
519
+ - a non-pointer value nullable (such as `int?`) cannot be a parameter or return type; only pointer-like `T?` may cross the boundary.
520
+
521
+ ## 4. Statements
522
+
523
+ Supported statements:
524
+
525
+ - local declaration (`let`, `var`)
526
+ - assignment
527
+ - `if` / `else if` / `else`
528
+ - `match`
529
+ - `when` — compile-time conditional; only the chosen branch is type-checked and emitted
530
+ - `inline for` — loop over a compile-time-known array, unrolled at compile time
531
+ - `inline while` — loop with a compile-time-known condition, unrolled at compile time
532
+ - `inline match` — match with a compile-time-known scrutinee, unrolled at compile time
533
+ - `inline if` — if with a compile-time-known condition; only the chosen branch is type-checked and emitted
534
+ - `unsafe`
535
+ - `static_assert`
536
+ - `for`
537
+ - `while`
538
+ - `parallel for` — data-parallel loop dispatched across CPU cores
539
+ - `parallel:` — structured fork-join block with statements
540
+ - `pass`
541
+ - `break`
542
+ - `continue`
543
+ - `return`
544
+ - `defer`
545
+ - `emit` — compile-time code generation; only valid inside `const function` or `inline for/while/if/match` bodies
546
+ - expression statement
547
+
548
+ ### 4.1 If
549
+
550
+ Condition must be `bool`.
551
+
552
+ `pass` is an explicit no-op statement for intentionally empty block bodies.
553
+
554
+ `if` supports an inline single-statement form where the body and `else` branch appear on the same line:
555
+
556
+ ```mt
557
+ if ready: return 1 else: return 0
558
+ if x > 10: return "big" else if x > 5: return "med" else: return "small"
559
+ ```
560
+
561
+ The inline form supports all statement kinds in the body (`return`, `let`, `var`, `defer`, assignment, expression statement, etc.). Blocks and inline form may be mixed: any branch may use either form independently.
562
+
563
+ ### 4.2 Match
564
+
565
+ Scrutinee types supported:
566
+
567
+ - Enum: arm patterns must be members of that enum.
568
+ - Variant: arm patterns must be arms of that variant; a payload arm may bind its fields with `as name` or destructure them inline with struct patterns.
569
+ - Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals or char literals.
570
+ - `str`: arm patterns must be string literals (e.g., `"lex": ...`). Matches via full content comparison using the `equal` builtin.
571
+ - Tuple: arm patterns must be tuple literal patterns whose elements may be integer literals, char literals, string literals, booleans, or `_` discard.
572
+
573
+ `_` is a wildcard arm that matches any value not covered by preceding arms. It maps to a C `default:` case.
574
+
575
+ Multiple pattern values may share a single arm body using `|`:
576
+
577
+ ```mt
578
+ match kind:
579
+ EventKind.quit | EventKind.cancel:
580
+ return 0
581
+ _:
582
+ return 1
583
+ ```
584
+
585
+ Rules:
586
+
587
+ - For enum scrutinee: all members must be covered unless a `_` arm is present.
588
+ - For variant scrutinee: all arms must be covered unless a `_` arm is present; `as name` binds the payload struct for arms that have fields.
589
+ - For integer scrutinee: a `_` arm is required (integers are unbounded).
590
+ - For `str` scrutinee: a `_` arm is required (strings are unbounded). Arm patterns must be string literals. Matches via full content comparison.
591
+ - For tuple scrutinee: a `_` arm is required. Arm patterns must be tuple literal patterns. Each element may be an integer literal, char literal, string literal, boolean literal, or `_` discard. Arm arity must match the scrutinee tuple. A fully literal arm is checked for duplicates; arms containing `_` wildcard elements are not.
592
+ - Duplicate arm values (or duplicate `_`) are rejected.
593
+ - Match must be exhaustive (enum/variant without `_`) or include `_` (integer, str, or partial enum/variant).
594
+
595
+ `match` may also be used as an expression. Each arm provides a `:` value instead of an indented body, and all arm values must have compatible types:
596
+
597
+ ```mt
598
+ let label = match code:
599
+ 1: "one"
600
+ 2: "two"
601
+ _: "other"
602
+ ```
603
+
604
+ Statement form examples:
605
+
606
+ ```mt
607
+ match kind:
608
+ EventKind.quit:
609
+ return 0
610
+ _:
611
+ return 1
612
+
613
+ match key_code:
614
+ 65:
615
+ fire()
616
+ 27:
617
+ quit()
618
+ _:
619
+ return
620
+
621
+ match ch:
622
+ '(':
623
+ open_paren()
624
+ ')':
625
+ close_paren()
626
+ '+':
627
+ handle_plus()
628
+ _:
629
+ return
630
+
631
+ match token:
632
+ Token.ident as t:
633
+ use_name(t.text)
634
+ Token.number as n:
635
+ use_value(n.value)
636
+ Token.eof:
637
+ return
638
+
639
+ match color:
640
+ (255, 0, 0):
641
+ set_red()
642
+ (0, 255, 0):
643
+ set_green()
644
+ _:
645
+ return
646
+ ```
647
+
648
+ #### Struct patterns in variant match arms
649
+
650
+ When a variant arm carries payload fields, those fields may be destructured inline. Inside `VariantName.arm_name(...)`, each argument is one of:
651
+
652
+ - **Binding:** a bare field name creates an immutable local bound to that field.
653
+ - **Discard:** `_` discards a field value without binding it. Use it to skip unneeded fields in multi-field payload arms.
654
+ - **Guard:** `field comparison_op expr` extracts the field and skips the arm if the condition is false. Supported operators: `==`, `!=`, `<`, `<=`, `>`, `>=`.
655
+ - **Equality pattern:** `field = expr` is a shorthand guard — the arm matches only when the field equals the expression value.
656
+
657
+ ```mt
658
+ match entity:
659
+ Entity.player(hp > 0, position):
660
+ render(position)
661
+ Entity.player:
662
+ remove()
663
+ Entity.enemy:
664
+ return
665
+ ```
666
+
667
+ Rules for struct patterns:
668
+
669
+ - Each field name must appear at most once per arm. `_` may appear any number of times as a positional discard.
670
+ - Guards and equality patterns are refutable: they do not count toward exhaustiveness. Exception: when equality patterns for an enum-typed field gatherively cover every member of the enum, the arm is considered exhaustive. An arm with only bindings and no guards counts as exhaustive.
671
+ - Struct patterns compose with `as name` bindings: `Entity.player(hp > 0) as p` binds both `hp` (guard-checked) and `p` (the full payload struct).
672
+ - Struct patterns do not apply to enum or integer match scrutinees.
673
+ - When a variant arm has exactly one payload field of struct type, and no pattern argument references that field name, the struct's own fields are transparently destructured. For example, `Entity.positioned(x, y)` where `positioned(loc: Pos)` destructures through `Pos` to bind `x` and `y`.
674
+
675
+ ### 4.3 Loops
676
+
677
+ Single-form `for` supports:
678
+
679
+ - `start..stop` — exclusive integer range via range expression
680
+ - `array[T, N]`
681
+ - `span[T]`
682
+ - custom structural iterables with a non-editable zero-argument `iter()` method
683
+
684
+ Iterator protocol for custom structural iterables:
685
+
686
+ - the iterable value must expose `iter()` with no parameters
687
+ - the returned iterator must expose either `next() ->` nullable pointer-like item or `next() -> bool` together with `current()`
688
+
689
+ Parallel `for` is also supported:
690
+
691
+ ```mt
692
+ for entity, position, velocity in entities, positions, velocities:
693
+ update(entity, position, velocity)
694
+ ```
695
+
696
+ Rules for parallel `for`:
697
+
698
+ - each iterable must be an `array[T, N]` or `span[T]`
699
+ - ranges are not supported in the parallel form
700
+ - iterable counts must match the binding count
701
+ - static array lengths must match; span lengths are checked at runtime
702
+
703
+ `break` and `continue` must be inside loops.
704
+
705
+ ### 4.3a Parallel For (data-parallel)
706
+
707
+ `parallel for` dispatches loop iterations across multiple CPU cores using real OS threads (libuv):
708
+
709
+ ```mt
710
+ parallel for i in 0..entity_count:
711
+ positions[i] += velocities[i] * dt
712
+ ```
713
+
714
+ Rules:
715
+
716
+ - Only range iteration is supported (`0..N`).
717
+ - The loop body must not contain `break`, `continue`, `return`, `defer`, or nested `parallel for`.
718
+ - Captured `ref[T]` values are rejected at compile time.
719
+ - Array captures are passed by pointer (writes affect the original); span and scalar captures are passed by value.
720
+ - The compiler automatically detects `parallel for` usage and links libuv for thread dispatch.
721
+ - Thread count is determined at runtime via CPU core detection; one chunk runs on the calling thread.
722
+
723
+ ### 4.3b Parallel Blocks (structured fork-join)
724
+
725
+ `parallel:` blocks run each statement concurrently, blocking the caller until all complete:
726
+
727
+ ```mt
728
+ parallel:
729
+ textures = load_textures(path)
730
+ sounds = load_sounds(path)
731
+ ```
732
+
733
+ Rules:
734
+
735
+ - A `parallel:` block must contain at least two statements.
736
+ - `do` is a contextual keyword — only recognized inside `parallel:` blocks, not reserved globally.
737
+ - Each statement must not contain `break`, `continue`, `return`, or `defer`.
738
+ - The compiler enforces single-writer-or-multiple-readers: if a variable is written in one statement, no other block may access it.
739
+ - Captured `ref[T]` values are rejected at compile time.
740
+ - Each statement runs on its own OS thread (one on the calling thread, the rest on worker threads).
741
+
742
+ ### 4.3c Detach and Collect (detached concurrency)
743
+
744
+ `detach` spawns work on a separate OS thread and returns a `Handle`. `gather` blocks until all handles complete:
745
+
746
+ ```mt
747
+ let a = detach load_textures(path)
748
+ let b = detach load_sounds(path)
749
+ process_other_stuff()
750
+ gather a, b
751
+ ```
752
+
753
+ Rules:
754
+
755
+ - `detach` is an expression returning a `Handle` — must be bound with `let` or `var`.
756
+ - By design, `detach` accepts only global function calls with no captured local variables. This is a deliberate boundary — it avoids lifetime concerns on detached work without ownership annotations. For passing data to a detached call, use explicit heap-allocated storage or shared-state primitives such as `std.sync`.
757
+ - `gather` takes one or more `Handle` values, joined in order.
758
+ - Captured `ref[T]` values are rejected at compile time.
759
+ - `detach` and `gather` are keywords. The compiler automatically links libuv for thread dispatch.
760
+
761
+ ### 4.4 Defer
762
+
763
+ Forms:
764
+
765
+ ```mt
766
+ defer cleanup()
767
+
768
+ # or
769
+
770
+ defer:
771
+ release_a()
772
+ release_b()
773
+ ```
774
+
775
+ `return` is not allowed inside defer blocks.
776
+
777
+ ### 4.5 Unsafe
778
+
779
+ Forms:
780
+
781
+ ```mt
782
+ unsafe: pointer[0] = 1
783
+ let raw = unsafe: read(ptr)
784
+
785
+ # or
786
+
787
+ unsafe:
788
+ pointer[0] = 1
789
+ pointer[1] = 2
790
+ ```
791
+
792
+ Unsafe context is required for raw-pointer-level operations such as:
793
+
794
+ - pointer indexing
795
+ - raw dereference
796
+ - pointer arithmetic
797
+ - pointer casts
798
+ - `reinterpret[...]`
799
+
800
+ ### 4.6 Range index assignment
801
+
802
+ Contiguous indexed slices may be assigned from an expression list:
803
+
804
+ ```mt
805
+ var buf: array[float, 4]
806
+ buf[0..3] = (1.0, 2.0, 3.0)
807
+ ```
808
+
809
+ Rules:
810
+
811
+ - the target must be an addressable array-, span-, or pointer-indexable lvalue
812
+ - the index must be a range expression with integer literal bounds
813
+ - the range is start-inclusive and end-exclusive
814
+ - the right-hand side must be an expression list whose length exactly matches the range width
815
+ - each element must be assignable to the indexed element type
816
+
817
+ ### 4.7 When (compile-time conditional)
818
+
819
+ ```mt
820
+ when TARGET_OS:
821
+ TargetOs.linux:
822
+ return open_linux(path)
823
+ TargetOs.windows:
824
+ return open_windows(path)
825
+ ```
826
+
827
+ Rules:
828
+
829
+ - The discriminant must be a compile-time constant.
830
+ - Only the chosen branch is type-checked and emitted. The other branches are not checked.
831
+ - `else` is required when the discriminant is not a finite type. `else` is optional only when the discriminant is an enum and every member is covered.
832
+ - `when` may appear at module level to conditionally include declarations, imports, or type definitions.
833
+ - There is no `when` expression form.
834
+
835
+ ### 4.8 Inline for
836
+
837
+ ```mt
838
+ inline for field in fields_of(Particle):
839
+ static_assert(field.type == float, "Particle fields must be float")
840
+ ```
841
+
842
+ Rules:
843
+
844
+ - The iterable must be a compile-time-known array. The most common source is a reflection builtin (`fields_of`, `members_of`, `attributes_of`). A literal array is also accepted.
845
+ - The loop is unrolled once per element at compile time.
846
+
847
+ ### 4.9 Inline while
848
+
849
+ ```mt
850
+ inline while n < 1024:
851
+ n = n * 2
852
+ ```
853
+
854
+ Rules:
855
+
856
+ - The condition must be a compile-time constant.
857
+ - The loop unrolls to a fixed number of iterations, capped at 10,000.
858
+ - A non-terminating `inline while` is a compile error.
859
+
860
+ ### 4.10 Inline match
861
+
862
+ ```mt
863
+ inline match TARGET_BACKEND:
864
+ Backend.gl:
865
+ gl_draw(item)
866
+ Backend.metal:
867
+ metal_draw(item)
868
+ Backend.vulkan:
869
+ vk_draw(item)
870
+ ```
871
+
872
+ Rules:
873
+
874
+ - The scrutinee must be a compile-time constant.
875
+ - Only the chosen arm is type-checked and emitted.
876
+ - An `inline match` is not required to be exhaustive; unchosen arms are dropped.
877
+
878
+ ### 4.11 Inline if
879
+
880
+ ```mt
881
+ const DEBUG_RENDER: bool = false
882
+
883
+ function draw() -> void:
884
+ inline if DEBUG_RENDER:
885
+ debug_overlay()
886
+ else:
887
+ normal_draw()
888
+ ```
889
+
890
+ Rules:
891
+
892
+ - The condition must be a compile-time constant `bool`.
893
+ - A compile-time **type comparison** is a valid condition: `T == int`, `field.type == float`, etc., where `T` is a generic type parameter (substituted at specialization) and `field.type` is a reflected field type. This enables type-based dispatch in generic bodies (e.g. `std.fmt.format_value[T]`).
894
+ - Only the chosen branch is type-checked and emitted. The dead branch may reference types and symbols that do not exist.
895
+ - `inline if` supports `else` and `else if` branches with the same dead-branch elimination.
896
+
897
+ ## 5. Expressions
898
+
899
+ ### 5.1 Primary
900
+
901
+ - identifier
902
+ - literals
903
+ - parenthesized expression
904
+ - tuple literal: `(a, b)` — positional; `(x = 1, y = 2)` — named
905
+ - `size_of(T)` — accepts a type name or a compile-time type expression
906
+ - `align_of(T)` — accepts a type name or a compile-time type expression
907
+ - `offset_of(T, field)` — the field argument may be a literal field name or a compile-time `field_handle` expression
908
+ - `proc(...) -> T: ...` — anonymous function expression, e.g. `let fn_ptr = proc(x: int) -> int: return x + 1`
909
+ - `proc(...) -> T: expr` — expression-bodied anonymous function, implicitly returning `expr`
910
+ - `if cond: a else: b`
911
+ - `match scrutinee: arm: value ...` — expression form of match; every arm provides a `:` value and all arm values must have compatible types
912
+
913
+ ### 5.2 Postfix
914
+
915
+ - member access: `a.b`
916
+ - indexing: `a[i]`
917
+ - call: `f(x)`
918
+ - partial field update: `v.with(x = 10.0)` — returns a copy with specified fields replaced; supported on structs and native types (vector, matrix, quaternion)
919
+ - specialization: `name[T]`, `name[32]`, `mod.name[T]`
920
+ - explicit specialization is only accepted on bare or module-qualified names; `value.member[32](...)` remains indexed-call syntax, so value-member calls rely on inference instead of explicit literal specialization
921
+ - variant membership test: `expr is Variant.Arm` — returns `true` if the variant value matches the arm, `false` otherwise. Desugars to `match expr: Variant.Arm: true; _: false` during parsing. Works for both no-payload arms (discriminant equality) and payload arms (any payload matches). Use `match` with `as name` for payload destructuring.
922
+
923
+ ### 5.3 Operator precedence (low to high)
924
+
925
+ 1. `or`
926
+ 2. `and`
927
+ 3. `not` (unary prefix)
928
+ 4. `is`
929
+ 5. `|`
930
+ 6. `^`
931
+ 7. `&`
932
+ 8. `==`, `!=`
933
+ 9. `<`, `<=`, `>`, `>=`
934
+ 10. `<<`, `>>`
935
+ 11. `+`, `-`
936
+ 12. `*`, `/`, `%`
937
+
938
+ ### 5.4 Assignment operators
939
+
940
+ - `=`
941
+ - `+=` `-=` `*=` `/=`
942
+ - `%=`
943
+ - `&=` `|=` `^=`
944
+ - `<<=` `>>=`
945
+
946
+ ## 6. Type System
947
+
948
+ ### 6.1 Primitive types
949
+
950
+ - `bool`
951
+ - `byte` `short` `int` `long`
952
+ - `ubyte` `ushort` `uint` `ulong`
953
+ - `char`
954
+ - `ptr_int` `ptr_uint`
955
+ - `float` `double`
956
+ - `void`
957
+ - `str`
958
+ - `cstr`
959
+ - `vec2` `vec3` `vec4` — float vectors with `.x` `.y` `.z` `.w` fields; support component-wise `+` `-` `*` `/` and unary `-`
960
+ - `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
961
+ - `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
962
+ - `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
963
+
964
+ Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
965
+
966
+ Native vector, matrix, and quaternion types support aggregate construction with named fields. Omitted fields default to zero:
967
+
968
+ ```mt
969
+ let v = vec3(x = 1.0, y = 2.0, z = 3.0)
970
+ let m = mat4(col0 = vec4(x = 1.0, ...), ...)
971
+ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
972
+ ```
973
+
974
+ ### 6.2 Type constructors
975
+
976
+ - `ptr[T]`
977
+ - `const_ptr[T]`
978
+ - `own[T]`
979
+ - owning heap pointer: like `ptr[T]` but auto-dereferences (no `unsafe: read()` needed for field/method access)
980
+ - nullable: `own[T]?` lowers to a nullable `T*`; `null` is the absent value
981
+ - storable in struct/variant fields and returnable from functions — unlike `ref[T]`, `own[T]` has no non-owning restriction
982
+ - created via `heap.must_alloc[T](count)` (returns `own[T]`) or `heap.alloc[T](count)` (returns `own[T]?`); freed via `heap.release(ptr)`
983
+ - compiles to plain `T*` in C, with auto-deref handled at the language level
984
+ - `ref[T]`
985
+ - receiver-modifier type: passes a stored value by reference, allowing methods to mutate the underlying storage
986
+ - functions like `append(output: ref[string.String], text: str)` receive a safe pointer to stored data
987
+ - passing a mutable addressable `T` to a `ref[T]` parameter borrows it implicitly, as if the call had written `ref_of(value)` at the call site
988
+ - `ref` types are non-null and cannot be nullable
989
+ - `span[T]`
990
+ - `array[T, N]`
991
+ - `str_buffer[N]`
992
+ - `Task[T]`
993
+ - `fn(params...) -> R`
994
+ - `proc(params...) -> R`
995
+ - `Option[T]`
996
+ - `Result[T, E]`
997
+ - `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
998
+ - `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
999
+ - `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins. `atomic[T]` is zero-initializable and sendable.
1000
+ - `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
1001
+
1002
+ ### 6.3 Nullability
1003
+
1004
+ - nullable form: `T?`, valid for any type
1005
+ - for pointer-like bases (`ptr[T]`, `const_ptr[T]`, `own[T]`, `cstr`, `fn(...)`, `proc(...)`, opaque), `T?` is a nullable pointer with `null` as the absent value
1006
+ - for non-pointer value bases (`int`, `bool`, `float`, structs, ...), `T?` is stored inline by value as a tagged optional (a presence flag plus the value); it copies by value with no hidden heap allocation or pointer aliasing
1007
+ - `null` expresses absence in any nullable context; the explicit typed `null[...]` form's target must be pointer-like
1008
+ - in nullable pointer-like contexts, use `null` instead of `zero[ptr[T]]`
1009
+ - at an FFI boundary (`external` / `foreign function` parameters and returns), only pointer-like `T?` is allowed; a non-pointer value nullable such as `int?` is rejected — use `ptr[T]?` or pass an explicit struct
1010
+
1011
+ ### 6.4 Generics
1012
+
1013
+ Supported:
1014
+
1015
+ - generic structs
1016
+ - generic variants
1017
+ - generic interfaces
1018
+ - generic functions
1019
+ - generic methods
1020
+ - generic foreign functions
1021
+
1022
+ Rules:
1023
+
1024
+ - Constraints are supported on generic structs, variants, interfaces, functions, and methods.
1025
+ - Interface constraints use `implements`, and multiple interfaces on the same type parameter use `and`: `T implements A and B`.
1026
+ - There are no separate `hashes` or `equates` constraints. Generic bodies that call `hash[T](...)`, `equal[T](...)`, or `order[T](...)` rely on specialization-time checking of the canonical associated functions.
1027
+ - Current type parameters may be used as type expressions for associated function calls in generic bodies, such as `T.default()` or `T.tag()`.
1028
+ - Constraint kinds compose with `and`: `T implements ScreenState and Named` remains valid.
1029
+
1030
+ Type arguments can be:
1031
+
1032
+ - types
1033
+ - integer literals
1034
+ - named integer constants
1035
+
1036
+ Generic value parameters use the form `[N: int]` to declare a compile-time integer usable in expressions within the generic body:
1037
+
1038
+ ```mt
1039
+ function int_with_bits[N: int]() -> type:
1040
+ if N == 8:
1041
+ return byte
1042
+ else if N == 16:
1043
+ return short
1044
+ else if N == 32:
1045
+ return int
1046
+ else if N == 64:
1047
+ return long
1048
+ else:
1049
+ static_assert(false, "unsupported bit width")
1050
+ ```
1051
+
1052
+ The call site specializes with a literal: `int_with_bits[64]`.
1053
+
1054
+ `type` is a built-in type name representing the type of types. A function may declare `-> type` as its return type to select and return a type expression at compile time. Such functions may only be called from compile-time contexts (block-bodied `const`, `when` discriminants, `inline for` bodies, generic bodies, or other `type`-returning functions). The body follows the same restrictions as a block-bodied `const`.
1055
+
1056
+ ## 7. Built-In Callable Surface
1057
+
1058
+ Special recognized callables:
1059
+
1060
+ - `fatal(message)`
1061
+ - `ref_of(x)`
1062
+ - `const_ptr_of(x)`
1063
+ - `read(r)`
1064
+ - `read(p)`
1065
+ - `ptr_of(x)`
1066
+ - `T<-value`
1067
+ - `reinterpret[T](value)`
1068
+ - `zero[T]`
1069
+ - `default[T]`
1070
+ - `hash[T](value)`
1071
+ - `equal[T](left, right)`
1072
+ - `order[T](left, right)`
1073
+ - `array[T, N](...)`
1074
+ - `span[T](data = ..., len = ...)`
1075
+ - `get(coll, index)` — recoverable array/span indexing returning `ptr[T]?`; null on out‑of‑bounds instead of aborting
1076
+ - `adapt[I](value)` — constructs a `dyn[I]` runtime interface value; verifies `value`'s type implements `I` at compile time
1077
+
1078
+ `default[T]` requires an accessible zero-argument associated function `T.default()` that returns `T`.
1079
+
1080
+ `hash[T](value)` lowers to `T.hash(value: const_ptr[T]) -> uint`, `equal[T](left, right)` lowers to `T.equal(left: const_ptr[T], right: const_ptr[T]) -> bool`, and `order[T](left, right)` lowers to `T.order(left: const_ptr[T], right: const_ptr[T]) -> int`. Each argument must already be a `ref[T]`, `ptr[T]`, or `const_ptr[T]`, or be a safe stored `T` lvalue that can be borrowed implicitly.
1081
+
1082
+ `T.order(...) -> int` returns a negative value when `left < right`, `0` when the values compare equal, and a positive value when `left > right`.
1083
+
1084
+ There are no separate `hashes` or `equates` constraints; the builtins themselves force those hook requirements at specialization time.
1085
+
1086
+ There is no separate `defaults` constraint. A generic body that uses `default[T]` relies on specialization-time checking that `T.default()` exists.
1087
+
1088
+ For recoverable failures, use `Result[T, E]`. Its `.success(...)` and `.failure(...)` constructors are variant arms, not built-in callables.
1089
+
1090
+ For repeated pointer-plus-length span construction, use the built-in `span[T](data = ..., len = ...)` form directly. If the pattern repeats often in one codebase, define a small local helper in your own module instead of depending on a standard helper module.
1091
+
1092
+ When a `span[T]` is expected, an addressable `array[T, N]` value may be passed directly through the existing boundary coercion rules. Arrays also expose `.as_span()` for explicit conversion when the target type is not a call boundary.
1093
+
1094
+ ### 7.0 Compile-time reflection builtins
1095
+
1096
+ Compile-time reflection builtins return handle values that represent type structure:
1097
+
1098
+ - `field_of(T, name)` — returns a `field_handle` for the named field of `T`.
1099
+ - `callable_of(T, name)` — returns a `callable_handle` for the named callable of `T`.
1100
+ - `attribute_of(T, name)` — returns an `attribute_handle` for the named attribute on `T`.
1101
+ - `has_attribute(T, name)` — returns `bool`; true if `T` has the named attribute applied.
1102
+ - `attribute_arg[T]` — returns the `T`-typed argument of a resolved attribute handle.
1103
+ - `fields_of(T)` — returns `array[field_handle, N]` of all fields of struct `T`, in declaration order.
1104
+ - `members_of(E)` — returns `array[member_handle, N]` of all members of enum or variant `E`.
1105
+ - `attributes_of(T)` — returns `array[attribute_handle, N]` of all attributes on `T`.
1106
+ - `attributes_of(T, name)` — returns `array[attribute_handle, N]` of attributes whose kind matches `name`.
1107
+
1108
+ Handle field access: `field_handle` exposes `.name` (`str`) and `.type` (the field's type). `member_handle` exposes `.name` (`str`) and, for enum members with explicit values, `.value` (an integer). `attribute_handle` provides access to attribute arguments via `attribute_arg[T]`.
1109
+
1110
+ A field handle's `.type` is also usable directly **in type position** inside a compile-time context (e.g. within `inline for field in fields_of(T)`): it resolves to the field's concrete type in type-argument and type-constructor positions such as `const_ptr[field.type]`, `equal[field.type](...)`, and `Vec[field.type]`. This powers reflective generics like `std.hash`'s `equal_struct`/`hash_struct`/`order_struct`, which dispatch each field through its canonical hook, and `std.fmt.format_value[T]`, which recursively renders a struct's fields. The `inline for` body is type-checked once **per element**, so dispatch on each field — including recursion into a nested-struct field's type — is validated at check time.
1111
+
1112
+ `read(r)` still explicitly projects a `ref[T]` to its referent value, but ordinary member access and method calls auto-dereference `ref[T]` receivers. That means `handle.field`, `handle.edit_method()`, and `handle.read()` are accepted without writing `read(handle)` first. Calls in the other direction are also lighter now: if a function expects `ref[T]`, passing a mutable addressable `T` borrows it implicitly.
1113
+
1114
+ ### 7.1 Current standard collection modules
1115
+
1116
+ The current shipped collection modules in `std` are:
1117
+
1118
+ - `std.vec.Vec[T]`: contiguous growable storage with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `as_span`, `get`, `at`, `first`, `last`, `find`, `find_index`, `reserve`, `clear`, `release`, `append_span`, `append_array`, `insert`, `push`, `pop`, `remove`, and `swap_remove`.
1119
+ - `std.deque.Deque[T]`: growable ring buffer with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `get`, `at`, `first`, `last`, `reserve`, `clear`, `release`, `push_front`, `push_back`, `insert`, `pop_front`, `pop_back`, `remove`, `rotate_left`, and `rotate_right`.
1120
+ - `std.binary_heap.BinaryHeap[T]`: max-heap keyed by the canonical `order[T](...)` hook, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `reserve`, `clear`, `release`, `push`, and `pop`.
1121
+ - `std.priority_queue.PriorityQueue[T]`: task-oriented facade over `BinaryHeap[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `reserve`, `clear`, `release`, `enqueue`, and `dequeue`.
1122
+ - `std.ordered_set.OrderedSet[T]`: AVL-backed unique sorted set keyed by the canonical `order[T](...)` hook, with `create`, `len`, `is_empty`, `get`, `at`, `contains`, `iter`, `clear`, `release`, `insert`, and `remove`.
1123
+ - `std.ordered_map.OrderedMap[K, V]`: AVL-backed ordered map keyed by the canonical `order[K](...)` hook, with `create`, `len`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `set`, `get_or_insert`, `remove_entry`, and `remove`.
1124
+ - `std.map.Map[K, V]`: hash table keyed by the canonical `hash[K](...)` and `equal[K](...)` hooks, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `reserve`, `set`, `get_or_insert`, `remove_entry`, and `remove` (`iter()` is the same traversal as `entries()`).
1125
+ - `std.set.Set[T]`: hash set built on `Map[T, bool]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `contains`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `clear`, `release`, `reserve`, `insert`, and `remove`. Set union is spelled `union_with` because `union` is a reserved keyword.
1126
+ - `std.linked_map.LinkedMap[K, V]`: insertion-ordered hash map keyed by the canonical `hash[K](...)` and `equal[K](...)` hooks, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `reserve`, `set`, `get_or_insert`, `remove_entry`, and `remove`.
1127
+ - `std.linked_set.LinkedSet[T]`: insertion-ordered hash set built on `LinkedMap[T, bool]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `contains`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `clear`, `release`, `reserve`, `insert`, and `remove`.
1128
+ - `std.counter.Counter[T]`: insertion-ordered frequency table built on `LinkedMap[T, ptr_uint]`, with `create`, `with_capacity`, `len`, `total_count`, `capacity`, `is_empty`, `count`, `contains`, `keys`, `counts`, `entries`, `iter`, `clear`, `release`, `reserve`, `add`, `increment`, `remove_one`, and `remove`.
1129
+ - `std.multiset.MultiSet[T]`: insertion-ordered bag built on `Counter[T]`, with `create`, `with_capacity`, `len`, `total_count`, `distinct_len`, `capacity`, `is_empty`, `count`, `contains`, `values`, `entries`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `symmetric_difference`, `clear`, `release`, `reserve`, `insert`, `add`, `remove_one`, and `remove_all`.
1130
+ - `std.queue.Queue[T]`: FIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `enqueue`, and `dequeue`.
1131
+ - `std.stack.Stack[T]`: LIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `push`, and `pop`.
1132
+ - `std.linked_map_view.SnapshotValues[K, V]`: read-only snapshot view over `LinkedMap` values in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
1133
+ - `std.linked_map_view.SnapshotEntries[K, V]`: read-only snapshot view over `LinkedMap` entries in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
1134
+
1135
+ Iterator notes for those collection modules:
1136
+
1137
+ - `Vec.iter()` and `Deque.iter()` use the pointer-returning iterator form.
1138
+ - `BinaryHeap.iter()` uses the pointer-returning iterator form with read-only element pointers, and `peek()` is also read-only because arbitrary element mutation would violate the heap invariant.
1139
+ - `PriorityQueue.iter()` uses the same read-only pointer-returning iterator form as `BinaryHeap.iter()`.
1140
+ - `OrderedSet.iter()` uses the pointer-returning iterator form with read-only element pointers so in-place mutation cannot violate the sorted uniqueness invariant.
1141
+ - `OrderedMap.keys()` uses the pointer-returning iterator form with read-only key pointers, `OrderedMap.values()` returns mutable value pointers in key order, and `OrderedMap.entries()` / `OrderedMap.iter()` use the `next() -> bool` plus `current()` iterator form in key order.
1142
+ - `Map.keys()` and `Set.iter()` use the pointer-returning iterator form.
1143
+ - `Map.values()` returns mutable value pointers during iteration.
1144
+ - `Map.entries()` and `Map.iter()` use the `next() -> bool` plus `current()` iterator form.
1145
+ - `LinkedMap.keys()` and `LinkedSet.iter()` use the pointer-returning iterator form with read-only key pointers in insertion order.
1146
+ - `LinkedMap.values()` returns mutable value pointers in insertion order.
1147
+ - `LinkedMap.entries()` and `LinkedMap.iter()` use the `next() -> bool` plus `current()` iterator form in insertion order.
1148
+ - `Counter.keys()` uses the pointer-returning iterator form with read-only key pointers in first-seen order.
1149
+ - `Counter.counts()` uses the `next() -> bool` plus `current()` iterator form and yields copied `ptr_uint` counts so totals cannot be mutated out of sync.
1150
+ - `Counter.entries()` and `Counter.iter()` use the `next() -> bool` plus `current()` iterator form and yield immutable `{ key, count }` snapshots.
1151
+ - `MultiSet.values()` uses the pointer-returning iterator form with read-only value pointers in first-seen order.
1152
+ - `MultiSet.entries()` and `MultiSet.iter()` use the `next() -> bool` plus `current()` iterator form and yield immutable `{ value, count }` snapshots.
1153
+ - `Queue.iter()` and `Stack.iter()` use the same mutable pointer-returning iterator form as `Deque.iter()`, and `peek()` returns a mutable element pointer because changing an element value does not violate FIFO/LIFO ordering invariants.
1154
+ - `SnapshotValues.iter()` and `SnapshotEntries.iter()` use the `next() -> bool` plus `current()` iterator form in insertion order.
1155
+
1156
+ ## 8. Strings, C Strings, And Format Strings
1157
+
1158
+ String categories:
1159
+
1160
+ - `str` -> string view
1161
+ - `cstr` -> C ABI string
1162
+ - `str_buffer[N]` -> fixed-capacity mutable UTF-8 text buffer
1163
+
1164
+ `str_buffer[N]` methods:
1165
+
1166
+ - `clear()`
1167
+ - `assign(str)`
1168
+ - `append(str)`
1169
+ - `assign_format(str)`
1170
+ - `append_format(str)`
1171
+ - `len()`
1172
+ - `capacity()`
1173
+ - `as_str()`
1174
+ - `as_cstr()`
1175
+
1176
+ Format string syntax:
1177
+
1178
+ ```mt
1179
+ f"count=#{count} ok=#{ready}"
1180
+ ```
1181
+
1182
+ Heredoc syntax:
1183
+
1184
+ ```mt
1185
+ const shader: cstr = c<<-GLSL
1186
+ #version 330
1187
+ void main()
1188
+ {
1189
+ }
1190
+ GLSL
1191
+ ```
1192
+
1193
+ `<<-TAG`, `c<<-TAG`, and `f<<-TAG` read all following lines until a line containing only the terminator tag, optionally surrounded by spaces. Nonblank content lines are dedented by their shared leading spaces. The trailing newline before the terminator is preserved.
1194
+
1195
+ Ordinary `"..."` and `c"..."` literals may continue across following indented lines when each continued line starts with the same literal kind and contains nothing else. The segments concatenate exactly with no inserted separator, so any spaces or punctuation between pieces must be written explicitly.
1196
+
1197
+ In the VS Code extension, specific heredoc tags opt into embedded highlighting without changing the Milk Tea type: `GLSL`, `VERT`, `FRAG`, `COMP`, `JSON`, `JSONC`, `SQL`, `HTML`, and `MT`. `MT` recursively applies Milk Tea syntax highlighting to the heredoc content.
1198
+
1199
+ Format strings have type `str` and are valid anywhere a `str` value is accepted. Interpolated expressions must be one of: `str`, `cstr`, `bool`, a numeric primitive, an integer-backed enum or flags type, or a type implementing both `format_len() -> ptr_uint` and `append_format(output: ref[std.string.String]) -> void`. A precision specifier `:.N` is allowed on `float` and `double` interpolations. Integer-base specifiers `:x` / `:X` (hex), `:o` / `:O` (octal), and `:b` / `:B` (binary) are allowed on integer primitives and integer-backed enum/flags interpolations.
1200
+
1201
+ The following standard library functions receive special lowering for format strings — they build the formatted output directly without an intermediate allocation:
1202
+
1203
+ - `std.fmt.format` — returns `string.String`
1204
+ - `std.fmt.append_format` / `std.fmt.assign_format` — write directly into an existing `string.String` sink
1205
+ - `std.string.String.append_format` / `std.string.String.assign_format` — same direct-sink lowering on the builder methods
1206
+ - `str_buffer[N].append_format` / `str_buffer[N].assign_format` — same direct-sink lowering on fixed-capacity string buffers
1207
+
1208
+ Custom formatting hook notes:
1209
+
1210
+ - The hook pair is compiler-known; it is not declared through a separate interface.
1211
+ - `format_len()` and `append_format(...)` must both be present, non-editable, and use the exact signatures above.
1212
+ - Direct `string.String` sinks call the custom `append_format(...)` hook directly.
1213
+ - Plain `f"..."` expressions and `str_buffer` sinks still need a raw `str` result, so the compiler passes each custom part a borrowed `string.String` view onto the destination slice.
1214
+ - For those borrowed-slice paths, `append_format(...)` must write exactly `format_len()` bytes; writing fewer or more bytes raises a runtime error.
1215
+
1216
+ ## 9. Safety And Conversion Rules
1217
+
1218
+ - conditions must be `bool`
1219
+ - no truthy/falsy integer or pointer coercion
1220
+ - mixed signed/unsigned integer arithmetic requires an explicit `T<-value` cast
1221
+ - non-widening integer conversions (narrowing, signed → unsigned) require an explicit `T<-value` cast; lossless widening is implicit
1222
+ - `%` requires integer-compatible operands
1223
+ - bitwise operators require matching integer/flags types
1224
+ - shift operators require integer operands
1225
+ - safe array indexing requires an addressable array value
1226
+ - safe indexing (`arr[i]`) is bounds-checked and calls `fatal` on out-of-bounds access
1227
+ - use `get(arr, i)` for recoverable indexing that returns `ptr[T]?` (null on out-of-bounds) instead of aborting
1228
+ - pointer indexing requires `unsafe`
1229
+ - `read(...)` of raw pointer requires `unsafe`
1230
+ - pointer casts require `unsafe`
1231
+ - `reinterpret[...]` requires `unsafe`, non-array concrete sized types, and equal-size source and target types.
1232
+ - variant types support `==` and `!=` (generates per-variant comparison helper)
1233
+
1234
+ ## 10. Async Semantics
1235
+
1236
+ ```mt
1237
+ async function child() -> int:
1238
+ return 41
1239
+
1240
+ async function parent() -> int:
1241
+ let v = await child()
1242
+ return v + 1
1243
+ ```
1244
+
1245
+ Rules:
1246
+
1247
+ - async function return type is lifted to `Task[T]`
1248
+ - `await` is only allowed inside async functions
1249
+ - `async main` is compiler-bootstrapped, but async helpers remain library surface; import `std.async as aio` when you want helpers such as `sleep`, `completed`, `result`, `wait`, `run`, or runtime control
1250
+ - `aio.wait(...)` and `aio.run(...)` accept either a zero-arg task root or a direct task expression; the compiler defers direct task expressions automatically
1251
+ - `async main` pre-lift return type must be `int` or `void`
1252
+
1253
+ Current async limitations:
1254
+
1255
+ - `await` is supported inside `if` expressions, `if`/`else if`/`else` bodies and conditions, `while` bodies and conditions, single-form and parallel `for` bodies and iterables, `match` discriminants and arms, `let ... else:` initializers and else bodies, `unsafe` blocks, short-circuit `and`/`or` expressions, and assignment targets
1256
+ - `defer` is supported in async functions, including cleanup bodies that `await`
1257
+
1258
+ ## 11. Events
1259
+
1260
+ Event declarations provide a built-in typed publisher/subscriber surface with fixed-capacity listener storage.
1261
+
1262
+ ### 11.1 Declaration
1263
+
1264
+ ```mt
1265
+ event name[capacity]
1266
+ event name[capacity](PayloadType)
1267
+ public event name[capacity]
1268
+ public event name[capacity](PayloadType)
1269
+ ```
1270
+
1271
+ Examples:
1272
+
1273
+ ```mt
1274
+ public event closed[4]
1275
+ public event resized[8](ResizeEvent)
1276
+ ```
1277
+
1278
+ Rules:
1279
+
1280
+ - The capacity expression must be a compile-time positive integer literal.
1281
+ - An event carries either no payload or exactly one payload value.
1282
+ - The payload type must be a storable type; `ref[T]` payloads are rejected.
1283
+ - Event declarations are valid at top level and as struct members.
1284
+ - `emit` is only callable from within the declaring module.
1285
+
1286
+ ### 11.2 Built-in operations
1287
+
1288
+ - `event.subscribe(listener) -> Result[Subscription, EventError]` — register a stateless listener
1289
+ - `event.subscribe_once(listener) -> Result[Subscription, EventError]` — register a one-shot listener
1290
+ - `event.subscribe(state: ptr[State], listener: fn(ptr[State], ...)) -> Result[Subscription, EventError]` — register a stateful listener by passing 2 positional arguments; the state pointer is stored verbatim and passed to the listener on each dispatch
1291
+ - `event.subscribe_once(state: ptr[State], listener: fn(ptr[State], ...)) -> Result[Subscription, EventError]` — register a stateful one-shot listener by passing 2 positional arguments
1292
+ - `event.unsubscribe(subscription) -> bool` — remove a listener by subscription handle; returns `true` if the listener was active and removed, `false` if the handle was stale or invalid
1293
+ - `event.emit()` or `event.emit(payload)` — synchronously dispatch to all listeners; only callable from the declaring module
1294
+ - `event.wait() -> Task[Result[T, EventError]]` — async wait for the next emission; returns the payload for payload events, or `Result[void, EventError]` for no-payload events
1295
+
1296
+ `EventError` is a built-in enum with a single member `full` (value `0`), returned when listener capacity is exhausted.
1297
+
1298
+ `Subscription` is a built-in opaque handle type returned by `subscribe` and `subscribe_once`.
1299
+
1300
+ ### 11.3 Example
1301
+
1302
+ ```mt
1303
+ struct ResizeEvent:
1304
+ width: int
1305
+ height: int
1306
+
1307
+ struct Window:
1308
+ public event closed[4]
1309
+ public event resized[8](ResizeEvent)
1310
+
1311
+ function on_close() -> void:
1312
+ stdio.print_line("closed")
1313
+
1314
+ function on_resize(evt: ResizeEvent) -> void:
1315
+ stdio.print_line(f"resize -> #{evt.width}x#{evt.height}")
1316
+
1317
+ function attach(window: ref[Window]) -> Result[void, EventError]:
1318
+ let closed_sub = window.closed.subscribe(on_close)?
1319
+ let resized_sub = window.resized.subscribe(on_resize)?
1320
+
1321
+ defer window.closed.unsubscribe(closed_sub)
1322
+ defer window.resized.unsubscribe(resized_sub)
1323
+
1324
+ return Result[void, EventError].success()
1325
+ ```
1326
+
1327
+ ## 12. Linting
1328
+
1329
+ The linter checks for common issues and style problems without changing program behavior.
1330
+
1331
+ ### 12.1 Running the linter
1332
+
1333
+ ```sh
1334
+ mtc lint path/to/file.mt
1335
+ mtc lint src/ # lint all .mt files in a directory
1336
+ mtc lint --fix path/to/file.mt # apply auto-fixes in place
1337
+ mtc lint --select prefer-let,dead-assignment file.mt
1338
+ mtc lint --ignore shadow file.mt
1339
+ ```
1340
+
1341
+ ### 12.2 Rules
1342
+
1343
+ The auto-fix column corresponds to `mtc lint --fix`.
1344
+
1345
+ | Code | Severity | Auto-fix | Description |
1346
+ |---|---|---|---|
1347
+ | `borrow-and-mutate` | warning | — | Local is borrowed with `ref_of` or `ptr_of` and also mutated in the same scope |
1348
+ | `constant-condition` | warning | — | Branch or loop condition is provably always `true` or always `false` |
1349
+ | `dead-assignment` | warning | — | Assigned value is overwritten before any read |
1350
+ | `duplicate-if-condition` | warning | — | `if`/`else if` branch repeats a previous condition and is unreachable |
1351
+ | `directional-ffi-arg` | hint | — | Legacy `ptr_of` / `ref_of` / `out` call-site wrapper is redundant for directional FFI parameters |
1352
+ | `doc-tag` | hint | — | `##` documentation comment tag (`@param`, `@return`, `@throws`, `@see`) is invalid or inconsistent with the declaration |
1353
+ | `event-capacity` | warning | — | Event capacity may copy too many listeners to stack on emit |
1354
+ | `line-too-long` | warning | — | Source line exceeds configured maximum length |
1355
+ | `loop-single-iteration` | warning | — | Loop body always exits on the first iteration |
1356
+ | `missing-return` | error | — | Function with a non-void return type lacks a guaranteed return on all paths |
1357
+ | `noop-compound-assignment` | hint | — | Compound assignment uses an identity value and has no effect |
1358
+ | `platform-api-drift` | warning | — | Public API differs across sibling platform-specific variants of the same module |
1359
+ | `prefer-let` | hint | yes | `var` binding is never mutated; use `let` instead |
1360
+ | `prefer-let-else` | hint | yes | Nullable guard can be rewritten as `let ... else:` |
1361
+ | `prefer-var-else` | hint | yes | Nullable guard can be rewritten as `var ... else:` |
1362
+ | `redundant-bool-compare` | hint | yes | Comparing a boolean expression to `true`/`false` is redundant |
1363
+ | `redundant-else` | warning | yes | `else` block is unnecessary because all prior branches return |
1364
+ | `redundant-ignored-match-binding` | hint | yes | Ignored `as _` match binding is redundant |
1365
+ | `redundant-null-check` | hint | — | Null check on a value already known to be non-null by flow analysis |
1366
+ | `redundant-return` | hint | yes | Final bare `return` in a `void` function is unnecessary |
1367
+ | `reserved-primitive-name` | warning | yes | Binding uses a reserved built-in type name in its active namespace |
1368
+ | `self-assignment` | warning | — | Variable is assigned to itself |
1369
+ | `self-comparison` | warning | — | Value is compared to itself, making the condition constant |
1370
+ | `shadow` | warning | — | Local binding shadows an outer binding with the same name |
1371
+ | `trailing-list-comma` | hint | yes | Trailing comma in call argument list is redundant |
1372
+ | `unreachable-code` | warning | — | Code after a guaranteed terminator cannot execute |
1373
+ | `unused-import` | warning | yes | Import alias is never referenced |
1374
+ | `unused-local` | warning | — | Local binding is never referenced |
1375
+ | `unused-param` | warning | — | Parameter is never referenced |
1376
+ | `useless-expression` | warning | — | Expression statement has no side effects and its result is unused |
1377
+
1378
+ ### 12.3 Config file
1379
+
1380
+ Create a default config with:
1381
+
1382
+ ```sh
1383
+ mtc lint --init
1384
+ ```
1385
+
1386
+ Or place `.mt-lint.yml` in the project root (or any ancestor directory):
1387
+
1388
+ ```yaml
1389
+ max_line_length: 120
1390
+ select:
1391
+ - line-too-long
1392
+ - prefer-let
1393
+ - missing-return
1394
+ ignore:
1395
+ - shadow
1396
+ - useless-expression
1397
+ ```
1398
+
1399
+ When both `select` and `ignore` are present, `select` takes precedence and `ignore` is unused.
1400
+
1401
+ `max_line_length` defaults to `120` when omitted.
1402
+
1403
+ `line-too-long` code actions currently rewrite only parser-valid same-line comma-delimited `()` groups and type-position `[]` groups. Tuple literals may be rewritten without a trailing comma when the parser requires it.
1404
+
1405
+ ### 12.4 Per-line suppressions
1406
+
1407
+ ```mt
1408
+ var count = 0 # lint: ignore
1409
+ var total = 0 # lint: ignore(prefer-let, dead-assignment)
1410
+ ```
1411
+
1412
+ `# lint: ignore` silences all rules on that line. `# lint: ignore(rule1, rule2)` silences only the listed rules.
1413
+
1414
+ ## 13. Language Limitations
1415
+
1416
+ The compiler intentionally rejects the following patterns. These are design constraints, not missing features.
1417
+
1418
+ ### 13.1 Interface restrictions
1419
+
1420
+ - interface method signatures cannot be `async` or generic
1421
+ - interface conformance must be declared on the nominal type, not on an `extending` block
1422
+ - bare interface names are not runtime storage types — `Damageable` as a field, local, parameter, or return type — use `dyn[Interface]`
1423
+ - `dyn[Interface]` for a generic interface must be fully specified: `dyn[Mapper[int]]`, not `dyn[Mapper]`
1424
+ - a type implementing an interface must match method kinds, receiver editability, parameter types, return type, and asyncness exactly
1425
+
1426
+ ### 13.2 External and FFI restrictions
1427
+
1428
+ - `external function` cannot be generic, async, or array-taking / array-returning
1429
+ - `external function` cannot take `ref[...]` or `proc(...)` parameters
1430
+ - `external function` parameters cannot use `as`, `in`, `out`, or `inout`
1431
+ - `foreign function` cannot be async and cannot take `proc(...)` parameters
1432
+ - a `foreign function` with `consuming` parameter(s) must return `void`
1433
+ - consuming foreign calls must appear as top-level expression statements
1434
+ - external files have a restricted declaration surface: `const`, `type`, `struct`, `union`, `enum`, `flags`, `opaque`, and `external function` only
1435
+ - `public` is rejected in external files — declarations are implicitly exported
1436
+ - `import` statements in external files must appear before directives and declarations
1437
+
1438
+ ### 13.3 Event restrictions
1439
+
1440
+ - event payload cannot be `ref[T]` in v1; the payload type must be storable
1441
+ - event storage types cannot be returned from functions, passed through non-pointer/non-ref parameters, or copied into locals
1442
+ - `emit` is only callable from within the declaring module
1443
+ - event methods (`subscribe`, `subscribe_once`, `unsubscribe`, `emit`, `wait`) do not support named arguments
1444
+
1445
+ ### 13.4 ref[T] restrictions
1446
+
1447
+ - `ref[T]` values are rejected in constants, module variables, and nested local storage such as arrays or generic containers
1448
+ - functions cannot return `ref[T]` or a non-owning struct (one containing `ref` via auto-generated lifetime)
1449
+ - `ref[T]` values are not capturable by `proc`
1450
+ - `external function` cannot take `ref[...]` parameters
1451
+ - `ref[T]` is non-null and cannot be nullable
1452
+ - stored callable values (`fn(...)`, `proc(...)`) may use `ref[...]` only in direct callable parameter positions, not in return types
1453
+
1454
+ ### 13.5 Pointer and safety restrictions
1455
+
1456
+ - pointer indexing, raw pointer dereference, pointer arithmetic, and pointer casts require `unsafe`
1457
+ - `reinterpret[T]` requires `unsafe` and non-array concrete sized types
1458
+ - `ptr_of`, `const_ptr_of`, and `ref_of` require an addressable source expression
1459
+ - safe array indexing requires an addressable array value; `get(arr, i)` provides recoverable bounds-checked access
1460
+ - legacy call-site markers `in`, `out`, and `inout` are rejected; parameter modes are declared on `foreign function`
1461
+ - pointer comparison is not treated as boolean truthiness
1462
+
1463
+ ### 13.6 Type system restrictions
1464
+
1465
+ - conditions must be `bool`; integers and pointers have no implicit truthy or falsy coercion
1466
+ - mixed signed and unsigned integer arithmetic requires an explicit cast
1467
+ - enum and flags values do not implicitly coerce to their backing integer types outside external-call boundaries
1468
+ - `enum` backing types must be integer primitives; flags members must be compile-time integer constants
1469
+ - variant arm payloads cannot use `ref[T]` in v1
1470
+ - `null` must be used instead of `zero[ptr[T]]` in typed nullable pointer-like contexts
1471
+
1472
+ ### 13.7 Operator and expression restrictions
1473
+
1474
+ - `+` does not support `str`/`cstr` concatenation
1475
+ - `==` and `!=` are not supported on struct types; use `equal[T]`
1476
+ - range expressions are restricted to `for`-loop iterables and range-index assignment targets
1477
+ - functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
1478
+ - `read(...)` of a raw pointer requires `unsafe`
1479
+
1480
+ ### 13.8 Control flow restrictions
1481
+
1482
+ - `break` and `continue` must be inside loops
1483
+ - `return` is not allowed inside `defer` blocks
1484
+ - `match` on enum/variant must be exhaustive unless `_` is present; `match` on integer or `str` requires `_`
1485
+ - `let ... else:` and `var ... else:` require `T?`, `Option[T]`, or `Result[T, E]`; the `else` block must terminate control flow
1486
+ - `?` propagation is only allowed inside function and proc bodies, not in `defer` blocks
1487
+ - `await` is only allowed inside async functions
1488
+
1489
+ ### 13.9 Declaration restrictions
1490
+
1491
+ - `main` cannot be generic
1492
+ - `async main` pre-lift return type must be `int` or `void`
1493
+ - `public` is rejected on `extending` blocks, ordinary `external` declarations, `static_assert`, and in external files
1494
+ - `public` on struct members is only allowed on event declarations, not on fields
1495
+ - explicit C names are only allowed on external structs and unions
1496
+ - `proc(...)` values that capture local state are rejected in constants and module variables; capture-free procs (body only references module-level functions, constants, and types) are allowed in module variables
1497
+ - generic interfaces require type arguments when used with `dyn[Interface]`
1498
+
1499
+ ### 13.10 Format string restrictions
1500
+
1501
+ - `:.N` precision is only valid on `float` and `double` interpolations
1502
+ - `:x`/`:X`, `:o`/`:O`, and `:b`/`:B` are only valid on integer primitives and integer-backed enums/flags
1503
+
1504
+ ### 13.11 Attribute restrictions
1505
+
1506
+ - user-defined `attribute` declarations target `struct`, `field`, `callable`, `const`, `event`, `enum`, `flags`, `union`, or `variant`
1507
+ - `@[...]` attribute applications are only accepted on the above declaration kinds
1508
+ - attribute declarations are not allowed in external files
1509
+ - only built-in `packed` and `align(...)` struct attributes are allowed in external files
1510
+
1511
+ ## 14. Example
1512
+
1513
+ ```mt
1514
+ import std.fmt as fmt
1515
+
1516
+ struct Counter:
1517
+ value: int
1518
+
1519
+ extending Counter:
1520
+ editable function bump() -> void:
1521
+ this.value += 1
1522
+
1523
+ function read() -> int:
1524
+ return this.value
1525
+
1526
+ function main() -> int:
1527
+ var c = Counter(value = 0)
1528
+
1529
+ for i in 0..3:
1530
+ c.bump()
1531
+
1532
+ let text = f"count=#{c.read()}"
1533
+ return 0
1534
+ ```