semantifold 0.3.0 → 0.5.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 (303) hide show
  1. package/README.md +61 -15
  2. package/build/index.d.ts +52 -2
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +60 -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/android.d.ts +49 -0
  11. package/build/src/backends/android.d.ts.map +1 -0
  12. package/build/src/backends/android.js +1274 -0
  13. package/build/src/backends/c-validation.d.ts.map +1 -1
  14. package/build/src/backends/c-validation.js +7 -3
  15. package/build/src/backends/cpp-validation.d.ts.map +1 -1
  16. package/build/src/backends/cpp-validation.js +7 -3
  17. package/build/src/backends/csharp.js +7 -1
  18. package/build/src/backends/dart-runtime.d.ts +2 -0
  19. package/build/src/backends/dart-runtime.d.ts.map +1 -0
  20. package/build/src/backends/dart-runtime.js +41 -0
  21. package/build/src/backends/dart.d.ts +20 -0
  22. package/build/src/backends/dart.d.ts.map +1 -0
  23. package/build/src/backends/dart.js +321 -0
  24. package/build/src/backends/effects.d.ts +60 -0
  25. package/build/src/backends/effects.d.ts.map +1 -0
  26. package/build/src/backends/effects.js +746 -0
  27. package/build/src/backends/go.js +18 -4
  28. package/build/src/backends/identifiers.d.ts +36 -4
  29. package/build/src/backends/identifiers.d.ts.map +1 -1
  30. package/build/src/backends/identifiers.js +160 -10
  31. package/build/src/backends/index.d.ts.map +1 -1
  32. package/build/src/backends/index.js +27 -4
  33. package/build/src/backends/ios.d.ts +68 -0
  34. package/build/src/backends/ios.d.ts.map +1 -0
  35. package/build/src/backends/ios.js +1503 -0
  36. package/build/src/backends/java.d.ts.map +1 -1
  37. package/build/src/backends/java.js +429 -36
  38. package/build/src/backends/javascript.d.ts +6 -0
  39. package/build/src/backends/javascript.d.ts.map +1 -1
  40. package/build/src/backends/javascript.js +380 -24
  41. package/build/src/backends/kotlin-runtime.d.ts +3 -0
  42. package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
  43. package/build/src/backends/kotlin-runtime.js +29 -0
  44. package/build/src/backends/kotlin.d.ts +33 -0
  45. package/build/src/backends/kotlin.d.ts.map +1 -0
  46. package/build/src/backends/kotlin.js +138 -0
  47. package/build/src/backends/native-validation.d.ts +2 -2
  48. package/build/src/backends/native-validation.d.ts.map +1 -1
  49. package/build/src/backends/native-validation.js +2 -2
  50. package/build/src/backends/ordered-expressions.d.ts.map +1 -1
  51. package/build/src/backends/ordered-expressions.js +39 -7
  52. package/build/src/backends/php-stdlib-support.d.ts +19 -0
  53. package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
  54. package/build/src/backends/php-stdlib-support.js +227 -0
  55. package/build/src/backends/php.d.ts.map +1 -1
  56. package/build/src/backends/php.js +448 -33
  57. package/build/src/backends/program.d.ts +83 -0
  58. package/build/src/backends/program.d.ts.map +1 -0
  59. package/build/src/backends/program.js +1309 -0
  60. package/build/src/backends/python.js +7 -1
  61. package/build/src/backends/ruby.d.ts.map +1 -1
  62. package/build/src/backends/ruby.js +310 -24
  63. package/build/src/backends/rust-runtime.d.ts +7 -0
  64. package/build/src/backends/rust-runtime.d.ts.map +1 -0
  65. package/build/src/backends/rust-runtime.js +32 -0
  66. package/build/src/backends/rust-validation.d.ts +9 -0
  67. package/build/src/backends/rust-validation.d.ts.map +1 -0
  68. package/build/src/backends/rust-validation.js +78 -0
  69. package/build/src/backends/rust.d.ts +24 -0
  70. package/build/src/backends/rust.d.ts.map +1 -0
  71. package/build/src/backends/rust.js +344 -0
  72. package/build/src/backends/scalars.d.ts +14 -6
  73. package/build/src/backends/scalars.d.ts.map +1 -1
  74. package/build/src/backends/scalars.js +167 -17
  75. package/build/src/backends/shared.d.ts +23 -4
  76. package/build/src/backends/shared.d.ts.map +1 -1
  77. package/build/src/backends/shared.js +2174 -62
  78. package/build/src/backends/stdlib-support.d.ts +14 -0
  79. package/build/src/backends/stdlib-support.d.ts.map +1 -0
  80. package/build/src/backends/stdlib-support.js +237 -0
  81. package/build/src/backends/swift-runtime.d.ts +3 -0
  82. package/build/src/backends/swift-runtime.d.ts.map +1 -0
  83. package/build/src/backends/swift-runtime.js +14 -0
  84. package/build/src/backends/swift.d.ts +8 -0
  85. package/build/src/backends/swift.d.ts.map +1 -0
  86. package/build/src/backends/swift.js +127 -0
  87. package/build/src/backends/typescript.d.ts.map +1 -1
  88. package/build/src/backends/typescript.js +344 -24
  89. package/build/src/backends/wasm.d.ts +43 -0
  90. package/build/src/backends/wasm.d.ts.map +1 -0
  91. package/build/src/backends/wasm.js +2164 -0
  92. package/build/src/backends/writer.d.ts +255 -4
  93. package/build/src/backends/writer.d.ts.map +1 -1
  94. package/build/src/backends/writer.js +491 -3
  95. package/build/src/diagnostic.d.ts +4 -3
  96. package/build/src/diagnostic.d.ts.map +1 -1
  97. package/build/src/diagnostic.js +5 -3
  98. package/build/src/frontends/c.d.ts.map +1 -1
  99. package/build/src/frontends/c.js +5 -3
  100. package/build/src/frontends/cpp.d.ts.map +1 -1
  101. package/build/src/frontends/cpp.js +5 -3
  102. package/build/src/frontends/csharp.d.ts.map +1 -1
  103. package/build/src/frontends/csharp.js +7 -3
  104. package/build/src/frontends/dart.d.ts +10 -0
  105. package/build/src/frontends/dart.d.ts.map +1 -0
  106. package/build/src/frontends/dart.js +913 -0
  107. package/build/src/frontends/index.d.ts +3 -1
  108. package/build/src/frontends/index.d.ts.map +1 -1
  109. package/build/src/frontends/index.js +15 -5
  110. package/build/src/frontends/java.d.ts +110 -1
  111. package/build/src/frontends/java.d.ts.map +1 -1
  112. package/build/src/frontends/java.js +1725 -70
  113. package/build/src/frontends/javascript-typescript.d.ts +69 -1
  114. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  115. package/build/src/frontends/javascript-typescript.js +1825 -93
  116. package/build/src/frontends/kotlin.d.ts +10 -0
  117. package/build/src/frontends/kotlin.d.ts.map +1 -0
  118. package/build/src/frontends/kotlin.js +685 -0
  119. package/build/src/frontends/php.d.ts +118 -1
  120. package/build/src/frontends/php.d.ts.map +1 -1
  121. package/build/src/frontends/php.js +1583 -76
  122. package/build/src/frontends/program.d.ts +74 -0
  123. package/build/src/frontends/program.d.ts.map +1 -0
  124. package/build/src/frontends/program.js +989 -0
  125. package/build/src/frontends/ruby.d.ts +68 -1
  126. package/build/src/frontends/ruby.d.ts.map +1 -1
  127. package/build/src/frontends/ruby.js +1446 -101
  128. package/build/src/frontends/rust-parser.d.ts +11 -0
  129. package/build/src/frontends/rust-parser.d.ts.map +1 -0
  130. package/build/src/frontends/rust-parser.js +15 -0
  131. package/build/src/frontends/rust.d.ts +14 -0
  132. package/build/src/frontends/rust.d.ts.map +1 -0
  133. package/build/src/frontends/rust.js +715 -0
  134. package/build/src/frontends/scalars.d.ts +10 -0
  135. package/build/src/frontends/scalars.d.ts.map +1 -1
  136. package/build/src/frontends/scalars.js +28 -2
  137. package/build/src/frontends/swift.d.ts +10 -0
  138. package/build/src/frontends/swift.d.ts.map +1 -0
  139. package/build/src/frontends/swift.js +860 -0
  140. package/build/src/frontends/types.d.ts +186 -0
  141. package/build/src/frontends/types.d.ts.map +1 -0
  142. package/build/src/frontends/types.js +634 -0
  143. package/build/src/language-registry.d.ts +46 -4
  144. package/build/src/language-registry.d.ts.map +1 -1
  145. package/build/src/language-registry.js +177 -13
  146. package/build/src/mapping.d.ts +2 -2
  147. package/build/src/mapping.d.ts.map +1 -1
  148. package/build/src/mapping.js +31 -10
  149. package/build/src/semantic/capabilities.d.ts +66 -0
  150. package/build/src/semantic/capabilities.d.ts.map +1 -0
  151. package/build/src/semantic/capabilities.js +649 -0
  152. package/build/src/semantic/effects.d.ts +26 -0
  153. package/build/src/semantic/effects.d.ts.map +1 -0
  154. package/build/src/semantic/effects.js +764 -0
  155. package/build/src/semantic/generics.d.ts +23 -0
  156. package/build/src/semantic/generics.d.ts.map +1 -0
  157. package/build/src/semantic/generics.js +61 -0
  158. package/build/src/semantic/lifetime.d.ts +37 -0
  159. package/build/src/semantic/lifetime.d.ts.map +1 -0
  160. package/build/src/semantic/lifetime.js +527 -0
  161. package/build/src/semantic/provenance.d.ts +9 -2
  162. package/build/src/semantic/provenance.d.ts.map +1 -1
  163. package/build/src/semantic/provenance.js +462 -20
  164. package/build/src/semantic/scalars.d.ts +11 -0
  165. package/build/src/semantic/scalars.d.ts.map +1 -1
  166. package/build/src/semantic/scalars.js +16 -1
  167. package/build/src/semantic/stdlib.d.ts +254 -0
  168. package/build/src/semantic/stdlib.d.ts.map +1 -0
  169. package/build/src/semantic/stdlib.js +880 -0
  170. package/build/src/semantic/types.d.ts +3108 -178
  171. package/build/src/semantic/types.d.ts.map +1 -1
  172. package/build/src/semantic/types.js +730 -15
  173. package/build/src/semantic/validate.d.ts +85 -5
  174. package/build/src/semantic/validate.d.ts.map +1 -1
  175. package/build/src/semantic/validate.js +2576 -98
  176. package/build/src/stdlib-facades.d.ts +304 -0
  177. package/build/src/stdlib-facades.d.ts.map +1 -0
  178. package/build/src/stdlib-facades.js +1167 -0
  179. package/build/src/stdlib-negotiation.d.ts +69 -0
  180. package/build/src/stdlib-negotiation.d.ts.map +1 -0
  181. package/build/src/stdlib-negotiation.js +403 -0
  182. package/build/src/stdlib-providers.d.ts +72 -0
  183. package/build/src/stdlib-providers.d.ts.map +1 -0
  184. package/build/src/stdlib-providers.js +339 -0
  185. package/build/src/toolchains.d.ts +55 -1
  186. package/build/src/toolchains.d.ts.map +1 -1
  187. package/build/src/toolchains.js +15 -4
  188. package/docs/android.md +79 -0
  189. package/docs/architecture.md +47 -13
  190. package/docs/dart.md +60 -0
  191. package/docs/goals.md +3 -3
  192. package/docs/ios.md +121 -0
  193. package/docs/kotlin.md +42 -0
  194. package/docs/language-support.md +238 -26
  195. package/docs/multi-file-modules.md +52 -0
  196. package/docs/parser-qualification.md +121 -8
  197. package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
  198. package/docs/rust.md +88 -0
  199. package/docs/source-maps.md +17 -7
  200. package/docs/specification.md +5 -0
  201. package/docs/standard-library-portability.md +39 -8
  202. package/docs/swift.md +44 -0
  203. package/docs/testing.md +381 -4
  204. package/docs/webassembly.md +52 -0
  205. package/index.js +66 -8
  206. package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
  207. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
  208. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
  209. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
  210. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
  211. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
  212. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
  213. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
  214. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
  215. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
  216. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
  217. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
  218. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
  219. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
  220. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
  221. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
  222. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
  223. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
  224. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
  225. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
  226. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
  227. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
  228. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
  229. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
  230. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
  231. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
  232. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
  233. package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
  234. package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
  235. package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
  236. package/package.json +5 -2
  237. package/src/artifact-path.js +47 -2
  238. package/src/artifacts.js +67 -0
  239. package/src/backends/android.js +1370 -0
  240. package/src/backends/c-validation.js +6 -1
  241. package/src/backends/cpp-validation.js +6 -1
  242. package/src/backends/csharp.js +3 -0
  243. package/src/backends/dart-runtime.js +41 -0
  244. package/src/backends/dart.js +326 -0
  245. package/src/backends/effects.js +705 -0
  246. package/src/backends/go.js +14 -2
  247. package/src/backends/identifiers.js +166 -9
  248. package/src/backends/index.js +27 -2
  249. package/src/backends/ios.js +1612 -0
  250. package/src/backends/java.js +454 -36
  251. package/src/backends/javascript.js +389 -23
  252. package/src/backends/kotlin-runtime.js +29 -0
  253. package/src/backends/kotlin.js +144 -0
  254. package/src/backends/native-validation.js +1 -1
  255. package/src/backends/ordered-expressions.js +40 -6
  256. package/src/backends/php-stdlib-support.js +239 -0
  257. package/src/backends/php.js +469 -32
  258. package/src/backends/program.js +1408 -0
  259. package/src/backends/python.js +3 -0
  260. package/src/backends/ruby.js +326 -23
  261. package/src/backends/rust-runtime.js +34 -0
  262. package/src/backends/rust-validation.js +75 -0
  263. package/src/backends/rust.js +335 -0
  264. package/src/backends/scalars.js +141 -16
  265. package/src/backends/shared.js +2314 -115
  266. package/src/backends/stdlib-support.js +234 -0
  267. package/src/backends/swift-runtime.js +14 -0
  268. package/src/backends/swift.js +132 -0
  269. package/src/backends/typescript.js +349 -23
  270. package/src/backends/wasm.js +2208 -0
  271. package/src/backends/writer.js +545 -2
  272. package/src/diagnostic.js +4 -2
  273. package/src/frontends/c.js +4 -2
  274. package/src/frontends/cpp.js +4 -2
  275. package/src/frontends/csharp.js +4 -2
  276. package/src/frontends/dart.js +963 -0
  277. package/src/frontends/index.js +14 -4
  278. package/src/frontends/java.js +1977 -69
  279. package/src/frontends/javascript-typescript.js +2046 -102
  280. package/src/frontends/kotlin.js +713 -0
  281. package/src/frontends/php.js +1725 -79
  282. package/src/frontends/program.js +1062 -0
  283. package/src/frontends/ruby.js +1624 -98
  284. package/src/frontends/rust-parser.js +18 -0
  285. package/src/frontends/rust.js +714 -0
  286. package/src/frontends/scalars.js +31 -1
  287. package/src/frontends/swift.js +893 -0
  288. package/src/frontends/types.js +750 -0
  289. package/src/language-registry.js +182 -12
  290. package/src/mapping.js +33 -9
  291. package/src/semantic/capabilities.js +671 -0
  292. package/src/semantic/effects.js +737 -0
  293. package/src/semantic/generics.js +58 -0
  294. package/src/semantic/lifetime.js +517 -0
  295. package/src/semantic/provenance.js +412 -19
  296. package/src/semantic/scalars.js +17 -0
  297. package/src/semantic/stdlib.js +921 -0
  298. package/src/semantic/types.js +810 -14
  299. package/src/semantic/validate.js +2934 -209
  300. package/src/stdlib-facades.js +1211 -0
  301. package/src/stdlib-negotiation.js +446 -0
  302. package/src/stdlib-providers.js +377 -0
  303. package/src/toolchains.js +16 -3
