semantifold 0.3.0 → 0.4.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 (297) hide show
  1. package/README.md +58 -15
  2. package/build/index.d.ts +48 -2
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +57 -8
  5. package/build/src/artifact-path.d.ts +23 -1
  6. package/build/src/artifact-path.d.ts.map +1 -1
  7. package/build/src/artifact-path.js +45 -3
  8. package/build/src/artifacts.d.ts.map +1 -1
  9. package/build/src/artifacts.js +66 -1
  10. package/build/src/backends/c-validation.d.ts.map +1 -1
  11. package/build/src/backends/c-validation.js +7 -3
  12. package/build/src/backends/cpp-validation.d.ts.map +1 -1
  13. package/build/src/backends/cpp-validation.js +7 -3
  14. package/build/src/backends/csharp.js +7 -1
  15. package/build/src/backends/dart-runtime.d.ts +2 -0
  16. package/build/src/backends/dart-runtime.d.ts.map +1 -0
  17. package/build/src/backends/dart-runtime.js +41 -0
  18. package/build/src/backends/dart.d.ts +20 -0
  19. package/build/src/backends/dart.d.ts.map +1 -0
  20. package/build/src/backends/dart.js +321 -0
  21. package/build/src/backends/effects.d.ts +60 -0
  22. package/build/src/backends/effects.d.ts.map +1 -0
  23. package/build/src/backends/effects.js +746 -0
  24. package/build/src/backends/go.js +18 -4
  25. package/build/src/backends/identifiers.d.ts +36 -4
  26. package/build/src/backends/identifiers.d.ts.map +1 -1
  27. package/build/src/backends/identifiers.js +154 -10
  28. package/build/src/backends/index.d.ts.map +1 -1
  29. package/build/src/backends/index.js +27 -4
  30. package/build/src/backends/ios.d.ts +68 -0
  31. package/build/src/backends/ios.d.ts.map +1 -0
  32. package/build/src/backends/ios.js +1503 -0
  33. package/build/src/backends/java.d.ts.map +1 -1
  34. package/build/src/backends/java.js +429 -36
  35. package/build/src/backends/javascript.d.ts +6 -0
  36. package/build/src/backends/javascript.d.ts.map +1 -1
  37. package/build/src/backends/javascript.js +380 -24
  38. package/build/src/backends/kotlin-runtime.d.ts +3 -0
  39. package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
  40. package/build/src/backends/kotlin-runtime.js +29 -0
  41. package/build/src/backends/kotlin.d.ts +23 -0
  42. package/build/src/backends/kotlin.d.ts.map +1 -0
  43. package/build/src/backends/kotlin.js +136 -0
  44. package/build/src/backends/native-validation.d.ts +2 -2
  45. package/build/src/backends/native-validation.d.ts.map +1 -1
  46. package/build/src/backends/native-validation.js +2 -2
  47. package/build/src/backends/ordered-expressions.d.ts.map +1 -1
  48. package/build/src/backends/ordered-expressions.js +39 -7
  49. package/build/src/backends/php-stdlib-support.d.ts +19 -0
  50. package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
  51. package/build/src/backends/php-stdlib-support.js +227 -0
  52. package/build/src/backends/php.d.ts.map +1 -1
  53. package/build/src/backends/php.js +448 -33
  54. package/build/src/backends/program.d.ts +81 -0
  55. package/build/src/backends/program.d.ts.map +1 -0
  56. package/build/src/backends/program.js +1308 -0
  57. package/build/src/backends/python.js +7 -1
  58. package/build/src/backends/ruby.d.ts.map +1 -1
  59. package/build/src/backends/ruby.js +310 -24
  60. package/build/src/backends/rust-runtime.d.ts +7 -0
  61. package/build/src/backends/rust-runtime.d.ts.map +1 -0
  62. package/build/src/backends/rust-runtime.js +32 -0
  63. package/build/src/backends/rust-validation.d.ts +9 -0
  64. package/build/src/backends/rust-validation.d.ts.map +1 -0
  65. package/build/src/backends/rust-validation.js +78 -0
  66. package/build/src/backends/rust.d.ts +24 -0
  67. package/build/src/backends/rust.d.ts.map +1 -0
  68. package/build/src/backends/rust.js +344 -0
  69. package/build/src/backends/scalars.d.ts +14 -6
  70. package/build/src/backends/scalars.d.ts.map +1 -1
  71. package/build/src/backends/scalars.js +167 -17
  72. package/build/src/backends/shared.d.ts +23 -4
  73. package/build/src/backends/shared.d.ts.map +1 -1
  74. package/build/src/backends/shared.js +2158 -62
  75. package/build/src/backends/stdlib-support.d.ts +14 -0
  76. package/build/src/backends/stdlib-support.d.ts.map +1 -0
  77. package/build/src/backends/stdlib-support.js +237 -0
  78. package/build/src/backends/swift-runtime.d.ts +3 -0
  79. package/build/src/backends/swift-runtime.d.ts.map +1 -0
  80. package/build/src/backends/swift-runtime.js +14 -0
  81. package/build/src/backends/swift.d.ts +8 -0
  82. package/build/src/backends/swift.d.ts.map +1 -0
  83. package/build/src/backends/swift.js +127 -0
  84. package/build/src/backends/typescript.d.ts.map +1 -1
  85. package/build/src/backends/typescript.js +344 -24
  86. package/build/src/backends/wasm.d.ts +43 -0
  87. package/build/src/backends/wasm.d.ts.map +1 -0
  88. package/build/src/backends/wasm.js +2164 -0
  89. package/build/src/backends/writer.d.ts +235 -4
  90. package/build/src/backends/writer.d.ts.map +1 -1
  91. package/build/src/backends/writer.js +468 -3
  92. package/build/src/diagnostic.d.ts +4 -3
  93. package/build/src/diagnostic.d.ts.map +1 -1
  94. package/build/src/diagnostic.js +5 -3
  95. package/build/src/frontends/c.d.ts.map +1 -1
  96. package/build/src/frontends/c.js +5 -3
  97. package/build/src/frontends/cpp.d.ts.map +1 -1
  98. package/build/src/frontends/cpp.js +5 -3
  99. package/build/src/frontends/csharp.d.ts.map +1 -1
  100. package/build/src/frontends/csharp.js +7 -3
  101. package/build/src/frontends/dart.d.ts +10 -0
  102. package/build/src/frontends/dart.d.ts.map +1 -0
  103. package/build/src/frontends/dart.js +913 -0
  104. package/build/src/frontends/index.d.ts +3 -1
  105. package/build/src/frontends/index.d.ts.map +1 -1
  106. package/build/src/frontends/index.js +15 -5
  107. package/build/src/frontends/java.d.ts +110 -1
  108. package/build/src/frontends/java.d.ts.map +1 -1
  109. package/build/src/frontends/java.js +1725 -70
  110. package/build/src/frontends/javascript-typescript.d.ts +69 -1
  111. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  112. package/build/src/frontends/javascript-typescript.js +1825 -93
  113. package/build/src/frontends/kotlin.d.ts +10 -0
  114. package/build/src/frontends/kotlin.d.ts.map +1 -0
  115. package/build/src/frontends/kotlin.js +685 -0
  116. package/build/src/frontends/php.d.ts +118 -1
  117. package/build/src/frontends/php.d.ts.map +1 -1
  118. package/build/src/frontends/php.js +1583 -76
  119. package/build/src/frontends/program.d.ts +74 -0
  120. package/build/src/frontends/program.d.ts.map +1 -0
  121. package/build/src/frontends/program.js +989 -0
  122. package/build/src/frontends/ruby.d.ts +68 -1
  123. package/build/src/frontends/ruby.d.ts.map +1 -1
  124. package/build/src/frontends/ruby.js +1446 -101
  125. package/build/src/frontends/rust-parser.d.ts +11 -0
  126. package/build/src/frontends/rust-parser.d.ts.map +1 -0
  127. package/build/src/frontends/rust-parser.js +15 -0
  128. package/build/src/frontends/rust.d.ts +14 -0
  129. package/build/src/frontends/rust.d.ts.map +1 -0
  130. package/build/src/frontends/rust.js +715 -0
  131. package/build/src/frontends/scalars.d.ts +10 -0
  132. package/build/src/frontends/scalars.d.ts.map +1 -1
  133. package/build/src/frontends/scalars.js +28 -2
  134. package/build/src/frontends/swift.d.ts +10 -0
  135. package/build/src/frontends/swift.d.ts.map +1 -0
  136. package/build/src/frontends/swift.js +860 -0
  137. package/build/src/frontends/types.d.ts +186 -0
  138. package/build/src/frontends/types.d.ts.map +1 -0
  139. package/build/src/frontends/types.js +634 -0
  140. package/build/src/language-registry.d.ts +46 -4
  141. package/build/src/language-registry.d.ts.map +1 -1
  142. package/build/src/language-registry.js +167 -13
  143. package/build/src/mapping.d.ts +2 -2
  144. package/build/src/mapping.d.ts.map +1 -1
  145. package/build/src/mapping.js +31 -10
  146. package/build/src/semantic/capabilities.d.ts +66 -0
  147. package/build/src/semantic/capabilities.d.ts.map +1 -0
  148. package/build/src/semantic/capabilities.js +649 -0
  149. package/build/src/semantic/effects.d.ts +26 -0
  150. package/build/src/semantic/effects.d.ts.map +1 -0
  151. package/build/src/semantic/effects.js +764 -0
  152. package/build/src/semantic/generics.d.ts +23 -0
  153. package/build/src/semantic/generics.d.ts.map +1 -0
  154. package/build/src/semantic/generics.js +61 -0
  155. package/build/src/semantic/lifetime.d.ts +37 -0
  156. package/build/src/semantic/lifetime.d.ts.map +1 -0
  157. package/build/src/semantic/lifetime.js +527 -0
  158. package/build/src/semantic/provenance.d.ts +9 -2
  159. package/build/src/semantic/provenance.d.ts.map +1 -1
  160. package/build/src/semantic/provenance.js +462 -20
  161. package/build/src/semantic/scalars.d.ts +11 -0
  162. package/build/src/semantic/scalars.d.ts.map +1 -1
  163. package/build/src/semantic/scalars.js +16 -1
  164. package/build/src/semantic/stdlib.d.ts +254 -0
  165. package/build/src/semantic/stdlib.d.ts.map +1 -0
  166. package/build/src/semantic/stdlib.js +880 -0
  167. package/build/src/semantic/types.d.ts +2952 -202
  168. package/build/src/semantic/types.d.ts.map +1 -1
  169. package/build/src/semantic/types.js +688 -15
  170. package/build/src/semantic/validate.d.ts +85 -5
  171. package/build/src/semantic/validate.d.ts.map +1 -1
  172. package/build/src/semantic/validate.js +2576 -98
  173. package/build/src/stdlib-facades.d.ts +304 -0
  174. package/build/src/stdlib-facades.d.ts.map +1 -0
  175. package/build/src/stdlib-facades.js +1167 -0
  176. package/build/src/stdlib-negotiation.d.ts +69 -0
  177. package/build/src/stdlib-negotiation.d.ts.map +1 -0
  178. package/build/src/stdlib-negotiation.js +403 -0
  179. package/build/src/stdlib-providers.d.ts +72 -0
  180. package/build/src/stdlib-providers.d.ts.map +1 -0
  181. package/build/src/stdlib-providers.js +339 -0
  182. package/build/src/toolchains.d.ts +55 -1
  183. package/build/src/toolchains.d.ts.map +1 -1
  184. package/build/src/toolchains.js +15 -4
  185. package/docs/architecture.md +45 -13
  186. package/docs/dart.md +60 -0
  187. package/docs/goals.md +3 -3
  188. package/docs/ios.md +121 -0
  189. package/docs/kotlin.md +42 -0
  190. package/docs/language-support.md +231 -26
  191. package/docs/multi-file-modules.md +52 -0
  192. package/docs/parser-qualification.md +121 -8
  193. package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
  194. package/docs/rust.md +88 -0
  195. package/docs/source-maps.md +15 -7
  196. package/docs/standard-library-portability.md +39 -8
  197. package/docs/swift.md +44 -0
  198. package/docs/testing.md +366 -4
  199. package/docs/webassembly.md +52 -0
  200. package/index.js +63 -8
  201. package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
  202. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
  203. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
  204. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
  205. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
  206. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
  207. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
  208. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
  209. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
  210. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
  211. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
  212. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
  213. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
  214. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
  215. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
  216. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
  217. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
  218. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
  219. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
  220. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
  221. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
  222. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
  223. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
  224. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
  225. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
  226. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
  227. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
  228. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
  229. package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
  230. package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
  231. package/package.json +5 -2
  232. package/src/artifact-path.js +47 -2
  233. package/src/artifacts.js +67 -0
  234. package/src/backends/c-validation.js +6 -1
  235. package/src/backends/cpp-validation.js +6 -1
  236. package/src/backends/csharp.js +3 -0
  237. package/src/backends/dart-runtime.js +41 -0
  238. package/src/backends/dart.js +326 -0
  239. package/src/backends/effects.js +705 -0
  240. package/src/backends/go.js +14 -2
  241. package/src/backends/identifiers.js +160 -9
  242. package/src/backends/index.js +27 -2
  243. package/src/backends/ios.js +1612 -0
  244. package/src/backends/java.js +454 -36
  245. package/src/backends/javascript.js +389 -23
  246. package/src/backends/kotlin-runtime.js +29 -0
  247. package/src/backends/kotlin.js +141 -0
  248. package/src/backends/native-validation.js +1 -1
  249. package/src/backends/ordered-expressions.js +40 -6
  250. package/src/backends/php-stdlib-support.js +239 -0
  251. package/src/backends/php.js +469 -32
  252. package/src/backends/program.js +1407 -0
  253. package/src/backends/python.js +3 -0
  254. package/src/backends/ruby.js +326 -23
  255. package/src/backends/rust-runtime.js +34 -0
  256. package/src/backends/rust-validation.js +75 -0
  257. package/src/backends/rust.js +335 -0
  258. package/src/backends/scalars.js +141 -16
  259. package/src/backends/shared.js +2304 -124
  260. package/src/backends/stdlib-support.js +234 -0
  261. package/src/backends/swift-runtime.js +14 -0
  262. package/src/backends/swift.js +132 -0
  263. package/src/backends/typescript.js +349 -23
  264. package/src/backends/wasm.js +2208 -0
  265. package/src/backends/writer.js +520 -2
  266. package/src/diagnostic.js +4 -2
  267. package/src/frontends/c.js +4 -2
  268. package/src/frontends/cpp.js +4 -2
  269. package/src/frontends/csharp.js +4 -2
  270. package/src/frontends/dart.js +963 -0
  271. package/src/frontends/index.js +14 -4
  272. package/src/frontends/java.js +1977 -69
  273. package/src/frontends/javascript-typescript.js +2046 -102
  274. package/src/frontends/kotlin.js +713 -0
  275. package/src/frontends/php.js +1725 -79
  276. package/src/frontends/program.js +1062 -0
  277. package/src/frontends/ruby.js +1624 -98
  278. package/src/frontends/rust-parser.js +18 -0
  279. package/src/frontends/rust.js +714 -0
  280. package/src/frontends/scalars.js +31 -1
  281. package/src/frontends/swift.js +893 -0
  282. package/src/frontends/types.js +750 -0
  283. package/src/language-registry.js +172 -12
  284. package/src/mapping.js +33 -9
  285. package/src/semantic/capabilities.js +671 -0
  286. package/src/semantic/effects.js +737 -0
  287. package/src/semantic/generics.js +58 -0
  288. package/src/semantic/lifetime.js +517 -0
  289. package/src/semantic/provenance.js +412 -19
  290. package/src/semantic/scalars.js +17 -0
  291. package/src/semantic/stdlib.js +921 -0
  292. package/src/semantic/types.js +765 -14
  293. package/src/semantic/validate.js +2934 -209
  294. package/src/stdlib-facades.js +1211 -0
  295. package/src/stdlib-negotiation.js +446 -0
  296. package/src/stdlib-providers.js +377 -0
  297. package/src/toolchains.js +16 -3
@@ -1,21 +1,46 @@
1
1
  // @ts-check
2
2
 
3
3
  import {semanticFailure, unsupportedCapability, unsupportedSyntax} from "../diagnostic.js"
4
- import {hasOnlyUnicodeScalars, isScalarTypeName} from "./scalars.js"
4
+ import {recordTypeSubstitutions, substituteValueType, typeContainsAnyVariable} from "./generics.js"
5
+ import {hasOnlyUnicodeScalars, isScalarTypeName, scalarType} from "./scalars.js"
5
6
  import {adaptedOperationFor} from "./operators.js"
7
+ import {parserRangeFor} from "./provenance.js"
8
+ import {validateEffectGraph} from "./effects.js"
9
+ import {validateResourceLifetimes} from "./lifetime.js"
10
+
11
+ const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "dart"])
12
+
13
+ /** @typedef {Map<string, import("./types.js").RecordDeclaration>} RecordRegistry */
14
+ /** @typedef {Map<string, import("./types.js").ClassDeclaration>} ClassRegistry */
15
+ /** @typedef {Map<string, import("./types.js").ErrorDeclaration>} ErrorRegistry */
16
+ /** @type {WeakMap<RecordRegistry, ClassRegistry>} */
17
+ const referenceClassesByRecordRegistry = new WeakMap()
18
+ /** @type {WeakMap<RecordRegistry, Set<string>>} */
19
+ const resourceIdsByRecordRegistry = new WeakMap()
6
20
 
7
21
  /**
8
22
  * @typedef Binding
9
23
  * @property {boolean} mutable - Whether assignment is allowed.
10
- * @property {import("./types.js").SemanticTypeName} type - Binding type.
24
+ * @property {import("./types.js").SemanticBindingType} type - Binding type.
25
+ * @property {import("./types.js").Expression | undefined} knownValue - Current statically known immutable value.
11
26
  */
12
27
 
13
28
  /**
14
29
  * @typedef Scope
15
30
  * @property {Map<string, Binding>} bindings - Bindings declared directly in this scope.
16
31
  * @property {Set<string>} pending - Names declared later in this scope.
32
+ * @property {Set<Binding>} presenceProofs - Optional bindings proven present on this path.
17
33
  * @property {Scope | undefined} parent - Enclosing lexical scope.
34
+ * @property {Set<string>} typeParameters - Type parameters visible throughout the enclosing declaration.
18
35
  * @property {Set<string>} usedNames - All names used by the enclosing function or entry point.
36
+ * @property {import("./types.js").ClassDeclaration | undefined} currentClass - Declaring class for receiver-private access.
37
+ */
38
+
39
+ /**
40
+ * @typedef LoopFlowContext
41
+ * @property {import("./types.js").ForEachStatement | import("./types.js").ForEachMapStatement | import("./types.js").WhileStatement} [nearest] - Nearest active loop.
42
+ * @property {object} owner - Enclosing function or entry-point identity.
43
+ * @property {Map<string, object>} owners - Loop owner by module-local loop identity.
19
44
  */
20
45
 
21
46
  /**
@@ -27,34 +52,92 @@ import {adaptedOperationFor} from "./operators.js"
27
52
  * Enforces the coherent release-candidate semantic subset after adaptation.
28
53
  * @param {import("./types.js").SemanticModule} module - Adapted semantic module.
29
54
  * @param {import("./types.js").SemanticLanguage} language - Source language.
55
+ * @param {{classes?: Map<string, import("./types.js").ClassDeclaration>, functions?: Map<string, import("./types.js").FunctionDeclaration>, records?: Map<string, import("./types.js").RecordDeclaration>, errors?: Map<string, import("./types.js").ErrorDeclaration>, callEffects?: Map<string, Set<string>>}} [visible] - Program imports visible during validation.
30
56
  * @returns {import("./types.js").SemanticModule} Validated module.
31
57
  */
