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.
- package/README.md +61 -15
- package/build/index.d.ts +52 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +60 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/android.d.ts +49 -0
- package/build/src/backends/android.d.ts.map +1 -0
- package/build/src/backends/android.js +1274 -0
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +160 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +33 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +138 -0
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +83 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1309 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2174 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +255 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +491 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +177 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +3108 -178
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +730 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/android.md +79 -0
- package/docs/architecture.md +47 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +238 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +17 -7
- package/docs/specification.md +5 -0
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +381 -4
- package/docs/webassembly.md +52 -0
- package/index.js +66 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/android.js +1370 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +166 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +144 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1408 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2314 -115
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +545 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +182 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +810 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {authorizedOperation, isTask034OperationName} from "./capabilities.js"
|
|
4
|
+
import {parseProtectedEntryName} from "./stdlib.js"
|
|
5
|
+
|
|
6
|
+
/** @typedef {{failureIds: Set<string>, host: boolean}} EffectSummary */
|
|
7
|
+
/** @typedef {(expression: import("./types.js").Expression) => void} ExpressionVisitor */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves compiler-authorized calls before ordinary semantic type validation.
|
|
11
|
+
* Source functions always win; authority is never inferred from spelling alone.
|
|
12
|
+
* @param {import("./types.js").SemanticModule} module - Parser-authored module.
|
|
13
|
+
* @param {string} language - Source language identity for protected entry targets.
|
|
14
|
+
* @param {Map<string, import("./types.js").FunctionDeclaration>} functions - Visible ordinary functions.
|
|
15
|
+
* @param {(code: string, detail: string, location: import("./types.js").SourceLocation) => never} fail - Located failure.
|
|
16
|
+
* @param {boolean} normalize - Whether parser-authored operation intent may be normalized.
|
|
17
|
+
* @param {Map<string, import("./types.js").ClassDeclaration>} [visibleClasses] - Resolved reference classes.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
export function validateEffectGraph(module, language, functions, fail, normalize, visibleClasses = new Map()) {
|
|
21
|
+
const capabilities = module.capabilities ?? []
|
|
22
|
+
const capabilityIds = new Set()
|
|
23
|
+
const operationIds = new Set()
|
|
24
|
+
const resourceIds = new Set()
|
|
25
|
+
const failureIds = new Set()
|
|
26
|
+
const operationNames = new Set()
|
|
27
|
+
|
|
28
|
+
if (!Array.isArray(capabilities)) fail("INVALID_CAPABILITY_AUTHORITY", "Capabilities must be an ordered array.", module.location)
|
|
29
|
+
for (const [capabilityIndex, capability] of capabilities.entries()) {
|
|
30
|
+
const legacyId = `capability:${capabilityIndex}`
|
|
31
|
+
|
|
32
|
+
if (!capability || capability.kind != "EffectCapabilityDeclaration" ||
|
|
33
|
+
capability.id != legacyId && !/^module:\d+\/capability:\d+$/u.test(capability.id) || capabilityIds.has(capability.id) ||
|
|
34
|
+
typeof capability.authorityId != "string" || typeof capability.name != "string" || !Array.isArray(capability.resources) ||
|
|
35
|
+
!Array.isArray(capability.failures) || !Array.isArray(capability.operations)) {
|
|
36
|
+
fail("INVALID_CAPABILITY_AUTHORITY", "Malformed capability declaration graph.", module.location)
|
|
37
|
+
}
|
|
38
|
+
capabilityIds.add(capability.id)
|
|
39
|
+
for (const [index, resource] of capability.resources.entries()) {
|
|
40
|
+
if (resource.kind != "EffectResourceDeclaration" || resource.id != `${capability.id}/resource:${index}` ||
|
|
41
|
+
resourceIds.has(resource.id)) fail("INVALID_CAPABILITY_AUTHORITY", "Malformed capability resource identity.", module.location)
|
|
42
|
+
resourceIds.add(resource.id)
|
|
43
|
+
}
|
|
44
|
+
for (const [index, failure] of capability.failures.entries()) {
|
|
45
|
+
if (failure.kind != "EffectFailureDeclaration" || failure.id != `${capability.id}/failure:${index}` ||
|
|
46
|
+
failureIds.has(failure.id)) fail("INVALID_CAPABILITY_AUTHORITY", "Malformed capability failure identity.", module.location)
|
|
47
|
+
failureIds.add(failure.id)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const capability of capabilities) {
|
|
51
|
+
for (const [index, operation] of capability.operations.entries()) {
|
|
52
|
+
if (operation.kind != "EffectOperationDeclaration" || operation.id != `${capability.id}/operation:${index}` ||
|
|
53
|
+
operationIds.has(operation.id) || operationNames.has(operation.name) || !Array.isArray(operation.parameters) ||
|
|
54
|
+
!Array.isArray(operation.failureIds) || operation.effects?.length != 1 || operation.effects[0] != "host" ||
|
|
55
|
+
operation.failureIds.some((id) => !failureIds.has(id))) {
|
|
56
|
+
fail("INVALID_CAPABILITY_AUTHORITY", "Malformed or ambiguous capability operation graph.", module.location)
|
|
57
|
+
}
|
|
58
|
+
operationIds.add(operation.id)
|
|
59
|
+
operationNames.add(operation.name)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const declaration of [
|
|
64
|
+
...module.functions,
|
|
65
|
+
...(module.records ?? []),
|
|
66
|
+
...(module.classes ?? []),
|
|
67
|
+
...(module.errors ?? [])
|
|
68
|
+
]) {
|
|
69
|
+
if (parseProtectedEntryName(declaration.name) !== null) {
|
|
70
|
+
fail("STDLIB_PROTECTED_BINDING_ACCESS",
|
|
71
|
+
`Source declaration '${declaration.name}' captures a reserved protected provider binding.`, declaration.location)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
forEachExpression(module, (expression) => {
|
|
76
|
+
if (expression.kind == "EffectCallExpression") {
|
|
77
|
+
const capability = capabilities.find(({id}) => id == expression.resolution?.capabilityId)
|
|
78
|
+
const operation = capability?.operations.find(({id}) => id == expression.operation)
|
|
79
|
+
|
|
80
|
+
if (operation && expression.resolution.operationId == operation.id &&
|
|
81
|
+
(!Array.isArray(expression.resolution.failureIds) ||
|
|
82
|
+
expression.resolution.failureIds.join(",") != operation.failureIds.join(","))) {
|
|
83
|
+
fail("UNDECLARED_FAILURE", "UNDECLARED_FAILURE: Effect call failure boundary does not match its compiler authority.",
|
|
84
|
+
expression.location)
|
|
85
|
+
}
|
|
86
|
+
if (!operation || expression.resolution.operationId != operation.id ||
|
|
87
|
+
JSON.stringify(expression.resolution) != JSON.stringify(signature(/** @type {import("./types.js").EffectCapabilityDeclaration} */ (capability), operation))) {
|
|
88
|
+
fail("UNDECLARED_EFFECT", "Effect call resolution does not match its compiler authority.", expression.location)
|
|
89
|
+
}
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if (expression.kind == "IdentifierExpression" && parseProtectedEntryName(expression.name) !== null) {
|
|
93
|
+
fail("STDLIB_PROTECTED_BINDING_ACCESS",
|
|
94
|
+
`Reference to protected provider entry '${expression.name}' is not portable code.`, expression.location)
|
|
95
|
+
}
|
|
96
|
+
if (expression.kind != "CallExpression") return
|
|
97
|
+
if (functions.has(expression.callee)) return
|
|
98
|
+
let operationName = expression.callee
|
|
99
|
+
const protectedEntry = parseProtectedEntryName(expression.callee)
|
|
100
|
+
|
|
101
|
+
if (protectedEntry !== null) {
|
|
102
|
+
if (capabilities.length == 0) {
|
|
103
|
+
fail("STDLIB_PROTECTED_BINDING_ACCESS",
|
|
104
|
+
`Protected provider entry '${expression.callee}' requires an explicit capability authority.`, expression.location)
|
|
105
|
+
}
|
|
106
|
+
if (protectedEntry.target != language) {
|
|
107
|
+
fail("STDLIB_PROTECTED_BINDING_ACCESS",
|
|
108
|
+
`Protected provider entry target '${protectedEntry.target}' does not match source language '${language}'.`,
|
|
109
|
+
expression.location)
|
|
110
|
+
}
|
|
111
|
+
operationName = protectedEntry.operation
|
|
112
|
+
}
|
|
113
|
+
const match = authorizedOperation(capabilities, operationName)
|
|
114
|
+
|
|
115
|
+
if (!match) {
|
|
116
|
+
if (protectedEntry !== null) {
|
|
117
|
+
fail("STDLIB_PROTECTED_BINDING_ACCESS",
|
|
118
|
+
`Protected provider entry for undeclared operation '${operationName}' has no authorized capability operation.`,
|
|
119
|
+
expression.location)
|
|
120
|
+
}
|
|
121
|
+
if (isTask034OperationName(operationName)) {
|
|
122
|
+
fail("MISSING_CAPABILITY_AUTHORITY", `Operation '${operationName}' requires explicit compiler authority.`, expression.location)
|
|
123
|
+
}
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
if (!normalize) {
|
|
127
|
+
fail("UNDECLARED_EFFECT", `Caller-authored ordinary call '${expression.callee}' cannot forge a capability operation.`, expression.location)
|
|
128
|
+
}
|
|
129
|
+
const {capability, operation} = match
|
|
130
|
+
const flow = operation.resourceFlow
|
|
131
|
+
|
|
132
|
+
if ((flow.kind == "borrow" || flow.kind == "close") && expression.arguments[flow.parameterIndex]) {
|
|
133
|
+
const argument = expression.arguments[flow.parameterIndex]
|
|
134
|
+
if (argument.kind != "IdentifierExpression" && argument.kind != "PrivateFieldRead" && argument.kind != "ReceiverExpression") {
|
|
135
|
+
fail("INVALID_RESOURCE_BORROW", "An owned borrow must name one immediate resource place.", argument.location)
|
|
136
|
+
}
|
|
137
|
+
expression.arguments[flow.parameterIndex] = {
|
|
138
|
+
expression: argument,
|
|
139
|
+
kind: "OwnedBorrowExpression",
|
|
140
|
+
location: argument.location,
|
|
141
|
+
mode: flow.kind == "close" ? "exclusive" : "shared",
|
|
142
|
+
sourceProvenance: argument.sourceProvenance,
|
|
143
|
+
type: /** @type {import("./types.js").OwnedResourceType | import("./types.js").OwnedReferenceType} */ (
|
|
144
|
+
operation.parameters[flow.parameterIndex].type
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const replacement = /** @type {import("./types.js").EffectCallExpression} */ (/** @type {unknown} */ (expression))
|
|
149
|
+
delete /** @type {{callee?: string}} */ (replacement).callee
|
|
150
|
+
replacement.kind = "EffectCallExpression"
|
|
151
|
+
replacement.operation = operation.id
|
|
152
|
+
replacement.resolution = signature(capability, operation)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const complete = completeEffectSummaries(module, failureIds, visibleClasses)
|
|
156
|
+
const summaries = complete.functions
|
|
157
|
+
const methodSummaries = complete.methods
|
|
158
|
+
const constructorSummaries = complete.constructors
|
|
159
|
+
|
|
160
|
+
for (const declaration of module.classes ?? []) {
|
|
161
|
+
applyDeclarationSummary(declaration.constructor,
|
|
162
|
+
constructorSummaries.get(/** @type {string} */ (declaration.constructor.id)), normalize, fail)
|
|
163
|
+
for (const method of declaration.methods) {
|
|
164
|
+
applyDeclarationSummary(method, methodSummaries.get(/** @type {string} */ (method.id)), normalize, fail)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let site = 0
|
|
168
|
+
|
|
169
|
+
forEachExpression(module, (expression) => {
|
|
170
|
+
if (expression.kind == "EffectCallExpression") {
|
|
171
|
+
if (normalize) expression.effectSiteId = `effect:${site}`
|
|
172
|
+
else validateSite(expression, site, fail)
|
|
173
|
+
site += 1
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
if (expression.kind == "CallExpression") {
|
|
177
|
+
const summary = summaries.get(expression.callee)
|
|
178
|
+
if (!summary?.host) {
|
|
179
|
+
if (normalize) {
|
|
180
|
+
delete expression.effectSiteId
|
|
181
|
+
delete expression.effects
|
|
182
|
+
delete expression.failureIds
|
|
183
|
+
}
|
|
184
|
+
if (!normalize && Array.isArray(expression.failureIds) && expression.failureIds.length > 0) {
|
|
185
|
+
fail("UNDECLARED_FAILURE", "UNDECLARED_FAILURE: Pure call carries a forged typed failure boundary.", expression.location)
|
|
186
|
+
}
|
|
187
|
+
if (!normalize && (expression.effectSiteId !== undefined || expression.effects !== undefined || expression.failureIds !== undefined)) {
|
|
188
|
+
fail("UNDECLARED_EFFECT", "Pure call carries forged effect metadata.", expression.location)
|
|
189
|
+
}
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if (normalize) {
|
|
193
|
+
expression.effectSiteId = `effect:${site}`
|
|
194
|
+
expression.effects = ["host"]
|
|
195
|
+
expression.failureIds = [...summary.failureIds]
|
|
196
|
+
} else {
|
|
197
|
+
validateSite(expression, site, fail)
|
|
198
|
+
if (!Array.isArray(expression.failureIds) || expression.failureIds.join(",") != [...summary.failureIds].join(",")) {
|
|
199
|
+
fail("UNDECLARED_FAILURE", "UNDECLARED_FAILURE: Call failure boundary does not match its resolved function.", expression.location)
|
|
200
|
+
}
|
|
201
|
+
if (expression.effects?.length != 1 || expression.effects[0] != "host") {
|
|
202
|
+
fail("UNDECLARED_EFFECT", "Call effect summary does not match its resolved function.", expression.location)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
site += 1
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
if (expression.kind == "MethodCallExpression" || expression.kind == "ReferenceConstruction") {
|
|
209
|
+
const summary = expression.kind == "MethodCallExpression"
|
|
210
|
+
? methodSummaries.get(expression.method)
|
|
211
|
+
: constructorSummaries.get(expression.resolution?.declarationId ?? `${expression.reference.declarationId}:constructor`)
|
|
212
|
+
const label = expression.kind == "MethodCallExpression" ? "method call" : "construction"
|
|
213
|
+
if (!summary?.host) {
|
|
214
|
+
if (normalize) {
|
|
215
|
+
delete expression.effectSiteId
|
|
216
|
+
delete expression.effects
|
|
217
|
+
delete expression.failureIds
|
|
218
|
+
}
|
|
219
|
+
if (!normalize && Array.isArray(expression.failureIds) && expression.failureIds.length > 0) {
|
|
220
|
+
fail("UNDECLARED_FAILURE", `UNDECLARED_FAILURE: Pure ${label} carries a forged typed failure boundary.`, expression.location)
|
|
221
|
+
}
|
|
222
|
+
if (!normalize && (expression.effectSiteId !== undefined || expression.effects !== undefined || expression.failureIds !== undefined)) {
|
|
223
|
+
fail("UNDECLARED_EFFECT", `Pure ${label} carries forged effect metadata.`, expression.location)
|
|
224
|
+
}
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
if (normalize) {
|
|
228
|
+
expression.effectSiteId = `effect:${site}`
|
|
229
|
+
expression.effects = ["host"]
|
|
230
|
+
expression.failureIds = [...summary.failureIds]
|
|
231
|
+
} else {
|
|
232
|
+
validateSite(expression, site, fail)
|
|
233
|
+
if (!Array.isArray(expression.failureIds) || expression.failureIds.join(",") != [...summary.failureIds].join(",")) {
|
|
234
|
+
fail("UNDECLARED_FAILURE", `UNDECLARED_FAILURE: ${label} failure boundary does not match its resolved declaration.`, expression.location)
|
|
235
|
+
}
|
|
236
|
+
if (expression.effects?.length != 1 || expression.effects[0] != "host") {
|
|
237
|
+
fail("UNDECLARED_EFFECT", `${label} effect summary does not match its resolved declaration.`, expression.location)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
site += 1
|
|
241
|
+
}
|
|
242
|
+
}, true)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Computes the joint finite fixed point across functions and resolved receiver methods.
|
|
247
|
+
* @param {import("./types.js").SemanticModule} module - Resolved module.
|
|
248
|
+
* @param {Set<string>} capabilityFailureIds - Declared capability failures.
|
|
249
|
+
* @param {Map<string, import("./types.js").ClassDeclaration>} visibleClasses - Resolved reference classes.
|
|
250
|
+
* @returns {{constructors: Map<string, EffectSummary>, functions: Map<string, EffectSummary>, methods: Map<string, EffectSummary>}} Complete summaries.
|
|
251
|
+
*/
|
|
252
|
+
function completeEffectSummaries(module, capabilityFailureIds, visibleClasses) {
|
|
253
|
+
const importedMethods = declaredClassSummaries(visibleClasses, "methods")
|
|
254
|
+
let functions = effectSummaries(module.functions, new Map(), capabilityFailureIds)
|
|
255
|
+
let methods = directMethodSummaries(module, functions, importedMethods, capabilityFailureIds, importedMethods)
|
|
256
|
+
|
|
257
|
+
while (true) {
|
|
258
|
+
const nextFunctions = effectSummaries(module.functions, methods, capabilityFailureIds)
|
|
259
|
+
const nextMethods = directMethodSummaries(module, nextFunctions, methods, capabilityFailureIds, importedMethods)
|
|
260
|
+
|
|
261
|
+
if (sameSummaries(functions, nextFunctions) && sameSummaries(methods, nextMethods)) {
|
|
262
|
+
return {constructors: directConstructorSummaries(module, nextFunctions, nextMethods, capabilityFailureIds,
|
|
263
|
+
declaredClassSummaries(visibleClasses, "constructors")), functions: nextFunctions, methods: nextMethods}
|
|
264
|
+
}
|
|
265
|
+
functions = nextFunctions
|
|
266
|
+
methods = nextMethods
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Compares stable host/failure summary maps.
|
|
272
|
+
* @param {Map<string, EffectSummary>} left - Previous summaries.
|
|
273
|
+
* @param {Map<string, EffectSummary>} right - Next summaries.
|
|
274
|
+
* @returns {boolean} Whether the summaries are identical.
|
|
275
|
+
*/
|
|
276
|
+
function sameSummaries(left, right) {
|
|
277
|
+
if (left.size != right.size) return false
|
|
278
|
+
for (const [name, summary] of left) {
|
|
279
|
+
const candidate = right.get(name)
|
|
280
|
+
|
|
281
|
+
if (!candidate || summary.host != candidate.host || summary.failureIds.size != candidate.failureIds.size ||
|
|
282
|
+
[...summary.failureIds].some((id) => !candidate.failureIds.has(id))) return false
|
|
283
|
+
}
|
|
284
|
+
return true
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Builds the complete immutable resolution recorded at an effect call site.
|
|
289
|
+
* @param {import("./types.js").EffectCapabilityDeclaration} capability - Declaring capability.
|
|
290
|
+
* @param {import("./types.js").EffectOperationDeclaration} operation - Resolved operation.
|
|
291
|
+
* @returns {import("./types.js").ResolvedEffectOperationSignature} Complete resolution.
|
|
292
|
+
*/
|
|
293
|
+
function signature(capability, operation) {
|
|
294
|
+
return {
|
|
295
|
+
capabilityId: capability.id,
|
|
296
|
+
effects: /** @type {const} */ (["host"]),
|
|
297
|
+
failureIds: [...operation.failureIds],
|
|
298
|
+
kind: /** @type {const} */ ("ResolvedEffectOperationSignature"),
|
|
299
|
+
operationId: operation.id,
|
|
300
|
+
parameterTypes: operation.parameters.map(({type}) => /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type))),
|
|
301
|
+
resourceFlow: structuredClone(operation.resourceFlow),
|
|
302
|
+
returnType: typeIdentity(operation.returnType)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Removes locations and provenance from one resolved type.
|
|
308
|
+
* @param {import("./types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
309
|
+
* @returns {import("./types.js").FunctionReturnTypeIdentity} Stable type identity.
|
|
310
|
+
*/
|
|
311
|
+
function typeIdentity(type) {
|
|
312
|
+
if (type.kind == "TypeReference") return type.name
|
|
313
|
+
if (type.kind == "OwnedResourceType") return {kind: type.kind, resourceId: type.resourceId}
|
|
314
|
+
if (type.kind == "OwnedReferenceType") return {declarationId: type.declarationId, kind: type.kind}
|
|
315
|
+
if (type.kind == "OptionalType") return {kind: type.kind,
|
|
316
|
+
valueType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.valueType))}
|
|
317
|
+
if (type.kind == "ListType") return {
|
|
318
|
+
elementType: /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.elementType)), kind: type.kind}
|
|
319
|
+
if (type.kind == "MapType" || type.kind == "OrderedMapType") {
|
|
320
|
+
const keyType = /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.keyType))
|
|
321
|
+
const valueType = /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(type.valueType))
|
|
322
|
+
|
|
323
|
+
return type.kind == "OrderedMapType" ? {keyType, kind: type.kind, order: "insertion", valueType} :
|
|
324
|
+
{keyType, kind: type.kind, valueType}
|
|
325
|
+
}
|
|
326
|
+
if (type.kind == "RecordType") return {arguments: type.arguments?.map((argument) =>
|
|
327
|
+
/** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(argument))), declarationId: type.declarationId, kind: type.kind}
|
|
328
|
+
if (type.kind == "ReferenceType") return {declarationId: type.declarationId, kind: type.kind}
|
|
329
|
+
return {kind: type.kind, parameterId: type.parameterId}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Computes the fixed-point host-effect summary for ordinary functions.
|
|
334
|
+
* @param {import("./types.js").FunctionDeclaration[]} functions - Module functions.
|
|
335
|
+
* @param {Map<string, EffectSummary>} [methodSummaries] - Receiver-call summaries from the current fixed-point round.
|
|
336
|
+
* @param {Set<string>} [capabilityFailureIds] - Capability failures eligible for call-site propagation.
|
|
337
|
+
* @returns {Map<string, EffectSummary>} Summaries by source function name.
|
|
338
|
+
*/
|
|
339
|
+
function effectSummaries(functions, methodSummaries = new Map(), capabilityFailureIds = new Set()) {
|
|
340
|
+
const summaries = new Map(functions.map(({name}) => /** @type {[string, EffectSummary]} */ (
|
|
341
|
+
[name, {failureIds: new Set(), host: false}]
|
|
342
|
+
)))
|
|
343
|
+
let changed = true
|
|
344
|
+
|
|
345
|
+
while (changed) {
|
|
346
|
+
changed = false
|
|
347
|
+
for (const declaration of functions) {
|
|
348
|
+
const summary = summaries.get(declaration.name)
|
|
349
|
+
if (!summary) continue
|
|
350
|
+
forEachBlockExpression(declaration.body, (expression) => {
|
|
351
|
+
if (expression.kind == "EffectCallExpression") {
|
|
352
|
+
if (!summary.host) { summary.host = true; changed = true }
|
|
353
|
+
} else if (expression.kind == "CallExpression") {
|
|
354
|
+
const called = summaries.get(expression.callee)
|
|
355
|
+
if (called?.host && !summary.host) { summary.host = true; changed = true }
|
|
356
|
+
} else if (expression.kind == "MethodCallExpression") {
|
|
357
|
+
const called = methodSummaries.get(expression.method)
|
|
358
|
+
if (called?.host && !summary.host) { summary.host = true; changed = true }
|
|
359
|
+
}
|
|
360
|
+
}, false)
|
|
361
|
+
const escaping = escapingBlockFailures(declaration.body, summaries, methodSummaries, capabilityFailureIds)
|
|
362
|
+
|
|
363
|
+
for (const id of escaping) if (!summary.failureIds.has(id)) { summary.failureIds.add(id); changed = true }
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return summaries
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Computes the capability failures escaping one structured block.
|
|
371
|
+
* @param {import("./types.js").Block} block - Function body.
|
|
372
|
+
* @param {Map<string, EffectSummary>} functionSummaries - Current function fixed point.
|
|
373
|
+
* @param {Map<string, EffectSummary>} methodSummaries - Resolved method summaries.
|
|
374
|
+
* @param {Set<string>} capabilityFailureIds - Declared capability failures.
|
|
375
|
+
* @returns {Set<string>} Escaping failure identities.
|
|
376
|
+
*/
|
|
377
|
+
function escapingBlockFailures(block, functionSummaries, methodSummaries, capabilityFailureIds) {
|
|
378
|
+
const result = new Set()
|
|
379
|
+
|
|
380
|
+
for (const statement of block.statements) {
|
|
381
|
+
if (statement.kind == "TryStatement") {
|
|
382
|
+
const body = escapingBlockFailures(statement.body, functionSummaries, methodSummaries, capabilityFailureIds)
|
|
383
|
+
|
|
384
|
+
if (body.delete(statement.catchType.declarationId)) {
|
|
385
|
+
addFailures(result, escapingBlockFailures(statement.catchBody, functionSummaries, methodSummaries, capabilityFailureIds))
|
|
386
|
+
}
|
|
387
|
+
addFailures(result, body)
|
|
388
|
+
continue
|
|
389
|
+
}
|
|
390
|
+
for (const expression of statementExpressions(statement)) {
|
|
391
|
+
addExpressionFailures(result, expression, functionSummaries, methodSummaries)
|
|
392
|
+
}
|
|
393
|
+
if (statement.kind == "RaiseStatement" && capabilityFailureIds.has(statement.error.error.declarationId)) {
|
|
394
|
+
result.add(statement.error.error.declarationId)
|
|
395
|
+
}
|
|
396
|
+
for (const child of statementBlocks(statement)) {
|
|
397
|
+
addFailures(result, escapingBlockFailures(child, functionSummaries, methodSummaries, capabilityFailureIds))
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return result
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Adds failures evaluated by one expression subtree.
|
|
405
|
+
* @param {Set<string>} target - Destination identities.
|
|
406
|
+
* @param {import("./types.js").Expression} expression - Expression root.
|
|
407
|
+
* @param {Map<string, EffectSummary>} functionSummaries - Function summaries.
|
|
408
|
+
* @param {Map<string, EffectSummary>} methodSummaries - Method summaries.
|
|
409
|
+
* @returns {void}
|
|
410
|
+
*/
|
|
411
|
+
function addExpressionFailures(target, expression, functionSummaries, methodSummaries) {
|
|
412
|
+
if (expression.kind == "EffectCallExpression") addFailures(target, expression.resolution.failureIds)
|
|
413
|
+
else if (expression.kind == "CallExpression") addFailures(target, functionSummaries.get(expression.callee)?.failureIds ?? [])
|
|
414
|
+
else if (expression.kind == "MethodCallExpression") addFailures(target, methodSummaries.get(expression.method)?.failureIds ?? [])
|
|
415
|
+
for (const child of expressionChildren(expression)) addExpressionFailures(target, child, functionSummaries, methodSummaries)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Returns directly evaluated expressions for one statement.
|
|
420
|
+
* @param {import("./types.js").Statement} statement - Statement.
|
|
421
|
+
* @returns {import("./types.js").Expression[]} Expression roots.
|
|
422
|
+
*/
|
|
423
|
+
function statementExpressions(statement) {
|
|
424
|
+
if (statement.kind == "LocalDeclaration") return [statement.initializer]
|
|
425
|
+
if (statement.kind == "AssignmentStatement" || statement.kind == "PrivateFieldWriteStatement" ||
|
|
426
|
+
statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement") return [statement.expression]
|
|
427
|
+
if (statement.kind == "ReturnStatement") return statement.expression ? [statement.expression] : []
|
|
428
|
+
if (statement.kind == "RaiseStatement") return [statement.error.message]
|
|
429
|
+
if (statement.kind == "IfStatement" || statement.kind == "WhileStatement") return [statement.condition]
|
|
430
|
+
if (statement.kind == "ForEachStatement") return [statement.list]
|
|
431
|
+
if (statement.kind == "ForEachMapStatement") return [statement.map]
|
|
432
|
+
return []
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Returns nested blocks whose failures propagate through one statement.
|
|
437
|
+
* @param {import("./types.js").Statement} statement - Statement.
|
|
438
|
+
* @returns {import("./types.js").Block[]} Child blocks.
|
|
439
|
+
*/
|
|
440
|
+
function statementBlocks(statement) {
|
|
441
|
+
if (statement.kind == "IfStatement") return [statement.consequent, ...(statement.alternate ? [statement.alternate] : [])]
|
|
442
|
+
if (statement.kind == "WhileStatement" || statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement") {
|
|
443
|
+
return [statement.body]
|
|
444
|
+
}
|
|
445
|
+
return []
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Adds an iterable of failure identities to a set.
|
|
450
|
+
* @param {Set<string>} target - Destination.
|
|
451
|
+
* @param {Iterable<string>} source - Source identities.
|
|
452
|
+
* @returns {void}
|
|
453
|
+
*/
|
|
454
|
+
function addFailures(target, source) {
|
|
455
|
+
for (const id of source) target.add(id)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Computes bounded direct summaries for class methods.
|
|
460
|
+
* @param {import("./types.js").SemanticModule} module - Resolved module.
|
|
461
|
+
* @param {Map<string, EffectSummary>} functionSummaries - Ordinary function summaries.
|
|
462
|
+
* @param {Map<string, EffectSummary>} priorMethodSummaries - Receiver summaries from the previous fixed-point round.
|
|
463
|
+
* @param {Set<string>} capabilityFailureIds - Declared capability failures.
|
|
464
|
+
* @param {Map<string, EffectSummary>} baseSummaries - Imported receiver summaries.
|
|
465
|
+
* @returns {Map<string, EffectSummary>} Summaries by resolved method identity.
|
|
466
|
+
*/
|
|
467
|
+
function directMethodSummaries(module, functionSummaries, priorMethodSummaries, capabilityFailureIds, baseSummaries = new Map()) {
|
|
468
|
+
/** @type {Map<string, EffectSummary>} */
|
|
469
|
+
const summaries = new Map(baseSummaries)
|
|
470
|
+
|
|
471
|
+
for (const declaration of module.classes ?? []) {
|
|
472
|
+
for (const method of declaration.methods) {
|
|
473
|
+
/** @type {EffectSummary} */
|
|
474
|
+
const summary = {failureIds: escapingBlockFailures(method.body, functionSummaries, priorMethodSummaries, capabilityFailureIds), host: false}
|
|
475
|
+
|
|
476
|
+
forEachBlockExpression(method.body, (expression) => {
|
|
477
|
+
if (expression.kind == "EffectCallExpression") {
|
|
478
|
+
summary.host = true
|
|
479
|
+
const flow = expression.resolution.resourceFlow
|
|
480
|
+
const argument = flow.kind == "borrow" || flow.kind == "close" ? expression.arguments[flow.parameterIndex] : undefined
|
|
481
|
+
const borrowedParameterName = argument?.kind == "OwnedBorrowExpression" && argument.expression.kind == "IdentifierExpression"
|
|
482
|
+
? argument.expression.name : undefined
|
|
483
|
+
const knownOpenBorrow = declaration.ownership?.kind == "ownedResource" && argument?.kind == "OwnedBorrowExpression" && (
|
|
484
|
+
argument.expression.kind == "PrivateFieldRead" && argument.expression.field == declaration.ownership.fieldId ||
|
|
485
|
+
borrowedParameterName !== undefined && method.parameters.some((parameter) =>
|
|
486
|
+
parameter.name == borrowedParameterName &&
|
|
487
|
+
(parameter.type.kind == "OwnedResourceType" || parameter.type.kind == "OwnedReferenceType"))
|
|
488
|
+
)
|
|
489
|
+
const terminalFailureId = knownOpenBorrow
|
|
490
|
+
? flow.kind == "borrow" || flow.kind == "close" ? flow.terminalFailureId : undefined
|
|
491
|
+
: undefined
|
|
492
|
+
|
|
493
|
+
if (terminalFailureId) summary.failureIds.delete(terminalFailureId)
|
|
494
|
+
} else if (expression.kind == "CallExpression") {
|
|
495
|
+
const called = functionSummaries.get(expression.callee)
|
|
496
|
+
if (called?.host) summary.host = true
|
|
497
|
+
} else if (expression.kind == "MethodCallExpression") {
|
|
498
|
+
const called = priorMethodSummaries.get(expression.method)
|
|
499
|
+
if (called?.host) summary.host = true
|
|
500
|
+
}
|
|
501
|
+
}, false)
|
|
502
|
+
summaries.set(/** @type {string} */ (method.id), summary)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return summaries
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Reads checked summaries already proved on imported class declarations.
|
|
510
|
+
* @param {Map<string, import("./types.js").ClassDeclaration>} classes - Visible classes.
|
|
511
|
+
* @param {"constructors" | "methods"} kind - Declaration cohort.
|
|
512
|
+
* @returns {Map<string, EffectSummary>} Imported summaries by declaration identity.
|
|
513
|
+
*/
|
|
514
|
+
function declaredClassSummaries(classes, kind) {
|
|
515
|
+
/** @type {Map<string, EffectSummary>} */
|
|
516
|
+
const summaries = new Map()
|
|
517
|
+
|
|
518
|
+
for (const declaration of classes.values()) {
|
|
519
|
+
const members = kind == "constructors" ? [declaration.constructor] : declaration.methods
|
|
520
|
+
|
|
521
|
+
for (const member of members) {
|
|
522
|
+
if (member.effects?.length == 1 && member.effects[0] == "host" && Array.isArray(member.failureIds)) {
|
|
523
|
+
summaries.set(/** @type {string} */ (member.id), {failureIds: new Set(member.failureIds), host: true})
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return summaries
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Computes direct constructor effects after function and method fixed points stabilize.
|
|
532
|
+
* @param {import("./types.js").SemanticModule} module - Resolved module.
|
|
533
|
+
* @param {Map<string, EffectSummary>} functionSummaries - Stable function summaries.
|
|
534
|
+
* @param {Map<string, EffectSummary>} methodSummaries - Stable method summaries.
|
|
535
|
+
* @param {Set<string>} capabilityFailureIds - Declared capability failures.
|
|
536
|
+
* @param {Map<string, EffectSummary>} baseSummaries - Imported constructor summaries.
|
|
537
|
+
* @returns {Map<string, EffectSummary>} Constructor summaries by identity.
|
|
538
|
+
*/
|
|
539
|
+
function directConstructorSummaries(module, functionSummaries, methodSummaries, capabilityFailureIds, baseSummaries) {
|
|
540
|
+
const summaries = new Map(baseSummaries)
|
|
541
|
+
|
|
542
|
+
for (const declaration of module.classes ?? []) {
|
|
543
|
+
const constructor = declaration.constructor
|
|
544
|
+
/** @type {EffectSummary} */
|
|
545
|
+
const summary = {
|
|
546
|
+
failureIds: escapingBlockFailures(constructor.body, functionSummaries, methodSummaries, capabilityFailureIds),
|
|
547
|
+
host: false
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
forEachBlockExpression(constructor.body, (expression) => {
|
|
551
|
+
if (expression.kind == "EffectCallExpression") summary.host = true
|
|
552
|
+
else if (expression.kind == "CallExpression" && functionSummaries.get(expression.callee)?.host) summary.host = true
|
|
553
|
+
else if (expression.kind == "MethodCallExpression" && methodSummaries.get(expression.method)?.host) summary.host = true
|
|
554
|
+
}, false)
|
|
555
|
+
summaries.set(/** @type {string} */ (constructor.id), summary)
|
|
556
|
+
}
|
|
557
|
+
return summaries
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Records or verifies one checked class-member summary.
|
|
562
|
+
* @param {import("./types.js").ConstructorDeclaration | import("./types.js").MethodDeclaration} declaration - Class member.
|
|
563
|
+
* @param {EffectSummary | undefined} summary - Computed summary.
|
|
564
|
+
* @param {boolean} normalize - Whether metadata may be written.
|
|
565
|
+
* @param {(code: string, detail: string, location: import("./types.js").SourceLocation) => never} fail - Located failure.
|
|
566
|
+
* @returns {void}
|
|
567
|
+
*/
|
|
568
|
+
function applyDeclarationSummary(declaration, summary, normalize, fail) {
|
|
569
|
+
if (!summary?.host) {
|
|
570
|
+
if (normalize) {
|
|
571
|
+
delete declaration.effects
|
|
572
|
+
delete declaration.failureIds
|
|
573
|
+
} else if (declaration.effects !== undefined || declaration.failureIds !== undefined) {
|
|
574
|
+
fail("UNDECLARED_EFFECT", "Pure class member carries forged effect metadata.", declaration.location)
|
|
575
|
+
}
|
|
576
|
+
return
|
|
577
|
+
}
|
|
578
|
+
const failureIds = [...summary.failureIds]
|
|
579
|
+
|
|
580
|
+
if (normalize) {
|
|
581
|
+
declaration.effects = ["host"]
|
|
582
|
+
declaration.failureIds = failureIds
|
|
583
|
+
} else if (declaration.effects?.length != 1 || declaration.effects[0] != "host" ||
|
|
584
|
+
!Array.isArray(declaration.failureIds) || declaration.failureIds.join(",") != failureIds.join(",")) {
|
|
585
|
+
fail("UNDECLARED_EFFECT", "Class member effect summary does not match its body.", declaration.location)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Validates one deterministic effect-site identity.
|
|
591
|
+
* @param {import("./types.js").CallExpression | import("./types.js").EffectCallExpression | import("./types.js").MethodCallExpression | import("./types.js").ReferenceConstruction} expression - Effectful call.
|
|
592
|
+
* @param {number} site - Expected traversal index.
|
|
593
|
+
* @param {(code: string, detail: string, location: import("./types.js").SourceLocation) => never} fail - Located failure.
|
|
594
|
+
* @returns {void}
|
|
595
|
+
*/
|
|
596
|
+
function validateSite(expression, site, fail) {
|
|
597
|
+
if (expression.effectSiteId != `effect:${site}`) {
|
|
598
|
+
fail("INVALID_EFFECT_ORDER", `Effect site '${String(expression.effectSiteId)}' is not expected site 'effect:${site}'.`, expression.location)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Visits every expression in deterministic declaration and evaluation order.
|
|
604
|
+
* @param {import("./types.js").SemanticModule} module - Module to traverse.
|
|
605
|
+
* @param {ExpressionVisitor} visitor - Expression visitor.
|
|
606
|
+
* @param {boolean} [postorder] - Visit children before parents.
|
|
607
|
+
* @returns {void}
|
|
608
|
+
*/
|
|
609
|
+
function forEachExpression(module, visitor, postorder = false) {
|
|
610
|
+
for (const declaration of module.classes ?? []) {
|
|
611
|
+
forEachBlockExpression(declaration.constructor.body, visitor, postorder)
|
|
612
|
+
for (const method of declaration.methods) forEachBlockExpression(method.body, visitor, postorder)
|
|
613
|
+
}
|
|
614
|
+
for (const declaration of module.functions) forEachBlockExpression(declaration.body, visitor, postorder)
|
|
615
|
+
forEachBlockExpression(module.entryPoint.body, visitor, postorder)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Visits expressions in one structured block.
|
|
620
|
+
* @param {import("./types.js").Block} block - Block to traverse.
|
|
621
|
+
* @param {ExpressionVisitor} visitor - Expression visitor.
|
|
622
|
+
* @param {boolean} postorder - Visit children before parents.
|
|
623
|
+
* @returns {void}
|
|
624
|
+
*/
|
|
625
|
+
function forEachBlockExpression(block, visitor, postorder) {
|
|
626
|
+
for (const statement of block.statements) {
|
|
627
|
+
if (statement.kind == "LocalDeclaration") visit(statement.initializer)
|
|
628
|
+
else if (statement.kind == "AssignmentStatement" || statement.kind == "PrivateFieldWriteStatement") visit(statement.expression)
|
|
629
|
+
else if (statement.kind == "ReturnStatement" && statement.expression) visit(statement.expression)
|
|
630
|
+
else if (statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement") visit(statement.expression)
|
|
631
|
+
else if (statement.kind == "IfStatement") {
|
|
632
|
+
visit(statement.condition); forEachBlockExpression(statement.consequent, visitor, postorder)
|
|
633
|
+
if (statement.alternate) forEachBlockExpression(statement.alternate, visitor, postorder)
|
|
634
|
+
} else if (statement.kind == "ForEachStatement") {
|
|
635
|
+
visit(statement.list); forEachBlockExpression(statement.body, visitor, postorder)
|
|
636
|
+
} else if (statement.kind == "ForEachMapStatement") {
|
|
637
|
+
visit(statement.map); forEachBlockExpression(statement.body, visitor, postorder)
|
|
638
|
+
} else if (statement.kind == "WhileStatement") {
|
|
639
|
+
visit(statement.condition); forEachBlockExpression(statement.body, visitor, postorder)
|
|
640
|
+
} else if (statement.kind == "RaiseStatement") visit(statement.error.message)
|
|
641
|
+
else if (statement.kind == "TryStatement") {
|
|
642
|
+
forEachBlockExpression(statement.body, visitor, postorder); forEachBlockExpression(statement.catchBody, visitor, postorder)
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Visits one expression subtree.
|
|
648
|
+
* @param {import("./types.js").Expression} expression - Expression root.
|
|
649
|
+
* @returns {void}
|
|
650
|
+
*/
|
|
651
|
+
function visit(expression) {
|
|
652
|
+
if (!postorder) visitor(expression)
|
|
653
|
+
for (const child of expressionChildren(expression)) visit(child)
|
|
654
|
+
if (postorder) visitor(expression)
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Reports whether one semantic block references a reserved protected provider entry.
|
|
660
|
+
* @param {import("./types.js").Block} block - Semantic block.
|
|
661
|
+
* @returns {boolean} Whether a protected entry name is referenced.
|
|
662
|
+
*/
|
|
663
|
+
export function blockReferencesProtectedEntry(block) {
|
|
664
|
+
return block.statements.some(statementReferencesProtectedEntry)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Reports whether one semantic statement references a reserved protected provider entry.
|
|
669
|
+
* @param {import("./types.js").Statement} statement - Semantic statement.
|
|
670
|
+
* @returns {boolean} Whether a protected entry name is referenced.
|
|
671
|
+
*/
|
|
672
|
+
function statementReferencesProtectedEntry(statement) {
|
|
673
|
+
switch (statement.kind) {
|
|
674
|
+
case "LocalDeclaration":
|
|
675
|
+
return expressionReferencesProtectedEntry(statement.initializer)
|
|
676
|
+
case "AssignmentStatement":
|
|
677
|
+
case "PrivateFieldWriteStatement":
|
|
678
|
+
return expressionReferencesProtectedEntry(statement.expression)
|
|
679
|
+
case "ReturnStatement":
|
|
680
|
+
return statement.expression ? expressionReferencesProtectedEntry(statement.expression) : false
|
|
681
|
+
case "ExpressionStatement":
|
|
682
|
+
case "PrintStatement":
|
|
683
|
+
return expressionReferencesProtectedEntry(statement.expression)
|
|
684
|
+
case "IfStatement":
|
|
685
|
+
return expressionReferencesProtectedEntry(statement.condition) ||
|
|
686
|
+
blockReferencesProtectedEntry(statement.consequent) ||
|
|
687
|
+
(statement.alternate ? blockReferencesProtectedEntry(statement.alternate) : false)
|
|
688
|
+
case "ForEachStatement":
|
|
689
|
+
return expressionReferencesProtectedEntry(statement.list) || blockReferencesProtectedEntry(statement.body)
|
|
690
|
+
case "ForEachMapStatement":
|
|
691
|
+
return expressionReferencesProtectedEntry(statement.map) || blockReferencesProtectedEntry(statement.body)
|
|
692
|
+
case "WhileStatement":
|
|
693
|
+
return expressionReferencesProtectedEntry(statement.condition) || blockReferencesProtectedEntry(statement.body)
|
|
694
|
+
case "RaiseStatement":
|
|
695
|
+
return expressionReferencesProtectedEntry(statement.error.message)
|
|
696
|
+
case "TryStatement":
|
|
697
|
+
return blockReferencesProtectedEntry(statement.body) || blockReferencesProtectedEntry(statement.catchBody)
|
|
698
|
+
default:
|
|
699
|
+
return false
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Reports whether one semantic expression references a reserved protected provider entry.
|
|
705
|
+
* @param {import("./types.js").Expression} expression - Semantic expression.
|
|
706
|
+
* @returns {boolean} Whether a protected entry name is referenced.
|
|
707
|
+
*/
|
|
708
|
+
function expressionReferencesProtectedEntry(expression) {
|
|
709
|
+
if (expression.kind == "IdentifierExpression" && parseProtectedEntryName(expression.name) !== null) return true
|
|
710
|
+
if (expression.kind == "CallExpression" && parseProtectedEntryName(expression.callee) !== null) return true
|
|
711
|
+
return expressionChildren(expression).some(expressionReferencesProtectedEntry)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Returns semantic child expressions in evaluation order.
|
|
716
|
+
* @param {import("./types.js").Expression} expression - Parent expression.
|
|
717
|
+
* @returns {import("./types.js").Expression[]} Children.
|
|
718
|
+
*/
|
|
719
|
+
function expressionChildren(expression) {
|
|
720
|
+
if (expression.kind == "CallExpression" || expression.kind == "EffectCallExpression" || expression.kind == "ReferenceConstruction" ||
|
|
721
|
+
expression.kind == "RecordConstruction") return expression.arguments
|
|
722
|
+
if (expression.kind == "MethodCallExpression") return [expression.receiver, ...expression.arguments]
|
|
723
|
+
if (expression.kind == "OwnedBorrowExpression" || expression.kind == "OwnedMoveExpression") return [expression.expression]
|
|
724
|
+
if (expression.kind == "BinaryExpression") return [expression.left, expression.right]
|
|
725
|
+
if (expression.kind == "UnaryExpression") return [expression.operand]
|
|
726
|
+
if (expression.kind == "OptionalSome") return [expression.value]
|
|
727
|
+
if (expression.kind == "OptionalIsPresent" || expression.kind == "OptionalUnwrap") return [expression.operand]
|
|
728
|
+
if (expression.kind == "ListLiteral") return expression.elements
|
|
729
|
+
if (expression.kind == "MapLiteral" || expression.kind == "OrderedMapLiteral") return expression.entries.map(({value}) => value)
|
|
730
|
+
if (expression.kind == "ListIndexExpression") return [expression.collection, expression.index]
|
|
731
|
+
if (expression.kind == "MapLookupExpression") return [expression.collection, expression.key]
|
|
732
|
+
if (expression.kind == "CollectionSizeExpression") return [expression.collection]
|
|
733
|
+
if (expression.kind == "MemberRead") return [expression.receiver]
|
|
734
|
+
if (expression.kind == "PrivateFieldRead") return [expression.receiver]
|
|
735
|
+
if (expression.kind == "ErrorMessageRead") return [expression.receiver]
|
|
736
|
+
return []
|
|
737
|
+
}
|