@@ -0,0 +1,58 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Builds an ordered substitution map for a closed generic record application.
5
+ * @param {import("./types.js").RecordType} type - Closed record application.
6
+ * @param {import("./types.js").RecordDeclaration} declaration - Resolved declaration.
7
+ * @returns {Map<string, import("./types.js").SemanticValueType>} Substitutions.
8
+ */
9
+ export function recordTypeSubstitutions(type, declaration) {
10
+ return new Map((declaration.typeParameters ?? []).map((parameter, index) => [
11
+ /** @type {string} */ (parameter.id),
12
+ /** @type {import("./types.js").SemanticValueType} */ (type.arguments?.[index])
13
+ ]))
14
+ }
15
+
16
+ /**
17
+ * Applies complete recursive substitution without mutating declaration-owned types.
18
+ * @param {import("./types.js").SemanticValueType} type - Open semantic type.
19
+ * @param {Map<string, import("./types.js").SemanticValueType>} substitutions - Closed substitutions.
20
+ * @returns {import("./types.js").SemanticValueType} Substituted type.
21
+ */
22
+ export function substituteValueType(type, substitutions) {
23
+ if (type.kind == "TypeVariableReference") return substitutions.get(type.parameterId) ?? type
24
+ if (type.kind == "ListType") return {...type, elementType: substituteValueType(type.elementType, substitutions)}
25
+ if (type.kind == "MapType" || type.kind == "OrderedMapType") return {
26
+ ...type,
27
+ keyType: /** @type {import("./types.js").TypeReference} */ (substituteValueType(type.keyType, substitutions)),
28
+ valueType: substituteValueType(type.valueType, substitutions)
29
+ }
30
+ if (type.kind == "OptionalType") return {...type, valueType: substituteValueType(type.valueType, substitutions)}
31
+ if (type.kind == "RecordType" && type.arguments) {
32
+ return {...type, arguments: type.arguments.map((argument) => substituteValueType(argument, substitutions))}
33
+ }
34
+
35
+ return type
36
+ }
37
+
38
+ /**
39
+ * Checks whether a recursive type contains any declaration-scoped variable
40
+ * outside an explicitly visible identity set.
41
+ * @param {import("./types.js").SemanticValueType} type - Candidate open type.
42
+ * @param {Set<string>} [visibleParameterIds] - Exact variable identities permitted by the caller's lexical scope.
43
+ * @returns {boolean} Whether any non-visible type variable occurs.
44
+ */
45
+ export function typeContainsAnyVariable(type, visibleParameterIds = new Set()) {
46
+ if (type.kind == "TypeVariableReference") return !visibleParameterIds.has(type.parameterId)
47
+ if (type.kind == "RecordType") {
48
+ return type.arguments?.some((argument) => typeContainsAnyVariable(argument, visibleParameterIds)) ?? false
49
+ }
50
+ if (type.kind == "ListType") return typeContainsAnyVariable(type.elementType, visibleParameterIds)
51
+ if (type.kind == "MapType" || type.kind == "OrderedMapType") {
52
+ return typeContainsAnyVariable(type.keyType, visibleParameterIds) ||
53
+ typeContainsAnyVariable(type.valueType, visibleParameterIds)
54
+ }
55
+ if (type.kind == "OptionalType") return typeContainsAnyVariable(type.valueType, visibleParameterIds)
56
+
57
+ return false
58
+ }
@@ -0,0 +1,517 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {(code: string, detail: string, location: import("./types.js").SourceLocation) => never} LifetimeFail */
4
+ /** @typedef {{kind: "open", originId: string, origin: import("./types.js").SourceLocation} | {kind: "moved", originId: string, origin: import("./types.js").SourceLocation, destination: string} | {kind: "terminal", originId: string, origin: import("./types.js").SourceLocation, closeSiteId: string | import("./types.js").SourceLocation}} OwnerState */
5
+ /** @typedef {{kind: "normal" | "return" | "raise" | "break" | "continue", state: Map<string, OwnerState>, location?: import("./types.js").SourceLocation, errorId?: string}} LifetimeExit */
6
+
7
+ /**
8
+ * Performs path-sensitive structured lifetime validation for Task 034 owned values.
9
+ * @param {import("./types.js").SemanticModule} module - Fully type-resolved module.
10
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
11
+ * @param {LifetimeFail} fail - Located failure.
12
+ * @returns {void}
13
+ */
14
+ export function validateResourceLifetimes(module, errorEffects, fail) {
15
+ if (!(module.capabilities ?? []).some(({resources}) => resources.length > 0) && !moduleContainsOwnedType(module)) return
16
+
17
+ for (const declaration of module.classes ?? []) {
18
+ analyzeOwner(declaration.constructor.parameters, declaration.constructor.body, fail, undefined, errorEffects)
19
+ for (const method of declaration.methods) analyzeOwner(method.parameters, method.body, fail,
20
+ declaration.ownership?.kind == "ownedResource" ? declaration.ownership.fieldId : undefined, errorEffects)
21
+ }
22
+ for (const declaration of module.functions) analyzeOwner(declaration.parameters, declaration.body, fail, undefined, errorEffects)
23
+ analyzeOwner([], module.entryPoint.body, fail, undefined, errorEffects)
24
+ }
25
+
26
+ /**
27
+ * Analyzes one function, constructor, method, or entry-point owner.
28
+ * @param {import("./types.js").Parameter[]} parameters - Owned parameters.
29
+ * @param {import("./types.js").Block} body - Structured body.
30
+ * @param {LifetimeFail} fail - Located failure.
31
+ * @param {string | undefined} borrowedFieldId - Owned receiver field, when present.
32
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
33
+ * @returns {void}
34
+ */
35
+ function analyzeOwner(parameters, body, fail, borrowedFieldId, errorEffects) {
36
+ /** @type {Map<string, OwnerState>} */
37
+ const initial = new Map()
38
+ const obligations = new Set(parameters.filter(({type}) => isOwned(type)).map(({name}) => name))
39
+
40
+ for (const parameter of parameters) {
41
+ if (isOwned(parameter.type)) initial.set(parameter.name, openState(`parameter:${parameter.name}`, parameter.location))
42
+ }
43
+ if (borrowedFieldId) initial.set(`this.${borrowedFieldId}`, openState(`receiver:${borrowedFieldId}`, body.location))
44
+ const exits = analyzeBlock(body, [{kind: "normal", state: initial}], fail, errorEffects)
45
+
46
+ for (const exit of exits) checkLeaks(exit.state, obligations, exit.location ?? body.location, fail)
47
+ }
48
+
49
+ /**
50
+ * Evaluates one lexical block while retaining every abrupt exit separately.
51
+ * @param {import("./types.js").Block} block - Structured block.
52
+ * @param {LifetimeExit[]} incoming - Incoming control-flow states.
53
+ * @param {LifetimeFail} fail - Located failure.
54
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
55
+ * @returns {LifetimeExit[]} Reachable exits.
56
+ */
57
+ function analyzeBlock(block, incoming, fail, errorEffects) {
58
+ let active = incoming.filter(({kind}) => kind == "normal")
59
+ const departed = incoming.filter(({kind}) => kind != "normal")
60
+ /** @type {Set<string>} */
61
+ const locals = new Set()
62
+
63
+ for (const statement of block.statements) {
64
+ if (active.length == 0) break
65
+ const next = []
66
+
67
+ for (const flow of active) next.push(...analyzeStatement(statement, flow.state, fail, locals, errorEffects))
68
+ departed.push(...next.filter(({kind}) => kind != "normal"))
69
+ active = mergeEquivalentNormals(next.filter(({kind}) => kind == "normal"), statement.location, fail)
70
+ }
71
+ const exits = [...departed, ...active.map((flow) => ({...flow, location: flow.location ?? block.location}))]
72
+
73
+ for (const exit of exits) {
74
+ checkLeaks(exit.state, locals, exit.location ?? block.location, fail)
75
+ for (const name of locals) exit.state.delete(name)
76
+ }
77
+ return exits
78
+ }
79
+
80
+ /**
81
+ * Evaluates one statement from one normal state.
82
+ * @param {import("./types.js").Statement} statement - Statement.
83
+ * @param {Map<string, OwnerState>} state - Incoming ownership state.
84
+ * @param {LifetimeFail} fail - Located failure.
85
+ * @param {Set<string>} locals - Locals introduced in the current block.
86
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
87
+ * @returns {LifetimeExit[]} Reachable exits.
88
+ */
89
+ function analyzeStatement(statement, state, fail, locals, errorEffects) {
90
+ if (statement.kind == "LocalDeclaration") {
91
+ const flows = evaluateExpression(statement.initializer, cloneState(state), fail, errorEffects)
92
+
93
+ if (isOwned(statement.type)) {
94
+ locals.add(statement.name)
95
+ for (const flow of flows) {
96
+ if (flow.kind != "normal") continue
97
+ const origin = statement.initializer.kind == "OwnedMoveExpression"
98
+ ? flow.state.get(statement.initializer.expression.name)?.origin ?? statement.initializer.location
99
+ : statement.initializer.location
100
+ flow.state.set(statement.name, openState(`local:${statement.name}`, origin))
101
+ }
102
+ }
103
+ return flows.map((flow) => ({...flow, location: statement.location}))
104
+ }
105
+ if (statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement") {
106
+ return evaluateExpression(statement.expression, cloneState(state), fail, errorEffects)
107
+ .map((flow) => ({...flow, location: statement.location}))
108
+ }
109
+ if (statement.kind == "AssignmentStatement" || statement.kind == "PrivateFieldWriteStatement") {
110
+ return evaluateExpression(statement.expression, cloneState(state), fail, errorEffects)
111
+ .map((flow) => ({...flow, location: statement.location}))
112
+ }
113
+ if (statement.kind == "ReturnStatement") {
114
+ const flows = statement.expression ? evaluateExpression(statement.expression, cloneState(state), fail, errorEffects) :
115
+ /** @type {LifetimeExit[]} */ ([{kind: "normal", state: cloneState(state)}])
116
+ return flows.map((flow) => flow.kind == "normal"
117
+ ? {kind: "return", location: statement.location, state: flow.state}
118
+ : {...flow, location: statement.location})
119
+ }
120
+ if (statement.kind == "RaiseStatement") {
121
+ const flows = evaluateExpression(statement.error.message, cloneState(state), fail, errorEffects)
122
+ return flows.map((flow) => flow.kind == "normal"
123
+ ? {errorId: statement.error.error.declarationId, kind: "raise", location: statement.location, state: flow.state}
124
+ : {...flow, location: statement.location})
125
+ }
126
+ if (statement.kind == "IfStatement") {
127
+ const conditions = evaluateExpression(statement.condition, cloneState(state), fail, errorEffects)
128
+ /** @type {LifetimeExit[]} */
129
+ const result = conditions.filter(({kind}) => kind != "normal")
130
+
131
+ for (const condition of conditions.filter(({kind}) => kind == "normal")) {
132
+ result.push(...analyzeBlock(statement.consequent, [{kind: "normal", state: cloneState(condition.state)}], fail,
133
+ errorEffects))
134
+ const alternate = statement.alternate
135
+ ? analyzeBlock(statement.alternate, [{kind: "normal", state: cloneState(condition.state)}], fail, errorEffects)
136
+ : /** @type {LifetimeExit[]} */ ([{kind: "normal", location: statement.location, state: cloneState(condition.state)}])
137
+
138
+ result.push(...alternate)
139
+ }
140
+ return mergeEquivalentNormals(result, statement.location, fail)
141
+ }
142
+ if (statement.kind == "TryStatement") {
143
+ const bodyExits = analyzeBlock(statement.body, [{kind: "normal", state: cloneState(state)}], fail, errorEffects)
144
+ /** @type {LifetimeExit[]} */
145
+ const result = bodyExits.filter((flow) => flow.kind != "raise" || flow.errorId != statement.catchType.declarationId)
146
+
147
+ for (const caught of bodyExits.filter((flow) => flow.kind == "raise" && flow.errorId == statement.catchType.declarationId)) {
148
+ result.push(...analyzeBlock(statement.catchBody, [{kind: "normal", state: cloneState(caught.state)}], fail, errorEffects))
149
+ }
150
+ return mergeEquivalentNormals(result, statement.location, fail)
151
+ }
152
+ if (statement.kind == "WhileStatement") {
153
+ const conditions = evaluateExpression(statement.condition, cloneState(state), fail, errorEffects)
154
+ /** @type {LifetimeExit[]} */
155
+ const result = conditions.filter(({kind}) => kind != "normal")
156
+
157
+ for (const condition of conditions.filter(({kind}) => kind == "normal")) {
158
+ const bodyExits = analyzeBlock(statement.body, [{kind: "normal", state: cloneState(condition.state)}], fail,
159
+ errorEffects)
160
+ for (const exit of bodyExits) {
161
+ if (exit.kind == "normal" || exit.kind == "continue") requireSameState(condition.state, exit.state, statement.location, fail)
162
+ else if (exit.kind == "break") {
163
+ requireSameState(condition.state, exit.state, statement.location, fail)
164
+ result.push({kind: "normal", location: statement.location, state: exit.state})
165
+ } else result.push(exit)
166
+ }
167
+ result.push({kind: "normal", location: statement.location, state: condition.state})
168
+ }
169
+ return mergeEquivalentNormals(result, statement.location, fail)
170
+ }
171
+ if (statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement") {
172
+ const operand = statement.kind == "ForEachStatement" ? statement.list : statement.map
173
+ const operands = evaluateExpression(operand, cloneState(state), fail, errorEffects)
174
+ /** @type {LifetimeExit[]} */
175
+ const result = operands.filter(({kind}) => kind != "normal")
176
+
177
+ for (const evaluated of operands.filter(({kind}) => kind == "normal")) {
178
+ const bodyExits = analyzeBlock(statement.body, [{kind: "normal", state: cloneState(evaluated.state)}], fail,
179
+ errorEffects)
180
+ for (const exit of bodyExits) {
181
+ if (exit.kind == "normal" || exit.kind == "continue") requireSameState(evaluated.state, exit.state, statement.location, fail)
182
+ else if (exit.kind == "break") {
183
+ requireSameState(evaluated.state, exit.state, statement.location, fail)
184
+ result.push({kind: "normal", location: statement.location, state: exit.state})
185
+ } else result.push(exit)
186
+ }
187
+ result.push({kind: "normal", location: statement.location, state: evaluated.state})
188
+ }
189
+ return mergeEquivalentNormals(result, statement.location, fail)
190
+ }
191
+ if (statement.kind == "BreakStatement") return [{kind: "break", location: statement.location, state: cloneState(state)}]
192
+ if (statement.kind == "ContinueStatement") return [{kind: "continue", location: statement.location, state: cloneState(state)}]
193
+
194
+ return []
195
+ }
196
+
197
+ /**
198
+ * Evaluates one expression with failure states at exact evaluation boundaries.
199
+ * @param {import("./types.js").Expression} expression - Expression.
200
+ * @param {Map<string, OwnerState>} state - Incoming ownership state.
201
+ * @param {LifetimeFail} fail - Located failure.
202
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
203
+ * @returns {LifetimeExit[]} Reachable exits.
204
+ */
205
+ function evaluateExpression(expression, state, fail, errorEffects) {
206
+ if (expression.kind == "OwnedMoveExpression") {
207
+ const owner = requireOwner(expression.expression.name, state, expression.location, fail)
208
+
209
+ if (owner.kind == "moved") fail("USE_AFTER_MOVE", `Resource '${expression.expression.name}' was already moved.`, expression.location)
210
+ if (owner.kind == "terminal") fail("USE_AFTER_CLOSE", `Resource '${expression.expression.name}' is terminal after close.`, expression.location)
211
+ state.set(expression.expression.name, {...owner, destination: "transfer", kind: "moved"})
212
+ return [{kind: "normal", state}]
213
+ }
214
+ if (expression.kind == "OwnedBorrowExpression") {
215
+ const name = placeName(expression.expression)
216
+ const owner = requireOwner(name, state, expression.location, fail)
217
+
218
+ if (owner.kind == "moved") fail("USE_AFTER_MOVE", `Resource '${name}' was moved before this borrow.`, expression.location)
219
+ return [{kind: "normal", state}]
220
+ }
221
+ if (expression.kind == "EffectCallExpression") return evaluateEffectCall(expression, state, fail, errorEffects)
222
+ if (expression.kind == "CallExpression" || expression.kind == "MethodCallExpression" || expression.kind == "ReferenceConstruction") {
223
+ return evaluateOrdinaryCall(expression, state, fail, errorEffects)
224
+ }
225
+
226
+ /** @type {LifetimeExit[]} */
227
+ let flows = [{kind: "normal", state}]
228
+ for (const child of expressionChildren(expression)) flows = evaluateForFlows(child, flows, fail, errorEffects)
229
+ return flows
230
+ }
231
+
232
+ /**
233
+ * Applies one capability operation's declared ownership and failure flow.
234
+ * @param {import("./types.js").EffectCallExpression} expression - Effect call.
235
+ * @param {Map<string, OwnerState>} state - Incoming ownership state.
236
+ * @param {LifetimeFail} fail - Located failure.
237
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
238
+ * @returns {LifetimeExit[]} Reachable exits.
239
+ */
240
+ function evaluateEffectCall(expression, state, fail, errorEffects) {
241
+ /** @type {LifetimeExit[]} */
242
+ let flows = [{kind: "normal", state}]
243
+
244
+ for (const argument of expression.arguments) flows = evaluateForFlows(argument, flows, fail, errorEffects)
245
+ const result = flows.filter(({kind}) => kind != "normal")
246
+ const resourceFlow = expression.resolution.resourceFlow
247
+
248
+ for (const flow of flows.filter(({kind}) => kind == "normal")) {
249
+ let operationState = cloneState(flow.state)
250
+ let terminalFailure
251
+
252
+ if (resourceFlow.kind == "borrow" || resourceFlow.kind == "close") {
253
+ const argument = expression.arguments[resourceFlow.parameterIndex]
254
+ if (argument.kind != "OwnedBorrowExpression") fail("INVALID_RESOURCE_BORROW", "Resource operation lacks an immediate borrow.", argument.location)
255
+ const name = placeName(argument.expression)
256
+ const owner = requireOwner(name, operationState, argument.location, fail)
257
+
258
+ if (owner.kind == "moved") fail("USE_AFTER_MOVE", `Resource '${name}' was moved before this operation.`, argument.location)
259
+ if (owner.kind == "terminal") terminalFailure = resourceFlow.terminalFailureId
260
+ else if (resourceFlow.kind == "close") operationState.set(name, {...owner, closeSiteId: expression.effectSiteId, kind: "terminal"})
261
+ }
262
+ if (terminalFailure) {
263
+ result.push({errorId: terminalFailure, kind: "raise", location: expression.location, state: operationState})
264
+ continue
265
+ }
266
+ const failureIds = expression.resolution.failureIds.filter((id) =>
267
+ resourceFlow.kind != "borrow" && resourceFlow.kind != "close" || id != resourceFlow.terminalFailureId)
268
+
269
+ result.push({kind: "normal", state: cloneState(operationState)})
270
+ for (const errorId of failureIds) {
271
+ result.push({errorId, kind: "raise", location: expression.location, state: cloneState(operationState)})
272
+ }
273
+ }
274
+ return result
275
+ }
276
+
277
+ /**
278
+ * Applies call-entry ownership commit semantics after ordered argument evaluation.
279
+ * @param {import("./types.js").CallExpression | import("./types.js").MethodCallExpression | import("./types.js").ReferenceConstruction} expression - Call.
280
+ * @param {Map<string, OwnerState>} state - Incoming ownership state.
281
+ * @param {LifetimeFail} fail - Located failure.
282
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
283
+ * @returns {LifetimeExit[]} Reachable exits.
284
+ */
285
+ function evaluateOrdinaryCall(expression, state, fail, errorEffects) {
286
+ const arguments_ = expression.kind == "MethodCallExpression" ? [expression.receiver, ...expression.arguments] : expression.arguments
287
+ /** @type {LifetimeExit[]} */
288
+ let flows = [{kind: "normal", state}]
289
+ /** @type {string[]} */
290
+ const reservedMoves = []
291
+
292
+ for (const argument of arguments_) {
293
+ if (argument.kind == "OwnedMoveExpression") {
294
+ for (const flow of flows.filter(({kind}) => kind == "normal")) {
295
+ const owner = requireOwner(argument.expression.name, flow.state, argument.location, fail)
296
+ if (owner.kind == "moved") fail("USE_AFTER_MOVE", `Resource '${argument.expression.name}' was already moved.`, argument.location)
297
+ if (owner.kind == "terminal") fail("USE_AFTER_CLOSE", `Resource '${argument.expression.name}' is terminal after close.`, argument.location)
298
+ }
299
+ if (reservedMoves.includes(argument.expression.name)) {
300
+ fail("RESOURCE_ALIAS", `Resource '${argument.expression.name}' cannot be transferred to two parameters.`, argument.location)
301
+ }
302
+ reservedMoves.push(argument.expression.name)
303
+ } else flows = evaluateForFlows(argument, flows, fail, errorEffects)
304
+ }
305
+ const result = flows.filter(({kind}) => kind != "normal")
306
+
307
+ for (const flow of flows.filter(({kind}) => kind == "normal")) {
308
+ const entered = cloneState(flow.state)
309
+ for (const name of reservedMoves) {
310
+ const owner = requireOwner(name, entered, expression.location, fail)
311
+ if (owner.kind == "moved") fail("USE_AFTER_MOVE", `Resource '${name}' was already moved.`, expression.location)
312
+ if (owner.kind == "terminal") fail("USE_AFTER_CLOSE", `Resource '${name}' is terminal after close.`, expression.location)
313
+ entered.set(name, {...owner, destination: "callee", kind: "moved"})
314
+ }
315
+ if (expression.kind == "MethodCallExpression" && expression.resolution?.resourceFlow) {
316
+ const receiver = expression.receiver.kind == "OwnedBorrowExpression" ? expression.receiver.expression : expression.receiver
317
+ if (receiver.kind != "IdentifierExpression" && receiver.kind != "ReceiverExpression" && receiver.kind != "PrivateFieldRead") {
318
+ fail("INVALID_RESOURCE_BORROW", "Owned method receiver must be an immediate borrowed place.", receiver.location)
319
+ }
320
+ const name = placeName(receiver)
321
+ const owner = requireOwner(name, entered, expression.location, fail)
322
+
323
+ if (owner.kind == "terminal") {
324
+ if (expression.resolution.resourceFlow.terminalFailureId) {
325
+ result.push({errorId: expression.resolution.resourceFlow.terminalFailureId, kind: "raise",
326
+ location: expression.location, state: entered})
327
+ continue
328
+ }
329
+ fail("USE_AFTER_CLOSE", `Resource '${name}' is terminal after close.`, expression.location)
330
+ }
331
+ if (expression.resolution.resourceFlow.kind == "terminal") {
332
+ entered.set(name, {...owner, closeSiteId: expression.effectSiteId ?? expression.location, kind: "terminal"})
333
+ }
334
+ }
335
+ result.push({kind: "normal", state: cloneState(entered)})
336
+ const escaping = new Set(expression.failureIds ?? [])
337
+
338
+ if (expression.kind == "CallExpression") {
339
+ for (const errorId of errorEffects.functions.get(expression.callee) ?? []) escaping.add(errorId)
340
+ } else if (expression.kind == "MethodCallExpression") {
341
+ for (const errorId of errorEffects.methods.get(expression.method) ?? []) escaping.add(errorId)
342
+ }
343
+ for (const errorId of escaping) {
344
+ result.push({errorId, kind: "raise", location: expression.location, state: cloneState(entered)})
345
+ }
346
+ }
347
+ return result
348
+ }
349
+
350
+ /**
351
+ * Evaluates a child expression only along normal incoming paths.
352
+ * @param {import("./types.js").Expression} expression - Child expression.
353
+ * @param {LifetimeExit[]} flows - Incoming paths.
354
+ * @param {LifetimeFail} fail - Located failure.
355
+ * @param {{functions: Map<string, Set<string>>, methods: Map<string, Set<string>>}} errorEffects - Complete escaping error sets per function and method.
356
+ * @returns {LifetimeExit[]} Reachable exits.
357
+ */
358
+ function evaluateForFlows(expression, flows, fail, errorEffects) {
359
+ const result = flows.filter(({kind}) => kind != "normal")
360
+ for (const flow of flows.filter(({kind}) => kind == "normal")) {
361
+ result.push(...evaluateExpression(expression, cloneState(flow.state), fail, errorEffects))
362
+ }
363
+ return result
364
+ }
365
+
366
+ /**
367
+ * Returns non-call expression children in evaluation order.
368
+ * @param {import("./types.js").Expression} expression - Parent expression.
369
+ * @returns {import("./types.js").Expression[]} Children.
370
+ */
371
+ function expressionChildren(expression) {
372
+ if (expression.kind == "BinaryExpression") return [expression.left, expression.right]
373
+ if (expression.kind == "UnaryExpression") return [expression.operand]
374
+ if (expression.kind == "OptionalSome") return [expression.value]
375
+ if (expression.kind == "OptionalIsPresent" || expression.kind == "OptionalUnwrap") return [expression.operand]
376
+ if (expression.kind == "ListLiteral") return expression.elements
377
+ if (expression.kind == "MapLiteral" || expression.kind == "OrderedMapLiteral") return expression.entries.map(({value}) => value)
378
+ if (expression.kind == "ListIndexExpression") return [expression.collection, expression.index]
379
+ if (expression.kind == "MapLookupExpression") return [expression.collection, expression.key]
380
+ if (expression.kind == "CollectionSizeExpression") return [expression.collection]
381
+ if (expression.kind == "RecordConstruction") return expression.arguments
382
+ if (expression.kind == "MemberRead") return [expression.receiver]
383
+ if (expression.kind == "PrivateFieldRead") return [expression.receiver]
384
+ if (expression.kind == "ErrorMessageRead") return [expression.receiver]
385
+ return []
386
+ }
387
+
388
+ /**
389
+ * Merges normal paths only after proving identical ownership states.
390
+ * @param {LifetimeExit[]} flows - Candidate paths.
391
+ * @param {import("./types.js").SourceLocation} location - Join location.
392
+ * @param {LifetimeFail} fail - Located failure.
393
+ * @returns {LifetimeExit[]} Merged paths.
394
+ */
395
+ function mergeEquivalentNormals(flows, location, fail) {
396
+ const normal = flows.filter(({kind}) => kind == "normal")
397
+ if (normal.length > 1) {
398
+ for (const candidate of normal.slice(1)) requireSameState(normal[0].state, candidate.state, location, fail)
399
+ }
400
+ return [...flows.filter(({kind}) => kind != "normal"), ...(normal.length > 0 ? [normal[0]] : [])]
401
+ }
402
+
403
+ /**
404
+ * Requires a finite ownership invariant at a branch or loop join.
405
+ * @param {Map<string, OwnerState>} left - First ownership state.
406
+ * @param {Map<string, OwnerState>} right - Second ownership state.
407
+ * @param {import("./types.js").SourceLocation} location - Join location.
408
+ * @param {LifetimeFail} fail - Located failure.
409
+ * @returns {void}
410
+ */
411
+ function requireSameState(left, right, location, fail) {
412
+ const names = new Set([...left.keys(), ...right.keys()])
413
+ for (const name of names) {
414
+ const leftState = left.get(name)
415
+ const rightState = right.get(name)
416
+ if (leftState?.kind != rightState?.kind || leftState?.originId != rightState?.originId) {
417
+ fail("RESOURCE_STATE_MISMATCH", `Resource '${name}' has different ownership state across reachable paths.`, location)
418
+ }
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Rejects every owned value that leaves its introducing scope open.
424
+ * @param {Map<string, OwnerState>} state - Ownership state.
425
+ * @param {Set<string>} names - Owners introduced in the scope.
426
+ * @param {import("./types.js").SourceLocation} location - Exit location.
427
+ * @param {LifetimeFail} fail - Located failure.
428
+ * @returns {void}
429
+ */
430
+ function checkLeaks(state, names, location, fail) {
431
+ for (const name of names) {
432
+ const owner = state.get(name)
433
+ if (owner?.kind == "open") {
434
+ const origin = owner.origin
435
+ fail("LEAKED_RESOURCE", `Owned resource '${name}' remains open; acquired at ${origin.filename}:${origin.start.line}:${origin.start.column}.`, location)
436
+ }
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Resolves an owned place in the current scope.
442
+ * @param {string} name - Stable place name.
443
+ * @param {Map<string, OwnerState>} state - Ownership state.
444
+ * @param {import("./types.js").SourceLocation} location - Use location.
445
+ * @param {LifetimeFail} fail - Located failure.
446
+ * @returns {OwnerState} Owner state.
447
+ */
448
+ function requireOwner(name, state, location, fail) {
449
+ const owner = state.get(name)
450
+ if (!owner) return fail("INVALID_RESOURCE_BORROW", `No owned resource place '${name}' is in scope.`, location)
451
+ return owner
452
+ }
453
+
454
+ /**
455
+ * Converts an immediate owned expression to a stable place name.
456
+ * @param {import("./types.js").IdentifierExpression | import("./types.js").ReceiverExpression | import("./types.js").PrivateFieldRead} expression - Owned place.
457
+ * @returns {string} Place identity.
458
+ */
459
+ function placeName(expression) {
460
+ if (expression.kind == "IdentifierExpression") return expression.name
461
+ if (expression.kind == "ReceiverExpression") return "this"
462
+ if (expression.kind == "PrivateFieldRead") return `this.${expression.field}`
463
+ return "<invalid>"
464
+ }
465
+
466
+ /**
467
+ * Creates one open ownership state.
468
+ * @param {string} originId - Deterministic origin identity.
469
+ * @param {import("./types.js").SourceLocation} origin - Acquisition location.
470
+ * @returns {OwnerState} Open state.
471
+ */
472
+ function openState(originId, origin) {
473
+ return {kind: "open", origin, originId}
474
+ }
475
+
476
+ /**
477
+ * Clones an ownership state for a control-flow fork.
478
+ * @param {Map<string, OwnerState>} state - State to clone.
479
+ * @returns {Map<string, OwnerState>} Detached state.
480
+ */
481
+ function cloneState(state) {
482
+ return new Map([...state].map(([name, value]) => [name, {...value}]))
483
+ }
484
+
485
+ /**
486
+ * Checks whether a type carries linear ownership.
487
+ * @param {import("./types.js").SemanticFunctionReturnType} type - Semantic type.
488
+ * @returns {boolean} Whether the type is owned.
489
+ */
490
+ function isOwned(type) {
491
+ return type.kind == "OwnedResourceType" || type.kind == "OwnedReferenceType"
492
+ }
493
+
494
+ /**
495
+ * Checks whether the module contains any owned semantic type.
496
+ * @param {import("./types.js").SemanticModule} module - Semantic module.
497
+ * @returns {boolean} Whether lifetime analysis is required.
498
+ */
499
+ function moduleContainsOwnedType(module) {
500
+ const pending = [/** @type {unknown} */ (module)]
501
+ const seen = new Set()
502
+
503
+ while (pending.length > 0) {
504
+ const value = pending.pop()
505
+
506
+ if (!value || typeof value != "object" || seen.has(value)) continue
507
+ seen.add(value)
508
+ if (!Array.isArray(value) && (Reflect.get(value, "kind") == "OwnedResourceType" ||
509
+ Reflect.get(value, "kind") == "OwnedReferenceType")) return true
510
+ for (const [key, child] of Object.entries(value)) {
511
+ if (["location", "provenance", "sourceProvenance"].includes(key)) continue
512
+ if (Array.isArray(child)) pending.push(...child)
513
+ else if (child && typeof child == "object") pending.push(child)
514
+ }
515
+ }
516
+ return false
517
+ }