32
- export function validateParsedModule(module, language) {
33
- validateModuleShape(module, (detail, location) => unsupportedSyntax(language, detail, location))
34
- validateModuleTypes(module, (code, detail, location) => semanticFailure(language, code, detail, location), true)
58
+ export function validateParsedModule(module, language, visible = {}) {
59
+ normalizeParsedOwnedReferences(module, visible.classes)
60
+ validateModuleShape(module, language, (detail, location) => unsupportedSyntax(language, detail, location))
61
+ /**
62
+ * Converts one parser failure into a located semantic diagnostic.
63
+ * @type {SemanticFail}
64
+ */
65
+ const fail = (code, detail, location) => semanticFailure(language, code, detail, location)
66
+
67
+ validateModuleTypes(module, language, fail, true, visible)
68
+ validateResourceLifetimes(module, inferEscapingErrorEffects(module, visible.callEffects), fail)
35
69
 
36
70
  return module
37
71
  }
38
72
 
73
+ /**
74
+ * Promotes parser-authored class spellings after all private field types are known.
75
+ * @param {import("./types.js").SemanticModule} module - Parser-authored module.
76
+ * @param {Map<string, import("./types.js").ClassDeclaration>} [visibleClasses] - Imported reference classes.
77
+ * @returns {void}
78
+ */
79
+ function normalizeParsedOwnedReferences(module, visibleClasses = new Map()) {
80
+ const ownedClassIds = new Set([...visibleClasses.values()].filter(({ownership}) => ownership?.kind == "ownedResource")
81
+ .map(({id}) => /** @type {string} */ (id)))
82
+
83
+ for (const declaration of module.classes ?? []) {
84
+ const fields = declaration.fields.filter(({type}) => type?.kind == "OwnedResourceType")
85
+ if (fields.length != 1) continue
86
+ const field = /** @type {import("./types.js").PrivateField & {type: import("./types.js").OwnedResourceType}} */ (fields[0])
87
+
88
+ declaration.ownership = {fieldId: /** @type {string} */ (field.id), kind: "ownedResource", resourceId: field.type.resourceId}
89
+ ownedClassIds.add(/** @type {string} */ (declaration.id))
90
+ }
91
+ /** @type {unknown[]} */
92
+ const pending = [module]
93
+ /** @type {Set<object>} */
94
+ const seen = new Set()
95
+
96
+ while (pending.length > 0) {
97
+ const value = pending.pop()
98
+ if (!value || typeof value != "object" || seen.has(value)) continue
99
+ seen.add(value)
100
+ if (!Array.isArray(value) && Reflect.get(value, "kind") == "ReferenceType" &&
101
+ ownedClassIds.has(Reflect.get(value, "declarationId"))) Reflect.set(value, "kind", "OwnedReferenceType")
102
+ for (const [key, child] of Object.entries(value)) {
103
+ if (["location", "sourceProvenance"].includes(key)) continue
104
+ if (child && typeof child == "object") pending.push(child)
105
+ }
106
+ }
107
+ }
108
+
39
109
  /**
40
110
  * Validates scalar types and bindings for a caller-supplied semantic module before emission.
41
111
  * @param {import("./types.js").SemanticModule} module - Semantic module.
42
- * @param {import("./types.js").SemanticLanguage} language - Backend language.
112
+ * @param {import("./types.js").BackendLanguage} language - Backend language or binary target.
113
+ * @param {{classes?: Map<string, import("./types.js").ClassDeclaration>, functions?: Map<string, import("./types.js").FunctionDeclaration>, records?: Map<string, import("./types.js").RecordDeclaration>, errors?: Map<string, import("./types.js").ErrorDeclaration>, callEffects?: Map<string, Set<string>>}} [visible] - Resolved program imports.
43
114
  * @returns {void}
44
115
  */
45
- export function validateBackendTypes(module, language) {
46
- validateModuleTypes(module, (_code, detail, location) => unsupportedCapability(language, detail, location), false)
116
+ export function validateBackendTypes(module, language, visible = {}) {
117
+ /**
118
+ * Converts one backend validation failure into an unsupported-capability diagnostic.
119
+ * @type {SemanticFail}
120
+ */
121
+ const fail = (_code, detail, location) => unsupportedCapability(language, detail, location)
122
+
123
+ validateModuleTypes(module, language, fail, false, visible)
124
+ validateResourceLifetimes(module, inferEscapingErrorEffects(module, visible.callEffects), fail)
47
125
  }
48
126
 
49
127
  /**
50
128
  * Checks parser-authored block and statement layouts before semantic validation.
51
129
  * @param {import("./types.js").SemanticModule} module - Semantic module.
130
+ * @param {import("./types.js").SemanticLanguage} language - Source language.
52
131
  * @param {(detail: string, location: import("./types.js").SourceLocation) => never} fail - Shape failure.
53
132
  * @returns {void}
54
133
  */
55
- function validateModuleShape(module, fail) {
134
+ function validateModuleShape(module, language, fail) {
135
+ for (const declaration of module.classes ?? []) {
136
+ validateBlockShape(declaration.constructor?.body, "constructor body", fail)
137
+ for (const method of declaration.methods ?? []) validateBlockShape(method.body, "method body", fail)
138
+ }
56
139
  for (const functionDeclaration of module.functions) {
57
- if (functionDeclaration.parameters.length != 2) {
140
+ if (!task005Languages.has(language) && functionDeclaration.parameters.length != 2) {
58
141
  fail("function parameter count other than two", functionDeclaration.location)
59
142
  }
60
143
 
@@ -77,312 +160,2954 @@ function validateBlockShape(block, detail, fail) {
77
160
  }
78
161
 
79
162
  for (const statement of block.statements) {
80
- if (!["AssignmentStatement", "IfStatement", "LocalDeclaration", "PrintStatement", "ReturnStatement"].includes(statement.kind)) {
163
+ if (!["AssignmentStatement", "BreakStatement", "ContinueStatement", "ExpressionStatement", "ForEachMapStatement", "ForEachStatement", "IfStatement", "LocalDeclaration", "PrivateFieldWriteStatement", "PrintStatement", "RaiseStatement", "ReturnStatement", "TryStatement", "WhileStatement"].includes(statement.kind)) {
81
164
  fail(`${detail} statement ${statement.kind}`, statement.location)
82
165
  }
83
166
  if (statement.kind == "IfStatement") {
84
167
  validateBlockShape(statement.consequent, "if consequent", fail)
85
168
  if (statement.alternate) validateBlockShape(statement.alternate, "if alternate", fail)
86
169
  }
170
+ if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement" || statement.kind == "WhileStatement") {
171
+ validateBlockShape(statement.body, "loop body", fail)
172
+ }
173
+ if (statement.kind == "TryStatement") {
174
+ validateBlockShape(statement.body, "try body", fail)
175
+ validateBlockShape(statement.catchBody, "catch body", fail)
176
+ }
87
177
  }
88
178
  }
89
179
 
90
180
  /**
91
181
  * Validates declarations and expression types within lexical scopes.
92
182
  * @param {import("./types.js").SemanticModule} module - Semantic module.
183
+ * @param {string} language - Source or backend language identity.
93
184
  * @param {SemanticFail} fail - Diagnostic callback.
94
185
  * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
186
+ * @param {{classes?: Map<string, import("./types.js").ClassDeclaration>, functions?: Map<string, import("./types.js").FunctionDeclaration>, records?: Map<string, import("./types.js").RecordDeclaration>, errors?: Map<string, import("./types.js").ErrorDeclaration>, callEffects?: Map<string, Set<string>>}} [visible] - Program imports visible during validation.
95
187
  * @returns {void}
96
188
  */
97
- function validateModuleTypes(module, fail, normalizeOperations) {
189
+ function validateModuleTypes(module, language, fail, normalizeOperations, visible = {}) {
190
+ const classes = registerClassDeclarations(module.classes ?? [], fail, normalizeOperations, visible.classes)
191
+ const records = validateRecordDeclarations(module.records ?? [], fail, normalizeOperations, visible.records, classes)
192
+ const errors = validateErrorDeclarations(module.errors ?? [], fail, normalizeOperations, visible.errors)
98
193
  /** @type {Map<string, import("./types.js").FunctionDeclaration>} */
99
- const functions = new Map()
100
-
101
- for (const functionDeclaration of module.functions) {
194
+ const functions = new Map(visible.functions ?? [])
195
+ const declarationIds = new Set([...functions.values()].flatMap(({id}) => typeof id == "string" ? [id] : []))
196
+
197
+ for (const [index, functionDeclaration] of module.functions.entries()) {
198
+ if (normalizeOperations) functionDeclaration.id = `function:${index}`
199
+ if (typeof functionDeclaration.id != "string" || !/^(?:[a-z][a-z0-9._-]*#)?function:[0-9]+$/u.test(functionDeclaration.id) ||
200
+ declarationIds.has(functionDeclaration.id)) {
201
+ fail("DUPLICATE_BINDING", "Duplicate or invalid function declaration identity.", functionDeclaration.location)
202
+ }
102
203
  if (functions.has(functionDeclaration.name)) {
103
- fail("DUPLICATE_BINDING", `duplicate function '${functionDeclaration.name}'.`, functionDeclaration.location)
204
+ fail("DUPLICATE_BINDING", `duplicate function '${functionDeclaration.name}'.`, roleLocation(functionDeclaration, "name"))
104
205
  }
206
+ declarationIds.add(functionDeclaration.id)
207
+ validateTypeParameters(functionDeclaration, fail, normalizeOperations)
105
208
  functions.set(functionDeclaration.name, functionDeclaration)
106
209
  }
107
210
 
108
- for (const functionDeclaration of module.functions) validateFunction(functionDeclaration, functions, fail, normalizeOperations)
211
+ validateEffectGraph(module, language, functions, fail, normalizeOperations, classes)
212
+ const capabilityResources = (module.capabilities ?? []).flatMap(({resources}) => resources)
213
+ const capabilityFailures = (module.capabilities ?? []).flatMap(({failures}) => failures)
214
+
215
+ resourceIdsByRecordRegistry.set(records, new Set(capabilityResources.map(({id}) => id)))
216
+ const existingErrorNames = new Set([...errors.values()].map(({name}) => name))
217
+ for (const failure of capabilityFailures) {
218
+ if (errors.has(failure.id) || existingErrorNames.has(failure.name)) {
219
+ fail("DUPLICATE_ERROR", `Duplicate capability failure '${failure.name}'.`, module.location)
220
+ }
221
+ errors.set(failure.id, /** @type {import("./types.js").ErrorDeclaration} */ (/** @type {unknown} */ (failure)))
222
+ existingErrorNames.add(failure.name)
223
+ }
224
+
225
+ const loopOwners = prepareLoopIdentities(module, normalizeOperations, fail)
226
+
227
+ const reservedValueNames = new Set([
228
+ ...functions.keys(),
229
+ ...[...records.values()].map((declaration) => declaration.name),
230
+ ...[...classes.values()].map((declaration) => declaration.name),
231
+ ...[...errors.values()].map((declaration) => declaration.name),
232
+ ...capabilityResources.map(({name}) => name)
233
+ ])
234
+
235
+ const nominalNames = new Set()
236
+
237
+ for (const declaration of [...module.records ?? [], ...module.classes ?? [], ...module.errors ?? []]) {
238
+ if (nominalNames.has(declaration.name)) {
239
+ fail("DUPLICATE_BINDING", `Duplicate nominal declaration '${declaration.name}'.`, roleLocation(declaration, "name"))
240
+ }
241
+ nominalNames.add(declaration.name)
242
+ }
243
+ for (const declaration of [...capabilityResources, ...capabilityFailures]) {
244
+ if (nominalNames.has(declaration.name)) {
245
+ fail("DUPLICATE_BINDING", `Duplicate nominal declaration '${declaration.name}'.`, module.location)
246
+ }
247
+ nominalNames.add(declaration.name)
248
+ }
249
+ const localFunctionNames = new Set(module.functions.map(({name}) => name))
250
+ const callEffects = inferCallEffects(functions, visible.callEffects, localFunctionNames)
251
+
252
+ for (const declaration of module.classes ?? []) {
253
+ validateClassDeclaration(declaration, functions, records, errors, callEffects, reservedValueNames, fail,
254
+ normalizeOperations, loopOwners)
255
+ }
109
256
 
110
- const callableNames = new Set(functions.keys())
257
+ for (const functionDeclaration of module.functions) {
258
+ validateFunction(functionDeclaration, functions, records, errors, callEffects, reservedValueNames, fail, normalizeOperations, loopOwners)
259
+ }
111
260
 
112
- const entryScope = createScope(undefined, module.entryPoint.body.statements, callableNames)
261
+ const entryScope = createScope(undefined, module.entryPoint.body.statements, reservedValueNames)
113
262
 
114
- validateBlock(module.entryPoint.body, entryScope, undefined, functions, fail, normalizeOperations)
263
+ validateBlock(module.entryPoint.body, entryScope, undefined, functions, records, errors, callEffects, fail,
264
+ normalizeOperations, {owner: module.entryPoint, owners: loopOwners})
265
+ if (normalizeOperations) validateEffectGraph(module, language, functions, fail, true, classes)
115
266
  }
116
267
 
117
268
  /**
118
- * Validates one non-void function and its complete control flow.
119
- * @param {import("./types.js").FunctionDeclaration} declaration - Function declaration.
120
- * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Function signatures.
269
+ * Assigns parser-authored loop/control identities and validates caller-owned identities without crossing a function boundary.
270
+ * @param {import("./types.js").SemanticModule} module - Semantic module.
271
+ * @param {boolean} normalizeOperations - Whether parser-authored identities are assigned.
121
272
  * @param {SemanticFail} fail - Diagnostic callback.
122
- * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
123
- * @returns {void}
273
+ * @returns {Map<string, object>} Loop owner by identity.
124
274
  */
125
- function validateFunction(declaration, functions, fail, normalizeOperations) {
126
- const scope = createScope(undefined, declaration.body.statements, new Set(functions.keys()))
127
-
128
- for (const parameter of declaration.parameters) {
129
- const type = validateTypeReference(parameter.type, parameter.location, fail)
130
-
131
- declareBinding(parameter.name, {mutable: false, type}, parameter.location, scope, fail)
275
+ function prepareLoopIdentities(module, normalizeOperations, fail) {
276
+ const owners = new Map()
277
+ let nextLoop = 0
278
+
279
+ const classOwners = (module.classes ?? []).flatMap((declaration) => [declaration.constructor, ...declaration.methods])
280
+
281
+ for (const owner of [...classOwners, ...module.functions, module.entryPoint]) visit(owner.body, owner, [])
282
+
283
+ return owners
284
+
285
+ /**
286
+ * Visits one block in lexical semantic order.
287
+ * @param {import("./types.js").Block} block - Candidate block.
288
+ * @param {object} owner - Enclosing function or entry point.
289
+ * @param {(import("./types.js").ForEachStatement | import("./types.js").ForEachMapStatement | import("./types.js").WhileStatement)[]} active - Active loops.
290
+ * @returns {void}
291
+ */
292
+ function visit(block, owner, active) {
293
+ if (!block || !Array.isArray(block.statements)) return
294
+ for (const statement of block.statements) {
295
+ if (!statement || typeof statement != "object") continue
296
+ if (["ForEachStatement", "ForEachMapStatement", "WhileStatement"].includes(statement.kind)) {
297
+ const loop = /** @type {import("./types.js").ForEachStatement | import("./types.js").ForEachMapStatement | import("./types.js").WhileStatement} */ (statement)
298
+
299
+ if (normalizeOperations) loop.id = `loop:${nextLoop}`
300
+ nextLoop += 1
301
+ if (typeof loop.id != "string" || !/^loop:[0-9]+$/u.test(loop.id) || owners.has(loop.id)) {
302
+ fail("INVALID_LOOP_TARGET", "Duplicate or invalid loop identity.", loop.location)
303
+ }
304
+ owners.set(loop.id, owner)
305
+ visit(loop.body, owner, [...active, loop])
306
+ } else if (statement.kind == "BreakStatement" || statement.kind == "ContinueStatement") {
307
+ if (normalizeOperations && active.length > 0) statement.targetLoopId = /** @type {string} */ (active.at(-1)?.id)
308
+ } else if (statement.kind == "IfStatement") {
309
+ visit(statement.consequent, owner, active)
310
+ if (statement.alternate) visit(statement.alternate, owner, active)
311
+ } else if (statement.kind == "TryStatement") {
312
+ visit(statement.body, owner, active)
313
+ visit(statement.catchBody, owner, active)
314
+ }
315
+ }
132
316
  }
317
+ }
318
+
319
+ /**
320
+ * Derives validator-only unchecked-error effects for a validated module's local functions.
321
+ * These effects never enter semantic function declarations or public signatures.
322
+ * @param {import("./types.js").SemanticModule} module - Validated semantic module.
323
+ * @param {{functions?: Map<string, import("./types.js").FunctionDeclaration>, callEffects?: Map<string, Set<string>>}} [visible] - Imported functions and already-resolved effects by local binding.
324
+ * @returns {Map<string, Set<string>>} Local function effects by source-visible name.
325
+ */
326
+ export function moduleUncheckedErrorEffects(module, visible = {}) {
327
+ const functions = new Map(visible.functions ?? [])
133
328
 
134
- const returnType = validateTypeReference(declaration.returnType, declaration.location, fail)
135
- const returns = validateBlock(declaration.body, scope, returnType, functions, fail, normalizeOperations)
329
+ for (const declaration of module.functions) functions.set(declaration.name, declaration)
330
+ const localFunctionNames = new Set(module.functions.map(({name}) => name))
331
+ const effects = inferCallEffects(functions, visible.callEffects, localFunctionNames)
136
332
 
137
- if (!returns) fail("MISSING_RETURN", `Function '${declaration.name}' does not return on every reachable path.`, declaration.location)
333
+ return new Map(module.functions.map((declaration) => [declaration.name, new Set(effects.get(declaration.name) ?? [])]))
138
334
  }
139
335
 
140
336
  /**
141
- * Validates one ordered block and reports whether every path returns.
142
- * @param {import("./types.js").Block} block - Semantic block.
143
- * @param {Scope} scope - Scope belonging to this block.
144
- * @param {import("./types.js").SemanticTypeName | undefined} returnType - Function return type, absent for entry points.
145
- * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Function signatures.
337
+ * Assigns and validates the closed nominal identities owned by reference classes.
338
+ * @param {unknown} declarations - Candidate declarations.
146
339
  * @param {SemanticFail} fail - Diagnostic callback.
147
- * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
148
- * @returns {boolean} Whether every path through the block returns.
340
+ * @param {boolean} normalizeOperations - Whether parser-authored identities are assigned.
341
+ * @param {Map<string, import("./types.js").ClassDeclaration>} [visibleClasses] - Imported classes by identity.
342
+ * @returns {ClassRegistry} Classes by stable identity.
149
343
  */
150
- function validateBlock(block, scope, returnType, functions, fail, normalizeOperations) {
151
- let alwaysReturns = false
152
-
153
- for (const statement of block.statements) {
154
- if (alwaysReturns) fail("UNREACHABLE_STATEMENT", "Statement is unreachable.", statement.location)
344
+ function registerClassDeclarations(declarations, fail, normalizeOperations, visibleClasses = new Map()) {
345
+ if (!Array.isArray(declarations)) {
346
+ return fail("TYPE_MISMATCH", "Reference class declarations must be an ordered array.", /** @type {never} */ (undefined))
347
+ }
348
+ /** @type {ClassRegistry} */
349
+ const classes = new Map(visibleClasses)
350
+ const names = new Set([...visibleClasses.values()].map(({name}) => name))
155
351
 
156
- if (statement.kind == "LocalDeclaration") {
157
- const declaredType = validateTypeReference(statement.type, statement.location, fail)
158
- const initializerType = inferExpressionType(statement.initializer, scope, functions, fail, normalizeOperations)
352
+ for (let classIndex = 0; classIndex < declarations.length; classIndex += 1) {
353
+ const declaration = /** @type {import("./types.js").ClassDeclaration} */ (declarations[classIndex])
159
354
 
160
- scope.pending.delete(statement.name)
161
- if (initializerType != declaredType) {
162
- fail("TYPE_MISMATCH", `Initializer type ${initializerType}; expected ${declaredType}.`, statement.initializer.location)
163
- }
164
- declareBinding(statement.name, {mutable: statement.mutable, type: declaredType}, statement.location, scope, fail)
165
- continue
355
+ if (!declaration || declaration.kind != "ClassDeclaration" || !Array.isArray(declaration.fields) ||
356
+ !Array.isArray(declaration.methods) || !declaration.constructor || declaration.constructor.kind != "ConstructorDeclaration") {
357
+ return fail("DUPLICATE_CLASS", "Malformed or invalid reference class declaration.", declaration?.location)
166
358
  }
167
- if (statement.kind == "AssignmentStatement") {
168
- const expressionType = inferExpressionType(statement.expression, scope, functions, fail, normalizeOperations)
169
- const binding = resolveBinding(statement.target.name, statement.target.location, scope, fail)
359
+ if (declaration.fields.length == 0) {
360
+ fail("INCOMPLETE_INITIALIZATION", "Reference classes must declare private instance state.", declaration.location)
361
+ }
362
+ if (normalizeOperations) declaration.id = `class:${classIndex}`
363
+ if (typeof declaration.id != "string" || !/^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+$/u.test(declaration.id) ||
364
+ classes.has(declaration.id) || typeof declaration.name != "string" || declaration.name.length == 0 || names.has(declaration.name)) {
365
+ fail("DUPLICATE_CLASS", `Duplicate or invalid reference class '${String(declaration.name)}'.`, roleLocation(declaration, "name"))
366
+ }
367
+ names.add(declaration.name)
368
+ classes.set(declaration.id, declaration)
170
369
 
171
- if (!binding.mutable) {
172
- fail("IMMUTABLE_ASSIGNMENT", `Cannot assign to immutable binding '${statement.target.name}'.`, statement.target.location)
370
+ const fieldNames = new Set()
371
+
372
+ for (let fieldIndex = 0; fieldIndex < declaration.fields.length; fieldIndex += 1) {
373
+ const field = declaration.fields[fieldIndex]
374
+ const expectedId = `${declaration.id}:field:${fieldIndex}`
375
+
376
+ if (!field || field.kind != "PrivateField") {
377
+ fail("DUPLICATE_PRIVATE_FIELD", "Malformed or invalid private field.", field?.location ?? declaration.location)
173
378
  }
174
- if (expressionType != binding.type) {
175
- fail("TYPE_MISMATCH", `Assignment type ${expressionType}; expected ${binding.type}.`, statement.expression.location)
379
+ if (normalizeOperations) field.id = expectedId
380
+ if (field.id != expectedId || typeof field.name != "string" || field.name.length == 0 || fieldNames.has(field.name)) {
381
+ fail("DUPLICATE_PRIVATE_FIELD", `Duplicate or invalid private field '${String(field.name)}'.`, roleLocation(field, "name"))
176
382
  }
177
- continue
178
- }
179
- if (statement.kind == "PrintStatement") {
180
- inferExpressionType(statement.expression, scope, functions, fail, normalizeOperations)
181
- continue
383
+ fieldNames.add(field.name)
182
384
  }
183
- if (statement.kind == "ReturnStatement") {
184
- if (!returnType) fail("ILLEGAL_RETURN_CONTEXT", "Return statement outside a function.", statement.location)
185
- const actualType = inferExpressionType(statement.expression, scope, functions, fail, normalizeOperations)
385
+ const invalidOwnedField = declaration.fields.find(({type}) => containsOwnedType(type) && type.kind != "OwnedResourceType")
186
386
 
187
- if (actualType != returnType) {
188
- fail("TYPE_MISMATCH", `Return type ${actualType}; expected ${returnType}.`, statement.expression.location)
189
- }
190
- alwaysReturns = true
191
- continue
387
+ if (invalidOwnedField) {
388
+ fail("RESOURCE_ALIAS", `Class '${declaration.name}' may contain ownership only in one direct resource field.`, invalidOwnedField.location)
192
389
  }
193
- if (statement.kind == "IfStatement") {
194
- const conditionType = inferExpressionType(statement.condition, scope, functions, fail, normalizeOperations)
390
+ const ownedFields = declaration.fields.filter(({type}) => type?.kind == "OwnedResourceType")
195
391
 
196
- if (conditionType != "boolean") {
197
- fail("NON_BOOLEAN_CONDITION", `If condition type ${conditionType}; expected boolean.`, statement.condition.location)
392
+ if (ownedFields.length > 1) {
393
+ fail("RESOURCE_ALIAS", `Class '${declaration.name}' may own exactly one direct resource field.`, declaration.location)
394
+ }
395
+ if (ownedFields.length == 1) {
396
+ const field = /** @type {import("./types.js").PrivateField & {type: import("./types.js").OwnedResourceType}} */ (ownedFields[0])
397
+ const expected = {fieldId: /** @type {string} */ (field.id), kind: /** @type {const} */ ("ownedResource"),
398
+ resourceId: field.type.resourceId}
399
+
400
+ if (normalizeOperations) declaration.ownership = expected
401
+ else if (JSON.stringify(declaration.ownership) != JSON.stringify(expected)) {
402
+ fail("RESOURCE_TYPE_MISMATCH", `Class '${declaration.name}' has malformed resource ownership metadata.`, declaration.location)
198
403
  }
199
- const consequentScope = createScope(scope, statement.consequent.statements)
200
- const consequentReturns = validateBlock(statement.consequent, consequentScope, returnType, functions, fail, normalizeOperations)
201
- let alternateReturns = false
404
+ } else if (declaration.ownership?.kind == "ownedResource") {
405
+ fail("RESOURCE_TYPE_MISMATCH", `Class '${declaration.name}' does not contain its declared resource field.`, declaration.location)
406
+ }
202
407
 
203
- if (statement.alternate) {
204
- const alternateScope = createScope(scope, statement.alternate.statements)
408
+ const constructorId = `${declaration.id}:constructor`
205
409
 
206
- alternateReturns = validateBlock(statement.alternate, alternateScope, returnType, functions, fail, normalizeOperations)
207
- }
208
- alwaysReturns = consequentReturns && alternateReturns
209
- continue
410
+ if (normalizeOperations) declaration.constructor.id = constructorId
411
+ if (declaration.constructor.id != constructorId || !Array.isArray(declaration.constructor.parameters) ||
412
+ !declaration.constructor.body || declaration.constructor.body.kind != "Block") {
413
+ fail("DUPLICATE_CONSTRUCTOR", "Malformed or invalid constructor declaration.", declaration.constructor.location)
210
414
  }
211
415
 
212
- const unexpected = /** @type {{kind: string, location: import("./types.js").SourceLocation}} */ (statement)
213
-
214
- fail("UNSUPPORTED_STATEMENT", unexpected.kind, unexpected.location)
215
- }
416
+ const methodNames = new Set()
216
417
 
217
- return alwaysReturns
218
- }
418
+ for (let methodIndex = 0; methodIndex < declaration.methods.length; methodIndex += 1) {
419
+ const method = declaration.methods[methodIndex]
420
+ const expectedId = `${declaration.id}:method:${methodIndex}`
219
421
 
220
- /**
221
- * Creates a lexical scope with declarations marked pending until visited.
222
- * @param {Scope | undefined} parent - Enclosing scope.
223
- * @param {{kind: string, name?: string}[]} statements - Scope statements.
224
- * @param {Set<string>} [reservedNames] - Names unavailable to root bindings.
225
- * @returns {Scope} Scope.
226
- */
227
- function createScope(parent, statements, reservedNames = new Set()) {
228
- const pending = new Set(statements.filter((statement) => statement.kind == "LocalDeclaration")
229
- .map((statement) => /** @type {{name: string}} */ (statement).name))
422
+ if (!method || method.kind != "MethodDeclaration") {
423
+ fail("DUPLICATE_METHOD", "Malformed or invalid method declaration.", method?.location ?? declaration.location)
424
+ }
425
+ if (normalizeOperations) method.id = expectedId
426
+ if (method.id != expectedId || typeof method.name != "string" || method.name.length == 0 || methodNames.has(method.name) ||
427
+ !Array.isArray(method.parameters) || !method.body || method.body.kind != "Block") {
428
+ fail("DUPLICATE_METHOD", `Duplicate or invalid method '${String(method.name)}'.`, roleLocation(method, "name"))
429
+ }
430
+ methodNames.add(method.name)
431
+ }
432
+ }
230
433
 
231
- return {bindings: new Map(), parent, pending, usedNames: parent?.usedNames ?? new Set(reservedNames)}
434
+ return classes
232
435
  }
233
436
 
234
437
  /**
235
- * Adds one binding while rejecting duplicates and shadowing.
236
- * @param {string} name - Binding name.
237
- * @param {Binding} binding - Binding metadata.
238
- * @param {import("./types.js").SourceLocation} location - Declaration location.
239
- * @param {Scope} scope - Current scope.
438
+ * Validates exact construction, private state, and method bodies for one class.
439
+ * @param {import("./types.js").ClassDeclaration} declaration - Class declaration.
440
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Function signatures.
441
+ * @param {RecordRegistry} records - Record and reference-class registries.
442
+ * @param {ErrorRegistry} errors - Error declarations.
443
+ * @param {Map<string, Set<string>>} callEffects - Function call effects.
444
+ * @param {Set<string>} reservedValueNames - Module-reserved names.
240
445
  * @param {SemanticFail} fail - Diagnostic callback.
446
+ * @param {boolean} normalizeOperations - Whether parser-authored resolutions are assigned.
447
+ * @param {Map<string, object>} loopOwners - Loop ownership registry.
241
448
  * @returns {void}
242
449
  */
243
- function declareBinding(name, binding, location, scope, fail) {
244
- if (scope.usedNames.has(name)) fail("DUPLICATE_BINDING", `Duplicate or shadowed binding '${name}'.`, location)
450
+ function validateClassDeclaration(declaration, functions, records, errors, callEffects, reservedValueNames, fail,
451
+ normalizeOperations, loopOwners) {
452
+ for (const field of declaration.fields) {
453
+ validateValueTypeReference(field.type, field.location, fail, undefined, records)
454
+ }
245
455
 
246
- scope.bindings.set(name, binding)
247
- scope.usedNames.add(name)
456
+ const constructor = declaration.constructor
457
+ const acquisitionStatement = constructor.body.statements[0]
458
+ const acquisitionExpression = acquisitionStatement?.kind == "PrivateFieldWriteStatement"
459
+ ? acquisitionStatement.expression : undefined
460
+ const ownedAcquisition = declaration.ownership?.kind == "ownedResource" && declaration.fields.length == 1 &&
461
+ constructor.body.statements.length == 1 && acquisitionStatement?.kind == "PrivateFieldWriteStatement" &&
462
+ acquisitionStatement.field == declaration.ownership.fieldId && acquisitionStatement.receiver.kind == "ReceiverExpression" &&
463
+ acquisitionStatement.receiver.classId == declaration.id && acquisitionExpression?.kind == "EffectCallExpression" &&
464
+ acquisitionExpression.resolution.resourceFlow.kind == "acquire" &&
465
+ acquisitionExpression.resolution.resourceFlow.resourceId == declaration.ownership.resourceId
466
+
467
+ if (syntacticBlockEffects(constructor.body, callEffects).size > 0 && !ownedAcquisition) {
468
+ fail("UNSUPPORTED_STATEMENT", "Reference constructors cannot expose unchecked-error effects in Task 033.", constructor.location)
469
+ }
470
+
471
+ if (!ownedAcquisition &&
472
+ (constructor.parameters.length != declaration.fields.length || constructor.body.statements.length != declaration.fields.length)) {
473
+ fail("INCOMPLETE_INITIALIZATION", `Constructor for '${declaration.name}' must initialize every private field exactly once in declaration order.`,
474
+ constructor.location)
475
+ }
476
+ const constructorScope = createScope(undefined, constructor.body.statements, reservedValueNames, new Set(), declaration)
477
+
478
+ for (let index = 0; index < constructor.parameters.length; index += 1) {
479
+ const parameter = constructor.parameters[index]
480
+ const field = declaration.fields[index]
481
+ const type = validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records)
482
+
483
+ if (!ownedAcquisition && !sameType(type, field.type)) {
484
+ fail("TYPE_MISMATCH", `Constructor parameter '${parameter.name}' must exactly match private field '${field.name}'.`, parameter.location)
485
+ }
486
+ declareBinding(parameter.name, {knownValue: undefined, mutable: false, type}, roleLocation(parameter, "name"), constructorScope, fail)
487
+ if (ownedAcquisition) continue
488
+ const statement = constructor.body.statements[index]
489
+ const initializer = statement?.kind == "PrivateFieldWriteStatement"
490
+ ? statement.expression.kind == "OwnedMoveExpression" ? statement.expression.expression : statement.expression
491
+ : undefined
492
+
493
+ if (!statement || statement.kind != "PrivateFieldWriteStatement" || statement.field != field.id ||
494
+ statement.receiver?.kind != "ReceiverExpression" || statement.receiver.classId != declaration.id ||
495
+ initializer?.kind != "IdentifierExpression" || initializer.name != parameter.name) {
496
+ fail("INCOMPLETE_INITIALIZATION", `Constructor for '${declaration.name}' must initialize '${field.name}' from its corresponding parameter.`,
497
+ statement?.location ?? constructor.location)
498
+ }
499
+ }
500
+ validateBlock(constructor.body, constructorScope, {kind: "TypeReference", name: "void"}, functions, records,
501
+ errors, callEffects, fail, normalizeOperations, {owner: constructor, owners: loopOwners})
502
+
503
+ for (const method of declaration.methods) {
504
+ const unsupportedMethodEffects = [...syntacticBlockEffects(method.body, callEffects)]
505
+ .filter((id) => !/^(?:module:[0-9]+\/)?capability:[0-9]+\/failure:[0-9]+$/u.test(id))
506
+
507
+ if (declaration.ownership?.kind != "ownedResource" && unsupportedMethodEffects.length > 0) {
508
+ fail("UNSUPPORTED_STATEMENT", `Reference method '${method.name}' cannot expose unchecked-error effects in Task 033.`, method.location)
509
+ }
510
+ const scope = createScope(undefined, method.body.statements, reservedValueNames, new Set(), declaration)
511
+
512
+ for (const parameter of method.parameters) {
513
+ const type = validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records)
514
+
515
+ declareBinding(parameter.name, {knownValue: undefined, mutable: false, type}, roleLocation(parameter, "name"), scope, fail)
516
+ }
517
+ const returnType = validateReturnTypeReference(method.returnType, method.location, fail, records)
518
+ const flow = validateBlock(method.body, scope, returnType, functions, records, errors, callEffects, fail,
519
+ normalizeOperations, {owner: method, owners: loopOwners})
520
+
521
+ if (declaration.ownership?.kind == "ownedResource") ownedMethodResourceFlow(declaration, method, fail)
522
+
523
+ if (!isVoidType(returnType) && flow.normal) {
524
+ fail("MISSING_RETURN", `Method '${method.name}' does not return on every reachable path.`, method.location)
525
+ }
526
+ }
248
527
  }
249
528
 
250
529
  /**
251
- * Resolves one identifier and distinguishes use-before-declaration.
252
- * @param {string} name - Binding name.
253
- * @param {import("./types.js").SourceLocation} location - Reference location.
254
- * @param {Scope} scope - Current scope.
530
+ * Validates nominal unchecked-error declarations.
531
+ * @param {unknown} declarations - Candidate declarations.
255
532
  * @param {SemanticFail} fail - Diagnostic callback.
256
- * @returns {Binding} Binding.
533
+ * @param {boolean} normalizeOperations - Whether parser-authored identities are assigned.
534
+ * @param {Map<string, import("./types.js").ErrorDeclaration>} [visibleErrors] - Imported error declarations by identity.
535
+ * @returns {ErrorRegistry} Validated declarations by identity.
257
536
  */
258
- function resolveBinding(name, location, scope, fail) {
259
- for (let current = /** @type {Scope | undefined} */ (scope); current; current = current.parent) {
260
- const binding = current.bindings.get(name)
537
+ function validateErrorDeclarations(declarations, fail, normalizeOperations, visibleErrors = new Map()) {
538
+ if (!Array.isArray(declarations)) return fail("TYPE_MISMATCH", "Error declarations must be an ordered array.", /** @type {never} */ (undefined))
539
+ /** @type {ErrorRegistry} */
540
+ const errors = new Map(visibleErrors)
541
+ const names = new Set()
261
542
 
262
- if (binding) return binding
263
- if (current.pending.has(name)) {
264
- return fail("USE_BEFORE_DECLARATION", `Binding '${name}' is used before its declaration.`, location)
543
+ for (let index = 0; index < declarations.length; index += 1) {
544
+ const declaration = declarations[index]
545
+
546
+ if (!declaration || declaration.kind != "ErrorDeclaration") {
547
+ return fail("TYPE_MISMATCH", "Malformed error declaration.", declaration?.location)
548
+ }
549
+ if (normalizeOperations) declaration.id = `error:${index}`
550
+ if (typeof declaration.id != "string" || !/^(?:[a-z][a-z0-9._-]*#)?error:[0-9]+$/u.test(declaration.id) || errors.has(declaration.id)) {
551
+ fail("DUPLICATE_ERROR", "Duplicate or invalid error declaration identity.", declaration.location)
552
+ }
553
+ if (typeof declaration.name != "string" || names.has(declaration.name)) {
554
+ fail("DUPLICATE_ERROR", `Duplicate or invalid error '${String(declaration.name)}'.`, roleLocation(declaration, "name"))
265
555
  }
556
+ names.add(declaration.name)
557
+ errors.set(declaration.id, declaration)
266
558
  }
267
559
 
268
- return fail("UNRESOLVED_BINDING", `Unknown binding '${name}'.`, location)
560
+ return errors
269
561
  }
270
562
 
271
563
  /**
272
- * Validates one semantic type reference.
273
- * @param {unknown} type - Candidate semantic type reference.
274
- * @param {import("./types.js").SourceLocation} location - Owning source location.
564
+ * Assigns and validates one declaration's invariant unbounded type parameters.
565
+ * @param {import("./types.js").FunctionDeclaration | import("./types.js").RecordDeclaration} declaration - Owning declaration.
275
566
  * @param {SemanticFail} fail - Diagnostic callback.
276
- * @returns {import("./types.js").SemanticTypeName} Validated scalar name.
567
+ * @param {boolean} normalizeOperations - Whether parser-authored identities are assigned.
568
+ * @returns {void}
277
569
  */
278
- function validateTypeReference(type, location, fail) {
279
- if (!type || typeof type != "object" || Array.isArray(type)) {
280
- return fail("TYPE_MISMATCH", "Unsupported scalar type.", location)
570
+ function validateTypeParameters(declaration, fail, normalizeOperations) {
571
+ if (declaration.typeParameters === undefined) return
572
+ if (!Array.isArray(declaration.typeParameters)) {
573
+ fail("TYPE_MISMATCH", "Type parameters must be a dense ordered array.", declaration.location)
281
574
  }
575
+ const names = new Set()
576
+
577
+ for (let index = 0; index < declaration.typeParameters.length; index += 1) {
578
+ const parameter = declaration.typeParameters[index]
579
+ const keys = parameter && typeof parameter == "object" && !Array.isArray(parameter)
580
+ ? Object.keys(parameter).filter((key) => key != "sourceProvenance").sort().join(",") : ""
282
581
 
283
- const candidate = /** @type {import("./types.js").TypeReference} */ (type)
582
+ if (!Object.hasOwn(declaration.typeParameters, index) || !parameter || parameter.kind != "TypeParameter" ||
583
+ keys != "id,kind,location,name" || typeof parameter.name != "string" || parameter.name.length == 0) {
584
+ fail("TYPE_MISMATCH", "Malformed type parameter declaration.", parameter?.location ?? declaration.location)
585
+ }
586
+ if (names.has(parameter.name)) {
587
+ fail("DUPLICATE_TYPE_PARAMETER", `Duplicate type parameter '${parameter.name}'.`, roleLocation(parameter, "name"))
588
+ }
589
+ names.add(parameter.name)
590
+ const expectedId = `${declaration.id}:type:${index}`
284
591
 
285
- if (candidate.kind != "TypeReference" || !isScalarTypeName(candidate.name)) {
286
- fail("TYPE_MISMATCH", "Unsupported scalar type.", location)
592
+ if (normalizeOperations) parameter.id = expectedId
593
+ if (parameter.id != expectedId) {
594
+ fail("DUPLICATE_TYPE_PARAMETER", "Duplicate or invalid type parameter identity.", parameter.location)
595
+ }
287
596
  }
597
+ }
288
598
 
289
- return candidate.name
599
+ /**
600
+ * Returns the closed identity scope owned by one validated declaration.
601
+ * @param {import("./types.js").FunctionDeclaration | import("./types.js").RecordDeclaration} declaration - Declaration.
602
+ * @returns {Set<string>} Type-parameter identities.
603
+ */
604
+ function declarationTypeParameterIds(declaration) {
605
+ return new Set((declaration.typeParameters ?? []).map(({id}) => /** @type {string} */ (id)))
290
606
  }
291
607
 
292
608
  /**
293
- * Infers and validates one expression.
294
- * @param {import("./types.js").Expression} expression - Semantic expression.
295
- * @param {Scope} scope - Visible lexical scope.
296
- * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Module function signatures.
609
+ * Validates nominal declarations and returns their stable identity registry.
610
+ * @param {unknown} declarations - Candidate ordered declarations.
297
611
  * @param {SemanticFail} fail - Diagnostic callback.
298
- * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
299
- * @returns {import("./types.js").SemanticTypeName} Expression type.
612
+ * @param {boolean} normalizeOperations - Whether parser-authored identities are assigned.
613
+ * @param {Map<string, import("./types.js").RecordDeclaration>} [visibleRecords] - Imported record declarations by identity.
614
+ * @param {ClassRegistry} [classes] - Module reference classes by identity.
615
+ * @returns {RecordRegistry} Validated declarations by identity.
300
616
  */
301
- function inferExpressionType(expression, scope, functions, fail, normalizeOperations) {
302
- if (expression.kind == "IdentifierExpression") return resolveBinding(expression.name, expression.location, scope, fail).type
617
+ function validateRecordDeclarations(declarations, fail, normalizeOperations, visibleRecords = new Map(), classes = new Map()) {
618
+ if (!Array.isArray(declarations)) return fail("TYPE_MISMATCH", "Record declarations must be an ordered array.", /** @type {never} */ (undefined))
619
+ /** @type {RecordRegistry} */
620
+ const records = new Map(visibleRecords)
303
621
 
304
- if (expression.kind == "IntegerLiteral") {
305
- if (!Number.isSafeInteger(expression.value)) fail("TYPE_MISMATCH", "Non-safe integer literal.", expression.location)
306
- return "integer"
307
- }
308
- if (expression.kind == "BooleanLiteral") {
309
- if (typeof Reflect.get(expression, "value") != "boolean") {
310
- fail("TYPE_MISMATCH", "Invalid boolean literal value.", expression.location)
311
- }
312
- return "boolean"
313
- }
314
- if (expression.kind == "StringLiteral") {
315
- const value = Reflect.get(expression, "value")
622
+ referenceClassesByRecordRegistry.set(records, classes)
623
+ const names = new Set()
316
624
 
317
- if (typeof value != "string" || !hasOnlyUnicodeScalars(value)) {
318
- fail("TYPE_MISMATCH", "Invalid Unicode string literal.", expression.location)
625
+ for (let recordIndex = 0; recordIndex < declarations.length; recordIndex += 1) {
626
+ const declaration = declarations[recordIndex]
627
+
628
+ if (!declaration || declaration.kind != "RecordDeclaration" || !Array.isArray(declaration.fields)) {
629
+ return fail("TYPE_MISMATCH", "Malformed record declaration.", declaration?.location)
630
+ }
631
+ if (normalizeOperations) declaration.id = `record:${recordIndex}`
632
+ if (typeof declaration.id != "string" || !/^(?:[a-z][a-z0-9._-]*#)?record:[0-9]+$/u.test(declaration.id) || records.has(declaration.id)) {
633
+ fail("DUPLICATE_RECORD", "Duplicate or invalid record declaration identity.", declaration.location)
319
634
  }
320
- return "string"
635
+ if (names.has(declaration.name)) {
636
+ fail("DUPLICATE_RECORD", `Duplicate record '${declaration.name}'.`, roleLocation(declaration, "name"))
637
+ }
638
+ names.add(declaration.name)
639
+ validateTypeParameters(declaration, fail, normalizeOperations)
640
+ records.set(declaration.id, declaration)
321
641
  }
322
- if (expression.kind == "CallExpression") {
323
- const functionDeclaration = functions.get(expression.callee)
324
642
 
325
- if (!functionDeclaration) return fail("UNRESOLVED_BINDING", `Unknown function '${expression.callee}'.`, expression.location)
326
- if (expression.arguments.length != functionDeclaration.parameters.length) {
327
- return fail("TYPE_MISMATCH", `Call argument count for '${expression.callee}'.`, expression.location)
328
- }
329
- for (let index = 0; index < expression.arguments.length; index++) {
330
- const argument = expression.arguments[index]
331
- const actualType = inferExpressionType(argument, scope, functions, fail, normalizeOperations)
332
- const expectedType = validateTypeReference(functionDeclaration.parameters[index].type, functionDeclaration.parameters[index].location, fail)
643
+ for (const declaration of declarations) {
644
+ const fieldNames = new Set()
645
+ const typeParameters = declarationTypeParameterIds(declaration)
333
646
 
334
- if (actualType != expectedType) {
335
- fail("TYPE_MISMATCH", `Call argument type ${actualType}; expected ${expectedType}.`, argument.location)
647
+ for (let fieldIndex = 0; fieldIndex < declaration.fields.length; fieldIndex += 1) {
648
+ const field = declaration.fields[fieldIndex]
649
+
650
+ if (!field || field.kind != "RecordField") fail("TYPE_MISMATCH", "Malformed record field.", field?.location ?? declaration.location)
651
+ if (normalizeOperations) field.id = `${declaration.id}:field:${fieldIndex}`
652
+ if (typeof field.id != "string" || field.id != `${declaration.id}:field:${fieldIndex}`) {
653
+ fail("DUPLICATE_FIELD", "Duplicate or invalid record field identity.", field.location)
654
+ }
655
+ if (fieldNames.has(field.name)) fail("DUPLICATE_FIELD", `Duplicate field '${field.name}'.`, roleLocation(field, "name"))
656
+ fieldNames.add(field.name)
657
+ if (containsOwnedType(field.type)) {
658
+ fail("RESOURCE_ALIAS", "Owned resources cannot be stored in immutable records.", field.location)
336
659
  }
660
+ validateValueTypeReference(field.type, field.location, fail, undefined, records, typeParameters)
337
661
  }
338
- return validateTypeReference(functionDeclaration.returnType, functionDeclaration.location, fail)
339
662
  }
340
- if (expression.kind == "UnaryExpression") {
341
- const operandType = inferExpressionType(expression.operand, scope, functions, fail, normalizeOperations)
342
- const operation = normalizeOperations
343
- ? normalizeUnaryOperation(adaptedOperationFor(expression), operandType, expression.operand.location, fail)
344
- : expression.operation
663
+ validateDirectRecordRecursion(declarations, records, fail)
345
664
 
346
- if (normalizeOperations) setNormalizedOperation(expression, operation, unaryOperationSignatures[operation].result)
665
+ return records
666
+ }
347
667
 
348
- const signature = unaryOperationSignatures[operation]
668
+ /**
669
+ * Rejects cycles composed solely of unmediated record fields.
670
+ * @param {import("./types.js").RecordDeclaration[]} declarations - Valid declarations.
671
+ * @param {RecordRegistry} records - Declarations by identity.
672
+ * @param {SemanticFail} fail - Diagnostic callback.
673
+ * @returns {void}
674
+ */
675
+ function validateDirectRecordRecursion(declarations, records, fail) {
676
+ const complete = new Set()
677
+
678
+ /**
679
+ * Visits one declaration along an unmediated record path.
680
+ * @param {import("./types.js").RecordDeclaration} declaration - Current declaration.
681
+ * @param {Set<string>} active - Declaration identities active on this path.
682
+ * @returns {void}
683
+ */
684
+ function visit(declaration, active) {
685
+ if (complete.has(/** @type {string} */ (declaration.id))) return
686
+ const next = new Set(active)
687
+
688
+ next.add(/** @type {string} */ (declaration.id))
689
+ for (const field of declaration.fields) {
690
+ if (field.type.kind != "RecordType") continue
691
+ if (next.has(field.type.declarationId)) {
692
+ fail("ILLEGAL_RECORD_RECURSION", `Direct value-recursive field '${field.name}'.`, typeLocation(field.type, field.location))
693
+ }
694
+ const referenced = records.get(field.type.declarationId)
349
695
 
350
- if (!signature) return fail("TYPE_MISMATCH", `Unknown unary operation ${String(operation)}.`, expression.location)
351
- if (operandType != signature.operand) {
352
- return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.operand}; received ${operandType}.`, expression.operand.location)
353
- }
354
- if (expression.type != signature.result) {
355
- return fail("TYPE_MISMATCH", `${operation} result type ${String(expression.type)}; expected ${signature.result}.`, expression.location)
696
+ if (referenced) visit(referenced, next)
356
697
  }
357
- return signature.result
698
+ complete.add(/** @type {string} */ (declaration.id))
358
699
  }
359
- if (expression.kind == "BinaryExpression") {
360
- const leftType = inferExpressionType(expression.left, scope, functions, fail, normalizeOperations)
361
- const rightType = inferExpressionType(expression.right, scope, functions, fail, normalizeOperations)
362
- const operation = normalizeOperations
363
- ? normalizeBinaryOperation(adaptedOperationFor(expression), leftType, rightType, expression.left.location, expression.right.location, expression.location, fail)
364
- : expression.operation
365
700
 
366
- if (normalizeOperations) setNormalizedOperation(expression, operation, binaryOperationSignatures[operation].result)
701
+ for (const declaration of declarations) visit(declaration, new Set())
702
+ }
367
703
 
368
- const signature = binaryOperationSignatures[operation]
704
+ /**
705
+ * Validates one non-void function and its complete control flow.
706
+ * @param {import("./types.js").FunctionDeclaration} declaration - Function declaration.
707
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Function signatures.
708
+ * @param {RecordRegistry} records - Record declarations by identity.
709
+ * @param {ErrorRegistry} errors - Error declarations by identity.
710
+ * @param {Map<string, Set<string>>} callEffects - Internally inferred unchecked-error effects.
711
+ * @param {Set<string>} reservedValueNames - Module functions and nominal constructors unavailable to lexical bindings.
712
+ * @param {SemanticFail} fail - Diagnostic callback.
713
+ * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
714
+ * @param {Map<string, object>} loopOwners - Loop owner by module-local identity.
715
+ * @returns {void}
716
+ */
717
+ function validateFunction(declaration, functions, records, errors, callEffects, reservedValueNames, fail, normalizeOperations, loopOwners) {
718
+ const typeParameters = declarationTypeParameterIds(declaration)
719
+ const scope = createScope(undefined, declaration.body.statements, reservedValueNames, typeParameters)
369
720
 
370
- if (!signature) return fail("TYPE_MISMATCH", `Unknown binary operation ${String(operation)}.`, expression.location)
371
- if (leftType != signature.left) {
372
- return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.left} left operand; received ${leftType}.`, expression.left.location)
373
- }
374
- if (rightType != signature.right) {
375
- return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.right} right operand; received ${rightType}.`, expression.right.location)
376
- }
377
- if (expression.type != signature.result) {
378
- return fail("TYPE_MISMATCH", `${operation} result type ${String(expression.type)}; expected ${signature.result}.`, expression.location)
379
- }
380
- return signature.result
721
+ for (const parameter of declaration.parameters) {
722
+ const type = validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records, typeParameters)
723
+
724
+ declareBinding(parameter.name, {knownValue: undefined, mutable: false, type}, roleLocation(parameter, "name"), scope, fail)
381
725
  }
382
726
 
383
- const unexpected = /** @type {{kind: string, location: import("./types.js").SourceLocation}} */ (expression)
727
+ const returnType = validateReturnTypeReference(declaration.returnType, declaration.location, fail, records, typeParameters)
728
+ const returns = validateBlock(declaration.body, scope, returnType, functions, records, errors, callEffects, fail,
729
+ normalizeOperations, {owner: declaration, owners: loopOwners})
384
730
 
385
- return fail("TYPE_MISMATCH", unexpected.kind, unexpected.location)
731
+ if (!isVoidType(returnType) && returns.normal) {
732
+ fail("MISSING_RETURN", `Function '${declaration.name}' does not return on every reachable path.`, declaration.location)
733
+ }
734
+ }
735
+
736
+ /**
737
+ * Validates one ordered block and reports whether every path returns.
738
+ * @param {import("./types.js").Block} block - Semantic block.
739
+ * @param {Scope} scope - Scope belonging to this block.
740
+ * @param {import("./types.js").SemanticFunctionReturnType | undefined} returnType - Function return type, absent for entry points.
741
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Function signatures.
742
+ * @param {RecordRegistry} records - Record declarations by identity.
743
+ * @param {ErrorRegistry} errors - Error declarations by identity.
744
+ * @param {Map<string, Set<string>>} callEffects - Internally inferred unchecked-error effects.
745
+ * @param {SemanticFail} fail - Diagnostic callback.
746
+ * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
747
+ * @param {LoopFlowContext} loopContext - Active loop and owner identities.
748
+ * @returns {{normal: boolean, raises: Set<string>}} Reachable normal continuation and escaping error identities.
749
+ */
750
+ function validateBlock(block, scope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext) {
751
+ let normal = true
752
+ const raises = new Set()
753
+
754
+ for (const statement of block.statements) {
755
+ if (!normal) fail("UNREACHABLE_STATEMENT", "Statement is unreachable.", statement.location)
756
+
757
+ if (statement.kind == "LocalDeclaration") {
758
+ const declaredType = validateValueTypeReference(statement.type, statement.location, fail, undefined, records, scope.typeParameters)
759
+ statement.initializer = normalizeOwnedMove(statement.initializer, declaredType, normalizeOperations, fail)
760
+ const initializerType = inferValueExpressionType(
761
+ statement.initializer, scope, functions, records, fail, normalizeOperations, "an initializer", declaredType
762
+ )
763
+
764
+ scope.pending.delete(statement.name)
765
+ if (!sameType(initializerType, declaredType)) {
766
+ if (initializerType.kind == "OptionalType" && declaredType.kind != "OptionalType") {
767
+ fail("FAILURE_ABSENCE_CONFLATION", "Optional absence cannot be coerced into a value or typed failure.", statement.initializer.location)
768
+ }
769
+ fail("TYPE_MISMATCH", `Initializer type ${typeDescription(initializerType)}; expected ${typeDescription(declaredType)}.`, statement.initializer.location)
770
+ }
771
+ declareBinding(statement.name, {
772
+ knownValue: knownValueForExpression(statement.initializer, scope),
773
+ mutable: statement.mutable,
774
+ type: declaredType
775
+ }, roleLocation(statement, "name"), scope, fail)
776
+ addExpressionEffects(raises, statement.initializer, callEffects)
777
+ continue
778
+ }
779
+ if (statement.kind == "AssignmentStatement") {
780
+ const binding = resolveBinding(statement.target.name, statement.target.location, scope, fail)
781
+
782
+ if (binding.type.kind == "ErrorType") {
783
+ fail("IMMUTABLE_ASSIGNMENT", `Cannot assign to immutable catch binding '${statement.target.name}'.`, statement.target.location)
784
+ }
785
+ if (binding.type.kind == "OwnedResourceType" || binding.type.kind == "OwnedReferenceType") {
786
+ fail("RESOURCE_ALIAS", "Owned resources cannot be reassigned or aliased.", statement.location)
787
+ }
788
+ const expressionType = inferValueExpressionType(
789
+ statement.expression, scope, functions, records, fail, normalizeOperations, "an assignment", binding.type
790
+ )
791
+
792
+ if (!binding.mutable) {
793
+ fail("IMMUTABLE_ASSIGNMENT", `Cannot assign to immutable binding '${statement.target.name}'.`, statement.target.location)
794
+ }
795
+ if (!sameType(expressionType, binding.type)) {
796
+ fail("TYPE_MISMATCH", `Assignment type ${typeDescription(expressionType)}; expected ${typeDescription(binding.type)}.`, statement.expression.location)
797
+ }
798
+ binding.knownValue = knownValueForExpression(statement.expression, scope)
799
+ scope.presenceProofs.delete(binding)
800
+ addExpressionEffects(raises, statement.expression, callEffects)
801
+ continue
802
+ }
803
+ if (statement.kind == "PrivateFieldWriteStatement") {
804
+ const declaration = scope.currentClass
805
+
806
+ if (!declaration || !statement.receiver || statement.receiver.kind != "ReceiverExpression" ||
807
+ statement.receiver.classId != declaration.id) {
808
+ fail("ILLEGAL_PRIVATE_ACCESS", "Private field receiver must be the declaring class receiver.", statement.location)
809
+ }
810
+ const field = declaration.fields.find((candidate) => candidate.id == statement.field)
811
+
812
+ if (!field) fail("ILLEGAL_PRIVATE_ACCESS", "Private field identity is not owned by the declaring class.", statement.location)
813
+ statement.expression = normalizeOwnedMove(statement.expression, field.type, normalizeOperations, fail)
814
+ const expressionType = inferValueExpressionType(
815
+ statement.expression, scope, functions, records, fail, normalizeOperations, "a private field assignment", field.type
816
+ )
817
+
818
+ if (!sameType(expressionType, field.type)) {
819
+ fail("TYPE_MISMATCH", `Private field assignment type ${typeDescription(expressionType)}; expected ${typeDescription(field.type)}.`,
820
+ statement.expression.location)
821
+ }
822
+ addExpressionEffects(raises, statement.expression, callEffects)
823
+ continue
824
+ }
825
+ if (statement.kind == "PrintStatement") {
826
+ const printedType = inferValueExpressionType(statement.expression, scope, functions, records, fail, normalizeOperations, "a print value")
827
+
828
+ if (printedType.kind == "OwnedResourceType" || printedType.kind == "OwnedReferenceType") {
829
+ fail("BORROW_ESCAPE", "Owned values cannot escape through printing or serialization.", statement.expression.location)
830
+ }
831
+ if (printedType.kind != "TypeReference") {
832
+ fail("TYPE_MISMATCH", "Collections and records cannot be printed directly.", statement.expression.location)
833
+ }
834
+ addExpressionEffects(raises, statement.expression, callEffects)
835
+ continue
836
+ }
837
+ if (statement.kind == "ExpressionStatement") {
838
+ if (!statement.expression || !["CallExpression", "EffectCallExpression", "MethodCallExpression"].includes(statement.expression.kind)) {
839
+ fail("TYPE_MISMATCH", "Expression statement must contain a direct or receiver call.", statement.location)
840
+ }
841
+ const expressionType = inferExpressionType(statement.expression, scope, functions, records, fail, normalizeOperations)
842
+
843
+ if (expressionType != "void") {
844
+ fail("TYPE_MISMATCH", "Expression statements may contain only void calls.", statement.expression.location)
845
+ }
846
+ addExpressionEffects(raises, statement.expression, callEffects)
847
+ continue
848
+ }
849
+ if (statement.kind == "BreakStatement" || statement.kind == "ContinueStatement") {
850
+ const control = statement.kind == "BreakStatement" ? "break" : "continue"
851
+
852
+ if (!loopContext.nearest) {
853
+ if (typeof statement.targetLoopId == "string" && loopContext.owners.has(statement.targetLoopId) &&
854
+ loopContext.owners.get(statement.targetLoopId) !== loopContext.owner) {
855
+ fail("CROSS_FUNCTION_LOOP_TARGET", `${control[0].toUpperCase()}${control.slice(1)} target crosses a function boundary.`, statement.location)
856
+ }
857
+ fail(`ILLEGAL_${control.toUpperCase()}_CONTEXT`, `${control[0].toUpperCase()}${control.slice(1)} statement outside a loop.`, statement.location)
858
+ }
859
+ if (typeof statement.targetLoopId != "string" || !loopContext.owners.has(statement.targetLoopId)) {
860
+ fail("INVALID_LOOP_TARGET", `${control[0].toUpperCase()}${control.slice(1)} has an invalid loop target identity.`, statement.location)
861
+ }
862
+ if (loopContext.owners.get(statement.targetLoopId) !== loopContext.owner) {
863
+ fail("CROSS_FUNCTION_LOOP_TARGET", `${control[0].toUpperCase()}${control.slice(1)} target crosses a function boundary.`, statement.location)
864
+ }
865
+ if (statement.targetLoopId != loopContext.nearest.id) {
866
+ fail("INVALID_LOOP_TARGET", `${control[0].toUpperCase()}${control.slice(1)} must target the nearest enclosing loop.`, statement.location)
867
+ }
868
+ normal = false
869
+ continue
870
+ }
871
+ if (statement.kind == "ReturnStatement") {
872
+ if (!returnType) fail("ILLEGAL_RETURN_CONTEXT", "Return statement outside a function.", statement.location)
873
+ if (isVoidType(returnType)) {
874
+ if (statement.expression) {
875
+ fail("VOID_RETURN_VALUE", "Void function cannot return a value.", statement.expression.location)
876
+ }
877
+ } else {
878
+ if (!statement.expression) {
879
+ fail("MISSING_RETURN_VALUE", `Function return requires a ${typeDescription(returnType)} value.`, statement.location)
880
+ }
881
+ statement.expression = normalizeOwnedMove(statement.expression, returnType, normalizeOperations, fail)
882
+ const actualType = inferValueExpressionType(
883
+ statement.expression,
884
+ scope,
885
+ functions,
886
+ records,
887
+ fail,
888
+ normalizeOperations,
889
+ "a returned value",
890
+ returnType
891
+ )
892
+
893
+ if (!sameType(actualType, returnType)) {
894
+ fail("TYPE_MISMATCH", `Return type ${typeDescription(actualType)}; expected ${typeDescription(returnType)}.`, statement.expression.location)
895
+ }
896
+ }
897
+ if (statement.expression) addExpressionEffects(raises, statement.expression, callEffects)
898
+ normal = false
899
+ continue
900
+ }
901
+ if (statement.kind == "IfStatement") {
902
+ const conditionType = inferValueExpressionType(statement.condition, scope, functions, records, fail, normalizeOperations, "a condition")
903
+
904
+ if (!isScalarType(conditionType, "boolean")) {
905
+ fail("NON_BOOLEAN_CONDITION", `If condition type ${typeDescription(conditionType)}; expected boolean.`, statement.condition.location)
906
+ }
907
+ const visibleBindings = bindingsVisibleFrom(scope)
908
+ const initialKnownValues = visibleBindings.map((binding) => binding.knownValue)
909
+ const consequentScope = createScope(scope, statement.consequent.statements)
910
+ const alternateScope = createScope(scope, statement.alternate?.statements ?? [])
911
+ applyPresenceNarrowing(statement.condition, scope, consequentScope, true)
912
+ applyPresenceNarrowing(statement.condition, scope, alternateScope, false)
913
+ const consequentReturns = validateBlock(statement.consequent, consequentScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
914
+ const consequentKnownValues = visibleBindings.map((binding) => binding.knownValue)
915
+
916
+ visibleBindings.forEach((binding, index) => {
917
+ binding.knownValue = initialKnownValues[index]
918
+ })
919
+ let alternateReturns = {normal: true, raises: new Set()}
920
+
921
+ if (statement.alternate) {
922
+ alternateReturns = validateBlock(statement.alternate, alternateScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
923
+ }
924
+ const alternateKnownValues = visibleBindings.map((binding) => binding.knownValue)
925
+
926
+ visibleBindings.forEach((binding, index) => {
927
+ if (!consequentReturns.normal && alternateReturns.normal) binding.knownValue = alternateKnownValues[index]
928
+ else if (consequentReturns.normal && !alternateReturns.normal) binding.knownValue = consequentKnownValues[index]
929
+ else if (consequentReturns.normal && alternateReturns.normal && consequentKnownValues[index] === alternateKnownValues[index]) {
930
+ binding.knownValue = consequentKnownValues[index]
931
+ } else binding.knownValue = undefined
932
+ })
933
+ const continuingProofs = !consequentReturns.normal && alternateReturns.normal
934
+ ? alternateScope.presenceProofs
935
+ : consequentReturns.normal && !alternateReturns.normal
936
+ ? consequentScope.presenceProofs
937
+ : consequentReturns.normal && alternateReturns.normal
938
+ ? new Set([...consequentScope.presenceProofs].filter((binding) => alternateScope.presenceProofs.has(binding)))
939
+ : new Set()
940
+ const visibleSet = new Set(visibleBindings)
941
+
942
+ scope.presenceProofs.clear()
943
+ for (const binding of continuingProofs) {
944
+ if (visibleSet.has(binding)) scope.presenceProofs.add(binding)
945
+ }
946
+ addExpressionEffects(raises, statement.condition, callEffects)
947
+ addAll(raises, consequentReturns.raises)
948
+ addAll(raises, alternateReturns.raises)
949
+ normal = consequentReturns.normal || alternateReturns.normal
950
+ continue
951
+ }
952
+ if (statement.kind == "WhileStatement") {
953
+ const assignedOuterBindings = outerMutableBindingsAssignedBy(statement.body, scope)
954
+
955
+ for (const binding of assignedOuterBindings) {
956
+ binding.knownValue = undefined
957
+ scope.presenceProofs.delete(binding)
958
+ }
959
+ const conditionType = inferValueExpressionType(
960
+ statement.condition, scope, functions, records, fail, normalizeOperations, "a condition-controlled loop condition"
961
+ )
962
+
963
+ if (!isScalarType(conditionType, "boolean")) {
964
+ fail("NON_BOOLEAN_CONDITION", `While condition type ${typeDescription(conditionType)}; expected boolean.`, statement.condition.location)
965
+ }
966
+ const loopScope = createScope(scope, statement.body.statements)
967
+
968
+ applyPresenceNarrowing(statement.condition, scope, loopScope, true)
969
+ const bodyFlow = validateBlock(statement.body, loopScope, returnType, functions, records, errors, callEffects, fail,
970
+ normalizeOperations, {nearest: statement, owner: loopContext.owner, owners: loopContext.owners})
971
+
972
+ addExpressionEffects(raises, statement.condition, callEffects)
973
+ addAll(raises, bodyFlow.raises)
974
+ for (const binding of assignedOuterBindings) {
975
+ binding.knownValue = undefined
976
+ scope.presenceProofs.delete(binding)
977
+ }
978
+ continue
979
+ }
980
+ if (statement.kind == "ForEachStatement") {
981
+ const listType = inferValueExpressionType(statement.list, scope, functions, records, fail, normalizeOperations, "an iteration collection")
982
+
983
+ if (listType.kind != "ListType") {
984
+ fail("TYPE_MISMATCH", `Iteration collection type ${typeDescription(listType)}; expected list.`, statement.list.location)
985
+ }
986
+ if (!statement.valueBinding || statement.valueBinding.kind != "ValueBinding") {
987
+ fail("TYPE_MISMATCH", "Iteration requires one typed value binding.", statement.location)
988
+ }
989
+ if (statement.valueBinding.mutable !== false) {
990
+ fail("TYPE_MISMATCH", "Iteration binding must be immutable.", statement.valueBinding.location ?? statement.location)
991
+ }
992
+ const bindingType = validateValueTypeReference(statement.valueBinding.type, statement.valueBinding.location, fail, undefined, records, scope.typeParameters)
993
+
994
+ if (!sameType(bindingType, listType.elementType)) {
995
+ fail("TYPE_MISMATCH", `Iteration binding type ${typeDescription(bindingType)}; expected ${typeDescription(listType.elementType)}.`,
996
+ typeLocation(statement.valueBinding.type, statement.valueBinding.location))
997
+ }
998
+ const assignedOuterBindings = outerMutableBindingsAssignedBy(statement.body, scope)
999
+
1000
+ for (const binding of assignedOuterBindings) {
1001
+ binding.knownValue = undefined
1002
+ scope.presenceProofs.delete(binding)
1003
+ }
1004
+ const loopScope = createScope(scope, statement.body.statements)
1005
+
1006
+ declareBinding(statement.valueBinding.name, {
1007
+ knownValue: undefined,
1008
+ mutable: false,
1009
+ type: bindingType
1010
+ }, roleLocation(statement.valueBinding, "name"), loopScope, fail)
1011
+ const bodyFlow = validateBlock(statement.body, loopScope, returnType, functions, records, errors, callEffects, fail,
1012
+ normalizeOperations, {nearest: statement, owner: loopContext.owner, owners: loopContext.owners})
1013
+ addExpressionEffects(raises, statement.list, callEffects)
1014
+ addAll(raises, bodyFlow.raises)
1015
+ for (const binding of assignedOuterBindings) {
1016
+ binding.knownValue = undefined
1017
+ scope.presenceProofs.delete(binding)
1018
+ }
1019
+ continue
1020
+ }
1021
+ if (statement.kind == "ForEachMapStatement") {
1022
+ const mapType = inferValueExpressionType(statement.map, scope, functions, records, fail, normalizeOperations,
1023
+ "a map iteration collection")
1024
+
1025
+ if (mapType.kind != "OrderedMapType") {
1026
+ fail("TYPE_MISMATCH", `Map iteration collection type ${typeDescription(mapType)}; expected ordered map.`, statement.map.location)
1027
+ }
1028
+ if (!statement.keyBinding || statement.keyBinding.kind != "ValueBinding" ||
1029
+ !statement.valueBinding || statement.valueBinding.kind != "ValueBinding") {
1030
+ fail("TYPE_MISMATCH", "Map iteration requires exact key and value bindings.", statement.location)
1031
+ }
1032
+ if (statement.keyBinding.mutable !== false || statement.valueBinding.mutable !== false) {
1033
+ fail("TYPE_MISMATCH", "Map iteration bindings must be immutable.",
1034
+ statement.keyBinding.mutable !== false ? statement.keyBinding.location : statement.valueBinding.location)
1035
+ }
1036
+ const keyType = validateValueTypeReference(statement.keyBinding.type, statement.keyBinding.location, fail,
1037
+ undefined, records, scope.typeParameters)
1038
+ const valueType = validateValueTypeReference(statement.valueBinding.type, statement.valueBinding.location, fail,
1039
+ undefined, records, scope.typeParameters)
1040
+
1041
+ if (!sameType(keyType, mapType.keyType)) {
1042
+ fail("TYPE_MISMATCH", `Map iteration key binding type ${typeDescription(keyType)}; expected ${typeDescription(mapType.keyType)}.`,
1043
+ typeLocation(statement.keyBinding.type, statement.keyBinding.location))
1044
+ }
1045
+ if (!sameType(valueType, mapType.valueType)) {
1046
+ fail("TYPE_MISMATCH", `Map iteration value binding type ${typeDescription(valueType)}; expected ${typeDescription(mapType.valueType)}.`,
1047
+ typeLocation(statement.valueBinding.type, statement.valueBinding.location))
1048
+ }
1049
+ const assignedOuterBindings = outerMutableBindingsAssignedBy(statement.body, scope)
1050
+
1051
+ for (const binding of assignedOuterBindings) {
1052
+ binding.knownValue = undefined
1053
+ scope.presenceProofs.delete(binding)
1054
+ }
1055
+ const loopScope = createScope(scope, statement.body.statements)
1056
+
1057
+ declareBinding(statement.keyBinding.name, {knownValue: undefined, mutable: false, type: keyType},
1058
+ roleLocation(statement.keyBinding, "name"), loopScope, fail)
1059
+ declareBinding(statement.valueBinding.name, {knownValue: undefined, mutable: false, type: valueType},
1060
+ roleLocation(statement.valueBinding, "name"), loopScope, fail)
1061
+ const bodyFlow = validateBlock(statement.body, loopScope, returnType, functions, records, errors, callEffects,
1062
+ fail, normalizeOperations, {nearest: statement, owner: loopContext.owner, owners: loopContext.owners})
1063
+
1064
+ addExpressionEffects(raises, statement.map, callEffects)
1065
+ addAll(raises, bodyFlow.raises)
1066
+ for (const binding of assignedOuterBindings) {
1067
+ binding.knownValue = undefined
1068
+ scope.presenceProofs.delete(binding)
1069
+ }
1070
+ continue
1071
+ }
1072
+ if (statement.kind == "RaiseStatement") {
1073
+ if (!statement.error || statement.error.kind != "ErrorConstruction") {
1074
+ fail("NON_ERROR_RAISE", "Raise requires one constructed semantic error.", statement.location)
1075
+ }
1076
+ const raisedType = validateErrorType(statement.error.error, statement.error.location, errors, fail)
1077
+ const messageType = inferValueExpressionType(
1078
+ statement.error.message, scope, functions, records, fail, normalizeOperations, "an error message", scalarType("string")
1079
+ )
1080
+
1081
+ if (!isScalarType(messageType, "string")) {
1082
+ fail("NON_ERROR_RAISE", `Error message type ${typeDescription(messageType)}; expected string.`, statement.error.message.location)
1083
+ }
1084
+ addExpressionEffects(raises, statement.error.message, callEffects)
1085
+ raises.add(raisedType.declarationId)
1086
+ normal = false
1087
+ continue
1088
+ }
1089
+ if (statement.kind == "TryStatement") {
1090
+ const caughtType = validateErrorType(statement.catchType, statement.location, errors, fail)
1091
+
1092
+ if (!statement.catchBinding || statement.catchBinding.kind != "CatchBinding" || statement.catchBinding.mutable !== false) {
1093
+ fail("INVALID_ERROR_HANDLER", "Catch requires one immutable typed binding.", statement.catchBinding?.location ?? statement.location)
1094
+ }
1095
+ const bindingType = validateErrorType(statement.catchBinding.type, statement.catchBinding.location, errors, fail)
1096
+
1097
+ if (bindingType.declarationId != caughtType.declarationId) {
1098
+ fail("INVALID_ERROR_HANDLER", "Catch binding type must exactly match its handler type.", statement.catchBinding.location)
1099
+ }
1100
+ const bodyAssignedOuterBindings = outerMutableBindingsAssignedBy(statement.body, scope)
1101
+ const assignedOuterBindings = new Set(bodyAssignedOuterBindings)
1102
+
1103
+ outerMutableBindingsAssignedBy(statement.catchBody, scope, assignedOuterBindings)
1104
+ const bodyScope = createScope(scope, statement.body.statements)
1105
+ const bodyFlow = validateBlock(statement.body, bodyScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
1106
+ const catchScope = createScope(scope, statement.catchBody.statements)
1107
+
1108
+ for (const binding of bodyAssignedOuterBindings) {
1109
+ binding.knownValue = undefined
1110
+ catchScope.presenceProofs.delete(binding)
1111
+ }
1112
+
1113
+ declareBinding(statement.catchBinding.name, {
1114
+ knownValue: undefined,
1115
+ mutable: false,
1116
+ type: bindingType
1117
+ }, roleLocation(statement.catchBinding, "name"), catchScope, fail)
1118
+ const catchFlow = validateBlock(statement.catchBody, catchScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
1119
+
1120
+ if (!bodyFlow.raises.has(caughtType.declarationId)) {
1121
+ fail("UNREACHABLE_HANDLER", `Handler for '${errors.get(caughtType.declarationId)?.name}' cannot be reached.`, typeLocation(statement.catchType, statement.location))
1122
+ }
1123
+ bodyFlow.raises.delete(caughtType.declarationId)
1124
+ addAll(raises, bodyFlow.raises)
1125
+ addAll(raises, catchFlow.raises)
1126
+ normal = bodyFlow.normal || catchFlow.normal
1127
+ for (const binding of assignedOuterBindings) {
1128
+ binding.knownValue = undefined
1129
+ scope.presenceProofs.delete(binding)
1130
+ }
1131
+ continue
1132
+ }
1133
+
1134
+ const unexpected = /** @type {{kind: string, location: import("./types.js").SourceLocation}} */ (statement)
1135
+
1136
+ fail("UNSUPPORTED_STATEMENT", unexpected.kind, unexpected.location)
1137
+ }
1138
+
1139
+ return {normal, raises}
1140
+ }
1141
+
1142
+ /**
1143
+ * Validates one exact nominal error type.
1144
+ * @param {unknown} type - Candidate type.
1145
+ * @param {import("./types.js").SourceLocation} location - Owning location.
1146
+ * @param {ErrorRegistry} errors - Visible error declarations.
1147
+ * @param {SemanticFail} fail - Diagnostic callback.
1148
+ * @returns {import("./types.js").ErrorType} Validated type.
1149
+ */
1150
+ function validateErrorType(type, location, errors, fail) {
1151
+ if (!type || typeof type != "object" || Array.isArray(type)) {
1152
+ return fail("UNKNOWN_ERROR", "Handler or raise is missing an exact error type.", location)
1153
+ }
1154
+ const candidate = /** @type {import("./types.js").ErrorType} */ (type)
1155
+
1156
+ if (candidate.kind != "ErrorType" ||
1157
+ Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "declarationId,kind" ||
1158
+ typeof candidate.declarationId != "string" || !errors.has(candidate.declarationId)) {
1159
+ fail("UNKNOWN_ERROR", `Unknown error declaration '${String(candidate.declarationId)}'.`, typeLocation(candidate, location))
1160
+ }
1161
+
1162
+ return candidate
1163
+ }
1164
+
1165
+ /**
1166
+ * Adds every source value to one set.
1167
+ * @param {Set<string>} target - Destination identities.
1168
+ * @param {Set<string>} source - Source identities.
1169
+ * @returns {void}
1170
+ */
1171
+ function addAll(target, source) {
1172
+ for (const value of source) target.add(value)
1173
+ }
1174
+
1175
+ /**
1176
+ * Adds unchecked errors raised while evaluating one expression.
1177
+ * @param {Set<string>} target - Destination identities.
1178
+ * @param {unknown} expression - Candidate semantic expression.
1179
+ * @param {Map<string, Set<string>>} callEffects - Inferred call effects.
1180
+ * @param {Map<string, Set<string>>} [methodEffects] - Inferred effects by resolved method identity.
1181
+ * @returns {void}
1182
+ */
1183
+ function addExpressionEffects(target, expression, callEffects, methodEffects = new Map()) {
1184
+ const pending = [expression]
1185
+ const seen = new Set()
1186
+
1187
+ while (pending.length > 0) {
1188
+ const value = pending.pop()
1189
+
1190
+ if (!value || typeof value != "object" || seen.has(value)) continue
1191
+ seen.add(value)
1192
+ if (Reflect.get(value, "kind") == "CallExpression") addAll(target, callEffects.get(Reflect.get(value, "callee")) ?? new Set())
1193
+ if (Reflect.get(value, "kind") == "MethodCallExpression" || Reflect.get(value, "kind") == "ReferenceConstruction") {
1194
+ const failures = Reflect.get(value, "failureIds")
1195
+ if (Array.isArray(failures)) for (const failure of failures) if (typeof failure == "string") target.add(failure)
1196
+ if (Reflect.get(value, "kind") == "MethodCallExpression") {
1197
+ addAll(target, methodEffects.get(Reflect.get(value, "method")) ?? new Set())
1198
+ }
1199
+ }
1200
+ if (Reflect.get(value, "kind") == "EffectCallExpression") {
1201
+ const resolution = Reflect.get(value, "resolution")
1202
+ const failures = resolution && typeof resolution == "object" ? Reflect.get(resolution, "failureIds") : undefined
1203
+
1204
+ if (Array.isArray(failures)) for (const failure of failures) if (typeof failure == "string") target.add(failure)
1205
+ }
1206
+ for (const [key, child] of Object.entries(value)) {
1207
+ if (["location", "sourceProvenance", "resolution"].includes(key)) continue
1208
+ if (Array.isArray(child)) pending.push(...child)
1209
+ else if (child && typeof child == "object") pending.push(child)
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ /**
1215
+ * Derives unchecked-error propagation over the resolved finite function graph.
1216
+ * This is validator state, never a checked effect in the public function signature.
1217
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Resolved functions.
1218
+ * @param {Map<string, Set<string>>} [seedEffects] - Dependency effects keyed by local import name.
1219
+ * @param {Set<string>} [localFunctionNames] - Functions whose bodies belong to the current module.
1220
+ * @returns {Map<string, Set<string>>} Escaping error identities by source-visible function name.
1221
+ */
1222
+ function inferCallEffects(functions, seedEffects = new Map(), localFunctionNames = new Set(functions.keys())) {
1223
+ const effects = new Map([...functions.keys()].map((name) => [name, new Set(seedEffects.get(name) ?? [])]))
1224
+ let changed = true
1225
+
1226
+ while (changed) {
1227
+ changed = false
1228
+ for (const name of localFunctionNames) {
1229
+ const declaration = functions.get(name)
1230
+
1231
+ if (!declaration) continue
1232
+ const next = syntacticBlockEffects(declaration.body, effects)
1233
+ const current = /** @type {Set<string>} */ (effects.get(name))
1234
+
1235
+ for (const error of next) {
1236
+ if (!current.has(error)) {
1237
+ current.add(error)
1238
+ changed = true
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+
1244
+ return effects
1245
+ }
1246
+
1247
+ /**
1248
+ * Computes escaping identities from one block for the call-effect fixed point.
1249
+ * @param {unknown} block - Candidate block.
1250
+ * @param {Map<string, Set<string>>} callEffects - Current fixed-point state.
1251
+ * @param {Map<string, Set<string>>} [methodEffects] - Current method fixed-point state.
1252
+ * @returns {Set<string>} Escaping identities.
1253
+ */
1254
+ function syntacticBlockEffects(block, callEffects, methodEffects = new Map()) {
1255
+ const result = new Set()
1256
+ const statements = Reflect.get(/** @type {object} */ (block ?? {}), "statements")
1257
+
1258
+ if (!Array.isArray(statements)) return result
1259
+ for (const statement of statements) {
1260
+ if (!statement || typeof statement != "object") continue
1261
+ const kind = Reflect.get(statement, "kind")
1262
+
1263
+ if (kind == "RaiseStatement") {
1264
+ const construction = Reflect.get(statement, "error")
1265
+ const type = construction && typeof construction == "object" ? Reflect.get(construction, "error") : undefined
1266
+ const declarationId = type && typeof type == "object" ? Reflect.get(type, "declarationId") : undefined
1267
+
1268
+ if (typeof declarationId == "string") result.add(declarationId)
1269
+ if (construction && typeof construction == "object") {
1270
+ addExpressionEffects(result, Reflect.get(construction, "message"), callEffects, methodEffects)
1271
+ }
1272
+ continue
1273
+ }
1274
+ if (kind == "TryStatement") {
1275
+ const bodyEffects = syntacticBlockEffects(Reflect.get(statement, "body"), callEffects, methodEffects)
1276
+ const catchType = Reflect.get(statement, "catchType")
1277
+ const caughtId = catchType && typeof catchType == "object" ? Reflect.get(catchType, "declarationId") : undefined
1278
+
1279
+ if (typeof caughtId == "string" && bodyEffects.delete(caughtId)) {
1280
+ addAll(bodyEffects, syntacticBlockEffects(Reflect.get(statement, "catchBody"), callEffects, methodEffects))
1281
+ }
1282
+ addAll(result, bodyEffects)
1283
+ continue
1284
+ }
1285
+ if (kind == "IfStatement") {
1286
+ addExpressionEffects(result, Reflect.get(statement, "condition"), callEffects, methodEffects)
1287
+ addAll(result, syntacticBlockEffects(Reflect.get(statement, "consequent"), callEffects, methodEffects))
1288
+ addAll(result, syntacticBlockEffects(Reflect.get(statement, "alternate"), callEffects, methodEffects))
1289
+ continue
1290
+ }
1291
+ if (kind == "ForEachStatement" || kind == "ForEachMapStatement") {
1292
+ addExpressionEffects(result, Reflect.get(statement, kind == "ForEachStatement" ? "list" : "map"), callEffects,
1293
+ methodEffects)
1294
+ addAll(result, syntacticBlockEffects(Reflect.get(statement, "body"), callEffects, methodEffects))
1295
+ continue
1296
+ }
1297
+ if (kind == "WhileStatement") {
1298
+ addExpressionEffects(result, Reflect.get(statement, "condition"), callEffects, methodEffects)
1299
+ addAll(result, syntacticBlockEffects(Reflect.get(statement, "body"), callEffects, methodEffects))
1300
+ continue
1301
+ }
1302
+ for (const key of ["expression", "initializer"]) {
1303
+ addExpressionEffects(result, Reflect.get(statement, key), callEffects, methodEffects)
1304
+ }
1305
+ }
1306
+
1307
+ return result
1308
+ }
1309
+
1310
+ /**
1311
+ * Derives the escaping user-defined error sets for local functions and resolved class methods.
1312
+ * The fixed point is computed over every unchecked error; capability failures are projected out
1313
+ * because call sites already carry their capability-failure boundary through resolution metadata.
1314
+ * @param {import("./types.js").SemanticModule} module - Resolved module.
1315
+ * @param {Map<string, Set<string>>} [seedEffects] - Dependency effects keyed by local import name.
1316
+ * @returns {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} Escaping user-defined error identities by function name and method identity.
1317
+ */
1318
+ export function inferEscapingErrorEffects(module, seedEffects = new Map()) {
1319
+ const functions = new Map(module.functions.map((declaration) => [declaration.name, declaration]))
1320
+ const functionEffects = new Map([...functions.keys()].map((name) => [name, new Set(seedEffects.get(name) ?? [])]))
1321
+ const classes = module.classes ?? []
1322
+ const methodEffects = new Map()
1323
+ const capabilityFailureIds = new Set((module.capabilities ?? []).flatMap(({failures}) => failures).map(({id}) => id))
1324
+
1325
+ for (const declaration of classes) for (const method of declaration.methods) methodEffects.set(method.id, new Set())
1326
+ let changed = true
1327
+
1328
+ while (changed) {
1329
+ changed = false
1330
+ for (const declaration of module.functions) {
1331
+ const current = /** @type {Set<string>} */ (functionEffects.get(declaration.name))
1332
+ for (const error of syntacticBlockEffects(declaration.body, functionEffects, methodEffects)) {
1333
+ if (!current.has(error)) { current.add(error); changed = true }
1334
+ }
1335
+ }
1336
+ for (const declaration of classes) {
1337
+ for (const method of declaration.methods) {
1338
+ const current = /** @type {Set<string>} */ (methodEffects.get(method.id))
1339
+ for (const error of syntacticBlockEffects(method.body, functionEffects, methodEffects)) {
1340
+ if (!current.has(error)) { current.add(error); changed = true }
1341
+ }
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ for (const effects of [functionEffects, methodEffects]) {
1347
+ for (const errors of effects.values()) {
1348
+ for (const error of [...errors]) if (capabilityFailureIds.has(error)) errors.delete(error)
1349
+ }
1350
+ }
1351
+
1352
+ return {functions: functionEffects, methods: methodEffects}
1353
+ }
1354
+
1355
+ /**
1356
+ * Creates a lexical scope with declarations marked pending until visited.
1357
+ * @param {Scope | undefined} parent - Enclosing scope.
1358
+ * @param {{kind: string, name?: string}[]} statements - Scope statements.
1359
+ * @param {Set<string>} [reservedNames] - Names unavailable to root bindings.
1360
+ * @param {Set<string>} [typeParameters] - Declaration-scoped type parameters.
1361
+ * @param {import("./types.js").ClassDeclaration} [currentClass] - Declaring class for receiver-private access.
1362
+ * @returns {Scope} Scope.
1363
+ */
1364
+ function createScope(parent, statements, reservedNames = new Set(), typeParameters = parent?.typeParameters ?? new Set(),
1365
+ currentClass = parent?.currentClass) {
1366
+ const pending = new Set(statements.filter((statement) => statement.kind == "LocalDeclaration")
1367
+ .map((statement) => /** @type {{name: string}} */ (statement).name))
1368
+
1369
+ return {
1370
+ bindings: new Map(),
1371
+ currentClass,
1372
+ parent,
1373
+ pending,
1374
+ presenceProofs: new Set(parent?.presenceProofs ?? []),
1375
+ typeParameters,
1376
+ usedNames: parent?.usedNames ?? new Set(reservedNames)
1377
+ }
1378
+ }
1379
+
1380
+ /**
1381
+ * Applies a simple-identifier presence proof to exactly the branch where the test is true.
1382
+ * @param {import("./types.js").Expression} condition - Validated branch condition.
1383
+ * @param {Scope} sourceScope - Scope containing the tested binding.
1384
+ * @param {Scope} branchScope - Branch-local proof scope.
1385
+ * @param {boolean} branchWhenTrue - Whether this is the condition's true branch.
1386
+ * @returns {void}
1387
+ */
1388
+ function applyPresenceNarrowing(condition, sourceScope, branchScope, branchWhenTrue) {
1389
+ let test = condition
1390
+ let presentWhenTrue = true
1391
+
1392
+ if (condition.kind == "UnaryExpression" && condition.operation == "BooleanNot" &&
1393
+ condition.operand.kind == "OptionalIsPresent") {
1394
+ test = condition.operand
1395
+ presentWhenTrue = false
1396
+ }
1397
+ if (test.kind != "OptionalIsPresent") return
1398
+ const binding = findBinding(test.operand.name, sourceScope)
1399
+
1400
+ if (!binding) return
1401
+ if (branchWhenTrue == presentWhenTrue) branchScope.presenceProofs.add(binding)
1402
+ else branchScope.presenceProofs.delete(binding)
1403
+ }
1404
+
1405
+ /**
1406
+ * Adds one binding while rejecting duplicates and shadowing.
1407
+ * @param {string} name - Binding name.
1408
+ * @param {Binding} binding - Binding metadata.
1409
+ * @param {import("./types.js").SourceLocation} location - Declaration location.
1410
+ * @param {Scope} scope - Current scope.
1411
+ * @param {SemanticFail} fail - Diagnostic callback.
1412
+ * @returns {void}
1413
+ */
1414
+ function declareBinding(name, binding, location, scope, fail) {
1415
+ if (scope.usedNames.has(name)) fail("DUPLICATE_BINDING", `Duplicate or shadowed binding '${name}'.`, location)
1416
+
1417
+ scope.bindings.set(name, binding)
1418
+ scope.usedNames.add(name)
1419
+ }
1420
+
1421
+ /**
1422
+ * Resolves one identifier and distinguishes use-before-declaration.
1423
+ * @param {string} name - Binding name.
1424
+ * @param {import("./types.js").SourceLocation} location - Reference location.
1425
+ * @param {Scope} scope - Current scope.
1426
+ * @param {SemanticFail} fail - Diagnostic callback.
1427
+ * @returns {Binding} Binding.
1428
+ */
1429
+ function resolveBinding(name, location, scope, fail) {
1430
+ for (let current = /** @type {Scope | undefined} */ (scope); current; current = current.parent) {
1431
+ const binding = current.bindings.get(name)
1432
+
1433
+ if (binding) return binding
1434
+ if (current.pending.has(name)) {
1435
+ return fail("USE_BEFORE_DECLARATION", `Binding '${name}' is used before its declaration.`, location)
1436
+ }
1437
+ }
1438
+
1439
+ return fail("UNRESOLVED_BINDING", `Unknown binding '${name}'.`, location)
1440
+ }
1441
+
1442
+ /**
1443
+ * Validates one semantic value type reference.
1444
+ * @param {unknown} type - Candidate semantic type reference.
1445
+ * @param {import("./types.js").SourceLocation} location - Owning source location.
1446
+ * @param {SemanticFail} fail - Diagnostic callback.
1447
+ * @param {Set<object>} [seen] - Active recursive type path.
1448
+ * @param {RecordRegistry} [records] - Record declarations by identity.
1449
+ * @param {Set<string>} [typeParameters] - Type-variable identities in declaration scope.
1450
+ * @returns {import("./types.js").SemanticValueType} Validated value type.
1451
+ */
1452
+ function validateValueTypeReference(type, location, fail, seen, records = new Map(), typeParameters = new Set()) {
1453
+ const candidate = validateTypeReference(type, location, fail, seen, records, typeParameters)
1454
+
1455
+ if (isVoidType(candidate)) return fail("VOID_AS_VALUE", "Void is valid only as a function return type.", typeLocation(type, location))
1456
+
1457
+ return candidate
1458
+ }
1459
+
1460
+ /**
1461
+ * Validates one function return type reference.
1462
+ * @param {unknown} type - Candidate semantic return type reference.
1463
+ * @param {import("./types.js").SourceLocation} location - Owning source location.
1464
+ * @param {SemanticFail} fail - Diagnostic callback.
1465
+ * @param {RecordRegistry} [records] - Record declarations by identity.
1466
+ * @param {Set<string>} [typeParameters] - Type-variable identities in declaration scope.
1467
+ * @returns {import("./types.js").SemanticFunctionReturnType} Validated return type.
1468
+ */
1469
+ function validateReturnTypeReference(type, location, fail, records = new Map(), typeParameters = new Set()) {
1470
+ return validateTypeReference(type, location, fail, new Set(), records, typeParameters)
1471
+ }
1472
+
1473
+ /**
1474
+ * Validates one recursive semantic type-reference shape.
1475
+ * @param {unknown} type - Candidate semantic type reference.
1476
+ * @param {import("./types.js").SourceLocation} location - Owning source location.
1477
+ * @param {SemanticFail} fail - Diagnostic callback.
1478
+ * @param {Set<object>} [seen] - Active recursive type path.
1479
+ * @param {RecordRegistry} [records] - Record declarations by identity.
1480
+ * @param {Set<string>} [typeParameters] - Type-variable identities in declaration scope.
1481
+ * @returns {import("./types.js").SemanticFunctionReturnType} Validated type.
1482
+ */
1483
+ function validateTypeReference(type, location, fail, seen = new Set(), records = new Map(), typeParameters = new Set()) {
1484
+ if (!type || typeof type != "object" || Array.isArray(type)) {
1485
+ return fail("TYPE_MISMATCH", "Unsupported semantic type.", location)
1486
+ }
1487
+
1488
+ const candidate = /** @type {import("./types.js").SemanticFunctionReturnType} */ (type)
1489
+
1490
+ if (["ListType", "MapType", "OptionalType", "OrderedMapType", "RecordType"].includes(candidate.kind) &&
1491
+ containsOwnedType(candidate)) {
1492
+ fail("RESOURCE_ALIAS", "Owned resources and references cannot be nested in value or generic types.", typeLocation(candidate, location))
1493
+ }
1494
+
1495
+ if (candidate.kind == "TypeReference") {
1496
+ if (!isScalarTypeName(candidate.name) && candidate.name != "void") {
1497
+ fail("TYPE_MISMATCH", "Unsupported scalar or void return type.", typeLocation(candidate, location))
1498
+ }
1499
+
1500
+ return candidate
1501
+ }
1502
+ if (candidate.kind == "TypeVariableReference") {
1503
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "kind,location,parameterId" ||
1504
+ typeof candidate.parameterId != "string" || !typeParameters.has(candidate.parameterId)) {
1505
+ fail("FREE_TYPE_VARIABLE", `Free or unknown type variable '${String(candidate.parameterId)}'.`, typeLocation(candidate, location))
1506
+ }
1507
+
1508
+ return candidate
1509
+ }
1510
+ if (candidate.kind == "RecordType") {
1511
+ const keys = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
1512
+ const declaration = records.get(candidate.declarationId)
1513
+
1514
+ if (!(["arguments,declarationId,kind", "declarationId,kind"].includes(keys)) ||
1515
+ typeof candidate.declarationId != "string" || !declaration) {
1516
+ fail("UNKNOWN_RECORD", `Unknown record declaration '${String(candidate.declarationId)}'.`, typeLocation(candidate, location))
1517
+ }
1518
+ const expectedArity = declaration.typeParameters?.length ?? 0
1519
+
1520
+ if (expectedArity > 0 && candidate.arguments === undefined) {
1521
+ fail("RAW_GENERIC_APPLICATION", `Generic record '${declaration.name}' requires ${expectedArity} type arguments.`, typeLocation(candidate, location))
1522
+ }
1523
+ if (expectedArity == 0 && candidate.arguments !== undefined) {
1524
+ fail("GENERIC_ARITY_MISMATCH", `Non-generic record '${declaration.name}' does not accept type arguments.`, typeLocation(candidate, location))
1525
+ }
1526
+ const candidateArguments = candidate.arguments
1527
+
1528
+ if (candidateArguments !== undefined && (!Array.isArray(candidateArguments) ||
1529
+ candidateArguments.some((_argument, index) => !Object.hasOwn(candidateArguments, index)))) {
1530
+ fail("TYPE_MISMATCH", "Generic record arguments must be a dense ordered array.", typeLocation(candidate, location))
1531
+ }
1532
+ const arguments_ = candidate.arguments ?? []
1533
+
1534
+ if (arguments_.length != expectedArity) {
1535
+ fail("GENERIC_ARITY_MISMATCH", `Record '${declaration.name}' has ${arguments_.length} type arguments; expected ${expectedArity}.`, typeLocation(candidate, location))
1536
+ }
1537
+ if (seen.has(candidate)) {
1538
+ return fail("TYPE_MISMATCH", "Recursive generic applications cannot contain cycles.", typeLocation(candidate, location))
1539
+ }
1540
+ seen.add(candidate)
1541
+ for (const argument of arguments_) {
1542
+ validateValueTypeReference(argument, typeLocation(argument, location), fail, seen, records, typeParameters)
1543
+ }
1544
+ seen.delete(candidate)
1545
+
1546
+ return candidate
1547
+ }
1548
+ if (candidate.kind == "ReferenceType") {
1549
+ const classes = referenceClassesFor(records)
1550
+
1551
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "declarationId,kind" ||
1552
+ typeof candidate.declarationId != "string" || !classes.has(candidate.declarationId)) {
1553
+ fail("UNKNOWN_CLASS", `Unknown reference class declaration '${String(candidate.declarationId)}'.`, typeLocation(candidate, location))
1554
+ }
1555
+
1556
+ return candidate
1557
+ }
1558
+ if (candidate.kind == "OwnedResourceType") {
1559
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "kind,resourceId" ||
1560
+ typeof candidate.resourceId != "string" || !resourceIdsByRecordRegistry.get(records)?.has(candidate.resourceId)) {
1561
+ fail("RESOURCE_TYPE_MISMATCH", `Unknown owned resource '${String(candidate.resourceId)}'.`, typeLocation(candidate, location))
1562
+ }
1563
+ return candidate
1564
+ }
1565
+ if (candidate.kind == "OwnedReferenceType") {
1566
+ const classes = referenceClassesFor(records)
1567
+
1568
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "declarationId,kind" ||
1569
+ typeof candidate.declarationId != "string" || classes.get(candidate.declarationId)?.ownership?.kind != "ownedResource") {
1570
+ fail("RESOURCE_TYPE_MISMATCH", `Unknown owned reference '${String(candidate.declarationId)}'.`, typeLocation(candidate, location))
1571
+ }
1572
+ return candidate
1573
+ }
1574
+ if (candidate.kind == "ListType") {
1575
+ if (seen.has(candidate)) return fail("TYPE_MISMATCH", "Recursive collection types cannot contain cycles.", typeLocation(candidate, location))
1576
+ seen.add(candidate)
1577
+ const elementType = validateValueTypeReference(candidate.elementType, typeLocation(candidate, location, "elementType"), fail, seen, records, typeParameters)
1578
+
1579
+ seen.delete(candidate)
1580
+
1581
+ if (candidate.elementType !== elementType) candidate.elementType = elementType
1582
+
1583
+ return candidate
1584
+ }
1585
+ if (candidate.kind == "MapType" || candidate.kind == "OrderedMapType") {
1586
+ const expectedFields = candidate.kind == "OrderedMapType" ? "keyType,kind,order,valueType" : "keyType,kind,valueType"
1587
+
1588
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != expectedFields) {
1589
+ return fail("TYPE_MISMATCH", `Malformed ${candidate.kind == "OrderedMapType" ? "ordered map" : "map"} type.`,
1590
+ typeLocation(candidate, location))
1591
+ }
1592
+ if (candidate.kind == "OrderedMapType" && candidate.order != "insertion") {
1593
+ return fail("TYPE_MISMATCH", "Ordered map type requires insertion order.", typeLocation(candidate, location))
1594
+ }
1595
+ if (seen.has(candidate)) return fail("TYPE_MISMATCH", "Recursive collection types cannot contain cycles.", typeLocation(candidate, location))
1596
+ seen.add(candidate)
1597
+ const keyType = validateValueTypeReference(candidate.keyType, typeLocation(candidate, location, "keyType"), fail, seen, records, typeParameters)
1598
+
1599
+ if (!isScalarType(keyType, "string")) {
1600
+ fail("TYPE_MISMATCH", `Map key type ${typeDescription(keyType)}; expected string.`, typeLocation(candidate.keyType, location))
1601
+ }
1602
+ const valueType = validateValueTypeReference(candidate.valueType, typeLocation(candidate, location, "valueType"), fail, seen, records, typeParameters)
1603
+
1604
+ seen.delete(candidate)
1605
+
1606
+ if (candidate.keyType !== keyType) candidate.keyType = /** @type {import("./types.js").TypeReference} */ (keyType)
1607
+ if (candidate.valueType !== valueType) candidate.valueType = valueType
1608
+
1609
+ return candidate
1610
+ }
1611
+ if (candidate.kind == "OptionalType") {
1612
+ if (Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",") != "kind,valueType") {
1613
+ return fail("TYPE_MISMATCH", "Malformed optional type.", typeLocation(candidate, location))
1614
+ }
1615
+ if (seen.has(candidate)) return fail("TYPE_MISMATCH", "Recursive optional types cannot contain cycles.", typeLocation(candidate, location))
1616
+ seen.add(candidate)
1617
+ const valueType = validateValueTypeReference(candidate.valueType, typeLocation(candidate, location, "valueType"), fail, seen, records, typeParameters)
1618
+
1619
+ seen.delete(candidate)
1620
+ if (valueType.kind == "OptionalType") {
1621
+ fail("INVALID_OPTIONAL_CONSTITUENT", "Optional values cannot directly contain another optional.", typeLocation(candidate, location, "valueType"))
1622
+ }
1623
+ if (candidate.valueType !== valueType) candidate.valueType = valueType
1624
+
1625
+ return candidate
1626
+ }
1627
+
1628
+ return fail("TYPE_MISMATCH", "Unsupported semantic type.", typeLocation(candidate, location))
1629
+ }
1630
+
1631
+ /**
1632
+ * Retrieves the reference-class registry attached to the record validation context.
1633
+ * @param {RecordRegistry} records - Record validation registry.
1634
+ * @returns {ClassRegistry} Reference classes.
1635
+ */
1636
+ function referenceClassesFor(records) {
1637
+ return referenceClassesByRecordRegistry.get(records) ?? new Map()
1638
+ }
1639
+
1640
+ /**
1641
+ * Infers a value expression and rejects the non-value result of a void call.
1642
+ * @param {import("./types.js").Expression} expression - Semantic expression.
1643
+ * @param {Scope} scope - Visible lexical scope.
1644
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Module function signatures.
1645
+ * @param {RecordRegistry} records - Record declarations by identity.
1646
+ * @param {SemanticFail} fail - Diagnostic callback.
1647
+ * @param {boolean} normalizeOperations - Whether to normalize frontend intent and call resolution.
1648
+ * @param {string} context - Value context for diagnostics.
1649
+ * @param {import("./types.js").SemanticValueType} [expectedType] - Contextual collection type.
1650
+ * @param {boolean} [inferCollectionElements] - Whether a generic-call evidence pass may infer nonempty collection literals.
1651
+ * @returns {import("./types.js").SemanticValueType} Expression value type.
1652
+ */
1653
+ function inferValueExpressionType(expression, scope, functions, records, fail, normalizeOperations, context, expectedType,
1654
+ inferCollectionElements = false) {
1655
+ const allowOwnedBorrow = context == "an effect argument" || context == "a method receiver"
1656
+ const type = inferExpressionType(expression, scope, functions, records, fail, normalizeOperations, expectedType,
1657
+ inferCollectionElements, allowOwnedBorrow)
1658
+
1659
+ if (type == "void") return fail("VOID_AS_VALUE", `Void call cannot be used as ${context}.`, expression.location)
1660
+ if (type.kind == "ErrorType") return fail("TYPE_MISMATCH", `Error value cannot be used as ${context}.`, expression.location)
1661
+
1662
+ return type
1663
+ }
1664
+
1665
+ /**
1666
+ * Infers and validates one expression.
1667
+ * @param {import("./types.js").Expression} expression - Semantic expression.
1668
+ * @param {Scope} scope - Visible lexical scope.
1669
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Module function signatures.
1670
+ * @param {RecordRegistry} records - Record declarations by identity.
1671
+ * @param {SemanticFail} fail - Diagnostic callback.
1672
+ * @param {boolean} normalizeOperations - Whether to replace transient frontend operation intent.
1673
+ * @param {import("./types.js").SemanticValueType} [expectedType] - Contextual collection type.
1674
+ * @param {boolean} [inferCollectionElements] - Whether a generic-call evidence pass may infer nonempty collection literals.
1675
+ * @param {boolean} [allowOwnedBorrow] - Whether this exact call position admits an immediate borrow.
1676
+ * @returns {import("./types.js").SemanticBindingType | "void"} Expression type.
1677
+ */
1678
+ function inferExpressionType(expression, scope, functions, records, fail, normalizeOperations, expectedType,
1679
+ inferCollectionElements = false, allowOwnedBorrow = false) {
1680
+ if (expression.kind == "IdentifierExpression") return resolveBinding(expression.name, expression.location, scope, fail).type
1681
+
1682
+ if (expression.kind == "ReceiverExpression") {
1683
+ if (!scope.currentClass || expression.classId != scope.currentClass.id) {
1684
+ return fail("ILLEGAL_PRIVATE_ACCESS", "Receiver expression is valid only inside its declaring class.", expression.location)
1685
+ }
1686
+
1687
+ return {declarationId: /** @type {string} */ (scope.currentClass.id), kind: "ReferenceType"}
1688
+ }
1689
+
1690
+ if (expression.kind == "PrivateFieldRead") {
1691
+ const declaration = scope.currentClass
1692
+
1693
+ if (!declaration || !expression.receiver || expression.receiver.kind != "ReceiverExpression" ||
1694
+ expression.receiver.classId != declaration.id) {
1695
+ return fail("ILLEGAL_PRIVATE_ACCESS", "Private field receiver must be the declaring class receiver.", expression.location)
1696
+ }
1697
+ const field = declaration.fields.find((candidate) => candidate.id == expression.field)
1698
+
1699
+ if (!field) return fail("ILLEGAL_PRIVATE_ACCESS", "Private field identity is not owned by the declaring class.", expression.location)
1700
+
1701
+ return field.type
1702
+ }
1703
+
1704
+ if (expression.kind == "OwnedBorrowExpression") {
1705
+ if (!allowOwnedBorrow) {
1706
+ return fail("BORROW_ESCAPE", "BORROW_ESCAPE: Owned borrows are valid only as immediate capability arguments or method receivers.",
1707
+ expression.location)
1708
+ }
1709
+ if (expression.mode != "shared" && expression.mode != "exclusive") {
1710
+ return fail("INVALID_RESOURCE_BORROW", "INVALID_RESOURCE_BORROW: Owned borrow mode must be shared or exclusive.",
1711
+ expression.location)
1712
+ }
1713
+ const borrowed = inferValueExpressionType(expression.expression, scope, functions, records, fail, normalizeOperations, "an owned borrow")
1714
+
1715
+ if (borrowed.kind != "OwnedResourceType" && borrowed.kind != "OwnedReferenceType") {
1716
+ return fail("INVALID_RESOURCE_BORROW", "Borrowed expression must be an owned resource or owned reference.", expression.location)
1717
+ }
1718
+ if (!sameType(borrowed, expression.type)) {
1719
+ fail("RESOURCE_TYPE_MISMATCH", "Borrow metadata does not match its resource place.", expression.location)
1720
+ }
1721
+ return borrowed
1722
+ }
1723
+
1724
+ if (expression.kind == "OwnedMoveExpression") {
1725
+ const moved = inferValueExpressionType(expression.expression, scope, functions, records, fail, normalizeOperations, "an owned move")
1726
+
1727
+ if (moved.kind != "OwnedResourceType" && moved.kind != "OwnedReferenceType") {
1728
+ return fail("INVALID_RESOURCE_TRANSFER", "Moved expression must be an owned resource or owned reference.", expression.location)
1729
+ }
1730
+ if (!sameType(moved, expression.type)) {
1731
+ fail("RESOURCE_TYPE_MISMATCH", "Move metadata does not match its resource place.", expression.location)
1732
+ }
1733
+ return moved
1734
+ }
1735
+
1736
+ if (expression.kind == "ErrorMessageRead") {
1737
+ if (!expression.receiver || expression.receiver.kind != "IdentifierExpression") {
1738
+ return fail("INVALID_MEMBER_RECEIVER", "Error message receiver must be one catch binding.", expression.location)
1739
+ }
1740
+ const receiver = resolveBinding(expression.receiver.name, expression.receiver.location, scope, fail)
1741
+
1742
+ if (receiver.type.kind != "ErrorType") {
1743
+ return fail("INVALID_MEMBER_RECEIVER", "Message member requires a typed error catch binding.", expression.receiver.location)
1744
+ }
1745
+
1746
+ return scalarType("string")
1747
+ }
1748
+
1749
+ if (expression.kind == "IntegerLiteral") {
1750
+ if (!Number.isSafeInteger(expression.value)) fail("TYPE_MISMATCH", "Non-safe integer literal.", expression.location)
1751
+ return scalarType("integer")
1752
+ }
1753
+ if (expression.kind == "BooleanLiteral") {
1754
+ if (typeof Reflect.get(expression, "value") != "boolean") {
1755
+ fail("TYPE_MISMATCH", "Invalid boolean literal value.", expression.location)
1756
+ }
1757
+ return scalarType("boolean")
1758
+ }
1759
+ if (expression.kind == "StringLiteral") {
1760
+ const value = Reflect.get(expression, "value")
1761
+
1762
+ if (typeof value != "string" || !hasOnlyUnicodeScalars(value)) {
1763
+ fail("TYPE_MISMATCH", "Invalid Unicode string literal.", expression.location)
1764
+ }
1765
+ return scalarType("string")
1766
+ }
1767
+ if (expression.kind == "OptionalNone") {
1768
+ if (!expectedType || expectedType.kind != "OptionalType") {
1769
+ return fail("MISSING_TYPE", "Optional absence requires an explicit optional type.", expression.location)
1770
+ }
1771
+
1772
+ return expectedType
1773
+ }
1774
+ if (expression.kind == "OptionalSome") {
1775
+ if (!expectedType || expectedType.kind != "OptionalType") {
1776
+ const valueType = inferValueExpressionType(
1777
+ expression.value, scope, functions, records, fail, normalizeOperations, "an optional present value"
1778
+ )
1779
+
1780
+ if (valueType.kind == "OptionalType") {
1781
+ return fail("INVALID_OPTIONAL_CONSTITUENT", "Optional values cannot directly contain another optional.", expression.value.location)
1782
+ }
1783
+
1784
+ return {kind: "OptionalType", valueType}
1785
+ }
1786
+ const valueType = inferValueExpressionType(
1787
+ expression.value, scope, functions, records, fail, normalizeOperations, "an optional present value", expectedType.valueType
1788
+ )
1789
+
1790
+ if (!sameType(valueType, expectedType.valueType)) {
1791
+ fail("TYPE_MISMATCH", `Optional present type ${typeDescription(valueType)}; expected ${typeDescription(expectedType.valueType)}.`, expression.value.location)
1792
+ }
1793
+
1794
+ return expectedType
1795
+ }
1796
+ if (expression.kind == "OptionalIsPresent") {
1797
+ if (!expression.operand || expression.operand.kind != "IdentifierExpression") {
1798
+ return fail("TYPE_MISMATCH", "Optional presence test requires one simple identifier.", expression.location)
1799
+ }
1800
+ const operandType = resolveBinding(expression.operand.name, expression.operand.location, scope, fail).type
1801
+
1802
+ if (operandType.kind != "OptionalType") {
1803
+ return fail("TYPE_MISMATCH", `Presence test received ${typeDescription(operandType)}; expected optional.`, expression.operand.location)
1804
+ }
1805
+
1806
+ return scalarType("boolean")
1807
+ }
1808
+ if (expression.kind == "OptionalUnwrap") {
1809
+ if (!expression.operand || expression.operand.kind != "IdentifierExpression") {
1810
+ return fail("TYPE_MISMATCH", "Optional unwrap requires one simple identifier.", expression.location)
1811
+ }
1812
+ const binding = resolveBinding(expression.operand.name, expression.operand.location, scope, fail)
1813
+
1814
+ if (binding.type.kind != "OptionalType") {
1815
+ return fail("TYPE_MISMATCH", `Optional unwrap received ${typeDescription(binding.type)}; expected optional.`, expression.operand.location)
1816
+ }
1817
+ if (!scope.presenceProofs.has(binding)) {
1818
+ return fail("UNCHECKED_OPTIONAL_UNWRAP", `Optional binding '${expression.operand.name}' is not proven present.`, expression.location)
1819
+ }
1820
+
1821
+ return binding.type.valueType
1822
+ }
1823
+ if (expression.kind == "ListLiteral") {
1824
+ if (!Array.isArray(expression.elements)) {
1825
+ return fail("TYPE_MISMATCH", "List literal elements must be a dense source-ordered array.", expression.location)
1826
+ }
1827
+ if ((!expectedType || expectedType.kind != "ListType") &&
1828
+ (!inferCollectionElements || expression.elements.length == 0)) {
1829
+ return fail("MISSING_TYPE", "List literal requires an explicit recursive list type.", expression.location)
1830
+ }
1831
+ /** @type {import("./types.js").SemanticValueType | undefined} */
1832
+ let inferredElementType
1833
+ for (let index = 0; index < expression.elements.length; index++) {
1834
+ if (!Object.hasOwn(expression.elements, index)) {
1835
+ fail("TYPE_MISMATCH", "Sparse list literals are outside the implemented subset.", expression.location)
1836
+ }
1837
+ const element = expression.elements[index]
1838
+ const actual = inferValueExpressionType(
1839
+ element, scope, functions, records, fail, normalizeOperations, "a list element",
1840
+ expectedType?.kind == "ListType" ? expectedType.elementType : inferredElementType, inferCollectionElements
1841
+ )
1842
+
1843
+ inferredElementType ??= actual
1844
+ const requiredElementType = expectedType?.kind == "ListType" ? expectedType.elementType : inferredElementType
1845
+
1846
+ if (!sameType(actual, requiredElementType)) {
1847
+ fail("TYPE_MISMATCH", `List element type ${typeDescription(actual)}; expected ${typeDescription(requiredElementType)}.`, element.location)
1848
+ }
1849
+ }
1850
+
1851
+ return expectedType?.kind == "ListType"
1852
+ ? expectedType
1853
+ : {elementType: /** @type {import("./types.js").SemanticValueType} */ (inferredElementType), kind: "ListType"}
1854
+ }
1855
+ if (expression.kind == "MapLiteral") {
1856
+ if (expectedType && expectedType.kind != "MapType") {
1857
+ return fail("TYPE_MISMATCH", `Map literal context ${typeDescription(expectedType)}; expected map.`, expression.location)
1858
+ }
1859
+ if (!expectedType && !inferCollectionElements) {
1860
+ return fail("MISSING_TYPE", "Map literal requires an explicit recursive map type.", expression.location)
1861
+ }
1862
+ if (expectedType?.kind == "MapType" && !isScalarType(expectedType.keyType, "string")) {
1863
+ return fail("TYPE_MISMATCH", "Map key type must be string.", typeLocation(expectedType.keyType, expression.location))
1864
+ }
1865
+ const entries = validateMapLiteralEntries(expression, scope, functions, records, fail, normalizeOperations,
1866
+ inferCollectionElements)
1867
+
1868
+ if (!expectedType && entries.length == 0) {
1869
+ return fail("MISSING_TYPE", "Empty map literal requires an explicit recursive map type.", expression.location)
1870
+ }
1871
+ /** @type {import("./types.js").SemanticValueType | undefined} */
1872
+ let inferredValueType
1873
+
1874
+ for (const entry of entries) {
1875
+ const actual = inferValueExpressionType(
1876
+ entry.value, scope, functions, records, fail, normalizeOperations, "a map value",
1877
+ expectedType?.kind == "MapType" ? expectedType.valueType : inferredValueType, inferCollectionElements
1878
+ )
1879
+ inferredValueType ??= actual
1880
+ const requiredValueType = expectedType?.kind == "MapType" ? expectedType.valueType : inferredValueType
1881
+
1882
+ if (!sameType(actual, requiredValueType)) {
1883
+ fail("TYPE_MISMATCH", `Map value type ${typeDescription(actual)}; expected ${typeDescription(requiredValueType)}.`, entry.value.location)
1884
+ }
1885
+ }
1886
+
1887
+ return expectedType?.kind == "MapType" ? expectedType : {
1888
+ keyType: scalarType("string"),
1889
+ kind: "MapType",
1890
+ valueType: /** @type {import("./types.js").SemanticValueType} */ (inferredValueType)
1891
+ }
1892
+ }
1893
+ if (expression.kind == "OrderedMapLiteral") {
1894
+ if (expectedType && expectedType.kind != "OrderedMapType") {
1895
+ return fail("TYPE_MISMATCH", `Ordered-map literal context ${typeDescription(expectedType)}; expected ordered map.`, expression.location)
1896
+ }
1897
+ if (!expectedType && !inferCollectionElements) {
1898
+ return fail("MISSING_TYPE", "Ordered-map literal requires an explicit recursive ordered-map type.", expression.location)
1899
+ }
1900
+ if (expectedType?.kind == "OrderedMapType" && !isScalarType(expectedType.keyType, "string")) {
1901
+ return fail("TYPE_MISMATCH", "Ordered-map key type must be string.", typeLocation(expectedType.keyType, expression.location))
1902
+ }
1903
+ const entries = validateMapLiteralEntries(expression, scope, functions, records, fail, normalizeOperations,
1904
+ inferCollectionElements)
1905
+
1906
+ if (!expectedType && entries.length == 0) {
1907
+ return fail("MISSING_TYPE", "Empty ordered-map literal requires an explicit recursive ordered-map type.", expression.location)
1908
+ }
1909
+ /** @type {import("./types.js").SemanticValueType | undefined} */
1910
+ let inferredValueType
1911
+
1912
+ for (const entry of entries) {
1913
+ const actual = inferValueExpressionType(
1914
+ entry.value, scope, functions, records, fail, normalizeOperations, "an ordered-map value",
1915
+ expectedType?.kind == "OrderedMapType" ? expectedType.valueType : inferredValueType, inferCollectionElements
1916
+ )
1917
+ inferredValueType ??= actual
1918
+ const requiredValueType = expectedType?.kind == "OrderedMapType" ? expectedType.valueType : inferredValueType
1919
+
1920
+ if (!sameType(actual, requiredValueType)) {
1921
+ fail("TYPE_MISMATCH", `Ordered-map value type ${typeDescription(actual)}; expected ${typeDescription(requiredValueType)}.`,
1922
+ entry.value.location)
1923
+ }
1924
+ }
1925
+
1926
+ return expectedType?.kind == "OrderedMapType" ? expectedType : {
1927
+ keyType: scalarType("string"),
1928
+ kind: "OrderedMapType",
1929
+ order: "insertion",
1930
+ valueType: /** @type {import("./types.js").SemanticValueType} */ (inferredValueType)
1931
+ }
1932
+ }
1933
+ if (expression.kind == "ListIndexExpression") {
1934
+ const collectionType = inferValueExpressionType(expression.collection, scope, functions, records, fail, normalizeOperations, "a list receiver")
1935
+ const indexType = inferValueExpressionType(expression.index, scope, functions, records, fail, normalizeOperations, "a list index", scalarType("integer"))
1936
+
1937
+ if (collectionType.kind != "ListType") {
1938
+ return fail("TYPE_MISMATCH", `List index receiver type ${typeDescription(collectionType)}; expected list.`, expression.collection.location)
1939
+ }
1940
+ if (!isScalarType(indexType, "integer")) {
1941
+ fail("TYPE_MISMATCH", `List index type ${typeDescription(indexType)}; expected integer.`, expression.index.location)
1942
+ }
1943
+ const knownCollection = knownValueForExpression(expression.collection, scope)
1944
+ const knownIndex = knownValueForExpression(expression.index, scope)
1945
+
1946
+ if (knownCollection?.kind == "ListLiteral" && knownIndex?.kind == "IntegerLiteral" &&
1947
+ (knownIndex.value < 0 || knownIndex.value >= knownCollection.elements.length)) {
1948
+ fail("INDEX_OUT_OF_BOUNDS", `List index ${knownIndex.value} is outside a literal list of size ${knownCollection.elements.length}.`, expression.location)
1949
+ }
1950
+ const proven = provenListElement(expression.collection, expression.index, scope)
1951
+
1952
+ validateAccessTotality(expression, proven !== undefined, normalizeOperations, fail)
1953
+
1954
+ return collectionType.elementType
1955
+ }
1956
+ if (expression.kind == "MapLookupExpression") {
1957
+ const collectionType = inferValueExpressionType(expression.collection, scope, functions, records, fail, normalizeOperations, "a map receiver")
1958
+ const keyType = inferValueExpressionType(expression.key, scope, functions, records, fail, normalizeOperations, "a map key", scalarType("string"))
1959
+
1960
+ if (collectionType.kind != "MapType" && collectionType.kind != "OrderedMapType") {
1961
+ return fail("TYPE_MISMATCH", `Map lookup receiver type ${typeDescription(collectionType)}; expected map.`, expression.collection.location)
1962
+ }
1963
+ if (!isScalarType(keyType, "string")) {
1964
+ fail("TYPE_MISMATCH", `Map lookup key type ${typeDescription(keyType)}; expected string.`, expression.key.location)
1965
+ }
1966
+ const knownCollection = knownValueForExpression(expression.collection, scope)
1967
+ const knownKey = knownValueForExpression(expression.key, scope)
1968
+
1969
+ if ((knownCollection?.kind == "MapLiteral" || knownCollection?.kind == "OrderedMapLiteral") &&
1970
+ knownKey?.kind == "StringLiteral" &&
1971
+ !knownCollection.entries.some((entry) => entry.key.value == knownKey.value)) {
1972
+ fail("MISSING_MAP_KEY", `Map key '${knownKey.value}' is absent from the literal initializer.`, expression.location)
1973
+ }
1974
+ const proven = provenMapValue(expression.collection, expression.key, scope)
1975
+
1976
+ validateAccessTotality(expression, proven !== undefined, normalizeOperations, fail)
1977
+
1978
+ return collectionType.valueType
1979
+ }
1980
+ if (expression.kind == "CollectionSizeExpression") {
1981
+ const collectionType = inferValueExpressionType(expression.collection, scope, functions, records, fail, normalizeOperations, "a collection-size receiver")
1982
+
1983
+ if (collectionType.kind != "ListType" && collectionType.kind != "MapType" && collectionType.kind != "OrderedMapType") {
1984
+ return fail("TYPE_MISMATCH", `Collection size receiver type ${typeDescription(collectionType)}; expected list or map.`, expression.collection.location)
1985
+ }
1986
+ const collectionKind = collectionType.kind == "ListType" ? "list" : "map"
1987
+
1988
+ if (normalizeOperations) {
1989
+ if (expression.collectionKind !== undefined && expression.collectionKind != collectionKind) {
1990
+ fail("TYPE_MISMATCH", "Collection size operation does not match its receiver type.", expression.location)
1991
+ }
1992
+ expression.collectionKind = collectionKind
1993
+ }
1994
+ else if (expression.collectionKind != collectionKind) {
1995
+ fail("TYPE_MISMATCH", "Collection size kind does not match its receiver type.", expression.location)
1996
+ }
1997
+
1998
+ return scalarType("integer")
1999
+ }
2000
+ if (expression.kind == "EffectCallExpression") {
2001
+ if (!Array.isArray(expression.arguments) || expression.arguments.length != expression.resolution.parameterTypes.length) {
2002
+ return fail("TYPE_MISMATCH", "Effect call argument count does not match its authorized operation.", expression.location)
2003
+ }
2004
+ const resourceFlow = expression.resolution.resourceFlow
2005
+
2006
+ if (resourceFlow.kind == "borrow" || resourceFlow.kind == "close") {
2007
+ const borrow = expression.arguments[resourceFlow.parameterIndex]
2008
+ const expectedMode = resourceFlow.kind == "close" ? "exclusive" : "shared"
2009
+
2010
+ if (borrow?.kind != "OwnedBorrowExpression" || borrow.mode != expectedMode) {
2011
+ fail("INVALID_RESOURCE_BORROW", `INVALID_RESOURCE_BORROW: ${resourceFlow.kind} requires an immediate ${expectedMode} borrow.`,
2012
+ borrow?.location ?? expression.location)
2013
+ }
2014
+ }
2015
+ for (let index = 0; index < expression.arguments.length; index += 1) {
2016
+ const expected = identityAsValueType(expression.resolution.parameterTypes[index], expression.arguments[index].location, fail)
2017
+ const actual = inferValueExpressionType(expression.arguments[index], scope, functions, records, fail, normalizeOperations,
2018
+ "an effect argument", expected)
2019
+
2020
+ if (!sameType(actual, expected)) {
2021
+ fail("RESOURCE_TYPE_MISMATCH", `Effect argument type ${typeDescription(actual)}; expected ${typeDescription(expected)}.`,
2022
+ expression.arguments[index].location)
2023
+ }
2024
+ }
2025
+ if (expression.resolution.returnType == "void") return "void"
2026
+ return identityAsValueType(expression.resolution.returnType, expression.location, fail)
2027
+ }
2028
+ if (expression.kind == "ReferenceConstruction") {
2029
+ const referenceType = validateValueTypeReference(expression.reference, expression.location, fail, undefined, records,
2030
+ scope.typeParameters)
2031
+
2032
+ if (referenceType.kind != "ReferenceType" && referenceType.kind != "OwnedReferenceType") {
2033
+ return fail("TYPE_MISMATCH", "Reference construction requires a nominal reference class type.", expression.location)
2034
+ }
2035
+ const declaration = referenceClassesFor(records).get(referenceType.declarationId)
2036
+
2037
+ if (!declaration) return fail("UNKNOWN_CLASS", `Unknown reference class '${referenceType.declarationId}'.`, expression.location)
2038
+ if (!isDenseArray(expression.arguments)) {
2039
+ return fail("TYPE_MISMATCH", "Constructor arguments must be a dense ordered array.", expression.location)
2040
+ }
2041
+ if (expression.arguments.length != declaration.constructor.parameters.length) {
2042
+ return fail("CONSTRUCTOR_ARITY_MISMATCH",
2043
+ `Constructor for '${declaration.name}' has ${expression.arguments.length} arguments; expected ${declaration.constructor.parameters.length}.`,
2044
+ expression.location)
2045
+ }
2046
+ const parameterTypes = declaration.constructor.parameters.map((parameter) =>
2047
+ validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records))
2048
+
2049
+ for (let index = 0; index < expression.arguments.length; index += 1) {
2050
+ expression.arguments[index] = normalizeOwnedMove(expression.arguments[index], parameterTypes[index], normalizeOperations, fail)
2051
+ const actual = inferValueExpressionType(expression.arguments[index], scope, functions, records, fail,
2052
+ normalizeOperations, "a constructor argument", parameterTypes[index])
2053
+
2054
+ if (!sameType(actual, parameterTypes[index])) {
2055
+ fail("TYPE_MISMATCH", `Constructor argument type ${typeDescription(actual)}; expected ${typeDescription(parameterTypes[index])}.`,
2056
+ expression.arguments[index].location)
2057
+ }
2058
+ }
2059
+ const resolution = {
2060
+ declarationId: /** @type {string} */ (declaration.constructor.id),
2061
+ kind: /** @type {const} */ ("ResolvedConstructorSignature"),
2062
+ parameterTypes: parameterTypes.map((type) => /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type)))
2063
+ }
2064
+
2065
+ if (normalizeOperations) expression.resolution = resolution
2066
+ else validateReferenceResolution(expression.resolution, resolution, expression.location, fail, "constructor")
2067
+
2068
+ return referenceType
2069
+ }
2070
+ if (expression.kind == "MethodCallExpression") {
2071
+ const receiverType = inferValueExpressionType(expression.receiver, scope, functions, records, fail,
2072
+ normalizeOperations, "a method receiver")
2073
+
2074
+ if (receiverType.kind != "ReferenceType" && receiverType.kind != "OwnedReferenceType") {
2075
+ return fail("INVALID_METHOD_RECEIVER", "Method call receiver must have a reference class type.", expression.receiver.location)
2076
+ }
2077
+ const declaration = referenceClassesFor(records).get(receiverType.declarationId)
2078
+
2079
+ if (!declaration) return fail("UNKNOWN_CLASS", `Unknown reference class '${receiverType.declarationId}'.`, expression.receiver.location)
2080
+ const method = declaration.methods.find((candidate) => candidate.id == expression.method) ??
2081
+ (normalizeOperations ? declaration.methods.find((candidate) => candidate.name == expression.method) : undefined)
2082
+
2083
+ if (!method) return fail("UNKNOWN_METHOD", `Class '${declaration.name}' has no method '${expression.method}'.`,
2084
+ roleLocation(expression, "member"))
2085
+ if (normalizeOperations) expression.method = /** @type {string} */ (method.id)
2086
+ if (!isDenseArray(expression.arguments)) return fail("TYPE_MISMATCH", "Method arguments must be a dense ordered array.", expression.location)
2087
+ if (expression.arguments.length != method.parameters.length) {
2088
+ return fail("METHOD_ARITY_MISMATCH",
2089
+ `Method '${method.name}' has ${expression.arguments.length} arguments; expected ${method.parameters.length}.`, expression.location)
2090
+ }
2091
+ const parameterTypes = method.parameters.map((parameter) =>
2092
+ validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records))
2093
+
2094
+ for (let index = 0; index < expression.arguments.length; index += 1) {
2095
+ expression.arguments[index] = normalizeOwnedMove(expression.arguments[index], parameterTypes[index], normalizeOperations, fail)
2096
+ const actual = inferValueExpressionType(expression.arguments[index], scope, functions, records, fail,
2097
+ normalizeOperations, "a method argument", parameterTypes[index])
2098
+
2099
+ if (!sameType(actual, parameterTypes[index])) {
2100
+ fail("TYPE_MISMATCH", `Method argument type ${typeDescription(actual)}; expected ${typeDescription(parameterTypes[index])}.`,
2101
+ expression.arguments[index].location)
2102
+ }
2103
+ }
2104
+ const returnType = validateReturnTypeReference(method.returnType, method.location, fail, records)
2105
+ const resourceFlow = declaration.ownership?.kind == "ownedResource"
2106
+ ? ownedMethodResourceFlow(declaration, method, fail) : undefined
2107
+ const resolution = {
2108
+ declarationId: /** @type {string} */ (method.id),
2109
+ kind: /** @type {const} */ ("ResolvedMethodSignature"),
2110
+ parameterTypes: parameterTypes.map((type) => /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type))),
2111
+ returnType: typeIdentity(returnType),
2112
+ ...(resourceFlow ? {resourceFlow} : {})
2113
+ }
2114
+
2115
+ if (expression.receiver.kind == "OwnedBorrowExpression" && resourceFlow) {
2116
+ const expectedMode = resourceFlow.kind == "terminal" ? "exclusive" : "shared"
2117
+
2118
+ if (expression.receiver.mode != expectedMode) {
2119
+ fail("INVALID_RESOURCE_BORROW", `INVALID_RESOURCE_BORROW: Owned method requires an immediate ${expectedMode} receiver borrow.`,
2120
+ expression.receiver.location)
2121
+ }
2122
+ }
2123
+
2124
+ if (receiverType.kind == "OwnedReferenceType" && expression.receiver.kind != "OwnedBorrowExpression") {
2125
+ if (!normalizeOperations) fail("INVALID_RESOURCE_BORROW", "Owned method receiver requires an immediate borrow.", expression.receiver.location)
2126
+ if (expression.receiver.kind != "IdentifierExpression") {
2127
+ fail("BORROW_ESCAPE", "Owned method receiver borrow must name one immediate binding.", expression.receiver.location)
2128
+ }
2129
+ expression.receiver = {
2130
+ expression: expression.receiver,
2131
+ kind: "OwnedBorrowExpression",
2132
+ location: expression.receiver.location,
2133
+ mode: resourceFlow?.kind == "terminal" ? "exclusive" : "shared",
2134
+ sourceProvenance: expression.receiver.sourceProvenance,
2135
+ type: receiverType
2136
+ }
2137
+ }
2138
+
2139
+ if (normalizeOperations) expression.resolution = resolution
2140
+ else validateReferenceResolution(expression.resolution, resolution, expression.location, fail, "method")
2141
+
2142
+ return isVoidType(returnType) ? "void" : returnType
2143
+ }
2144
+ if (expression.kind == "RecordConstruction") {
2145
+ const recordType = validateValueTypeReference(expression.record, expression.location, fail, undefined, records, scope.typeParameters)
2146
+
2147
+ if (recordType.kind != "RecordType") {
2148
+ return fail("TYPE_MISMATCH", "Record construction requires a nominal record type.", expression.location)
2149
+ }
2150
+ const declaration = records.get(recordType.declarationId)
2151
+
2152
+ if (!declaration) return fail("UNKNOWN_RECORD", `Unknown record declaration '${recordType.declarationId}'.`, expression.location)
2153
+ if (!Array.isArray(expression.arguments) || expression.arguments.some((_argument, index) => !Object.hasOwn(expression.arguments, index))) {
2154
+ return fail("TYPE_MISMATCH", "Record construction arguments must be a dense ordered array.", expression.location)
2155
+ }
2156
+ if (expression.arguments.length != declaration.fields.length) {
2157
+ return fail(
2158
+ "RECORD_ARITY_MISMATCH",
2159
+ `Record '${declaration.name}' construction has ${expression.arguments.length} arguments; expected ${declaration.fields.length}.`,
2160
+ expression.location
2161
+ )
2162
+ }
2163
+ const substitutions = recordTypeSubstitutions(recordType, declaration)
2164
+ for (let index = 0; index < expression.arguments.length; index += 1) {
2165
+ const argument = expression.arguments[index]
2166
+ const field = declaration.fields[index]
2167
+ const fieldType = substituteValueType(field.type, substitutions)
2168
+ const actual = inferValueExpressionType(
2169
+ argument, scope, functions, records, fail, normalizeOperations, `record field '${field.name}'`, fieldType
2170
+ )
2171
+
2172
+ if (!sameType(actual, fieldType)) {
2173
+ fail("TYPE_MISMATCH", `Record field '${field.name}' argument type ${typeDescription(actual)}; expected ${typeDescription(fieldType)}.`, argument.location)
2174
+ }
2175
+ }
2176
+
2177
+ return recordType
2178
+ }
2179
+ if (expression.kind == "MemberRead") {
2180
+ const receiverType = inferValueExpressionType(expression.receiver, scope, functions, records, fail, normalizeOperations, "a member receiver")
2181
+
2182
+ if (receiverType.kind != "RecordType") {
2183
+ return fail("INVALID_MEMBER_RECEIVER", `Member receiver type ${typeDescription(receiverType)}; expected record.`, expression.receiver.location)
2184
+ }
2185
+ const declaration = records.get(receiverType.declarationId)
2186
+
2187
+ if (!declaration) return fail("UNKNOWN_RECORD", `Unknown record declaration '${receiverType.declarationId}'.`, expression.receiver.location)
2188
+ const field = declaration.fields.find((candidate) => candidate.id == expression.field) ??
2189
+ (normalizeOperations ? declaration.fields.find((candidate) => candidate.name == expression.field) : undefined)
2190
+
2191
+ if (!field) fail("UNKNOWN_FIELD", `Record '${declaration.name}' has no field '${expression.field}'.`, roleLocation(expression, "member"))
2192
+ if (normalizeOperations) expression.field = /** @type {string} */ (field.id)
2193
+
2194
+ return substituteValueType(field.type, recordTypeSubstitutions(receiverType, declaration))
2195
+ }
2196
+ if (expression.kind == "CallExpression") {
2197
+ const functionDeclaration = functions.get(expression.callee)
2198
+
2199
+ if (!functionDeclaration) {
2200
+ return fail("UNRESOLVED_BINDING", `Unknown function '${expression.callee}'.`, roleLocation(expression, "callee"))
2201
+ }
2202
+ if (expression.arguments.length != functionDeclaration.parameters.length) {
2203
+ return fail("TYPE_MISMATCH", `Call argument count for '${expression.callee}'.`, expression.location)
2204
+ }
2205
+ const declarationTypeParameters = declarationTypeParameterIds(functionDeclaration)
2206
+ /** @type {Map<string, import("./types.js").SemanticValueType>} */
2207
+ const substitutions = new Map()
2208
+ const parameterTypes = functionDeclaration.parameters.map((parameter) => validateValueTypeReference(
2209
+ parameter.type, parameter.location, fail, undefined, records, declarationTypeParameters
2210
+ ))
2211
+ for (let index = 0; index < expression.arguments.length; index += 1) {
2212
+ expression.arguments[index] = normalizeOwnedMove(expression.arguments[index], parameterTypes[index], normalizeOperations, fail)
2213
+ }
2214
+ for (let index = 0; index < expression.arguments.length; index++) {
2215
+ const argument = expression.arguments[index]
2216
+ const expectedType = parameterTypes[index]
2217
+ const gathersGenericEvidence = typeContainsAnyVariable(expectedType)
2218
+
2219
+ if (gathersGenericEvidence) {
2220
+ inferGenericArgumentEvidence(expectedType, argument, substitutions, scope, functions, records, fail,
2221
+ normalizeOperations)
2222
+ continue
2223
+ }
2224
+ const actualType = inferValueExpressionType(
2225
+ argument, scope, functions, records, fail, normalizeOperations, "a call argument", expectedType
2226
+ )
2227
+
2228
+ if ((functionDeclaration.typeParameters?.length ?? 0) > 0) {
2229
+ inferTypeArguments(expectedType, actualType, substitutions, argument.location, fail)
2230
+ } else if (!sameType(actualType, expectedType)) {
2231
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actualType)}; expected ${typeDescription(expectedType)}.`, argument.location)
2232
+ }
2233
+ }
2234
+ for (const parameter of functionDeclaration.typeParameters ?? []) {
2235
+ if (!substitutions.has(/** @type {string} */ (parameter.id))) {
2236
+ const evidenceFreeIndex = parameterTypes.findIndex((type) =>
2237
+ typeContainsVariable(type, /** @type {string} */ (parameter.id)))
2238
+
2239
+ if (evidenceFreeIndex >= 0) {
2240
+ fail("GENERIC_INFERENCE_FAILURE", "Cannot infer a type parameter from an evidence-free call argument.",
2241
+ expression.arguments[evidenceFreeIndex].location)
2242
+ }
2243
+ fail("GENERIC_INFERENCE_FAILURE", `Cannot infer type parameter '${parameter.name}' from call arguments.`, expression.location)
2244
+ }
2245
+ }
2246
+ if ([...substitutions.values()].some((type) => containsOwnedType(type))) {
2247
+ fail("RESOURCE_ALIAS", "Owned resources and references cannot be inferred as generic arguments.", expression.location)
2248
+ }
2249
+ for (let index = 0; index < functionDeclaration.parameters.length; index += 1) {
2250
+ const expected = substituteValueType(functionDeclaration.parameters[index].type, substitutions)
2251
+ const actual = inferValueExpressionType(expression.arguments[index], scope, functions, records, fail,
2252
+ normalizeOperations, "a call argument", expected)
2253
+
2254
+ if (!sameType(actual, expected)) {
2255
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actual)}; expected ${typeDescription(expected)}.`, expression.arguments[index].location)
2256
+ }
2257
+ }
2258
+ const resolution = signatureFor(functionDeclaration, records, fail, substitutions)
2259
+
2260
+ if (normalizeOperations) expression.resolution = resolution
2261
+ else validateResolution(expression.resolution, resolution, expression.location, fail)
2262
+ const returnType = substituteReturnType(validateReturnTypeReference(functionDeclaration.returnType,
2263
+ functionDeclaration.location, fail, records, declarationTypeParameters), substitutions)
2264
+
2265
+ return isVoidType(returnType) ? "void" : returnType
2266
+ }
2267
+ if (expression.kind == "UnaryExpression") {
2268
+ const operand = inferValueExpressionType(expression.operand, scope, functions, records, fail, normalizeOperations, "a unary operand")
2269
+ const operandType = scalarNameForOperation(operand, expression.operand.location, fail)
2270
+ const operation = normalizeOperations
2271
+ ? normalizeUnaryOperation(adaptedOperationFor(expression), operandType, expression.operand.location, fail)
2272
+ : expression.operation
2273
+
2274
+ if (normalizeOperations) setNormalizedOperation(expression, operation, unaryOperationSignatures[operation].result)
2275
+
2276
+ const signature = unaryOperationSignatures[operation]
2277
+
2278
+ if (!signature) return fail("TYPE_MISMATCH", `Unknown unary operation ${String(operation)}.`, expression.location)
2279
+ if (operandType != signature.operand) {
2280
+ return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.operand}; received ${operandType}.`, expression.operand.location)
2281
+ }
2282
+ if (expression.type != signature.result) {
2283
+ return fail("TYPE_MISMATCH", `${operation} result type ${String(expression.type)}; expected ${signature.result}.`, expression.location)
2284
+ }
2285
+ return scalarType(signature.result)
2286
+ }
2287
+ if (expression.kind == "BinaryExpression") {
2288
+ const left = inferValueExpressionType(expression.left, scope, functions, records, fail, normalizeOperations, "a binary operand")
2289
+ const right = inferValueExpressionType(expression.right, scope, functions, records, fail, normalizeOperations, "a binary operand")
2290
+ const leftType = scalarNameForOperation(left, expression.left.location, fail)
2291
+ const rightType = scalarNameForOperation(right, expression.right.location, fail)
2292
+ const operation = normalizeOperations
2293
+ ? normalizeBinaryOperation(adaptedOperationFor(expression), leftType, rightType, expression.left.location, expression.right.location, expression.location, fail)
2294
+ : expression.operation
2295
+
2296
+ if (normalizeOperations) setNormalizedOperation(expression, operation, binaryOperationSignatures[operation].result)
2297
+
2298
+ const signature = binaryOperationSignatures[operation]
2299
+
2300
+ if (!signature) return fail("TYPE_MISMATCH", `Unknown binary operation ${String(operation)}.`, expression.location)
2301
+ if (leftType != signature.left) {
2302
+ return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.left} left operand; received ${leftType}.`, expression.left.location)
2303
+ }
2304
+ if (rightType != signature.right) {
2305
+ return fail("INVALID_OPERAND_TYPE", `${operation} requires ${signature.right} right operand; received ${rightType}.`, expression.right.location)
2306
+ }
2307
+ if (expression.type != signature.result) {
2308
+ return fail("TYPE_MISMATCH", `${operation} result type ${String(expression.type)}; expected ${signature.result}.`, expression.location)
2309
+ }
2310
+ return scalarType(signature.result)
2311
+ }
2312
+
2313
+ const unexpected = /** @type {{kind: string, location: import("./types.js").SourceLocation}} */ (expression)
2314
+
2315
+ return fail("TYPE_MISMATCH", unexpected.kind, unexpected.location)
2316
+ }
2317
+
2318
+ /**
2319
+ * Validates map layout and fixed string keys independently of contextual value inference.
2320
+ * @param {import("./types.js").MapLiteral | import("./types.js").OrderedMapLiteral} expression - Candidate map literal.
2321
+ * @param {Scope} scope - Visible lexical scope.
2322
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Module function signatures.
2323
+ * @param {RecordRegistry} records - Record declarations by identity.
2324
+ * @param {SemanticFail} fail - Diagnostic callback.
2325
+ * @param {boolean} normalizeOperations - Whether to normalize frontend intent.
2326
+ * @param {boolean} inferCollectionElements - Whether nested collection evidence may be inferred.
2327
+ * @returns {import("./types.js").MapEntry[]} Validated dense entries.
2328
+ */
2329
+ function validateMapLiteralEntries(expression, scope, functions, records, fail, normalizeOperations,
2330
+ inferCollectionElements) {
2331
+ if (!Array.isArray(expression.entries)) {
2332
+ return fail("TYPE_MISMATCH", "Map literal entries must be a dense source-ordered array.", expression.location)
2333
+ }
2334
+ if (!isDenseArray(expression.entries)) {
2335
+ return fail("TYPE_MISMATCH", "Sparse map literals are outside the implemented subset.", expression.location)
2336
+ }
2337
+ const keys = new Set()
2338
+
2339
+ for (const entry of expression.entries) {
2340
+ if (!entry || entry.kind != "MapEntry" || !entry.key || entry.key.kind != "StringLiteral") {
2341
+ fail("TYPE_MISMATCH", "Map entries require literal string keys.", entry?.location ?? expression.location)
2342
+ }
2343
+ inferValueExpressionType(entry.key, scope, functions, records, fail, normalizeOperations, "a map key",
2344
+ scalarType("string"), inferCollectionElements)
2345
+ if (isNumericString(entry.key.value)) {
2346
+ fail("INVALID_MAP_KEY", "Map initializer keys must be nonnumeric strings.", entry.key.location)
2347
+ }
2348
+ if (keys.has(entry.key.value)) {
2349
+ fail("DUPLICATE_MAP_KEY", `Duplicate ${expression.kind == "OrderedMapLiteral" ? "ordered-map" : "map"} key '${entry.key.value}'.`,
2350
+ entry.key.location)
2351
+ }
2352
+ keys.add(entry.key.value)
2353
+ }
2354
+
2355
+ return expression.entries
2356
+ }
2357
+
2358
+ /**
2359
+ * Collects argument-only generic evidence while deferring recursively contextual literals.
2360
+ * The complete argument is validated against its closed substituted type after inference.
2361
+ * @param {import("./types.js").SemanticValueType} formal - Open declaration parameter type.
2362
+ * @param {import("./types.js").Expression} expression - Call argument or nested collection value.
2363
+ * @param {Map<string, import("./types.js").SemanticValueType>} substitutions - Inference state.
2364
+ * @param {Scope} scope - Visible lexical scope.
2365
+ * @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Module function signatures.
2366
+ * @param {RecordRegistry} records - Record declarations by identity.
2367
+ * @param {SemanticFail} fail - Diagnostic callback.
2368
+ * @param {boolean} normalizeOperations - Whether to normalize frontend intent.
2369
+ * @returns {void}
2370
+ */
2371
+ function inferGenericArgumentEvidence(formal, expression, substitutions, scope, functions, records, fail,
2372
+ normalizeOperations) {
2373
+ if (expression.kind == "OptionalNone") return
2374
+ if (formal.kind == "OptionalType" && expression.kind == "OptionalSome") {
2375
+ inferGenericArgumentEvidence(formal.valueType, expression.value, substitutions, scope, functions, records, fail,
2376
+ normalizeOperations)
2377
+ return
2378
+ }
2379
+ if (formal.kind == "ListType" && expression.kind == "ListLiteral") {
2380
+ if (!Array.isArray(expression.elements)) {
2381
+ fail("TYPE_MISMATCH", "List literal elements must be a dense source-ordered array.", expression.location)
2382
+ }
2383
+ if (!isDenseArray(expression.elements)) {
2384
+ fail("TYPE_MISMATCH", "Sparse list literals are outside the implemented subset.", expression.location)
2385
+ }
2386
+ for (const element of expression.elements) {
2387
+ inferGenericArgumentEvidence(formal.elementType, element, substitutions, scope, functions, records, fail,
2388
+ normalizeOperations)
2389
+ }
2390
+ return
2391
+ }
2392
+ if ((formal.kind == "MapType" && expression.kind == "MapLiteral") ||
2393
+ (formal.kind == "OrderedMapType" && expression.kind == "OrderedMapLiteral")) {
2394
+ const entries = validateMapLiteralEntries(expression, scope, functions, records, fail, normalizeOperations, true)
2395
+
2396
+ for (const entry of entries) {
2397
+ inferGenericArgumentEvidence(formal.valueType, entry.value, substitutions, scope, functions, records, fail,
2398
+ normalizeOperations)
2399
+ }
2400
+ return
2401
+ }
2402
+ const actual = inferValueExpressionType(
2403
+ expression, scope, functions, records, fail, normalizeOperations, "a call argument", undefined, true
2404
+ )
2405
+
2406
+ inferTypeArguments(formal, actual, substitutions, expression.location, fail)
2407
+ }
2408
+
2409
+ /**
2410
+ * Unifies one declaration-owned formal type with one argument type.
2411
+ * @param {import("./types.js").SemanticValueType} formal - Generic signature type.
2412
+ * @param {import("./types.js").SemanticValueType} actual - Inferred argument type.
2413
+ * @param {Map<string, import("./types.js").SemanticValueType>} substitutions - Inference state.
2414
+ * @param {import("./types.js").SourceLocation} location - Argument location.
2415
+ * @param {SemanticFail} fail - Diagnostic callback.
2416
+ * @returns {void}
2417
+ */
2418
+ function inferTypeArguments(formal, actual, substitutions, location, fail) {
2419
+ if (formal.kind == "TypeVariableReference") {
2420
+ const prior = substitutions.get(formal.parameterId)
2421
+
2422
+ if (prior) {
2423
+ if (!sameType(prior, actual)) {
2424
+ fail("GENERIC_INFERENCE_CONFLICT", `Conflicting inference for type parameter '${formal.parameterId}'.`, location)
2425
+ }
2426
+ return
2427
+ }
2428
+ if (!(actual.kind == "TypeVariableReference" && actual.parameterId == formal.parameterId) &&
2429
+ typeContainsVariable(actual, formal.parameterId)) {
2430
+ fail("GENERIC_INFERENCE_RECURSION", `Recursive inference for type parameter '${formal.parameterId}'.`, location)
2431
+ }
2432
+ substitutions.set(formal.parameterId, actual)
2433
+ return
2434
+ }
2435
+ if (formal.kind != actual.kind) {
2436
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actual)}; expected ${typeDescription(formal)}.`, location)
2437
+ }
2438
+ if (formal.kind == "TypeReference") {
2439
+ if (!sameType(formal, actual)) {
2440
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actual)}; expected ${typeDescription(formal)}.`, location)
2441
+ }
2442
+ return
2443
+ }
2444
+ if (formal.kind == "ReferenceType" && actual.kind == "ReferenceType") {
2445
+ if (formal.declarationId != actual.declarationId) {
2446
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actual)}; expected ${typeDescription(formal)}.`, location)
2447
+ }
2448
+ return
2449
+ }
2450
+ if (formal.kind == "RecordType" && actual.kind == "RecordType") {
2451
+ if (formal.declarationId != actual.declarationId || (formal.arguments?.length ?? 0) != (actual.arguments?.length ?? 0)) {
2452
+ fail("TYPE_MISMATCH", `Call argument type ${typeDescription(actual)}; expected ${typeDescription(formal)}.`, location)
2453
+ }
2454
+ for (let index = 0; index < (formal.arguments?.length ?? 0); index += 1) {
2455
+ inferTypeArguments(/** @type {import("./types.js").SemanticValueType} */ (formal.arguments?.[index]),
2456
+ /** @type {import("./types.js").SemanticValueType} */ (actual.arguments?.[index]), substitutions, location, fail)
2457
+ }
2458
+ return
2459
+ }
2460
+ if (formal.kind == "ListType" && actual.kind == "ListType") {
2461
+ inferTypeArguments(formal.elementType, actual.elementType, substitutions, location, fail)
2462
+ return
2463
+ }
2464
+ if (formal.kind == "OptionalType" && actual.kind == "OptionalType") {
2465
+ inferTypeArguments(formal.valueType, actual.valueType, substitutions, location, fail)
2466
+ return
2467
+ }
2468
+ if ((formal.kind == "MapType" && actual.kind == "MapType") ||
2469
+ (formal.kind == "OrderedMapType" && actual.kind == "OrderedMapType")) {
2470
+ inferTypeArguments(formal.keyType, actual.keyType, substitutions, location, fail)
2471
+ inferTypeArguments(formal.valueType, actual.valueType, substitutions, location, fail)
2472
+ }
2473
+ }
2474
+
2475
+ /**
2476
+ * Checks the occurs constraint for first-order inference.
2477
+ * @param {import("./types.js").SemanticValueType} type - Candidate inferred type.
2478
+ * @param {string} parameterId - Parameter being inferred.
2479
+ * @returns {boolean} Whether the type recursively contains the parameter.
2480
+ */
2481
+ function typeContainsVariable(type, parameterId) {
2482
+ if (type.kind == "TypeVariableReference") return type.parameterId == parameterId
2483
+ if (type.kind == "RecordType") return type.arguments?.some((argument) => typeContainsVariable(argument, parameterId)) ?? false
2484
+ if (type.kind == "ListType") return typeContainsVariable(type.elementType, parameterId)
2485
+ if (type.kind == "MapType" || type.kind == "OrderedMapType") {
2486
+ return typeContainsVariable(type.keyType, parameterId) || typeContainsVariable(type.valueType, parameterId)
2487
+ }
2488
+ if (type.kind == "OptionalType") return typeContainsVariable(type.valueType, parameterId)
2489
+
2490
+ return false
2491
+ }
2492
+
2493
+ /**
2494
+ * Applies substitutions to a value or void return type.
2495
+ * @param {import("./types.js").SemanticFunctionReturnType} type - Open return type.
2496
+ * @param {Map<string, import("./types.js").SemanticValueType>} substitutions - Closed substitutions.
2497
+ * @returns {import("./types.js").SemanticFunctionReturnType} Substituted return type.
2498
+ */
2499
+ function substituteReturnType(type, substitutions) {
2500
+ return isVoidType(type) ? type : substituteValueType(type, substitutions)
2501
+ }
2502
+
2503
+ /**
2504
+ * Builds the exact semantic signature bound to one declaration.
2505
+ * @param {import("./types.js").FunctionDeclaration} declaration - Resolved declaration.
2506
+ * @param {RecordRegistry} records - Record declarations by identity.
2507
+ * @param {SemanticFail} fail - Diagnostic callback.
2508
+ * @param {Map<string, import("./types.js").SemanticValueType>} [substitutions] - Inferred generic substitutions.
2509
+ * @returns {import("./types.js").ResolvedFunctionSignature} Detached signature binding.
2510
+ */
2511
+ function signatureFor(declaration, records, fail, substitutions = new Map()) {
2512
+ const typeParameters = declarationTypeParameterIds(declaration)
2513
+ const declaredParameters = declaration.typeParameters ?? []
2514
+ const parameterTypes = declaration.parameters.map((parameter) =>
2515
+ substituteValueType(validateValueTypeReference(parameter.type, parameter.location, fail, undefined, records, typeParameters), substitutions))
2516
+ const returnType = substituteReturnType(
2517
+ validateReturnTypeReference(declaration.returnType, declaration.location, fail, records, typeParameters), substitutions)
2518
+
2519
+ return {
2520
+ declarationId: /** @type {string} */ (declaration.id),
2521
+ kind: "ResolvedFunctionSignature",
2522
+ parameterTypes: parameterTypes.map((type) => /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type))),
2523
+ returnType: typeIdentity(returnType),
2524
+ ...(declaredParameters.length > 0 ? {
2525
+ typeArguments: declaredParameters.map((parameter) =>
2526
+ /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(/** @type {import("./types.js").SemanticValueType} */ (
2527
+ substitutions.get(/** @type {string} */ (parameter.id))
2528
+ ))))
2529
+ } : {})
2530
+ }
2531
+ }
2532
+
2533
+ /**
2534
+ * Validates an externally supplied call binding against the declaration collected for this module.
2535
+ * @param {unknown} actual - Candidate call resolution.
2536
+ * @param {import("./types.js").ResolvedFunctionSignature} expected - Exact declaration signature.
2537
+ * @param {import("./types.js").SourceLocation} location - Call location.
2538
+ * @param {SemanticFail} fail - Diagnostic callback.
2539
+ * @returns {void}
2540
+ */
2541
+ function validateResolution(actual, expected, location, fail) {
2542
+ const expectedKeys = expected.typeArguments ? "declarationId,kind,parameterTypes,returnType,typeArguments" :
2543
+ "declarationId,kind,parameterTypes,returnType"
2544
+
2545
+ if (!actual || typeof actual != "object" || Array.isArray(actual) ||
2546
+ Object.keys(actual).sort().join(",") != expectedKeys) {
2547
+ fail("TYPE_MISMATCH", "Call is missing an exact resolved function signature.", location)
2548
+ }
2549
+ const candidate = /** @type {import("./types.js").ResolvedFunctionSignature} */ (actual)
2550
+
2551
+ if (candidate.kind != "ResolvedFunctionSignature" || typeof candidate.declarationId != "string" ||
2552
+ !Array.isArray(candidate.parameterTypes) || !validTypeIdentity(candidate.returnType, true) ||
2553
+ candidate.declarationId != expected.declarationId ||
2554
+ !sameTypeIdentity(candidate.returnType, expected.returnType) || candidate.parameterTypes.length != expected.parameterTypes.length) {
2555
+ fail("TYPE_MISMATCH", "Call resolution does not match its declaration signature.", location)
2556
+ }
2557
+ if (expected.typeArguments) {
2558
+ const candidateTypeArguments = candidate.typeArguments
2559
+
2560
+ if (!isDenseArray(candidateTypeArguments) || candidateTypeArguments.length != expected.typeArguments.length ||
2561
+ candidateTypeArguments.some((type, index) =>
2562
+ !validTypeIdentity(type, false) || !sameTypeIdentity(type, expected.typeArguments?.[index]))) {
2563
+ fail("TYPE_MISMATCH", "Call resolution does not match its inferred type arguments.", location)
2564
+ }
2565
+ }
2566
+ for (let index = 0; index < candidate.parameterTypes.length; index++) {
2567
+ const type = candidate.parameterTypes[index]
2568
+
2569
+ if (!Object.hasOwn(candidate.parameterTypes, index) || !validTypeIdentity(type, false) ||
2570
+ !sameTypeIdentity(type, expected.parameterTypes[index])) {
2571
+ fail("TYPE_MISMATCH", "Call resolution does not match its declaration signature.", location)
2572
+ }
2573
+ }
2574
+ }
2575
+
2576
+ /**
2577
+ * Validates exact caller-owned constructor or method resolution metadata.
2578
+ * @param {unknown} actual - Candidate resolution.
2579
+ * @param {import("./types.js").ResolvedConstructorSignature | import("./types.js").ResolvedMethodSignature} expected - Exact resolution.
2580
+ * @param {import("./types.js").SourceLocation} location - Expression location.
2581
+ * @param {SemanticFail} fail - Diagnostic callback.
2582
+ * @param {"constructor" | "method"} role - Resolution role.
2583
+ * @returns {void}
2584
+ */
2585
+ function validateReferenceResolution(actual, expected, location, fail, role) {
2586
+ const expectedMethod = role == "method" ? /** @type {import("./types.js").ResolvedMethodSignature} */ (expected) : undefined
2587
+ const expectedKeys = role == "constructor" ? "declarationId,kind,parameterTypes" :
2588
+ expectedMethod?.resourceFlow ? "declarationId,kind,parameterTypes,resourceFlow,returnType" :
2589
+ "declarationId,kind,parameterTypes,returnType"
2590
+
2591
+ if (!actual || typeof actual != "object" || Array.isArray(actual) || Object.keys(actual).sort().join(",") != expectedKeys) {
2592
+ fail("TYPE_MISMATCH", `Reference ${role} call is missing an exact resolved ${role} signature.`, location)
2593
+ }
2594
+ const candidate = /** @type {Record<string, unknown>} */ (actual)
2595
+
2596
+ if (candidate.kind != expected.kind || candidate.declarationId != expected.declarationId ||
2597
+ !isDenseArray(candidate.parameterTypes) || candidate.parameterTypes.length != expected.parameterTypes.length ||
2598
+ candidate.parameterTypes.some((type, index) => !validTypeIdentity(type, false) ||
2599
+ !sameTypeIdentity(type, expected.parameterTypes[index])) ||
2600
+ role == "method" && (!validTypeIdentity(candidate.returnType, true) ||
2601
+ !sameTypeIdentity(candidate.returnType, /** @type {import("./types.js").ResolvedMethodSignature} */ (expected).returnType) ||
2602
+ JSON.stringify(candidate.resourceFlow) != JSON.stringify(expectedMethod?.resourceFlow))) {
2603
+ fail("TYPE_MISMATCH", `Resolved ${role} signature does not match its declaration.`, location)
2604
+ }
2605
+ }
2606
+
2607
+ /**
2608
+ * Makes a source identifier's ownership transfer explicit in normalized IR.
2609
+ * @param {import("./types.js").Expression} expression - Candidate transfer expression.
2610
+ * @param {import("./types.js").SemanticFunctionReturnType} expected - Destination type.
2611
+ * @param {boolean} normalize - Whether parser intent may be normalized.
2612
+ * @param {SemanticFail} fail - Located failure.
2613
+ * @returns {import("./types.js").Expression} Explicit expression.
2614
+ */
2615
+ function normalizeOwnedMove(expression, expected, normalize, fail) {
2616
+ if (expected.kind != "OwnedResourceType" && expected.kind != "OwnedReferenceType") return expression
2617
+ if (expression.kind == "OwnedMoveExpression") return expression
2618
+ if (expression.kind == "PrivateFieldRead") {
2619
+ return fail("INVALID_RESOURCE_TRANSFER", "Private resource fields cannot be moved or exposed; transfer the owning reference.",
2620
+ expression.location)
2621
+ }
2622
+ if (expression.kind != "IdentifierExpression") return expression
2623
+ if (!normalize) return fail("RESOURCE_MOVE_REQUIRED", "Owned transfer requires an explicit move expression.", expression.location)
2624
+
2625
+ return {
2626
+ expression,
2627
+ kind: "OwnedMoveExpression",
2628
+ location: expression.location,
2629
+ sourceProvenance: expression.sourceProvenance,
2630
+ type: expected
2631
+ }
2632
+ }
2633
+
2634
+ /**
2635
+ * Classifies the deliberately bounded Task 034 receiver transition.
2636
+ * @param {import("./types.js").ClassDeclaration} declaration - Owned class.
2637
+ * @param {import("./types.js").MethodDeclaration} method - Candidate method.
2638
+ * @param {SemanticFail} fail - Located failure.
2639
+ * @returns {{kind: "preserve" | "terminal", terminalFailureId?: string}} Receiver transition.
2640
+ */
2641
+ function ownedMethodResourceFlow(declaration, method, fail) {
2642
+ const effectStatements = method.body.statements.flatMap((statement) => {
2643
+ const expression = statement.kind == "ExpressionStatement" || statement.kind == "ReturnStatement" ? statement.expression :
2644
+ statement.kind == "LocalDeclaration" ? statement.initializer : undefined
2645
+
2646
+ return expression?.kind == "EffectCallExpression" ? [expression] : []
2647
+ })
2648
+ const allEffects = methodEffectExpressions(method.body)
2649
+
2650
+ if (allEffects.length == 0) return {kind: "preserve"}
2651
+ if (method.body.statements.length != 1 || effectStatements.length != 1 || allEffects.length != 1 ||
2652
+ allEffects[0] !== effectStatements[0]) {
2653
+ return fail("UNSUPPORTED_STATEMENT",
2654
+ `Owned method '${method.name}' must contain one direct resource operation or only pure statements.`, method.location)
2655
+ }
2656
+ const operation = effectStatements[0]
2657
+ const flow = operation.resolution.resourceFlow
2658
+
2659
+ if (flow.kind == "none" || flow.kind == "acquire") return {kind: "preserve"}
2660
+ const argument = operation.arguments[flow.parameterIndex]
2661
+
2662
+ const owned = declaration.ownership?.kind == "ownedResource" ? declaration.ownership : undefined
2663
+ const borrowedParameterName = argument?.kind == "OwnedBorrowExpression" && argument.expression.kind == "IdentifierExpression"
2664
+ ? argument.expression.name : undefined
2665
+ const borrowedParameter = borrowedParameterName !== undefined &&
2666
+ method.parameters.some((parameter) => parameter.name == borrowedParameterName && containsOwnedType(parameter.type))
2667
+
2668
+ if (borrowedParameter) return {kind: "preserve"}
2669
+
2670
+ if (!argument || argument.kind != "OwnedBorrowExpression" || argument.expression.kind != "PrivateFieldRead" ||
2671
+ argument.expression.field != owned?.fieldId) {
2672
+ return fail("INVALID_RESOURCE_BORROW", `Owned method '${method.name}' must operate on its private resource field.`, operation.location)
2673
+ }
2674
+ return {kind: flow.kind == "close" ? "terminal" : "preserve", terminalFailureId: flow.terminalFailureId}
2675
+ }
2676
+
2677
+ /**
2678
+ * Collects every host-effect site nested in one owned method body.
2679
+ * @param {import("./types.js").Block} block - Method body.
2680
+ * @returns {import("./types.js").Expression[]} Effectful expressions.
2681
+ */
2682
+ function methodEffectExpressions(block) {
2683
+ /** @type {import("./types.js").Expression[]} */
2684
+ const effects = []
2685
+ const pending = [/** @type {unknown} */ (block)]
2686
+ const seen = new Set()
2687
+
2688
+ while (pending.length > 0) {
2689
+ const value = pending.pop()
2690
+
2691
+ if (!value || typeof value != "object" || seen.has(value)) continue
2692
+ seen.add(value)
2693
+ if (!Array.isArray(value) && (Reflect.get(value, "kind") == "EffectCallExpression" ||
2694
+ typeof Reflect.get(value, "effectSiteId") == "string")) {
2695
+ effects.push(/** @type {import("./types.js").Expression} */ (value))
2696
+ }
2697
+ for (const [key, child] of Object.entries(value)) {
2698
+ if (["location", "resolution", "sourceProvenance"].includes(key)) continue
2699
+ if (Array.isArray(child)) pending.push(...child)
2700
+ else if (child && typeof child == "object") pending.push(child)
2701
+ }
2702
+ }
2703
+ return effects
2704
+ }
2705
+
2706
+ /**
2707
+ * Rehydrates a closed capability type identity for ordinary expression checks.
2708
+ * @param {import("./types.js").FunctionReturnTypeIdentity} identity - Closed identity.
2709
+ * @param {import("./types.js").SourceLocation} location - Diagnostic location.
2710
+ * @param {SemanticFail} fail - Located failure.
2711
+ * @returns {import("./types.js").SemanticValueType} Value type.
2712
+ */
2713
+ function identityAsValueType(identity, location, fail) {
2714
+ if (typeof identity == "string") {
2715
+ if (!isScalarTypeName(identity)) return fail("TYPE_MISMATCH", "Void or invalid identity used as an effect value type.", location)
2716
+ return scalarType(identity)
2717
+ }
2718
+ if (!identity || typeof identity != "object" || Array.isArray(identity)) {
2719
+ return fail("TYPE_MISMATCH", "Malformed effect type identity.", location)
2720
+ }
2721
+ const candidate = /** @type {Exclude<import("./types.js").SemanticTypeIdentity, string>} */ (identity)
2722
+
2723
+ if (candidate.kind == "OwnedResourceType") return {kind: "OwnedResourceType", resourceId: candidate.resourceId}
2724
+ if (candidate.kind == "OwnedReferenceType") return {declarationId: candidate.declarationId, kind: "OwnedReferenceType"}
2725
+ if (candidate.kind == "OptionalType") return {kind: "OptionalType", valueType: identityAsValueType(candidate.valueType, location, fail)}
2726
+ return fail("TYPE_MISMATCH", "Unsupported effect type identity.", location)
2727
+ }
2728
+
2729
+ /**
2730
+ * Converts a semantic type to its detached call-resolution identity.
2731
+ * @param {import("./types.js").SemanticFunctionReturnType} type - Validated semantic type.
2732
+ * @returns {import("./types.js").FunctionReturnTypeIdentity} Detached identity.
2733
+ */
2734
+ function typeIdentity(type) {
2735
+ if (type.kind == "TypeReference") return type.name
2736
+ if (type.kind == "TypeVariableReference") return {kind: "TypeVariableReference", parameterId: type.parameterId}
2737
+ if (type.kind == "RecordType") return {
2738
+ declarationId: type.declarationId,
2739
+ kind: "RecordType",
2740
+ ...(type.arguments ? {arguments: type.arguments.map((argument) =>
2741
+ /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(argument)))} : {})
2742
+ }
2743
+ if (type.kind == "ReferenceType") return {declarationId: type.declarationId, kind: "ReferenceType"}
2744
+ if (type.kind == "OwnedResourceType") return {kind: "OwnedResourceType", resourceId: type.resourceId}
2745
+ if (type.kind == "OwnedReferenceType") return {declarationId: type.declarationId, kind: "OwnedReferenceType"}
2746
+ if (type.kind == "ListType") return {elementType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.elementType)), kind: "ListType"}
2747
+ if (type.kind == "OptionalType") return {kind: "OptionalType", valueType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.valueType))}
2748
+ if (type.kind == "OrderedMapType") return {
2749
+ keyType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.keyType)),
2750
+ kind: "OrderedMapType",
2751
+ order: "insertion",
2752
+ valueType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.valueType))
2753
+ }
2754
+
2755
+ return {
2756
+ keyType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.keyType)),
2757
+ kind: "MapType",
2758
+ valueType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.valueType))
2759
+ }
2760
+ }
2761
+
2762
+ /**
2763
+ * Checks one externally supplied recursive type identity.
2764
+ * @param {unknown} type - Candidate identity.
2765
+ * @param {boolean} allowVoid - Whether void is permitted.
2766
+ * @param {Set<object>} [seen] - Active recursion path.
2767
+ * @returns {boolean} Whether the identity is closed and acyclic.
2768
+ */
2769
+ function validTypeIdentity(type, allowVoid, seen = new Set()) {
2770
+ if (typeof type == "string") return isScalarTypeName(type) || allowVoid && type == "void"
2771
+ if (!type || typeof type != "object" || Array.isArray(type) || seen.has(type)) return false
2772
+
2773
+ seen.add(type)
2774
+ const candidate = /** @type {Record<string, unknown>} */ (type)
2775
+ let valid = false
2776
+
2777
+ if (candidate.kind == "ListType" && Object.keys(candidate).sort().join(",") == "elementType,kind") {
2778
+ valid = validTypeIdentity(candidate.elementType, false, seen)
2779
+ } else if (candidate.kind == "MapType" && Object.keys(candidate).sort().join(",") == "keyType,kind,valueType") {
2780
+ valid = candidate.keyType == "string" && validTypeIdentity(candidate.valueType, false, seen)
2781
+ } else if (candidate.kind == "OrderedMapType" &&
2782
+ Object.keys(candidate).sort().join(",") == "keyType,kind,order,valueType") {
2783
+ valid = candidate.keyType == "string" && candidate.order == "insertion" &&
2784
+ validTypeIdentity(candidate.valueType, false, seen)
2785
+ } else if (candidate.kind == "OptionalType" && Object.keys(candidate).sort().join(",") == "kind,valueType") {
2786
+ valid = validTypeIdentity(candidate.valueType, false, seen) &&
2787
+ !(candidate.valueType && typeof candidate.valueType == "object" && Reflect.get(candidate.valueType, "kind") == "OptionalType")
2788
+ } else if (candidate.kind == "TypeVariableReference" && Object.keys(candidate).sort().join(",") == "kind,parameterId") {
2789
+ valid = typeof candidate.parameterId == "string" && /^(?:[a-z][a-z0-9._-]*#)?(?:record|function):[0-9]+:type:[0-9]+$/u.test(candidate.parameterId)
2790
+ } else if (candidate.kind == "RecordType" &&
2791
+ ["arguments,declarationId,kind", "declarationId,kind"].includes(Object.keys(candidate).sort().join(","))) {
2792
+ const candidateArguments = candidate.arguments
2793
+
2794
+ valid = typeof candidate.declarationId == "string" && /^(?:[a-z][a-z0-9._-]*#)?record:[0-9]+$/u.test(candidate.declarationId) &&
2795
+ (candidateArguments === undefined || isDenseArray(candidateArguments) &&
2796
+ candidateArguments.every((argument) => validTypeIdentity(argument, false, seen)))
2797
+ } else if (candidate.kind == "ReferenceType" && Object.keys(candidate).sort().join(",") == "declarationId,kind") {
2798
+ valid = typeof candidate.declarationId == "string" &&
2799
+ /^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+$/u.test(candidate.declarationId)
2800
+ } else if (candidate.kind == "OwnedResourceType" && Object.keys(candidate).sort().join(",") == "kind,resourceId") {
2801
+ valid = typeof candidate.resourceId == "string" && /^(?:module:[0-9]+\/)?capability:[0-9]+\/resource:[0-9]+$/u
2802
+ .test(candidate.resourceId)
2803
+ } else if (candidate.kind == "OwnedReferenceType" && Object.keys(candidate).sort().join(",") == "declarationId,kind") {
2804
+ valid = typeof candidate.declarationId == "string" && /^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+$/u.test(candidate.declarationId)
2805
+ }
2806
+ seen.delete(type)
2807
+
2808
+ return valid
2809
+ }
2810
+
2811
+ /**
2812
+ * Checks array identity without relying on iteration methods that skip sparse slots.
2813
+ * @param {unknown} value - Candidate array.
2814
+ * @returns {value is unknown[]} Whether every indexed slot is present.
2815
+ */
2816
+ function isDenseArray(value) {
2817
+ if (!Array.isArray(value)) return false
2818
+
2819
+ for (let index = 0; index < value.length; index += 1) {
2820
+ if (!Object.hasOwn(value, index)) return false
2821
+ }
2822
+
2823
+ return true
2824
+ }
2825
+
2826
+ /**
2827
+ * Compares two closed type identities structurally.
2828
+ * @param {unknown} left - First identity.
2829
+ * @param {unknown} right - Second identity.
2830
+ * @returns {boolean} Whether the identities are equal.
2831
+ */
2832
+ function sameTypeIdentity(left, right) {
2833
+ if (typeof left == "string" || typeof right == "string") return left === right
2834
+ if (!left || !right || typeof left != "object" || typeof right != "object") return false
2835
+ const leftType = /** @type {Record<string, unknown>} */ (left)
2836
+ const rightType = /** @type {Record<string, unknown>} */ (right)
2837
+
2838
+ if (leftType.kind != rightType.kind) return false
2839
+ if (leftType.kind == "ListType") return sameTypeIdentity(leftType.elementType, rightType.elementType)
2840
+ if (leftType.kind == "OptionalType") return sameTypeIdentity(leftType.valueType, rightType.valueType)
2841
+ if (leftType.kind == "TypeVariableReference") return leftType.parameterId == rightType.parameterId
2842
+ if (leftType.kind == "RecordType") {
2843
+ const leftArguments = Array.isArray(leftType.arguments) ? leftType.arguments : []
2844
+ const rightArguments = Array.isArray(rightType.arguments) ? rightType.arguments : []
2845
+
2846
+ return leftType.declarationId == rightType.declarationId && leftArguments.length == rightArguments.length &&
2847
+ leftArguments.every((argument, index) => sameTypeIdentity(argument, rightArguments[index]))
2848
+ }
2849
+ if (leftType.kind == "ReferenceType") return leftType.declarationId == rightType.declarationId
2850
+ if (leftType.kind == "OwnedResourceType") return leftType.resourceId == rightType.resourceId
2851
+ if (leftType.kind == "OwnedReferenceType") return leftType.declarationId == rightType.declarationId
2852
+ if (leftType.kind == "MapType" || leftType.kind == "OrderedMapType") {
2853
+ return sameTypeIdentity(leftType.keyType, rightType.keyType) && sameTypeIdentity(leftType.valueType, rightType.valueType)
2854
+ }
2855
+
2856
+ return false
2857
+ }
2858
+
2859
+ /**
2860
+ * Compares two validated semantic types structurally.
2861
+ * @param {import("./types.js").SemanticFunctionReturnType} left - First type.
2862
+ * @param {import("./types.js").SemanticFunctionReturnType} right - Second type.
2863
+ * @returns {boolean} Whether the types are equal.
2864
+ */
2865
+ function sameType(left, right) {
2866
+ return sameTypeIdentity(typeIdentity(left), typeIdentity(right))
2867
+ }
2868
+
2869
+ /**
2870
+ * Formats one validated semantic type for diagnostics.
2871
+ * @param {import("./types.js").SemanticFunctionReturnType | import("./types.js").ErrorType} type - Semantic type.
2872
+ * @returns {string} Stable recursive spelling.
2873
+ */
2874
+ function typeDescription(type) {
2875
+ if (type.kind == "TypeReference") return type.name
2876
+ if (type.kind == "TypeVariableReference") return `type-variable<${type.parameterId}>`
2877
+ if (type.kind == "RecordType") return `record<${type.declarationId}${type.arguments?.length
2878
+ ? `, ${type.arguments.map(typeDescription).join(", ")}` : ""}>`
2879
+ if (type.kind == "ReferenceType") return `reference<${type.declarationId}>`
2880
+ if (type.kind == "OwnedResourceType") return `owned-resource<${type.resourceId}>`
2881
+ if (type.kind == "OwnedReferenceType") return `owned-reference<${type.declarationId}>`
2882
+ if (type.kind == "ErrorType") return `error<${type.declarationId}>`
2883
+ if (type.kind == "ListType") return `list<${typeDescription(type.elementType)}>`
2884
+ if (type.kind == "OptionalType") return `optional<${typeDescription(type.valueType)}>`
2885
+ if (type.kind == "OrderedMapType") return `ordered-map<string, ${typeDescription(type.valueType)}, insertion>`
2886
+
2887
+ return `map<string, ${typeDescription(type.valueType)}>`
2888
+ }
2889
+
2890
+ /**
2891
+ * Checks whether a type graph contains linear ownership.
2892
+ * @param {unknown} type - Candidate semantic type.
2893
+ * @param {Set<object>} [seen] - Visited type containers.
2894
+ * @returns {boolean} Whether ownership occurs at any depth.
2895
+ */
2896
+ function containsOwnedType(type, seen = new Set()) {
2897
+ if (!type || typeof type != "object" || Array.isArray(type) || seen.has(type)) return false
2898
+ seen.add(type)
2899
+ const kind = Reflect.get(type, "kind")
2900
+
2901
+ if (kind == "OwnedResourceType" || kind == "OwnedReferenceType") return true
2902
+ if (kind == "ListType") return containsOwnedType(Reflect.get(type, "elementType"), seen)
2903
+ if (kind == "OptionalType") return containsOwnedType(Reflect.get(type, "valueType"), seen)
2904
+ if (kind == "MapType" || kind == "OrderedMapType") {
2905
+ return containsOwnedType(Reflect.get(type, "keyType"), seen) || containsOwnedType(Reflect.get(type, "valueType"), seen)
2906
+ }
2907
+ if (kind == "RecordType") {
2908
+ const arguments_ = Reflect.get(type, "arguments")
2909
+
2910
+ return Array.isArray(arguments_) && arguments_.some((argument) => containsOwnedType(argument, seen))
2911
+ }
2912
+ return false
2913
+ }
2914
+
2915
+ /**
2916
+ * Checks one scalar semantic type.
2917
+ * @param {import("./types.js").SemanticFunctionReturnType} type - Semantic type.
2918
+ * @param {import("./types.js").SemanticTypeName} [name] - Optional required scalar.
2919
+ * @returns {type is import("./types.js").TypeReference} Whether it is the requested scalar.
2920
+ */
2921
+ function isScalarType(type, name) {
2922
+ return type.kind == "TypeReference" && isScalarTypeName(type.name) && (name === undefined || type.name == name)
2923
+ }
2924
+
2925
+ /**
2926
+ * Checks the sole non-value return type.
2927
+ * @param {import("./types.js").SemanticFunctionReturnType} type - Semantic type.
2928
+ * @returns {type is import("./types.js").FunctionReturnTypeReference} Whether it is void.
2929
+ */
2930
+ function isVoidType(type) {
2931
+ return type.kind == "TypeReference" && type.name == "void"
2932
+ }
2933
+
2934
+ /**
2935
+ * Requires a scalar type for the pre-collection operator contract.
2936
+ * @param {import("./types.js").SemanticValueType} type - Operand type.
2937
+ * @param {import("./types.js").SourceLocation} location - Operand location.
2938
+ * @param {SemanticFail} fail - Diagnostic callback.
2939
+ * @returns {import("./types.js").SemanticTypeName} Scalar name.
2940
+ */
2941
+ function scalarNameForOperation(type, location, fail) {
2942
+ if (!isScalarType(type)) {
2943
+ return fail("INVALID_OPERAND_TYPE", `Scalar operation received ${typeDescription(type)}.`, location)
2944
+ }
2945
+
2946
+ return type.name
2947
+ }
2948
+
2949
+ /**
2950
+ * Enforces that a total collection access is proven or uses a source operation that fails on absence.
2951
+ * @param {import("./types.js").ListIndexExpression | import("./types.js").MapLookupExpression} expression - Access.
2952
+ * @param {boolean} proven - Whether static literal propagation proves presence.
2953
+ * @param {boolean} normalizeOperations - Whether this is frontend admission.
2954
+ * @param {SemanticFail} fail - Diagnostic callback.
2955
+ * @returns {void}
2956
+ */
2957
+ function validateAccessTotality(expression, proven, normalizeOperations, fail) {
2958
+ if (proven) {
2959
+ if (normalizeOperations) expression.totality = "proven"
2960
+ else if (expression.totality != "proven" && expression.totality != "fail-on-absence") {
2961
+ fail("UNCHECKED_COLLECTION_ACCESS", "Collection access lacks a totality guarantee.", expression.location)
2962
+ }
2963
+
2964
+ return
2965
+ }
2966
+ if (expression.totality != "fail-on-absence") {
2967
+ fail("UNCHECKED_COLLECTION_ACCESS", "Collection access is not statically proven and does not fail on absence.", expression.location)
2968
+ }
2969
+ }
2970
+
2971
+ /**
2972
+ * Resolves a binding without producing a diagnostic.
2973
+ * @param {string} name - Binding name.
2974
+ * @param {Scope} scope - Visible scope.
2975
+ * @returns {Binding | undefined} Binding when visible.
2976
+ */
2977
+ function findBinding(name, scope) {
2978
+ for (let current = /** @type {Scope | undefined} */ (scope); current; current = current.parent) {
2979
+ const binding = current.bindings.get(name)
2980
+
2981
+ if (binding) return binding
2982
+ }
2983
+
2984
+ return undefined
2985
+ }
2986
+
2987
+ /**
2988
+ * Returns the existing bindings whose known values must be merged across a conditional.
2989
+ * @param {Scope} scope - Visible scope.
2990
+ * @returns {Binding[]} Nearest-to-farthest bindings.
2991
+ */
2992
+ function bindingsVisibleFrom(scope) {
2993
+ const bindings = []
2994
+
2995
+ for (let current = /** @type {Scope | undefined} */ (scope); current; current = current.parent) {
2996
+ bindings.push(...current.bindings.values())
2997
+ }
2998
+
2999
+ return bindings
3000
+ }
3001
+
3002
+ /**
3003
+ * Finds visible mutable bindings that a loop body may assign on any nested path.
3004
+ * The set is computed after validating the list expression because collection evaluation precedes body effects.
3005
+ * @param {import("./types.js").Block} block - Candidate loop body.
3006
+ * @param {Scope} outerScope - Scope visible before entering the loop.
3007
+ * @param {Set<Binding>} [assigned] - Accumulated binding identities.
3008
+ * @returns {Set<Binding>} Exactly the visible mutable bindings targeted by nested assignments.
3009
+ */
3010
+ function outerMutableBindingsAssignedBy(block, outerScope, assigned = new Set()) {
3011
+ for (const statement of block.statements) {
3012
+ if (statement.kind == "AssignmentStatement") {
3013
+ const binding = findBinding(statement.target.name, outerScope)
3014
+
3015
+ if (binding?.mutable) assigned.add(binding)
3016
+ } else if (statement.kind == "IfStatement") {
3017
+ outerMutableBindingsAssignedBy(statement.consequent, outerScope, assigned)
3018
+ if (statement.alternate) outerMutableBindingsAssignedBy(statement.alternate, outerScope, assigned)
3019
+ } else if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement" || statement.kind == "WhileStatement") {
3020
+ outerMutableBindingsAssignedBy(statement.body, outerScope, assigned)
3021
+ } else if (statement.kind == "TryStatement") {
3022
+ outerMutableBindingsAssignedBy(statement.body, outerScope, assigned)
3023
+ outerMutableBindingsAssignedBy(statement.catchBody, outerScope, assigned)
3024
+ }
3025
+ }
3026
+
3027
+ return assigned
3028
+ }
3029
+
3030
+ /**
3031
+ * Propagates only immutable values whose exact collection contents remain known.
3032
+ * @param {import("./types.js").Expression} expression - Expression.
3033
+ * @param {Scope} scope - Visible scope.
3034
+ * @returns {import("./types.js").Expression | undefined} Known value.
3035
+ */
3036
+ function knownValueForExpression(expression, scope) {
3037
+ if (["IntegerLiteral", "BooleanLiteral", "StringLiteral", "ListLiteral", "MapLiteral", "OrderedMapLiteral"].includes(expression.kind)) return expression
3038
+ if (expression.kind == "IdentifierExpression") return findBinding(expression.name, scope)?.knownValue
3039
+ if (expression.kind == "ListIndexExpression") return provenListElement(expression.collection, expression.index, scope)
3040
+ if (expression.kind == "MapLookupExpression") return provenMapValue(expression.collection, expression.key, scope)
3041
+
3042
+ return undefined
3043
+ }
3044
+
3045
+ /**
3046
+ * Finds the statically selected list element.
3047
+ * @param {import("./types.js").Expression} collection - List receiver.
3048
+ * @param {import("./types.js").Expression} index - Index expression.
3049
+ * @param {Scope} scope - Visible scope.
3050
+ * @returns {import("./types.js").Expression | undefined} Proven element.
3051
+ */
3052
+ function provenListElement(collection, index, scope) {
3053
+ const knownCollection = knownValueForExpression(collection, scope)
3054
+ const knownIndex = knownValueForExpression(index, scope)
3055
+
3056
+ if (knownCollection?.kind != "ListLiteral" || knownIndex?.kind != "IntegerLiteral" ||
3057
+ knownIndex.value < 0 || knownIndex.value >= knownCollection.elements.length) return undefined
3058
+
3059
+ return knownCollection.elements[knownIndex.value]
3060
+ }
3061
+
3062
+ /**
3063
+ * Finds the statically selected map value.
3064
+ * @param {import("./types.js").Expression} collection - Map receiver.
3065
+ * @param {import("./types.js").Expression} key - Key expression.
3066
+ * @param {Scope} scope - Visible scope.
3067
+ * @returns {import("./types.js").Expression | undefined} Proven value.
3068
+ */
3069
+ function provenMapValue(collection, key, scope) {
3070
+ const knownCollection = knownValueForExpression(collection, scope)
3071
+ const knownKey = knownValueForExpression(key, scope)
3072
+
3073
+ if (knownCollection?.kind != "MapLiteral" && knownCollection?.kind != "OrderedMapLiteral" ||
3074
+ knownKey?.kind != "StringLiteral") return undefined
3075
+
3076
+ return knownCollection.entries.find((entry) => entry.key.value == knownKey.value)?.value
3077
+ }
3078
+
3079
+ /**
3080
+ * Identifies numeric strings independently from any target's key coercions.
3081
+ * @param {string} value - Literal key.
3082
+ * @returns {boolean} Whether it is numeric.
3083
+ */
3084
+ function isNumericString(value) {
3085
+ return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/iu.test(value.trim())
3086
+ }
3087
+
3088
+ /**
3089
+ * Selects an exact semantic token location when parser provenance is available.
3090
+ * @param {{location: import("./types.js").SourceLocation, sourceProvenance?: import("./types.js").SemanticNodeSourceProvenance}} node - Located semantic node.
3091
+ * @param {string} role - Token role.
3092
+ * @returns {import("./types.js").SourceLocation} Exact or owning location.
3093
+ */
3094
+ function roleLocation(node, role) {
3095
+ return node.sourceProvenance?.ranges[role] ?? parserRangeFor(node, role) ?? node.location
3096
+ }
3097
+
3098
+ /**
3099
+ * Selects the exact type token or the owning declaration location.
3100
+ * @param {unknown} type - Candidate type reference.
3101
+ * @param {import("./types.js").SourceLocation} ownerLocation - Owning declaration location.
3102
+ * @param {string} [role] - Recursive type role.
3103
+ * @returns {import("./types.js").SourceLocation} Exact type location when known.
3104
+ */
3105
+ function typeLocation(type, ownerLocation, role = "type") {
3106
+ if (!type || typeof type != "object" || Array.isArray(type)) return ownerLocation
3107
+ const candidate = /** @type {import("./types.js").SemanticFunctionReturnType} */ (type)
3108
+
3109
+ return candidate.sourceProvenance?.ranges[role] ?? parserRangeFor(candidate, role) ??
3110
+ candidate.sourceProvenance?.ranges.type ?? parserRangeFor(candidate, "type") ?? ownerLocation
386
3111
  }
387
3112
 
388
3113
  /** @type {Readonly<Record<import("./types.js").SemanticUnaryOperation, {operand: import("./types.js").SemanticTypeName, result: import("./types.js").SemanticTypeName}>>} */