@scriptc/compiler 0.2.6 → 0.2.8
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/ambient/scriptc-node-fallback.d.ts +7 -18
- package/dist/backend/cycle-analysis.d.ts +21 -9
- package/dist/backend/cycle-analysis.js +139 -67
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +283 -3
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +39 -0
- package/dist/backend/cycle-mutability.js +205 -0
- package/dist/backend/cycle-mutability.js.map +1 -0
- package/dist/backend/link-plan-core.d.ts +2 -1
- package/dist/backend/link-plan-core.js +15 -3
- package/dist/backend/link-plan-core.js.map +1 -1
- package/dist/backend/link-plan.d.ts +2 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/alloc.d.ts +43 -0
- package/dist/backend/llvm/alloc.js +246 -0
- package/dist/backend/llvm/alloc.js.map +1 -0
- package/dist/backend/llvm/alloc.test.d.ts +1 -0
- package/dist/backend/llvm/alloc.test.js +102 -0
- package/dist/backend/llvm/alloc.test.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +9 -0
- package/dist/backend/llvm/blocks.js +9 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
- package/dist/backend/llvm/borrowed-receivers.js +39 -0
- package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.d.ts +18 -3
- package/dist/backend/llvm/call-lifetimes.js +54 -19
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +178 -3
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-narrows.d.ts +24 -0
- package/dist/backend/llvm/checked-narrows.js +108 -0
- package/dist/backend/llvm/checked-narrows.js.map +1 -0
- package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
- package/dist/backend/llvm/checked-narrows.test.js +208 -0
- package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +16 -1
- package/dist/backend/llvm/classes.js +60 -49
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +68 -6
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +13 -0
- package/dist/backend/llvm/common.js +30 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.d.ts +19 -3
- package/dist/backend/llvm/constant-callbacks.js +289 -52
- package/dist/backend/llvm/constant-callbacks.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.test.js +140 -3
- package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
- package/dist/backend/llvm/debug-info.d.ts +6 -2
- package/dist/backend/llvm/debug-info.js +11 -1
- package/dist/backend/llvm/debug-info.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.d.ts +29 -0
- package/dist/backend/llvm/dense-array-access.js +335 -0
- package/dist/backend/llvm/dense-array-access.js.map +1 -0
- package/dist/backend/llvm/dense-array-access.test.d.ts +1 -0
- package/dist/backend/llvm/dense-array-access.test.js +238 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +38 -0
- package/dist/backend/llvm/dyn.js +213 -14
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +308 -15
- package/dist/backend/llvm/emitter.js +1788 -206
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +89 -7
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +62 -8
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-async.test.d.ts +1 -0
- package/dist/backend/llvm/expr-async.test.js +62 -0
- package/dist/backend/llvm/expr-async.test.js.map +1 -0
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +85 -8
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +29 -21
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.d.ts +5 -0
- package/dist/backend/llvm/expr-calls.js +118 -15
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +7 -1
- package/dist/backend/llvm/expr-containers.js +246 -32
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +113 -8
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +244 -44
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js +3 -8
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +93 -36
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +51 -3
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +31 -17
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +96 -29
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/integer-remainder.d.ts +11 -0
- package/dist/backend/llvm/integer-remainder.js +91 -0
- package/dist/backend/llvm/integer-remainder.js.map +1 -1
- package/dist/backend/llvm/layouts.d.ts +3 -1
- package/dist/backend/llvm/layouts.js +3 -2
- package/dist/backend/llvm/layouts.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.d.ts +16 -0
- package/dist/backend/llvm/lazy-captures.js +96 -0
- package/dist/backend/llvm/lazy-captures.js.map +1 -0
- package/dist/backend/llvm/lazy-captures.test.d.ts +1 -0
- package/dist/backend/llvm/lazy-captures.test.js +78 -0
- package/dist/backend/llvm/lazy-captures.test.js.map +1 -0
- package/dist/backend/llvm/lib-abi.js +10 -0
- package/dist/backend/llvm/lib-abi.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +10 -15
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +8 -8
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +5 -1
- package/dist/backend/llvm/local-array-reads.js +147 -26
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +85 -4
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.d.ts +5 -1
- package/dist/backend/llvm/local-union-storage.js +54 -8
- package/dist/backend/llvm/local-union-storage.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +94 -6
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
- package/dist/backend/llvm/map-number-lookup.js +62 -0
- package/dist/backend/llvm/map-number-lookup.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
- package/dist/backend/llvm/map-read-lifetimes.js +24 -3
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +39 -20
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/nullable-fields.d.ts +28 -0
- package/dist/backend/llvm/nullable-fields.js +45 -0
- package/dist/backend/llvm/nullable-fields.js.map +1 -0
- package/dist/backend/llvm/nullable-fields.test.d.ts +1 -0
- package/dist/backend/llvm/nullable-fields.test.js +48 -0
- package/dist/backend/llvm/nullable-fields.test.js.map +1 -0
- package/dist/backend/llvm/nullable-unions.d.ts +68 -0
- package/dist/backend/llvm/nullable-unions.js +132 -0
- package/dist/backend/llvm/nullable-unions.js.map +1 -0
- package/dist/backend/llvm/publish.d.ts +58 -0
- package/dist/backend/llvm/publish.js +286 -0
- package/dist/backend/llvm/publish.js.map +1 -0
- package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
- package/dist/backend/llvm/rebound-parameters.js +72 -0
- package/dist/backend/llvm/rebound-parameters.js.map +1 -0
- package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
- package/dist/backend/llvm/rebound-parameters.test.js +67 -0
- package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
- package/dist/backend/llvm/reference-effects.d.ts +18 -2
- package/dist/backend/llvm/reference-effects.js +106 -16
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-effects.test.js +52 -1
- package/dist/backend/llvm/reference-effects.test.js.map +1 -1
- package/dist/backend/llvm/serialized-ir-emission.test.d.ts +1 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js +52 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +104 -9
- package/dist/backend/llvm/shapes.js +356 -46
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +139 -5
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/stack-callbacks.js +2 -3
- package/dist/backend/llvm/stack-callbacks.js.map +1 -1
- package/dist/backend/llvm/stack-captures.d.ts +3 -1
- package/dist/backend/llvm/stack-captures.js +5 -3
- package/dist/backend/llvm/stack-captures.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +43 -3
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.d.ts +10 -0
- package/dist/backend/llvm/string-construction.js +42 -0
- package/dist/backend/llvm/string-construction.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +1 -1
- package/dist/backend/llvm/string-key-hash.d.ts +15 -0
- package/dist/backend/llvm/string-key-hash.js +76 -0
- package/dist/backend/llvm/string-key-hash.js.map +1 -0
- package/dist/backend/llvm/string-key-hash.test.d.ts +1 -0
- package/dist/backend/llvm/string-key-hash.test.js +45 -0
- package/dist/backend/llvm/string-key-hash.test.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.test.js +65 -15
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-reads.d.ts +7 -0
- package/dist/backend/llvm/string-reads.js +76 -0
- package/dist/backend/llvm/string-reads.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.js +1 -1
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +3 -3
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
- package/dist/backend/llvm/test-boxed-unions.js +13 -0
- package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
- package/dist/backend/llvm/union-repr.d.ts +15 -0
- package/dist/backend/llvm/union-repr.js +89 -0
- package/dist/backend/llvm/union-repr.js.map +1 -0
- package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
- package/dist/backend/llvm/virtual-borrows.js +82 -0
- package/dist/backend/llvm/virtual-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.d.ts +43 -0
- package/dist/backend/llvm/walk-borrows.js +150 -0
- package/dist/backend/llvm/walk-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
- package/dist/backend/llvm/walk-borrows.test.js +119 -0
- package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +5 -1
- package/dist/backend/llvm/walkers.js +24 -21
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/may-throw.d.ts +29 -1
- package/dist/backend/may-throw.js +349 -3
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +114 -2
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native/cache-warm.d.ts +2 -1
- package/dist/backend/native/cache-warm.js.map +1 -1
- package/dist/backend/native/contracts.d.ts +3 -2
- package/dist/backend/native/contracts.js.map +1 -1
- package/dist/backend/native/runtime-inputs.d.ts +1 -1
- package/dist/backend/native/runtime-inputs.js +2 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +7 -4
- package/dist/backend/native-codegen-core.js +14 -9
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +9 -0
- package/dist/backend/native-codegen.js +53 -2
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +64 -10
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +3 -2
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +2 -1
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +2 -2
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +5 -1
- package/dist/backend/native-tools.js +3 -1
- package/dist/backend/native-tools.js.map +1 -1
- package/dist/backend/optimization.d.ts +25 -0
- package/dist/backend/optimization.js +21 -0
- package/dist/backend/optimization.js.map +1 -0
- package/dist/backend/optimization.test.d.ts +1 -0
- package/dist/backend/optimization.test.js +24 -0
- package/dist/backend/optimization.test.js.map +1 -0
- package/dist/backend/publication.d.ts +28 -0
- package/dist/backend/publication.js +155 -0
- package/dist/backend/publication.js.map +1 -0
- package/dist/backend/publication.test.d.ts +1 -0
- package/dist/backend/publication.test.js +72 -0
- package/dist/backend/publication.test.js.map +1 -0
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-core.d.ts +15 -4
- package/dist/backend/runtime-pack-core.js +23 -12
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack-native.d.ts +7 -3
- package/dist/backend/runtime-pack-native.js +0 -3
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.d.ts +20 -2
- package/dist/backend/runtime-pack.js +23 -0
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +115 -2
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/target-diagnostics.test.js +1 -1
- package/dist/backend/targets.d.ts +2 -1
- package/dist/backend/targets.js +1 -1
- package/dist/backend/targets.js.map +1 -1
- package/dist/cli/command.js +75 -11
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/host.d.ts +2 -1
- package/dist/cli/usage.d.ts +4 -1
- package/dist/cli/usage.js +13 -3
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +17 -5
- package/dist/coverage/report.d.ts +25 -2
- package/dist/coverage/report.js +161 -33
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/verdict-cache.d.ts +63 -0
- package/dist/coverage/verdict-cache.js +193 -0
- package/dist/coverage/verdict-cache.js.map +1 -0
- package/dist/coverage/verdict-cache.test.d.ts +1 -0
- package/dist/coverage/verdict-cache.test.js +52 -0
- package/dist/coverage/verdict-cache.test.js.map +1 -0
- package/dist/coverage/verdict-semantic.d.ts +2 -0
- package/dist/coverage/verdict-semantic.js +24 -0
- package/dist/coverage/verdict-semantic.js.map +1 -0
- package/dist/coverage/verdict-startup.d.ts +15 -0
- package/dist/coverage/verdict-startup.js +66 -0
- package/dist/coverage/verdict-startup.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +29 -1
- package/dist/diagnostics/diagnostic.js +84 -12
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/envelope.d.ts +130 -0
- package/dist/diagnostics/envelope.js +257 -0
- package/dist/diagnostics/envelope.js.map +1 -0
- package/dist/diagnostics/envelope.test.d.ts +1 -0
- package/dist/diagnostics/envelope.test.js +91 -0
- package/dist/diagnostics/envelope.test.js.map +1 -0
- package/dist/diagnostics/render.d.ts +3 -0
- package/dist/diagnostics/render.js +13 -2
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +2 -2
- package/dist/executable/executable-cache.js +7 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +20 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.d.ts +4 -0
- package/dist/executable/prepare.js +7 -1
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/analysis.js +1 -0
- package/dist/frontend/analysis.js.map +1 -1
- package/dist/frontend/lowering/abstract-equality.js +5 -49
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +30 -0
- package/dist/frontend/lowering/array-values.js +246 -1
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/errors.js +17 -2
- package/dist/frontend/lowering/builtins/errors.js.map +1 -1
- package/dist/frontend/lowering/builtins/numbers.js +29 -1
- package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
- package/dist/frontend/lowering/builtins/process.d.ts +10 -17
- package/dist/frontend/lowering/builtins/process.js +66 -29
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/builtins/workers.js +4 -2
- package/dist/frontend/lowering/builtins/workers.js.map +1 -1
- package/dist/frontend/lowering/caught-value-dispatch.d.ts +11 -0
- package/dist/frontend/lowering/caught-value-dispatch.js +87 -0
- package/dist/frontend/lowering/caught-value-dispatch.js.map +1 -0
- package/dist/frontend/lowering/class-assertions.d.ts +12 -0
- package/dist/frontend/lowering/class-assertions.js +229 -5
- package/dist/frontend/lowering/class-assertions.js.map +1 -1
- package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
- package/dist/frontend/lowering/class-callbacks.js +18 -5
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +12 -6
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.d.ts +5 -0
- package/dist/frontend/lowering/class-method-mutations.js +472 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -0
- package/dist/frontend/lowering/class-method-values.js +25 -5
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.js +8 -2
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-unions.d.ts +16 -0
- package/dist/frontend/lowering/class-unions.js +127 -0
- package/dist/frontend/lowering/class-unions.js.map +1 -0
- package/dist/frontend/lowering/coercions/builders.js +8 -1
- package/dist/frontend/lowering/coercions/builders.js.map +1 -1
- package/dist/frontend/lowering/coercions/function-adapters.js +2 -0
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
- package/dist/frontend/lowering/coercions/identity.d.ts +7 -0
- package/dist/frontend/lowering/coercions/identity.js +26 -0
- package/dist/frontend/lowering/coercions/identity.js.map +1 -0
- package/dist/frontend/lowering/coercions/state.d.ts +6 -0
- package/dist/frontend/lowering/coercions/state.js +2 -0
- package/dist/frontend/lowering/coercions/state.js.map +1 -1
- package/dist/frontend/lowering/coercions/structural.js +9 -6
- package/dist/frontend/lowering/coercions/structural.js.map +1 -1
- package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
- package/dist/frontend/lowering/coercions/unions.js +67 -2
- package/dist/frontend/lowering/coercions/unions.js.map +1 -1
- package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
- package/dist/frontend/lowering/collation-repertoire.js +19 -0
- package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
- package/dist/frontend/lowering/collection-narrowing.d.ts +6 -0
- package/dist/frontend/lowering/collection-narrowing.js +31 -0
- package/dist/frontend/lowering/collection-narrowing.js.map +1 -0
- package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
- package/dist/frontend/lowering/construction-escapes.js +146 -0
- package/dist/frontend/lowering/construction-escapes.js.map +1 -0
- package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
- package/dist/frontend/lowering/construction-escapes.test.js +150 -0
- package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callback-call.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-callback-call.js +52 -0
- package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callbacks.d.ts +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.js +416 -65
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-concat.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-concat.js +130 -0
- package/dist/frontend/lowering/containers/array-concat.js.map +1 -0
- package/dist/frontend/lowering/containers/array-construction.d.ts +2 -0
- package/dist/frontend/lowering/containers/array-construction.js +110 -5
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.d.ts +1 -11
- package/dist/frontend/lowering/containers/array-methods.js +274 -144
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
- package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
- package/dist/frontend/lowering/containers/collection-methods.js +10 -1
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/indexed-objects.js +56 -70
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/cycle-initialization.d.ts +46 -0
- package/dist/frontend/lowering/cycle-initialization.js +303 -0
- package/dist/frontend/lowering/cycle-initialization.js.map +1 -0
- package/dist/frontend/lowering/divergences.d.ts +9 -0
- package/dist/frontend/lowering/divergences.js +630 -0
- package/dist/frontend/lowering/divergences.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +4 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +178 -34
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +4 -4
- package/dist/frontend/lowering/expressions/union-field-write.js +54 -44
- package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
- package/dist/frontend/lowering/iterator-adapters.d.ts +20 -0
- package/dist/frontend/lowering/iterator-adapters.js +605 -0
- package/dist/frontend/lowering/iterator-adapters.js.map +1 -0
- package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
- package/dist/frontend/lowering/lower-array-sort.js +44 -5
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-array-sort.test.js +31 -0
- package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js +7 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.js +1 -0
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +8 -3
- package/dist/frontend/lowering/lower-calls.js +388 -278
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +6 -0
- package/dist/frontend/lowering/lower-classes.js +117 -65
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +22 -2
- package/dist/frontend/lowering/lower-exprs.js +811 -254
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.js +32 -19
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +19 -13
- package/dist/frontend/lowering/lower-generators.js +67 -44
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +29 -4
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +1 -1
- package/dist/frontend/lowering/lower-island.js +24 -7
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +100 -3
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +82 -64
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +115 -2
- package/dist/frontend/lowering/lowerer.js +1266 -88
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/native-ffi.js +3 -1
- package/dist/frontend/lowering/native-ffi.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
- package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
- package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
- package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
- package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-provenance.d.ts +16 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js +41 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -0
- package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
- package/dist/frontend/lowering/strict-equality.d.ts +9 -0
- package/dist/frontend/lowering/strict-equality.js +116 -0
- package/dist/frontend/lowering/strict-equality.js.map +1 -0
- package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
- package/dist/frontend/lowering/string-index-bounds.js +537 -0
- package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
- package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
- package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
- package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
- package/dist/frontend/lowering/surfaces.js +3 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/union-comparison.d.ts +5 -0
- package/dist/frontend/lowering/union-comparison.js +47 -0
- package/dist/frontend/lowering/union-comparison.js.map +1 -0
- package/dist/frontend/lowering/union-narrow.d.ts +2 -0
- package/dist/frontend/lowering/union-narrow.js +10 -6
- package/dist/frontend/lowering/union-narrow.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +10 -0
- package/dist/frontend/program-analysis.js +7 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program.d.ts +63 -2
- package/dist/frontend/program.js +1038 -61
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +122 -6
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/resolve.d.ts +6 -0
- package/dist/frontend/resolve.js +14 -0
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/threads-import.d.ts +5 -0
- package/dist/frontend/threads-import.js +21 -0
- package/dist/frontend/threads-import.js.map +1 -0
- package/dist/frontend/type-mapper.d.ts +4 -1
- package/dist/frontend/type-mapper.js +161 -20
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +15 -3
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/worker-target.d.ts +4 -0
- package/dist/frontend/worker-target.js +8 -1
- package/dist/frontend/worker-target.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +70 -16
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +3 -1
- package/dist/ir/analysis.js +4 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/build.d.ts +5 -0
- package/dist/ir/build.js +7 -0
- package/dist/ir/build.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +5 -3
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-tables.test.js +1 -1
- package/dist/ir/initializer-bindings.test.js +1 -1
- package/dist/ir/int32-slots.d.ts +72 -0
- package/dist/ir/int32-slots.js +837 -0
- package/dist/ir/int32-slots.js.map +1 -0
- package/dist/ir/int32-slots.test.d.ts +1 -0
- package/dist/ir/int32-slots.test.js +222 -0
- package/dist/ir/int32-slots.test.js.map +1 -0
- package/dist/ir/integer-ranges.d.ts +19 -3
- package/dist/ir/integer-ranges.js +168 -23
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +74 -2
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +101 -20
- package/dist/ir/ir.js +20 -6
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/scalar-records.test.js +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +3 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/traverse.js +8 -0
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +139 -22
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +308 -8
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/fence-eval.test.js +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/library-cache.test.js +5 -5
- package/dist/library/library-profile.d.ts +2 -1
- package/dist/library/library-profile.js +4 -3
- package/dist/library/library-profile.js.map +1 -1
- package/dist/native/cache.d.ts +3 -0
- package/dist/native/cache.js +49 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +21 -1
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/cli.js +1 -1
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.d.ts +5 -0
- package/dist/native/compiler.js +51 -4
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/compiler.test.js +2 -2
- package/dist/native/library.d.ts +2 -1
- package/dist/native/library.js.map +1 -1
- package/dist/native/prepare.d.ts +4 -0
- package/dist/native/prepare.js +28 -3
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/sanitizer.d.ts +3 -2
- package/dist/native/sanitizer.js +3 -13
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/startup-cache.d.ts +2 -0
- package/dist/startup-cache.js +2 -0
- package/dist/startup-cache.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +8 -2
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import { checkCycleBindingAccesses, refuseUncheckedCycleReads, } from "./cycle-initialization.js";
|
|
2
|
+
import { identityPreservingWidening } from "./coercions/identity.js";
|
|
3
|
+
import { scanDivergences } from "./divergences.js";
|
|
4
|
+
import { checkedClassAssertion } from "./class-assertions.js";
|
|
5
|
+
import { narrowStoredClassValue, narrowGenericClassValue } from "./class-unions.js";
|
|
6
|
+
import { isOptionalProcessStreamProperty } from "./builtins/process.js";
|
|
7
|
+
import { concreteCollectionNarrow } from "./collection-narrowing.js";
|
|
1
8
|
import { lowerWorkerMetadata } from "./builtins/workers.js";
|
|
2
9
|
import { dynUndefinedExpr, nodeThrowExpr, numLit, varRef } from "../../ir/build.js";
|
|
10
|
+
import { iteratorSlotAdapter } from "./iterator-adapters.js";
|
|
3
11
|
import { lowerBuiltinCall } from "./builtin-calls.js";
|
|
4
12
|
import { CoercionState } from "./coercions/state.js";
|
|
5
13
|
import { widthLiftPlan, unitOnlyElem, recordWidthPlan, describeRecordWidthBlocker, objToRecordPlan, recordToClassPlan, } from "./coercions/structural-plans.js";
|
|
6
14
|
import { widthCoerce, applyWidthLift, unionRecordWidthHelper, recordWidthHelper, tupleArrayWidthHelper, emptyArrayLiftHelper, arrayWidthHelper, objRecordWidthHelper, recordClassWidthHelper, classStaticsProjection, } from "./coercions/structural.js";
|
|
7
15
|
import { funcReturnWidthAdapter, coercibleValue, cleanFuncAdaptable, funcCoerceAdapter, spawnResFnAdapterPlan, spawnResFnAdapter, } from "./coercions/function-adapters.js";
|
|
8
|
-
import { unionRetagMappable, narrowedRetagHelper, strandedUnitTrap, strandedCoercionTrap, recordUnionWrapHelper, unionRetagHelper, narrowedArmHelper, deferredReadHelper, } from "./coercions/unions.js";
|
|
16
|
+
import { unionRetagMappable, narrowedRetagHelper, strandedUnitTrap, strandedCoercionTrap, recordUnionWrapHelper, unionRetagHelper, narrowedArmHelper, checkedNarrowCall, checkedNarrowOperand, deferredReadHelper, } from "./coercions/unions.js";
|
|
9
17
|
import { jsvalLiftable, jsvalLiftExpr, unionToJsvalHelper, recordToJsvalHelper, arrayToJsvalHelper, arrayToJsvalArrayHelper, } from "./coercions/island.js";
|
|
10
18
|
import { lowerUtilTypeValue, pathModuleValue, utilTypesModuleValue, } from "./lower-builtin-values.js";
|
|
11
19
|
import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
|
|
20
|
+
import { RuntimeOptionalProvenance } from "./runtime-optional-provenance.js";
|
|
21
|
+
import { presentAtUse, reassignedBindings } from "./runtime-optional-guards.js";
|
|
22
|
+
import { ArrayElementStates } from "./runtime-optional-elements.js";
|
|
23
|
+
import { indexReadInBounds } from "./runtime-optional-bounds.js";
|
|
24
|
+
import { ArrayOwnership } from "./runtime-optional-ownership.js";
|
|
25
|
+
import { StringIndexBounds } from "./string-index-bounds.js";
|
|
12
26
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
13
27
|
import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
|
|
14
28
|
import { UnregisteredClassTypes } from "./unregistered-class-types.js";
|
|
@@ -44,7 +58,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
|
|
|
44
58
|
import { arrayOf, BOOL, canAdaptDynFuncTo, canDynCheckTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, DYN_HANDLE_KINDS, F64, isDynTypedRefType, isJsonSafeType, isJsonStringifySafeType, isPrimitiveCollectionKey, isSupportedMapKey, isSupportedMapValue, isUndefinedArmedUnion, isUnitType, JSVAL, NULL_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID, } from "../../ir/ir.js";
|
|
45
59
|
import { provenanceActive } from "../provenance-registry.js";
|
|
46
60
|
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
47
|
-
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
61
|
+
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
48
62
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
|
|
49
63
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
|
|
50
64
|
import { prepareCjsModuleGraph } from "./lower-node-module.js";
|
|
@@ -189,6 +203,39 @@ export function stmtUsesIsland(stmts) {
|
|
|
189
203
|
return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
|
|
190
204
|
return !everyStmtChild(stmts, expr, nested);
|
|
191
205
|
}
|
|
206
|
+
/** Statements a never-lowered body would have contributed to the coverage
|
|
207
|
+
* totals: every element of a statement list (blocks, case clauses), plus
|
|
208
|
+
* the single-statement bodies of control flow, nested functions included —
|
|
209
|
+
* the same accounting lowerStmts applies to bodies that do lower. */
|
|
210
|
+
export function countBodyStatements(root) {
|
|
211
|
+
let n = 0;
|
|
212
|
+
const visit = (node) => {
|
|
213
|
+
if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
|
|
214
|
+
n += node.statements.length;
|
|
215
|
+
}
|
|
216
|
+
else if ((ts.isIfStatement(node) ||
|
|
217
|
+
ts.isForStatement(node) ||
|
|
218
|
+
ts.isForInStatement(node) ||
|
|
219
|
+
ts.isForOfStatement(node) ||
|
|
220
|
+
ts.isWhileStatement(node) ||
|
|
221
|
+
ts.isDoStatement(node) ||
|
|
222
|
+
ts.isLabeledStatement(node)) &&
|
|
223
|
+
node.parent !== undefined) {
|
|
224
|
+
const bodies = ts.isIfStatement(node)
|
|
225
|
+
? [node.thenStatement, node.elseStatement]
|
|
226
|
+
: ts.isLabeledStatement(node)
|
|
227
|
+
? [node.statement]
|
|
228
|
+
: [node.statement];
|
|
229
|
+
for (const body of bodies) {
|
|
230
|
+
if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
|
|
231
|
+
n++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
// Iterative: absurdly deep nesting must not overflow the stack here.
|
|
236
|
+
ts.walkPreorder(root, visit);
|
|
237
|
+
return n;
|
|
238
|
+
}
|
|
192
239
|
function directExternalTypeSpecifiersByFile(externalTypes) {
|
|
193
240
|
const out = new Map();
|
|
194
241
|
for (const [specifier, file] of externalTypes) {
|
|
@@ -341,6 +388,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
341
388
|
resultLowerer = emit;
|
|
342
389
|
timing("historical-order-relower");
|
|
343
390
|
}
|
|
391
|
+
// Divergence warnings over the program's own modules: computed once,
|
|
392
|
+
// after lowering, from the same checker (never part of a failure).
|
|
393
|
+
const divergences = scanDivergences(resultLowerer, moduleOrder);
|
|
394
|
+
timing("divergence-scan", { found: divergences.length });
|
|
395
|
+
if (divergences.length > 0)
|
|
396
|
+
result = { ...result, divergences };
|
|
344
397
|
if (options.coverage !== true)
|
|
345
398
|
return result;
|
|
346
399
|
const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
@@ -358,7 +411,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
358
411
|
externalTypeSpecifiersByFile,
|
|
359
412
|
});
|
|
360
413
|
const rem = remainder.run();
|
|
361
|
-
|
|
414
|
+
// Unreached JS statements whose fences deferred to runtime count as
|
|
415
|
+
// failed statements in the remainder's stats, so their diagnostics must
|
|
416
|
+
// travel with them: a failed statement is never left unexplained.
|
|
417
|
+
return {
|
|
418
|
+
...result,
|
|
419
|
+
unreached: {
|
|
420
|
+
diagnostics: rem.diagnostics,
|
|
421
|
+
stats: rem.stats,
|
|
422
|
+
...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
|
|
423
|
+
},
|
|
424
|
+
};
|
|
362
425
|
}
|
|
363
426
|
/** The island-handle type a `import(...)` initializer gives a binding
|
|
364
427
|
* whose DECLARED type has no static mapping (`Promise<typeof
|
|
@@ -874,6 +937,17 @@ export function jsFuncNameOf(node, lowerer) {
|
|
|
874
937
|
}
|
|
875
938
|
return null;
|
|
876
939
|
}
|
|
940
|
+
/** Answers `fallback` when a checker query panics inside the type checker. */
|
|
941
|
+
function panicSafe(query, fallback) {
|
|
942
|
+
try {
|
|
943
|
+
return query();
|
|
944
|
+
}
|
|
945
|
+
catch (e) {
|
|
946
|
+
if (isCheckerPanic(e))
|
|
947
|
+
return fallback;
|
|
948
|
+
throw e;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
877
951
|
export class Lowerer {
|
|
878
952
|
program;
|
|
879
953
|
entry;
|
|
@@ -902,6 +976,14 @@ export class Lowerer {
|
|
|
902
976
|
/** Per-symbol result of the never-reassigned file scan
|
|
903
977
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
904
978
|
neverReassignedCache = new Map();
|
|
979
|
+
stringIndexBoundsCache = null;
|
|
980
|
+
/** Proofs that a string element read names an existing code unit. The
|
|
981
|
+
* indexed-read analysis and expression lowering share one instance, so a
|
|
982
|
+
* read's representation and its callers' signatures always agree. */
|
|
983
|
+
get stringIndexBounds() {
|
|
984
|
+
this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
|
|
985
|
+
return this.stringIndexBoundsCache;
|
|
986
|
+
}
|
|
905
987
|
/** Syntactic writes indexed once per JS scope. Symbol and RHS type
|
|
906
988
|
* checks remain contextual to each binding probe. */
|
|
907
989
|
jsBindingWritesByOwner = new Map();
|
|
@@ -1148,6 +1230,11 @@ export class Lowerer {
|
|
|
1148
1230
|
* prepass. This includes explicit annotations: unchecked TS annotations do
|
|
1149
1231
|
* not prove that an array property read produced a value. */
|
|
1150
1232
|
runtimeOptionalBindingTypes = new Map();
|
|
1233
|
+
/** Bindings that receive an unchecked indexed read (or a value derived
|
|
1234
|
+
* from one) through an initializer or assignment. The checker narrows a
|
|
1235
|
+
* declared optional binding to that read's bare type, so presence tests
|
|
1236
|
+
* on these bindings consult the stored tag instead. */
|
|
1237
|
+
runtimeOptionalAssignedSymbols = new Set();
|
|
1151
1238
|
/** Concise callback/lambda returns promoted by the HOF callback prepass;
|
|
1152
1239
|
* arrows have no declaration symbol to key in fnSigsBySymbol. */
|
|
1153
1240
|
runtimeOptionalFunctionReturns = new Map();
|
|
@@ -1165,6 +1252,17 @@ export class Lowerer {
|
|
|
1165
1252
|
* every name destructured from a present source optional. */
|
|
1166
1253
|
runtimeOptionalPatternTypes = new Map();
|
|
1167
1254
|
runtimeOptionalReduceTypes = new Map();
|
|
1255
|
+
/** Non-null assertions whose value is forwarded into a slot promoted to
|
|
1256
|
+
* hold undefined. `x!` has no runtime effect, so these keep the stored
|
|
1257
|
+
* undefined state instead of trapping before any dereference. */
|
|
1258
|
+
runtimeOptionalAssertions = new Set();
|
|
1259
|
+
/** Array callback calls whose receiver the optional-read analysis proved
|
|
1260
|
+
* never holds an explicit undefined (or a hole, for the find family):
|
|
1261
|
+
* their callbacks take the element type itself. */
|
|
1262
|
+
presentElementCalls = new Set();
|
|
1263
|
+
/** for-of loops over arrays that can hold neither holes nor undefined:
|
|
1264
|
+
* each pass reads the element payload directly. */
|
|
1265
|
+
presentElementLoops = new Set();
|
|
1168
1266
|
/** Capture entries and their origin share one mutable box. Normalize each
|
|
1169
1267
|
* entry to the origin so writes and flow proofs stay synchronized. */
|
|
1170
1268
|
runtimeOptionalRoots = new Map();
|
|
@@ -1239,6 +1337,9 @@ export class Lowerer {
|
|
|
1239
1337
|
* optional-read analysis, so ordinary assertions keep their generic
|
|
1240
1338
|
* checked-narrow behavior. */
|
|
1241
1339
|
runtimeOptionalSourceValue(node, value) {
|
|
1340
|
+
const slot = this.deferredFieldSlot(value);
|
|
1341
|
+
if (slot)
|
|
1342
|
+
return slot;
|
|
1242
1343
|
let origin = node;
|
|
1243
1344
|
while (ts.isParenthesizedExpression(origin))
|
|
1244
1345
|
origin = origin.expression;
|
|
@@ -1273,12 +1374,9 @@ export class Lowerer {
|
|
|
1273
1374
|
value.value.type.kind === "dyn")
|
|
1274
1375
|
return value.value;
|
|
1275
1376
|
let source = value;
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
value.args[0]?.type.kind === "union") {
|
|
1280
|
-
source = value.args[0];
|
|
1281
|
-
}
|
|
1377
|
+
const narrowed = checkedNarrowOperand(this, value);
|
|
1378
|
+
if (narrowed?.type.kind === "union")
|
|
1379
|
+
source = narrowed;
|
|
1282
1380
|
return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
|
|
1283
1381
|
? source
|
|
1284
1382
|
: null;
|
|
@@ -1358,7 +1456,16 @@ export class Lowerer {
|
|
|
1358
1456
|
runtimeOptionalWidening(actual, expected) {
|
|
1359
1457
|
if (actual.kind !== "union" || this.armTag(actual.unionId, UNDEFINED_T) < 0)
|
|
1360
1458
|
return null;
|
|
1361
|
-
|
|
1459
|
+
const present = this.stripUndefinedArm(actual);
|
|
1460
|
+
if (typeEquals(present, expected))
|
|
1461
|
+
return actual;
|
|
1462
|
+
// Only a bare expected arm gains runtime-optional storage. A slot that
|
|
1463
|
+
// already admits undefined keeps its own checked representation.
|
|
1464
|
+
if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0)
|
|
1465
|
+
return null;
|
|
1466
|
+
return identityPreservingWidening(this, present, expected)
|
|
1467
|
+
? this.runtimeOptionalType(expected)
|
|
1468
|
+
: null;
|
|
1362
1469
|
}
|
|
1363
1470
|
runtimeOptionalType(t) {
|
|
1364
1471
|
if (t.kind === "union")
|
|
@@ -1382,6 +1489,41 @@ export class Lowerer {
|
|
|
1382
1489
|
runtimeOptionalFunctionReturnType(node, fallback) {
|
|
1383
1490
|
return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
|
|
1384
1491
|
}
|
|
1492
|
+
/** True when `actual` is `declared` with some record fields (directly,
|
|
1493
|
+
* in nested records, or in an array's element records) widened to runtime-optional unions:
|
|
1494
|
+
* fields filled from unchecked indexed reads the checker calls present.
|
|
1495
|
+
* Inferred bindings and destructured parameters keep that layout instead
|
|
1496
|
+
* of narrowing it away. */
|
|
1497
|
+
isRuntimeOptionalRecordVariant(actual, declared) {
|
|
1498
|
+
if (actual.kind === "array" && declared.kind === "array")
|
|
1499
|
+
return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
|
|
1500
|
+
if (actual.kind !== "record" || declared.kind !== "record")
|
|
1501
|
+
return false;
|
|
1502
|
+
if (actual.shapeId === declared.shapeId)
|
|
1503
|
+
return false;
|
|
1504
|
+
const a = this.shapes.get(actual.shapeId);
|
|
1505
|
+
const d = this.shapes.get(declared.shapeId);
|
|
1506
|
+
if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
|
|
1507
|
+
return false;
|
|
1508
|
+
if (a.indexValue || d.indexValue)
|
|
1509
|
+
return false;
|
|
1510
|
+
let widened = false;
|
|
1511
|
+
for (const field of d.fields) {
|
|
1512
|
+
const source = a.fields.find((f) => f.name === field.name);
|
|
1513
|
+
if (!source)
|
|
1514
|
+
return false;
|
|
1515
|
+
if (typeEquals(source.type, field.type))
|
|
1516
|
+
continue;
|
|
1517
|
+
if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
|
|
1518
|
+
(this.runtimeOptionalWidening(source.type, field.type) !== null ||
|
|
1519
|
+
this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
|
|
1520
|
+
widened = true;
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
return widened;
|
|
1526
|
+
}
|
|
1385
1527
|
/** Promote one field of a record storage shape to the value's runtime
|
|
1386
1528
|
* type. Shapes are immutable by identity, so this interns a sibling shape
|
|
1387
1529
|
* and records the field as runtime-optional for property-read narrowing. */
|
|
@@ -1427,6 +1569,8 @@ export class Lowerer {
|
|
|
1427
1569
|
/** True while re-lowering a base function's 2nd+ instance: the same source
|
|
1428
1570
|
* statements were already counted for the first instance. */
|
|
1429
1571
|
suppressStats = false;
|
|
1572
|
+
/** Comparisons interned by operand representations and equality semantics. */
|
|
1573
|
+
equalityHelpers = new Map();
|
|
1430
1574
|
/** Synthetic array-HOF loop functions (map/filter/forEach desugar),
|
|
1431
1575
|
* interned per method + element/callback-result type: key → fn name. */
|
|
1432
1576
|
arrHofHelpers = new Map();
|
|
@@ -1518,6 +1662,10 @@ export class Lowerer {
|
|
|
1518
1662
|
* node carrying the ?. token is marked handled so the receiver-typed
|
|
1519
1663
|
* lowerings stop declining it (chainBlocked). */
|
|
1520
1664
|
chainRecvByNode = new Map();
|
|
1665
|
+
/** Lowered void calls whose checked type is `never` (they throw or exit).
|
|
1666
|
+
* TypeScript lets such a call stand where any value is expected;
|
|
1667
|
+
* coerceToExpected turns them into diverging sequences. */
|
|
1668
|
+
neverValued = new Set();
|
|
1521
1669
|
chainNarrowedType = new Map();
|
|
1522
1670
|
chainHandled = new Set();
|
|
1523
1671
|
/** for-of-over-matchAll bindings whose `.index` reads the companion-index
|
|
@@ -1549,6 +1697,8 @@ export class Lowerer {
|
|
|
1549
1697
|
/** Named method slots observed before class collection need a callable
|
|
1550
1698
|
* ABI and shared prototype descriptors instead of call-site specialization. */
|
|
1551
1699
|
prototypeMethodAccesses = new Map();
|
|
1700
|
+
/** Field names whose complete source census stores only lexical arrows. */
|
|
1701
|
+
receiverFreeCallbackFields = new Set();
|
|
1552
1702
|
/** Inferred JS methods participating in an override chain keep a vtable
|
|
1553
1703
|
* ABI instead of call-site specialization. Filled before class collection. */
|
|
1554
1704
|
virtualJsMethods = new Set();
|
|
@@ -1674,6 +1824,9 @@ export class Lowerer {
|
|
|
1674
1824
|
* and therefore becomes the cycle's evaluation root. Dynamic imports
|
|
1675
1825
|
* wait on this shared verdict rather than a build-time-selected member. */
|
|
1676
1826
|
asyncCyclePromiseOf = new Map();
|
|
1827
|
+
/** Modules whose evaluation an import() can request again after it threw,
|
|
1828
|
+
* computed on first use (lower-modules.ts importRetryModules). */
|
|
1829
|
+
importRetryModules = null;
|
|
1677
1830
|
/** Record-shape interner: canonical (name-sorted) field list → shapeId.
|
|
1678
1831
|
* Threaded into every mapType call; its `shapes` array becomes
|
|
1679
1832
|
* IrModule.records. */
|
|
@@ -1726,6 +1879,10 @@ export class Lowerer {
|
|
|
1726
1879
|
* function called above the declaration statement reads that, never a
|
|
1727
1880
|
* NULL slot. Filled by collectGlobals. */
|
|
1728
1881
|
varGlobalEntryInits = new Map();
|
|
1882
|
+
/** Import-cycle bindings with an initialization flag, by global id, and
|
|
1883
|
+
* the top-level statements declaring them (cycle-initialization.ts). */
|
|
1884
|
+
cycleInitFlags = new Map();
|
|
1885
|
+
cycleInitDeclarations = new Map();
|
|
1729
1886
|
get ctx() {
|
|
1730
1887
|
const top = this.fnStack[this.fnStack.length - 1];
|
|
1731
1888
|
if (!top)
|
|
@@ -1766,6 +1923,13 @@ export class Lowerer {
|
|
|
1766
1923
|
* coverage remainder as alreadyFlushed). */
|
|
1767
1924
|
flushedSymbols = new Set();
|
|
1768
1925
|
alreadyFlushed;
|
|
1926
|
+
/** Deferred diagnostics a flush handed to a capture buffer (diagSink)
|
|
1927
|
+
* instead of the build, keyed by the copy in that buffer. A buffer can
|
|
1928
|
+
* be discarded (a declined probe, a collection that succeeded) or
|
|
1929
|
+
* re-deferred under another declaration, so the declaration stays
|
|
1930
|
+
* deferred until one of these copies reaches `diags` (pushDiag commits
|
|
1931
|
+
* it); until then a later flush reports the diagnostics again. */
|
|
1932
|
+
pendingFlushes = new Map();
|
|
1769
1933
|
/** The build's target platform ("win32" | "darwin" | "linux" | ...):
|
|
1770
1934
|
* selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
|
|
1771
1935
|
* builtinModuleConstOf in surfaces.ts). */
|
|
@@ -1944,6 +2108,12 @@ export class Lowerer {
|
|
|
1944
2108
|
this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
|
|
1945
2109
|
return this.classProtocols.usesCheckedIdentity(type);
|
|
1946
2110
|
}
|
|
2111
|
+
/** Declared class fields may store a DYN value only when its static type
|
|
2112
|
+
* names checked protocol identity or an owned runtime handle (a Worker
|
|
2113
|
+
* or MessagePort); `unknown` and `any` fields stay refused. */
|
|
2114
|
+
classFieldStoresDyn(type) {
|
|
2115
|
+
return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
|
|
2116
|
+
}
|
|
1947
2117
|
/** The nominal identity of a checker module-namespace type. Program
|
|
1948
2118
|
* modules use their normalized absolute source name; supported builtins
|
|
1949
2119
|
* use the lowering tables' canonical bare name. */
|
|
@@ -2874,9 +3044,13 @@ export class Lowerer {
|
|
|
2874
3044
|
* but it is not a runtime proof; promoting only the affected slots keeps
|
|
2875
3045
|
* the rest of the dense ABI unchanged. */
|
|
2876
3046
|
analyzeRuntimeOptionalArrayReads(parts) {
|
|
3047
|
+
const provenance = RuntimeOptionalProvenance.fromEnvironment();
|
|
3048
|
+
// The site whose value the current promotion follows (debug only).
|
|
3049
|
+
let provenanceSite;
|
|
2877
3050
|
const optionalSymbols = new Set();
|
|
2878
3051
|
const optionalReturns = new Set();
|
|
2879
3052
|
const arithmeticReturns = new Map();
|
|
3053
|
+
const primitiveBindings = new Map();
|
|
2880
3054
|
const optionalParams = new Map();
|
|
2881
3055
|
const optionalFields = new Map();
|
|
2882
3056
|
const dynamicObjectEntryRows = new Set();
|
|
@@ -2903,6 +3077,17 @@ export class Lowerer {
|
|
|
2903
3077
|
(projection?.kind === "declared-function" ? projection.signature : null));
|
|
2904
3078
|
};
|
|
2905
3079
|
const functionDeclBySymbol = new Map();
|
|
3080
|
+
const staticFieldsBySymbol = new Map();
|
|
3081
|
+
for (const info of this.classes.values()) {
|
|
3082
|
+
for (const field of info.staticFields) {
|
|
3083
|
+
const declaration = field.initializer.parent;
|
|
3084
|
+
if (!ts.isPropertyDeclaration(declaration))
|
|
3085
|
+
continue;
|
|
3086
|
+
const symbol = symbolOf(declaration.name);
|
|
3087
|
+
if (symbol)
|
|
3088
|
+
staticFieldsBySymbol.set(symbol, field);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
2906
3091
|
const signatureBySymbol = new Map();
|
|
2907
3092
|
const methodFamilies = new Map();
|
|
2908
3093
|
const familyBySymbol = new Map();
|
|
@@ -2999,11 +3184,76 @@ export class Lowerer {
|
|
|
2999
3184
|
}
|
|
3000
3185
|
return false;
|
|
3001
3186
|
};
|
|
3002
|
-
|
|
3187
|
+
// `parent!` passed to a call or bound to a name forwards the value
|
|
3188
|
+
// without reading it: a callee that never dereferences it observes
|
|
3189
|
+
// undefined, as in JavaScript. Only a class value with one undefined
|
|
3190
|
+
// arm qualifies; element reads keep the established bounds trap.
|
|
3191
|
+
const forwardedAssertion = (node) => {
|
|
3192
|
+
let e = node;
|
|
3193
|
+
while (ts.isParenthesizedExpression(e))
|
|
3194
|
+
e = e.expression;
|
|
3195
|
+
if (!ts.isNonNullExpression(e))
|
|
3196
|
+
return null;
|
|
3197
|
+
let operand = e.expression;
|
|
3198
|
+
while (ts.isParenthesizedExpression(operand))
|
|
3199
|
+
operand = operand.expression;
|
|
3200
|
+
if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
|
|
3201
|
+
return null;
|
|
3202
|
+
// A guard that already proves the binding present makes the
|
|
3203
|
+
// assertion true at runtime: nothing is forwarded as undefined.
|
|
3204
|
+
if (ts.isIdentifier(operand)) {
|
|
3205
|
+
const symbol = symbolOf(operand);
|
|
3206
|
+
if (symbol && provenPresent(operand, symbol))
|
|
3207
|
+
return null;
|
|
3208
|
+
}
|
|
3209
|
+
const stored = this.mapTypeOf(this.typeOf(e.expression));
|
|
3210
|
+
const asserted = this.mapTypeOf(this.typeOf(e));
|
|
3211
|
+
if (stored?.kind !== "union" || asserted?.kind !== "object")
|
|
3212
|
+
return null;
|
|
3213
|
+
const arms = this.unions.get(stored.unionId)?.arms;
|
|
3214
|
+
if (arms?.length !== 2 ||
|
|
3215
|
+
this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
|
|
3216
|
+
!arms.some((arm) => typeEquals(arm, asserted)))
|
|
3217
|
+
return null;
|
|
3218
|
+
return e;
|
|
3219
|
+
};
|
|
3220
|
+
// `decode(xs[i]!)`: a missing element is passed as undefined in
|
|
3221
|
+
// JavaScript, and the callee may never read it.
|
|
3222
|
+
const forwardedElementAssertion = (node) => {
|
|
3223
|
+
let e = node;
|
|
3224
|
+
while (ts.isParenthesizedExpression(e))
|
|
3225
|
+
e = e.expression;
|
|
3226
|
+
if (!ts.isNonNullExpression(e))
|
|
3227
|
+
return null;
|
|
3228
|
+
let read = e.expression;
|
|
3229
|
+
while (ts.isParenthesizedExpression(read))
|
|
3230
|
+
read = read.expression;
|
|
3231
|
+
if (!ts.isElementAccessExpression(read))
|
|
3232
|
+
return null;
|
|
3233
|
+
if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
|
|
3234
|
+
return null;
|
|
3235
|
+
// An in-bounds read of an array that never holds undefined or holes
|
|
3236
|
+
// always produces a present element.
|
|
3237
|
+
if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
|
|
3238
|
+
return null;
|
|
3239
|
+
const element = this.mapTypeOf(this.typeOf(e));
|
|
3240
|
+
if (!element ||
|
|
3241
|
+
element.kind === "void" ||
|
|
3242
|
+
element.kind === "dyn" ||
|
|
3243
|
+
element.kind === "jsval" ||
|
|
3244
|
+
element.kind === "generator")
|
|
3245
|
+
return null;
|
|
3246
|
+
return e;
|
|
3247
|
+
};
|
|
3248
|
+
const isIndexedRead = (node) => {
|
|
3003
3249
|
const e = peel(node);
|
|
3004
3250
|
if (!ts.isElementAccessExpression(e))
|
|
3005
3251
|
return false;
|
|
3006
|
-
|
|
3252
|
+
const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
|
|
3253
|
+
if (kind === "string")
|
|
3254
|
+
return !this.stringIndexBounds.inBounds(e);
|
|
3255
|
+
// Typed-array reads answer undefined for an invalid index too.
|
|
3256
|
+
return kind === "array" || kind === "bytes";
|
|
3007
3257
|
};
|
|
3008
3258
|
const isDynamicObjectEntryRead = (node) => {
|
|
3009
3259
|
const read = peel(node);
|
|
@@ -3031,6 +3281,37 @@ export class Lowerer {
|
|
|
3031
3281
|
return this.withUndefinedArm(t);
|
|
3032
3282
|
};
|
|
3033
3283
|
const fieldName = (node) => node.name.text;
|
|
3284
|
+
// The precision facts below ask about nodes ordinary lowering may never
|
|
3285
|
+
// query. A checker panic on one of them answers null, and each caller
|
|
3286
|
+
// falls back to its conservative fact.
|
|
3287
|
+
const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
|
|
3288
|
+
const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
|
|
3289
|
+
// Presence proofs only hold for a binding that keeps its one value for
|
|
3290
|
+
// its whole lifetime.
|
|
3291
|
+
const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
|
|
3292
|
+
const presenceProofs = new Map();
|
|
3293
|
+
const provenPresent = (use, symbol) => {
|
|
3294
|
+
if (reassigned.has(symbol))
|
|
3295
|
+
return false;
|
|
3296
|
+
const cached = presenceProofs.get(use);
|
|
3297
|
+
if (cached !== undefined)
|
|
3298
|
+
return cached;
|
|
3299
|
+
const proven = presentAtUse(use, {
|
|
3300
|
+
sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
|
|
3301
|
+
isUndefined: (node) => node.text === "undefined" &&
|
|
3302
|
+
((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
|
|
3303
|
+
});
|
|
3304
|
+
presenceProofs.set(use, proven);
|
|
3305
|
+
return proven;
|
|
3306
|
+
};
|
|
3307
|
+
// Bindings of a for-of loop over an array that can hold holes or
|
|
3308
|
+
// undefined. Lowering keeps such a binding's runtime union, so storing
|
|
3309
|
+
// it into another array can store undefined.
|
|
3310
|
+
const iterationSymbols = new Set();
|
|
3311
|
+
// The subset whose iterated array's own facts show a hole or undefined.
|
|
3312
|
+
// Such a binding reads `T | undefined`, so a typed parameter, local or
|
|
3313
|
+
// field it flows into widens like any other missing element read.
|
|
3314
|
+
const holeyIterationSymbols = new Set();
|
|
3034
3315
|
const mayBeOptional = (node) => {
|
|
3035
3316
|
// `xs[i]!` is the explicit proven-present form. Its array read keeps
|
|
3036
3317
|
// the established dense bounds trap and must not promote the enclosing
|
|
@@ -3038,7 +3319,9 @@ export class Lowerer {
|
|
|
3038
3319
|
if (explicitlyNonNull(node))
|
|
3039
3320
|
return false;
|
|
3040
3321
|
const e = peel(node);
|
|
3041
|
-
if (
|
|
3322
|
+
if (isIndexedRead(e))
|
|
3323
|
+
return true;
|
|
3324
|
+
if (ts.isPropertyAccessExpression(e) && isOptionalProcessStreamProperty(this, e))
|
|
3042
3325
|
return true;
|
|
3043
3326
|
if (ts.isCallExpression(e) && this.runtimeOptionalReduceTypes.has(e))
|
|
3044
3327
|
return true;
|
|
@@ -3056,7 +3339,14 @@ export class Lowerer {
|
|
|
3056
3339
|
return true;
|
|
3057
3340
|
if (ts.isIdentifier(e)) {
|
|
3058
3341
|
const symbol = symbolOf(e);
|
|
3059
|
-
return symbol !== null &&
|
|
3342
|
+
return (symbol !== null &&
|
|
3343
|
+
(optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
|
|
3344
|
+
!provenPresent(e, symbol));
|
|
3345
|
+
}
|
|
3346
|
+
if (ts.isPropertyAccessExpression(e)) {
|
|
3347
|
+
const symbol = symbolOf(e.name);
|
|
3348
|
+
if (symbol && optionalSymbols.has(symbol))
|
|
3349
|
+
return true;
|
|
3060
3350
|
}
|
|
3061
3351
|
if (ts.isCallExpression(e) && ts.isIdentifier(e.expression)) {
|
|
3062
3352
|
const symbol = callableSymbolOf(e.expression);
|
|
@@ -3085,11 +3375,31 @@ export class Lowerer {
|
|
|
3085
3375
|
}
|
|
3086
3376
|
return false;
|
|
3087
3377
|
};
|
|
3088
|
-
const
|
|
3378
|
+
const optionalPrimitiveResultType = (node) => {
|
|
3089
3379
|
const e = peel(node);
|
|
3090
|
-
if (
|
|
3380
|
+
if (ts.isIdentifier(e)) {
|
|
3381
|
+
const symbol = symbolOf(e);
|
|
3382
|
+
return symbol ? (primitiveBindings.get(symbol) ?? null) : null;
|
|
3383
|
+
}
|
|
3384
|
+
if (ts.isCallExpression(e)) {
|
|
3385
|
+
const symbol = callableSymbolOf(e.expression);
|
|
3386
|
+
return symbol ? (arithmeticReturns.get(symbol) ?? null) : null;
|
|
3387
|
+
}
|
|
3388
|
+
if (!ts.isBinaryExpression(e))
|
|
3389
|
+
return null;
|
|
3390
|
+
if (e.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken && mayBeOptional(e.left)) {
|
|
3391
|
+
const left = this.mapTypeOf(this.typeOf(e.left));
|
|
3392
|
+
const right = this.mapTypeOf(this.typeOf(e.right));
|
|
3393
|
+
const primitive = (t) => t?.kind === "f64" || t?.kind === "string" || t?.kind === "bool";
|
|
3394
|
+
if (primitive(left) && primitive(right) && !typeEquals(left, right)) {
|
|
3395
|
+
const arms = [left, right].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
3396
|
+
return { kind: "union", unionId: this.unions.intern(arms) };
|
|
3397
|
+
}
|
|
3398
|
+
return null;
|
|
3399
|
+
}
|
|
3400
|
+
if (e.operatorToken.kind !== ts.SyntaxKind.PlusToken)
|
|
3091
3401
|
return null;
|
|
3092
|
-
const
|
|
3402
|
+
const stringRead = (part) => {
|
|
3093
3403
|
const p = peel(part);
|
|
3094
3404
|
if (!ts.isElementAccessExpression(p))
|
|
3095
3405
|
return false;
|
|
@@ -3097,16 +3407,17 @@ export class Lowerer {
|
|
|
3097
3407
|
if (t?.kind !== "string")
|
|
3098
3408
|
return false;
|
|
3099
3409
|
const recv = this.mapTypeOf(this.typeOf(p.expression));
|
|
3410
|
+
if (recv?.kind === "string")
|
|
3411
|
+
return !this.stringIndexBounds.inBounds(p);
|
|
3100
3412
|
return recv?.kind === "array" && recv.elem.kind === "string";
|
|
3101
3413
|
};
|
|
3102
3414
|
const primitive = (part) => {
|
|
3103
3415
|
const t = this.mapTypeOf(this.typeOf(part));
|
|
3104
3416
|
return t?.kind === "f64" || t?.kind === "string";
|
|
3105
3417
|
};
|
|
3106
|
-
if (!
|
|
3418
|
+
if (!stringRead(e.left) && !stringRead(e.right))
|
|
3107
3419
|
return null;
|
|
3108
|
-
if (!
|
|
3109
|
-
!(primitive(e.right) || stringArrayRead(e.right)))
|
|
3420
|
+
if (!primitive(e.left) || !primitive(e.right))
|
|
3110
3421
|
return null;
|
|
3111
3422
|
return { kind: "union", unionId: this.unions.intern([F64, STRING]) };
|
|
3112
3423
|
};
|
|
@@ -3125,6 +3436,43 @@ export class Lowerer {
|
|
|
3125
3436
|
optionalFields.set(symbol, set);
|
|
3126
3437
|
return set.size !== before;
|
|
3127
3438
|
};
|
|
3439
|
+
// Runtime-optional record fields travel with the record value: a copy,
|
|
3440
|
+
// a spread or a parameter receives the source's optional fields.
|
|
3441
|
+
const fieldsOfValue = (node) => {
|
|
3442
|
+
const e = peel(node);
|
|
3443
|
+
if (!ts.isIdentifier(e))
|
|
3444
|
+
return undefined;
|
|
3445
|
+
const symbol = symbolOf(e);
|
|
3446
|
+
return symbol === null ? undefined : optionalFields.get(symbol);
|
|
3447
|
+
};
|
|
3448
|
+
const noteFieldsFrom = (symbol, source) => {
|
|
3449
|
+
const fields = fieldsOfValue(source);
|
|
3450
|
+
if (fields === undefined)
|
|
3451
|
+
return false;
|
|
3452
|
+
let changed = false;
|
|
3453
|
+
for (const field of fields)
|
|
3454
|
+
if (noteFieldSymbol(symbol, field))
|
|
3455
|
+
changed = true;
|
|
3456
|
+
return changed;
|
|
3457
|
+
};
|
|
3458
|
+
/** Optional fields an object literal takes from its spreads, minus
|
|
3459
|
+
* the properties a later member writes explicitly. */
|
|
3460
|
+
const spreadFieldsOf = (object) => {
|
|
3461
|
+
const out = new Set();
|
|
3462
|
+
for (const prop of object.properties) {
|
|
3463
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
3464
|
+
const fields = fieldsOfValue(prop.expression);
|
|
3465
|
+
if (fields !== undefined)
|
|
3466
|
+
for (const field of fields)
|
|
3467
|
+
out.add(field);
|
|
3468
|
+
}
|
|
3469
|
+
else if (prop.name !== undefined &&
|
|
3470
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
|
|
3471
|
+
out.delete(prop.name.text);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
return out;
|
|
3475
|
+
};
|
|
3128
3476
|
const scanPattern = (name, init) => {
|
|
3129
3477
|
if (!ts.isArrayBindingPattern(name))
|
|
3130
3478
|
return false;
|
|
@@ -3139,6 +3487,7 @@ export class Lowerer {
|
|
|
3139
3487
|
const symbol = symbolOf(el.name);
|
|
3140
3488
|
if (symbol && !el.initializer && !optionalSymbols.has(symbol)) {
|
|
3141
3489
|
optionalSymbols.add(symbol);
|
|
3490
|
+
provenance?.note("pattern-binding", el.name, init);
|
|
3142
3491
|
// Indexed flow facts can survive a mutating method call in the
|
|
3143
3492
|
// checker. The array's element ABI describes every value a
|
|
3144
3493
|
// position may hold after that call, so bind from that type.
|
|
@@ -3282,6 +3631,7 @@ export class Lowerer {
|
|
|
3282
3631
|
const widened = this.runtimeOptionalType(current);
|
|
3283
3632
|
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
3284
3633
|
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
3634
|
+
provenance?.note("callback-parameter", parameter.name, provenanceSite);
|
|
3285
3635
|
if (!previous || !typeEquals(previous, widened))
|
|
3286
3636
|
changed = true;
|
|
3287
3637
|
if (!optionalSymbols.has(symbol)) {
|
|
@@ -3293,6 +3643,7 @@ export class Lowerer {
|
|
|
3293
3643
|
const fnType = this.mapTypeOf(this.typeOf(fn));
|
|
3294
3644
|
if (fnType?.kind === "func" && bodyReturnsOptional(fn)) {
|
|
3295
3645
|
const widenedReturn = this.runtimeOptionalType(fnType.ret);
|
|
3646
|
+
provenance?.note("callback-return", fn, provenanceSite);
|
|
3296
3647
|
const previousReturn = this.runtimeOptionalFunctionReturnType(fn, fnType.ret);
|
|
3297
3648
|
this.runtimeOptionalFunctionReturns.set(fn, widenedReturn);
|
|
3298
3649
|
if (!typeEquals(previousReturn, widenedReturn))
|
|
@@ -3388,6 +3739,70 @@ export class Lowerer {
|
|
|
3388
3739
|
}
|
|
3389
3740
|
return changed;
|
|
3390
3741
|
};
|
|
3742
|
+
// A reduce callback over present elements keeps the reduce helper's
|
|
3743
|
+
// optional element ABI for its parameter storage, so the helper still
|
|
3744
|
+
// splits each visit into a plain-element call. Its value is never
|
|
3745
|
+
// absent, so nothing it flows into is widened.
|
|
3746
|
+
const retypeInlineCallback = (callback, parameterIndices) => {
|
|
3747
|
+
const fn = peel(callback);
|
|
3748
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
3749
|
+
return false;
|
|
3750
|
+
let changed = false;
|
|
3751
|
+
for (const parameterIndex of parameterIndices) {
|
|
3752
|
+
const parameter = fn.parameters[parameterIndex];
|
|
3753
|
+
if (!parameter)
|
|
3754
|
+
continue;
|
|
3755
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3756
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3757
|
+
continue;
|
|
3758
|
+
const current = this.mapTypeOf(checkerType);
|
|
3759
|
+
if (!current)
|
|
3760
|
+
continue;
|
|
3761
|
+
const widened = this.runtimeOptionalType(current);
|
|
3762
|
+
if (!ts.isIdentifier(parameter.name)) {
|
|
3763
|
+
const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
|
|
3764
|
+
this.runtimeOptionalPatternTypes.set(parameter.name, widened);
|
|
3765
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3766
|
+
changed = true;
|
|
3767
|
+
continue;
|
|
3768
|
+
}
|
|
3769
|
+
const symbol = symbolOf(parameter.name);
|
|
3770
|
+
if (!symbol)
|
|
3771
|
+
continue;
|
|
3772
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
3773
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
3774
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3775
|
+
changed = true;
|
|
3776
|
+
}
|
|
3777
|
+
return changed;
|
|
3778
|
+
};
|
|
3779
|
+
// A present-element helper passes the stored element itself, so the
|
|
3780
|
+
// callback must accept that exact value through a mechanical adapter.
|
|
3781
|
+
// A parameter spelled as a structural supertype (`(r: { pos: number })`
|
|
3782
|
+
// over a wider record) only converts through the optional element
|
|
3783
|
+
// ABI's union retag; keep that site on the optional visit. Unannotated
|
|
3784
|
+
// inline parameters take the element's contextual type, and generic
|
|
3785
|
+
// parameters specialize later, so neither restricts the site.
|
|
3786
|
+
const callbackTakesPresentElement = (callback, elem, index) => {
|
|
3787
|
+
const fn = peel(callback);
|
|
3788
|
+
let param;
|
|
3789
|
+
if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
|
|
3790
|
+
const parameter = fn.parameters[index];
|
|
3791
|
+
if (!parameter?.type || parameter.dotDotDotToken)
|
|
3792
|
+
return true;
|
|
3793
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3794
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3795
|
+
return true;
|
|
3796
|
+
param = this.mapTypeOf(checkerType);
|
|
3797
|
+
}
|
|
3798
|
+
else {
|
|
3799
|
+
const value = this.mapTypeOf(this.typeOf(fn));
|
|
3800
|
+
if (value?.kind !== "func" || value.rest === true)
|
|
3801
|
+
return true;
|
|
3802
|
+
param = value.params[index] ?? null;
|
|
3803
|
+
}
|
|
3804
|
+
return param === null || this.coercibleValue(elem, param);
|
|
3805
|
+
};
|
|
3391
3806
|
const callbackReturnsOptional = (callback, seen = new Set()) => {
|
|
3392
3807
|
const node = peel(callback);
|
|
3393
3808
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
@@ -3405,6 +3820,253 @@ export class Lowerer {
|
|
|
3405
3820
|
!!decl.initializer &&
|
|
3406
3821
|
callbackReturnsOptional(decl.initializer, seen));
|
|
3407
3822
|
};
|
|
3823
|
+
// Element ABIs whose own type admits undefined (`(T | undefined)[]`,
|
|
3824
|
+
// or a rest array widened for an absent argument).
|
|
3825
|
+
const optionalElementKeys = new Set();
|
|
3826
|
+
// Checker answers for the fixed point's repeated element-ABI queries.
|
|
3827
|
+
// Only a binding's widened storage can change between passes.
|
|
3828
|
+
const checkerTypes = new Map();
|
|
3829
|
+
const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
|
|
3830
|
+
const arrayKeysUnsafe = (node) => {
|
|
3831
|
+
let checkerType = checkerTypes.get(node);
|
|
3832
|
+
if (checkerType === undefined) {
|
|
3833
|
+
checkerType = this.typeOf(node);
|
|
3834
|
+
checkerTypes.set(node, checkerType);
|
|
3835
|
+
}
|
|
3836
|
+
const e = peel(node);
|
|
3837
|
+
// A binding the prepass widened stores its widened array ABI.
|
|
3838
|
+
const widened = ts.isIdentifier(e)
|
|
3839
|
+
? (() => {
|
|
3840
|
+
const symbol = symbolOf(e);
|
|
3841
|
+
return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
|
|
3842
|
+
})()
|
|
3843
|
+
: undefined;
|
|
3844
|
+
let mapped = widened ?? this.mapTypeOf(checkerType);
|
|
3845
|
+
if (mapped?.kind === "promise")
|
|
3846
|
+
mapped = mapped.inner;
|
|
3847
|
+
const anyElements = mapped?.kind === "array"
|
|
3848
|
+
? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
|
|
3849
|
+
: mapped === null &&
|
|
3850
|
+
this.checker.isArrayType(checkerType) &&
|
|
3851
|
+
!this.checker.isTupleType(checkerType) &&
|
|
3852
|
+
((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
|
|
3853
|
+
(ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
|
|
3854
|
+
0;
|
|
3855
|
+
if (anyElements) {
|
|
3856
|
+
// `new Array(n)` has an `any` element type; its storage takes the
|
|
3857
|
+
// ABI of the slot it initializes.
|
|
3858
|
+
const contextual = this.checker.getContextualType(node);
|
|
3859
|
+
const target = contextual ? this.mapTypeOf(contextual) : null;
|
|
3860
|
+
return target?.kind === "array" ? [typeKey(target.elem)] : [];
|
|
3861
|
+
}
|
|
3862
|
+
if (mapped === null) {
|
|
3863
|
+
// An array whose element type is a type parameter: each
|
|
3864
|
+
// instantiation picks its own element ABI.
|
|
3865
|
+
return this.checker.isArrayType(checkerType) ? null : [];
|
|
3866
|
+
}
|
|
3867
|
+
const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
|
|
3868
|
+
const keys = [];
|
|
3869
|
+
for (const arm of arms) {
|
|
3870
|
+
if (arm.kind !== "array")
|
|
3871
|
+
continue;
|
|
3872
|
+
const key = typeKey(arm.elem);
|
|
3873
|
+
if (arm.elem.kind === "union"
|
|
3874
|
+
? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
|
|
3875
|
+
: isUnitType(arm.elem) || arm.elem.kind === "void")
|
|
3876
|
+
optionalElementKeys.add(key);
|
|
3877
|
+
keys.push(key);
|
|
3878
|
+
}
|
|
3879
|
+
return keys;
|
|
3880
|
+
};
|
|
3881
|
+
const ownership = new ArrayOwnership(sourceFiles, {
|
|
3882
|
+
symbolOf,
|
|
3883
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3884
|
+
typeOf: typeOrNull,
|
|
3885
|
+
instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
|
|
3886
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3887
|
+
isArray: (node) => {
|
|
3888
|
+
const type = typeOrNull(node);
|
|
3889
|
+
return type !== null && this.checker.isArrayType(type);
|
|
3890
|
+
},
|
|
3891
|
+
fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
|
|
3892
|
+
dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
|
|
3893
|
+
});
|
|
3894
|
+
let dynamicViews = null;
|
|
3895
|
+
// Element ABIs of the native arrays that an `any` or `unknown` value is
|
|
3896
|
+
// converted to, including arrays nested in the converted value.
|
|
3897
|
+
const collectDynamicViews = () => {
|
|
3898
|
+
const keys = new Set();
|
|
3899
|
+
const dynamicValue = (node) => {
|
|
3900
|
+
// A binding's declared type says whether it holds dynamic data; a
|
|
3901
|
+
// flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
|
|
3902
|
+
const e = peel(node);
|
|
3903
|
+
const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
|
|
3904
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
3905
|
+
const declared = declaration &&
|
|
3906
|
+
(ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
|
|
3907
|
+
ts.isIdentifier(declaration.name)
|
|
3908
|
+
? declaration.name
|
|
3909
|
+
: null;
|
|
3910
|
+
const type = typeOrNull(declared ?? node);
|
|
3911
|
+
if (type === null)
|
|
3912
|
+
return true;
|
|
3913
|
+
if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
|
|
3914
|
+
return true;
|
|
3915
|
+
if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
|
|
3916
|
+
return false;
|
|
3917
|
+
const element = this.checker.getTypeArguments(type)[0];
|
|
3918
|
+
return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
|
|
3919
|
+
};
|
|
3920
|
+
const collect = (type, depth) => {
|
|
3921
|
+
if (!type || depth > 4)
|
|
3922
|
+
return;
|
|
3923
|
+
if (type.kind === "array") {
|
|
3924
|
+
keys.add(typeKey(type.elem));
|
|
3925
|
+
collect(type.elem, depth + 1);
|
|
3926
|
+
}
|
|
3927
|
+
else if (type.kind === "union") {
|
|
3928
|
+
for (const arm of this.unions.get(type.unionId)?.arms ?? [])
|
|
3929
|
+
collect(arm, depth + 1);
|
|
3930
|
+
}
|
|
3931
|
+
else if (type.kind === "record") {
|
|
3932
|
+
for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
|
|
3933
|
+
collect(field.type, depth + 1);
|
|
3934
|
+
}
|
|
3935
|
+
else if (type.kind === "promise")
|
|
3936
|
+
collect(type.inner, depth + 1);
|
|
3937
|
+
};
|
|
3938
|
+
const target = (type) => {
|
|
3939
|
+
// An unanswerable target may be any array.
|
|
3940
|
+
if (type === null)
|
|
3941
|
+
keys.add("*");
|
|
3942
|
+
else if (type)
|
|
3943
|
+
collect(this.mapTypeOf(type), 0);
|
|
3944
|
+
};
|
|
3945
|
+
const visit = (node) => {
|
|
3946
|
+
if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
|
|
3947
|
+
target(typeOrNull(node));
|
|
3948
|
+
else if (ts.isVariableDeclaration(node) &&
|
|
3949
|
+
node.type &&
|
|
3950
|
+
node.initializer &&
|
|
3951
|
+
dynamicValue(node.initializer))
|
|
3952
|
+
target(typeOrNull(node.name));
|
|
3953
|
+
else if (ts.isBinaryExpression(node) &&
|
|
3954
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
3955
|
+
dynamicValue(node.right))
|
|
3956
|
+
target(typeOrNull(node.left));
|
|
3957
|
+
else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
|
|
3958
|
+
target(contextualOrNull(node.expression));
|
|
3959
|
+
else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
3960
|
+
for (const arg of node.arguments ?? [])
|
|
3961
|
+
if (!ts.isSpreadElement(arg) && dynamicValue(arg))
|
|
3962
|
+
target(contextualOrNull(arg));
|
|
3963
|
+
}
|
|
3964
|
+
else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
|
|
3965
|
+
target(contextualOrNull(node.initializer));
|
|
3966
|
+
else if (ts.isArrayLiteralExpression(node)) {
|
|
3967
|
+
for (const element of node.elements)
|
|
3968
|
+
if (!ts.isSpreadElement(element) &&
|
|
3969
|
+
!ts.isOmittedExpression(element) &&
|
|
3970
|
+
dynamicValue(element))
|
|
3971
|
+
target(contextualOrNull(element));
|
|
3972
|
+
}
|
|
3973
|
+
else if (ts.isArrowFunction(node) &&
|
|
3974
|
+
!ts.isBlock(node.body) &&
|
|
3975
|
+
dynamicValue(node.body))
|
|
3976
|
+
target(contextualOrNull(node.body));
|
|
3977
|
+
};
|
|
3978
|
+
for (const sf of sourceFiles)
|
|
3979
|
+
ts.walkPreorder(sf, (node) => {
|
|
3980
|
+
// A checker panic hides this conversion's target: any array.
|
|
3981
|
+
if (!panicSafe(() => (visit(node), true), false))
|
|
3982
|
+
keys.add("*");
|
|
3983
|
+
return undefined;
|
|
3984
|
+
});
|
|
3985
|
+
return keys;
|
|
3986
|
+
};
|
|
3987
|
+
const elementStates = new ArrayElementStates({
|
|
3988
|
+
elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
|
|
3989
|
+
ownerOf: (node) => ownership.ownerOf(node),
|
|
3990
|
+
allocationOwner: (node) => ownership.allocationOwner(node),
|
|
3991
|
+
arrayKeys: arrayKeysOf,
|
|
3992
|
+
mayBeUndefined: (node) => {
|
|
3993
|
+
if (explicitlyNonNull(node))
|
|
3994
|
+
return false;
|
|
3995
|
+
if (mayBeOptional(node))
|
|
3996
|
+
return true;
|
|
3997
|
+
// `undefined as unknown as T` keeps the undefined value.
|
|
3998
|
+
for (let inner = node; ts.isParenthesizedExpression(inner) ||
|
|
3999
|
+
ts.isAsExpression(inner) ||
|
|
4000
|
+
ts.isTypeAssertion(inner) ||
|
|
4001
|
+
ts.isSatisfiesExpression(inner);) {
|
|
4002
|
+
inner = inner.expression;
|
|
4003
|
+
const type = typeOrNull(inner);
|
|
4004
|
+
if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
|
|
4005
|
+
return true;
|
|
4006
|
+
}
|
|
4007
|
+
const e = peel(node);
|
|
4008
|
+
if (!ts.isIdentifier(e))
|
|
4009
|
+
return false;
|
|
4010
|
+
const symbol = symbolOf(e);
|
|
4011
|
+
return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
|
|
4012
|
+
},
|
|
4013
|
+
callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
|
|
4014
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
4015
|
+
isString: (node) => {
|
|
4016
|
+
const type = typeOrNull(node);
|
|
4017
|
+
return type !== null && this.mapTypeOf(type)?.kind === "string";
|
|
4018
|
+
},
|
|
4019
|
+
isArrayLikeObject: (node) => {
|
|
4020
|
+
if (ts.isObjectLiteralExpression(peel(node)))
|
|
4021
|
+
return true;
|
|
4022
|
+
const type = typeOrNull(node);
|
|
4023
|
+
const mapped = type === null ? null : this.mapTypeOf(type);
|
|
4024
|
+
return (mapped === null ||
|
|
4025
|
+
mapped.kind === "record" ||
|
|
4026
|
+
mapped.kind === "object" ||
|
|
4027
|
+
mapped.kind === "dyn" ||
|
|
4028
|
+
mapped.kind === "jsval" ||
|
|
4029
|
+
mapped.kind === "union");
|
|
4030
|
+
},
|
|
4031
|
+
restKeys: (call) => {
|
|
4032
|
+
const callee = peel(call.expression);
|
|
4033
|
+
if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
|
|
4034
|
+
return null;
|
|
4035
|
+
const symbol = callableSymbolOf(callee);
|
|
4036
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4037
|
+
if (!sig)
|
|
4038
|
+
return ts.isPropertyAccessExpression(callee) ? [] : null;
|
|
4039
|
+
const rest = sig.params.find((shape) => shape.mode === "rest");
|
|
4040
|
+
return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
|
|
4041
|
+
},
|
|
4042
|
+
markIterationBinding: (node, known) => {
|
|
4043
|
+
const symbol = symbolOf(node);
|
|
4044
|
+
if (!symbol)
|
|
4045
|
+
return false;
|
|
4046
|
+
let changed = false;
|
|
4047
|
+
if (!iterationSymbols.has(symbol)) {
|
|
4048
|
+
iterationSymbols.add(symbol);
|
|
4049
|
+
changed = true;
|
|
4050
|
+
}
|
|
4051
|
+
if (known && !holeyIterationSymbols.has(symbol)) {
|
|
4052
|
+
holeyIterationSymbols.add(symbol);
|
|
4053
|
+
// `for (name of xs)` assigns an existing binding, whose own
|
|
4054
|
+
// storage must then hold the loop's undefined too.
|
|
4055
|
+
if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
|
|
4056
|
+
optionalSymbols.add(symbol);
|
|
4057
|
+
changed = true;
|
|
4058
|
+
}
|
|
4059
|
+
return changed;
|
|
4060
|
+
},
|
|
4061
|
+
note: provenance
|
|
4062
|
+
? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
|
|
4063
|
+
: null,
|
|
4064
|
+
});
|
|
4065
|
+
// Each pass revisits every array callback site; the last visit holds
|
|
4066
|
+
// the decision at the fixed point.
|
|
4067
|
+
const presentSites = new Map();
|
|
4068
|
+
// The same for each for-of loop over an array.
|
|
4069
|
+
const presentLoops = new Map();
|
|
3408
4070
|
const scanSites = new Map();
|
|
3409
4071
|
const sitesOf = (sf) => {
|
|
3410
4072
|
const cached = scanSites.get(sf);
|
|
@@ -3414,7 +4076,14 @@ export class Lowerer {
|
|
|
3414
4076
|
ts.walkPreorder(sf, (node) => {
|
|
3415
4077
|
if (ts.isVariableDeclaration(node) ||
|
|
3416
4078
|
ts.isBinaryExpression(node) ||
|
|
3417
|
-
ts.isCallExpression(node)
|
|
4079
|
+
ts.isCallExpression(node) ||
|
|
4080
|
+
ts.isNewExpression(node) ||
|
|
4081
|
+
ts.isArrayLiteralExpression(node) ||
|
|
4082
|
+
ts.isDeleteExpression(node) ||
|
|
4083
|
+
ts.isForOfStatement(node) ||
|
|
4084
|
+
ts.isArrayBindingPattern(node) ||
|
|
4085
|
+
ts.isPrefixUnaryExpression(node) ||
|
|
4086
|
+
ts.isPostfixUnaryExpression(node))
|
|
3418
4087
|
nodes.push(node);
|
|
3419
4088
|
});
|
|
3420
4089
|
scanSites.set(sf, nodes);
|
|
@@ -3423,8 +4092,22 @@ export class Lowerer {
|
|
|
3423
4092
|
const scanFile = (sf) => {
|
|
3424
4093
|
let changed = false;
|
|
3425
4094
|
const scan = (node) => {
|
|
4095
|
+
if (elementStates.scan(node))
|
|
4096
|
+
changed = true;
|
|
4097
|
+
if (ts.isForOfStatement(node))
|
|
4098
|
+
presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
|
|
3426
4099
|
if (ts.isVariableDeclaration(node)) {
|
|
3427
4100
|
if (node.initializer) {
|
|
4101
|
+
const result = optionalPrimitiveResultType(node.initializer);
|
|
4102
|
+
const symbol = ts.isIdentifier(node.name) ? symbolOf(node.name) : null;
|
|
4103
|
+
if (result?.kind === "union" && symbol) {
|
|
4104
|
+
const before = primitiveBindings.get(symbol);
|
|
4105
|
+
if (!before || !typeEquals(before, result)) {
|
|
4106
|
+
primitiveBindings.set(symbol, result);
|
|
4107
|
+
this.runtimeOptionalBindingTypes.set(symbol, result);
|
|
4108
|
+
changed = true;
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
3428
4111
|
if (ts.isIdentifier(node.name) && isDynamicObjectEntryRead(node.initializer)) {
|
|
3429
4112
|
const symbol = symbolOf(node.name);
|
|
3430
4113
|
if (symbol && !dynamicObjectEntryRows.has(symbol)) {
|
|
@@ -3436,6 +4119,17 @@ export class Lowerer {
|
|
|
3436
4119
|
const symbol = symbolOf(node.name);
|
|
3437
4120
|
if (symbol && !optionalSymbols.has(symbol)) {
|
|
3438
4121
|
optionalSymbols.add(symbol);
|
|
4122
|
+
provenance?.note("binding", node.name, node.initializer);
|
|
4123
|
+
changed = true;
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
else if (ts.isIdentifier(node.name)) {
|
|
4127
|
+
const assertion = forwardedAssertion(node.initializer);
|
|
4128
|
+
const symbol = assertion ? symbolOf(node.name) : null;
|
|
4129
|
+
if (assertion && symbol && !optionalSymbols.has(symbol)) {
|
|
4130
|
+
optionalSymbols.add(symbol);
|
|
4131
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4132
|
+
provenance?.note("asserted-binding", node.name, node.initializer);
|
|
3439
4133
|
changed = true;
|
|
3440
4134
|
}
|
|
3441
4135
|
}
|
|
@@ -3456,6 +4150,8 @@ export class Lowerer {
|
|
|
3456
4150
|
const aliasNew = !optionalReturns.has(symbol);
|
|
3457
4151
|
if (aliasNew)
|
|
3458
4152
|
optionalReturns.add(symbol);
|
|
4153
|
+
if (aliasNew)
|
|
4154
|
+
provenance?.note("function-alias", node.name, node.initializer);
|
|
3459
4155
|
if (aliasNew)
|
|
3460
4156
|
changed = true;
|
|
3461
4157
|
const valueType = this.mapTypeOf(this.typeOf(node.name));
|
|
@@ -3478,6 +4174,22 @@ export class Lowerer {
|
|
|
3478
4174
|
}
|
|
3479
4175
|
if (scanPattern(node.name, node.initializer))
|
|
3480
4176
|
changed = true;
|
|
4177
|
+
if (ts.isIdentifier(node.name)) {
|
|
4178
|
+
const symbol = symbolOf(node.name);
|
|
4179
|
+
const init = peel(node.initializer);
|
|
4180
|
+
if (symbol && noteFieldsFrom(symbol, init)) {
|
|
4181
|
+
provenance?.note("record-copy", node.name, node.initializer);
|
|
4182
|
+
changed = true;
|
|
4183
|
+
}
|
|
4184
|
+
if (symbol && ts.isObjectLiteralExpression(init)) {
|
|
4185
|
+
for (const field of spreadFieldsOf(init)) {
|
|
4186
|
+
if (noteFieldSymbol(symbol, field)) {
|
|
4187
|
+
provenance?.note("record-spread", node.name, node.initializer);
|
|
4188
|
+
changed = true;
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
3481
4193
|
if (ts.isIdentifier(node.name) &&
|
|
3482
4194
|
ts.isObjectLiteralExpression(peel(node.initializer))) {
|
|
3483
4195
|
const object = peel(node.initializer);
|
|
@@ -3491,12 +4203,44 @@ export class Lowerer {
|
|
|
3491
4203
|
const value = ts.isPropertyAssignment(prop)
|
|
3492
4204
|
? prop.initializer
|
|
3493
4205
|
: prop.name;
|
|
3494
|
-
if (mayBeOptional(value) && noteField(node, name.text))
|
|
4206
|
+
if (mayBeOptional(value) && noteField(node, name.text)) {
|
|
4207
|
+
provenance?.note("literal-field", name, value);
|
|
3495
4208
|
changed = true;
|
|
4209
|
+
}
|
|
3496
4210
|
}
|
|
3497
4211
|
}
|
|
3498
4212
|
}
|
|
3499
4213
|
}
|
|
4214
|
+
if (ts.isBinaryExpression(node) &&
|
|
4215
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4216
|
+
ts.isIdentifier(node.left)) {
|
|
4217
|
+
const symbol = symbolOf(node.left);
|
|
4218
|
+
if (symbol && noteFieldsFrom(symbol, node.right)) {
|
|
4219
|
+
provenance?.note("record-copy", node.left, node.right);
|
|
4220
|
+
changed = true;
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
if (ts.isBinaryExpression(node) &&
|
|
4224
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4225
|
+
ts.isIdentifier(node.left)) {
|
|
4226
|
+
const assertion = forwardedAssertion(node.right);
|
|
4227
|
+
const symbol = assertion ? symbolOf(node.left) : null;
|
|
4228
|
+
// Only a variable's storage is promoted (see the final pass).
|
|
4229
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
4230
|
+
if (assertion &&
|
|
4231
|
+
symbol &&
|
|
4232
|
+
declaration &&
|
|
4233
|
+
ts.isVariableDeclaration(declaration) &&
|
|
4234
|
+
ts.isIdentifier(declaration.name) &&
|
|
4235
|
+
!this.runtimeOptionalAssertions.has(assertion)) {
|
|
4236
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4237
|
+
if (!optionalSymbols.has(symbol)) {
|
|
4238
|
+
optionalSymbols.add(symbol);
|
|
4239
|
+
provenance?.note("asserted-binding", node.left, node.right);
|
|
4240
|
+
}
|
|
4241
|
+
changed = true;
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
3500
4244
|
if (ts.isBinaryExpression(node) &&
|
|
3501
4245
|
(node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
|
|
3502
4246
|
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
@@ -3507,14 +4251,24 @@ export class Lowerer {
|
|
|
3507
4251
|
const symbol = symbolOf(node.left);
|
|
3508
4252
|
if (symbol && !optionalSymbols.has(symbol)) {
|
|
3509
4253
|
optionalSymbols.add(symbol);
|
|
4254
|
+
provenance?.note("assigned-binding", node.left, node.right);
|
|
3510
4255
|
changed = true;
|
|
3511
4256
|
}
|
|
3512
4257
|
}
|
|
3513
|
-
else if (ts.isPropertyAccessExpression(node.left)
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
4258
|
+
else if (ts.isPropertyAccessExpression(node.left)) {
|
|
4259
|
+
const field = symbolOf(node.left.name);
|
|
4260
|
+
if (field && staticFieldsBySymbol.has(field) && !optionalSymbols.has(field)) {
|
|
4261
|
+
optionalSymbols.add(field);
|
|
4262
|
+
provenance?.note("static-field", node.left, node.right);
|
|
3517
4263
|
changed = true;
|
|
4264
|
+
}
|
|
4265
|
+
else if (ts.isIdentifier(node.left.expression)) {
|
|
4266
|
+
const symbol = symbolOf(node.left.expression);
|
|
4267
|
+
if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
|
|
4268
|
+
provenance?.note("assigned-field", node.left, node.right);
|
|
4269
|
+
changed = true;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
3518
4272
|
}
|
|
3519
4273
|
}
|
|
3520
4274
|
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
@@ -3555,11 +4309,26 @@ export class Lowerer {
|
|
|
3555
4309
|
}
|
|
3556
4310
|
}
|
|
3557
4311
|
if (nativeArrayReceiver) {
|
|
3558
|
-
|
|
3559
|
-
changed = true;
|
|
4312
|
+
provenanceSite = node;
|
|
3560
4313
|
const method = node.expression.name.text;
|
|
4314
|
+
// Callbacks skip holes (except the find family), so an array
|
|
4315
|
+
// that never holds undefined only passes present elements.
|
|
4316
|
+
const reduce = method === "reduce" || method === "reduceRight";
|
|
4317
|
+
const elementsPresent = receiver?.kind === "array" &&
|
|
4318
|
+
elementStates.elementsPresent(receiverNode, method) &&
|
|
4319
|
+
callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
|
|
4320
|
+
// A callback returning a runtime-absent value still needs its
|
|
4321
|
+
// widened return, whatever its elements.
|
|
4322
|
+
if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
|
|
4323
|
+
changed = true;
|
|
4324
|
+
// Element-typed callback helpers cover every method but
|
|
4325
|
+
// reduce, whose accumulator keeps the split visit.
|
|
4326
|
+
presentSites.set(node, elementsPresent && !reduce);
|
|
4327
|
+
if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
|
|
4328
|
+
changed = true;
|
|
3561
4329
|
if ((method === "reduce" || method === "reduceRight") &&
|
|
3562
|
-
(node.arguments.length < 2 ||
|
|
4330
|
+
((!elementsPresent && node.arguments.length < 2) ||
|
|
4331
|
+
callbackReturnsOptional(callback))) {
|
|
3563
4332
|
if (promoteHofCallback(callback, [0, 1]))
|
|
3564
4333
|
changed = true;
|
|
3565
4334
|
const result = this.runtimeOptionalType(this.irTypeOf(node));
|
|
@@ -3578,8 +4347,34 @@ export class Lowerer {
|
|
|
3578
4347
|
if (!symbol)
|
|
3579
4348
|
return;
|
|
3580
4349
|
const sig = signatureBySymbol.get(symbol);
|
|
3581
|
-
if (!sig)
|
|
4350
|
+
if (!sig) {
|
|
4351
|
+
// A direct call of a function-valued const (`const show = (n:
|
|
4352
|
+
// number) => ...`): a possibly missing argument widens that
|
|
4353
|
+
// parameter, as for a callback the same value is passed as.
|
|
4354
|
+
if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
|
|
4355
|
+
return;
|
|
4356
|
+
const declaration = this.checker.valueDeclarationOf(symbol);
|
|
4357
|
+
const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
4358
|
+
? peel(declaration.initializer)
|
|
4359
|
+
: null;
|
|
4360
|
+
if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
|
|
4361
|
+
return;
|
|
4362
|
+
// Optional, defaulted and rest parameters keep their own ABI.
|
|
4363
|
+
if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
|
|
4364
|
+
parameter.initializer !== undefined ||
|
|
4365
|
+
parameter.dotDotDotToken !== undefined))
|
|
4366
|
+
return;
|
|
4367
|
+
const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
|
|
4368
|
+
value.parameters[i] !== undefined &&
|
|
4369
|
+
!value.parameters[i].dotDotDotToken &&
|
|
4370
|
+
mayBeOptional(arg)
|
|
4371
|
+
? [i]
|
|
4372
|
+
: []);
|
|
4373
|
+
provenanceSite = node;
|
|
4374
|
+
if (indices.length > 0 && promoteHofCallback(node.expression, indices))
|
|
4375
|
+
changed = true;
|
|
3582
4376
|
return;
|
|
4377
|
+
}
|
|
3583
4378
|
// JSDoc describes the usual value, not a runtime arity check.
|
|
3584
4379
|
// A short JavaScript call still passes undefined. Widen only the
|
|
3585
4380
|
// observed slots before bodies lower so both caller and callee
|
|
@@ -3605,6 +4400,7 @@ export class Lowerer {
|
|
|
3605
4400
|
continue;
|
|
3606
4401
|
shape.type = widened;
|
|
3607
4402
|
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4403
|
+
provenance?.note("parameter", parameter, node, "(omitted or undefined argument)");
|
|
3608
4404
|
if (parameter) {
|
|
3609
4405
|
for (const bound of boundIdentifiersOf(parameter.name)) {
|
|
3610
4406
|
const boundSymbol = symbolOf(bound);
|
|
@@ -3629,13 +4425,63 @@ export class Lowerer {
|
|
|
3629
4425
|
const callbackSlot = shape?.type;
|
|
3630
4426
|
if (callbackSlot?.kind === "func" && !ts.isSpreadElement(arg)) {
|
|
3631
4427
|
const optionalCallbackParams = callbackSlot.params.flatMap((type, index) => type.kind === "union" && this.armTag(type.unionId, UNDEFINED_T) >= 0 ? [index] : []);
|
|
4428
|
+
provenanceSite = node;
|
|
3632
4429
|
if (optionalCallbackParams.length > 0 &&
|
|
3633
4430
|
promoteHofCallback(arg, optionalCallbackParams)) {
|
|
3634
4431
|
changed = true;
|
|
3635
4432
|
}
|
|
3636
4433
|
}
|
|
3637
|
-
if (ts.isSpreadElement(arg)
|
|
4434
|
+
if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
|
|
4435
|
+
const fields = fieldsOfValue(arg);
|
|
4436
|
+
for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
|
|
4437
|
+
const parameterShape = signatureBySymbol.get(target)?.params[i];
|
|
4438
|
+
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4439
|
+
if (!parameterShape || parameterShape.type.kind !== "record")
|
|
4440
|
+
continue;
|
|
4441
|
+
if (!parameter || !ts.isIdentifier(parameter.name))
|
|
4442
|
+
continue;
|
|
4443
|
+
const bound = symbolOf(parameter.name);
|
|
4444
|
+
if (!bound)
|
|
4445
|
+
continue;
|
|
4446
|
+
let promoted = parameterShape.type;
|
|
4447
|
+
for (const field of fields) {
|
|
4448
|
+
const source = promoted.kind === "record"
|
|
4449
|
+
? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
|
|
4450
|
+
: undefined;
|
|
4451
|
+
if (source) {
|
|
4452
|
+
promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
|
|
4453
|
+
}
|
|
4454
|
+
if (noteFieldSymbol(bound, field))
|
|
4455
|
+
changed = true;
|
|
4456
|
+
}
|
|
4457
|
+
if (!typeEquals(promoted, parameterShape.type)) {
|
|
4458
|
+
parameterShape.type = promoted;
|
|
4459
|
+
this.runtimeOptionalBindingTypes.set(bound, promoted);
|
|
4460
|
+
provenance?.note("record-parameter", parameter, arg);
|
|
4461
|
+
changed = true;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
if (ts.isSpreadElement(arg) || !shape)
|
|
3638
4466
|
return;
|
|
4467
|
+
const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
|
|
4468
|
+
if (!assertion && !mayBeOptional(arg))
|
|
4469
|
+
return;
|
|
4470
|
+
if (assertion) {
|
|
4471
|
+
if (i === restAt)
|
|
4472
|
+
return;
|
|
4473
|
+
// An omittable slot's ABI already holds undefined (an
|
|
4474
|
+
// optional or defaulted parameter applies its own default).
|
|
4475
|
+
if (shape.mode === "omittable") {
|
|
4476
|
+
if (shape.type.kind === "union" &&
|
|
4477
|
+
this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4478
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4479
|
+
return;
|
|
4480
|
+
}
|
|
4481
|
+
if (shape.mode !== "required")
|
|
4482
|
+
return;
|
|
4483
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4484
|
+
}
|
|
3639
4485
|
if (i === restAt) {
|
|
3640
4486
|
// A rest binding always receives an array. An unchecked
|
|
3641
4487
|
// argument can make its elements undefined, not the pack.
|
|
@@ -3649,6 +4495,7 @@ export class Lowerer {
|
|
|
3649
4495
|
continue;
|
|
3650
4496
|
rest.type = widened;
|
|
3651
4497
|
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4498
|
+
provenance?.note("rest-parameter", parameter, arg);
|
|
3652
4499
|
if (parameter && ts.isIdentifier(parameter.name)) {
|
|
3653
4500
|
const bound = symbolOf(parameter.name);
|
|
3654
4501
|
if (bound)
|
|
@@ -3677,6 +4524,7 @@ export class Lowerer {
|
|
|
3677
4524
|
if (set.size !== before) {
|
|
3678
4525
|
changed = true;
|
|
3679
4526
|
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4527
|
+
provenance?.note("parameter", parameter, arg, target === symbol ? "" : "(override)");
|
|
3680
4528
|
if (parameter) {
|
|
3681
4529
|
for (const bound of boundIdentifiersOf(parameter.name)) {
|
|
3682
4530
|
const boundSymbol = symbolOf(bound);
|
|
@@ -3700,9 +4548,10 @@ export class Lowerer {
|
|
|
3700
4548
|
for (const expression of returnsOf(decl)) {
|
|
3701
4549
|
if (mayBeOptional(expression) && !optionalReturns.has(symbol)) {
|
|
3702
4550
|
optionalReturns.add(symbol);
|
|
4551
|
+
provenance?.note("return", decl.name ?? decl, expression);
|
|
3703
4552
|
changed = true;
|
|
3704
4553
|
}
|
|
3705
|
-
const arithmetic =
|
|
4554
|
+
const arithmetic = optionalPrimitiveResultType(expression);
|
|
3706
4555
|
if (arithmetic && !arithmeticReturns.has(symbol)) {
|
|
3707
4556
|
arithmeticReturns.set(symbol, arithmetic);
|
|
3708
4557
|
changed = true;
|
|
@@ -3713,6 +4562,13 @@ export class Lowerer {
|
|
|
3713
4562
|
let changed = true;
|
|
3714
4563
|
while (changed) {
|
|
3715
4564
|
changed = false;
|
|
4565
|
+
for (const [symbol, field] of staticFieldsBySymbol) {
|
|
4566
|
+
if (!optionalSymbols.has(symbol) && mayBeOptional(field.initializer)) {
|
|
4567
|
+
optionalSymbols.add(symbol);
|
|
4568
|
+
provenance?.note("static-field", field.initializer.parent, field.initializer);
|
|
4569
|
+
changed = true;
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
3716
4572
|
for (const sf of sourceFiles)
|
|
3717
4573
|
if (scanFile(sf))
|
|
3718
4574
|
changed = true;
|
|
@@ -3744,11 +4600,33 @@ export class Lowerer {
|
|
|
3744
4600
|
for (const symbol of family) {
|
|
3745
4601
|
if (!optionalReturns.has(symbol)) {
|
|
3746
4602
|
optionalReturns.add(symbol);
|
|
4603
|
+
provenance?.note("return", functionDeclBySymbol.get(symbol), undefined, "(override family)");
|
|
3747
4604
|
changed = true;
|
|
3748
4605
|
}
|
|
3749
4606
|
}
|
|
3750
4607
|
}
|
|
3751
4608
|
}
|
|
4609
|
+
for (const [site, present] of presentSites)
|
|
4610
|
+
if (present)
|
|
4611
|
+
this.presentElementCalls.add(site);
|
|
4612
|
+
for (const [loop, present] of presentLoops)
|
|
4613
|
+
if (present)
|
|
4614
|
+
this.presentElementLoops.add(loop);
|
|
4615
|
+
const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
|
|
4616
|
+
for (const [symbol, field] of staticFieldsBySymbol) {
|
|
4617
|
+
if (!optionalSymbols.has(symbol))
|
|
4618
|
+
continue;
|
|
4619
|
+
const promoted = addUndefined(field.type);
|
|
4620
|
+
// An explicit optional annotation already has the right layout, but
|
|
4621
|
+
// an indexed assignment still invalidates the checker's presence fact.
|
|
4622
|
+
field.type = promoted;
|
|
4623
|
+
field.runtimeOptional = true;
|
|
4624
|
+
const global = globalsById.get(field.globalId);
|
|
4625
|
+
if (global) {
|
|
4626
|
+
global.type = promoted;
|
|
4627
|
+
this.runtimeOptionalGlobals.add(global);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
3752
4630
|
for (const [symbol, sig] of signatureBySymbol) {
|
|
3753
4631
|
const params = optionalParams.get(symbol);
|
|
3754
4632
|
if (params) {
|
|
@@ -3880,6 +4758,36 @@ export class Lowerer {
|
|
|
3880
4758
|
}
|
|
3881
4759
|
});
|
|
3882
4760
|
}
|
|
4761
|
+
for (const symbol of optionalSymbols)
|
|
4762
|
+
this.runtimeOptionalAssignedSymbols.add(symbol);
|
|
4763
|
+
// `decode(xs[i]!)` into a parameter whose final ABI already holds
|
|
4764
|
+
// undefined: the assertion has no runtime effect, so a missing element
|
|
4765
|
+
// reaches the callee as undefined instead of trapping at the read.
|
|
4766
|
+
for (const sf of sourceFiles) {
|
|
4767
|
+
for (const node of sitesOf(sf)) {
|
|
4768
|
+
if (!ts.isCallExpression(node))
|
|
4769
|
+
continue;
|
|
4770
|
+
if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
|
|
4771
|
+
continue;
|
|
4772
|
+
const symbol = callableSymbolOf(node.expression);
|
|
4773
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4774
|
+
if (!sig || node.arguments.some(ts.isSpreadElement))
|
|
4775
|
+
continue;
|
|
4776
|
+
node.arguments.forEach((arg, i) => {
|
|
4777
|
+
let e = arg;
|
|
4778
|
+
while (ts.isParenthesizedExpression(e))
|
|
4779
|
+
e = e.expression;
|
|
4780
|
+
if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
|
|
4781
|
+
return;
|
|
4782
|
+
const shape = sig.params[i];
|
|
4783
|
+
if (shape?.mode !== "required" && shape?.mode !== "omittable")
|
|
4784
|
+
return;
|
|
4785
|
+
if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4786
|
+
this.runtimeOptionalAssertions.add(e);
|
|
4787
|
+
});
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
provenance?.flush();
|
|
3883
4791
|
}
|
|
3884
4792
|
run() {
|
|
3885
4793
|
const parts = this.splitFiles();
|
|
@@ -3929,14 +4837,14 @@ export class Lowerer {
|
|
|
3929
4837
|
if (fn)
|
|
3930
4838
|
functions.push(fn);
|
|
3931
4839
|
else if (this.countsSkips())
|
|
3932
|
-
this.
|
|
4840
|
+
this.countSkippedBody(decl);
|
|
3933
4841
|
}
|
|
3934
4842
|
for (const decl of fp.classDecls) {
|
|
3935
4843
|
const info = this.classes.get(this.classNamer(decl));
|
|
3936
4844
|
if (info)
|
|
3937
4845
|
functions.push(...this.lowerClassMembers(info));
|
|
3938
4846
|
else if (this.countsSkips())
|
|
3939
|
-
this.
|
|
4847
|
+
this.countSkippedBody(decl);
|
|
3940
4848
|
}
|
|
3941
4849
|
}
|
|
3942
4850
|
// Each file's top-level statements form its run-once init function.
|
|
@@ -4024,10 +4932,10 @@ export class Lowerer {
|
|
|
4024
4932
|
// Deferred collection diagnostics nothing flushed — declarations no
|
|
4025
4933
|
// reference ever made relevant. They belong to the unreached group
|
|
4026
4934
|
// (collection order keeps them deterministic).
|
|
4027
|
-
for (const [symbol, diags] of this.deferredDiags) {
|
|
4935
|
+
for (const [symbol, diags] of [...this.deferredDiags]) {
|
|
4028
4936
|
if (this.alreadyFlushed.has(symbol))
|
|
4029
4937
|
continue;
|
|
4030
|
-
for (const d of diags)
|
|
4938
|
+
for (const d of [...diags])
|
|
4031
4939
|
this.pushDiag(d);
|
|
4032
4940
|
}
|
|
4033
4941
|
return {
|
|
@@ -4123,6 +5031,7 @@ export class Lowerer {
|
|
|
4123
5031
|
// the emitted program references, flushing deferred class diagnostics
|
|
4124
5032
|
// that a reached type makes relevant.
|
|
4125
5033
|
const artifacts = this.moduleArtifacts(functions);
|
|
5034
|
+
refuseUncheckedCycleReads(this, artifacts.classes ?? []);
|
|
4126
5035
|
// Types still naming a class that never REGISTERED after retention's
|
|
4127
5036
|
// flush (JS graphs whose class fences deferred to runtime — the
|
|
4128
5037
|
// sentence-walker's path params, printer tables whose func-typed
|
|
@@ -4137,7 +5046,7 @@ export class Lowerer {
|
|
|
4137
5046
|
const module = this.diags.length > 0
|
|
4138
5047
|
? null
|
|
4139
5048
|
: {
|
|
4140
|
-
irVersion:
|
|
5049
|
+
irVersion: 15,
|
|
4141
5050
|
sourceFile: this.entry.fileName,
|
|
4142
5051
|
functions,
|
|
4143
5052
|
classes: artifacts.classes,
|
|
@@ -4147,10 +5056,13 @@ export class Lowerer {
|
|
|
4147
5056
|
...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
|
|
4148
5057
|
entry: ENTRY_NAME,
|
|
4149
5058
|
...(this.usesWorkers ? { workers: true } : {}),
|
|
5059
|
+
...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
|
|
4150
5060
|
...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
|
|
4151
5061
|
};
|
|
4152
5062
|
if (module)
|
|
4153
5063
|
sanitizeUnregisteredClassTypes(module, (name) => this.classes.has(name));
|
|
5064
|
+
if (module)
|
|
5065
|
+
checkCycleBindingAccesses(this, module);
|
|
4154
5066
|
return {
|
|
4155
5067
|
module,
|
|
4156
5068
|
diagnostics: this.diags,
|
|
@@ -4166,41 +5078,72 @@ export class Lowerer {
|
|
|
4166
5078
|
* registered ClassInfo — the shape of a JS class whose collection fenced.
|
|
4167
5079
|
* Used by run()'s global pruning; shapes/unions recurse with a seen-set
|
|
4168
5080
|
* (interned ids can nest). */
|
|
4169
|
-
typeNamesUnregisteredClass(t
|
|
5081
|
+
typeNamesUnregisteredClass(t) {
|
|
5082
|
+
return this.unregisteredClassOf(t, new Set()) !== null;
|
|
5083
|
+
}
|
|
5084
|
+
/** The first class name typeNamesUnregisteredClass finds in `t`. */
|
|
5085
|
+
unregisteredClassOf(t, seen) {
|
|
5086
|
+
const first = (types) => {
|
|
5087
|
+
for (const type of types) {
|
|
5088
|
+
const name = this.unregisteredClassOf(type, seen);
|
|
5089
|
+
if (name !== null)
|
|
5090
|
+
return name;
|
|
5091
|
+
}
|
|
5092
|
+
return null;
|
|
5093
|
+
};
|
|
4170
5094
|
switch (t.kind) {
|
|
4171
5095
|
case "object":
|
|
4172
|
-
return
|
|
5096
|
+
return this.classes.has(t.className) ? null : t.className;
|
|
4173
5097
|
case "array":
|
|
4174
5098
|
case "set":
|
|
4175
|
-
return
|
|
5099
|
+
return first([t.elem]);
|
|
4176
5100
|
case "map":
|
|
4177
|
-
return (
|
|
4178
|
-
this.typeNamesUnregisteredClass(t.value, seen));
|
|
5101
|
+
return first([t.key, t.value]);
|
|
4179
5102
|
case "promise":
|
|
4180
|
-
return
|
|
5103
|
+
return first([t.inner]);
|
|
4181
5104
|
case "func":
|
|
4182
|
-
return (t.params
|
|
4183
|
-
this.typeNamesUnregisteredClass(t.ret, seen));
|
|
5105
|
+
return first([...t.params, t.ret]);
|
|
4184
5106
|
case "record": {
|
|
4185
5107
|
if (seen.has(t.shapeId))
|
|
4186
|
-
return
|
|
5108
|
+
return null;
|
|
4187
5109
|
seen.add(t.shapeId);
|
|
4188
5110
|
const shape = this.shapes.get(t.shapeId);
|
|
4189
5111
|
if (!shape)
|
|
4190
|
-
return
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
5112
|
+
return null;
|
|
5113
|
+
return first([
|
|
5114
|
+
...(shape.indexValue ? [shape.indexValue] : []),
|
|
5115
|
+
...shape.fields.map((f) => f.type),
|
|
5116
|
+
]);
|
|
4194
5117
|
}
|
|
4195
5118
|
case "union": {
|
|
4196
5119
|
if (seen.has(t.unionId))
|
|
4197
|
-
return
|
|
5120
|
+
return null;
|
|
4198
5121
|
seen.add(t.unionId);
|
|
4199
5122
|
const def = this.unions.get(t.unionId);
|
|
4200
|
-
return
|
|
5123
|
+
return def ? first(def.arms) : null;
|
|
4201
5124
|
}
|
|
4202
5125
|
default:
|
|
4203
|
-
return
|
|
5126
|
+
return null;
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
/** A declaration whose signature blocked never lowers its body, so its
|
|
5130
|
+
* statements would otherwise vanish from the coverage totals and the
|
|
5131
|
+
* percentage would overstate how much of the program compiles. Count
|
|
5132
|
+
* every statement the body holds as attempted and failed, in addition to
|
|
5133
|
+
* the skipped-function tally. */
|
|
5134
|
+
countSkippedBody(decl) {
|
|
5135
|
+
this.stats.functionsSkipped++;
|
|
5136
|
+
if (this.suppressStats)
|
|
5137
|
+
return;
|
|
5138
|
+
const n = countBodyStatements(decl);
|
|
5139
|
+
if (n === 0)
|
|
5140
|
+
return;
|
|
5141
|
+
this.stats.statementsTotal += n;
|
|
5142
|
+
this.stats.statementsFailed += n;
|
|
5143
|
+
const file = decl.getSourceFile().fileName;
|
|
5144
|
+
for (let i = 0; i < n; i++) {
|
|
5145
|
+
this.bumpFileStat(file, "total");
|
|
5146
|
+
this.bumpFileStat(file, "failed");
|
|
4204
5147
|
}
|
|
4205
5148
|
}
|
|
4206
5149
|
/** Whether run() counts a signature-blocked declaration in
|
|
@@ -4736,13 +5679,18 @@ export class Lowerer {
|
|
|
4736
5679
|
const d = this.instantiationContext
|
|
4737
5680
|
? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
|
|
4738
5681
|
: diag;
|
|
5682
|
+
const pending = this.pendingFlushes.get(diag);
|
|
4739
5683
|
// Deferred collection: the wrapper decides whether these ever report
|
|
4740
5684
|
// (a reference flushes them; unreferenced declarations stay silent in
|
|
4741
5685
|
// builds and report under coverage's unreached group).
|
|
4742
5686
|
if (this.diagSink) {
|
|
5687
|
+
if (pending && d !== diag)
|
|
5688
|
+
this.pendingFlushes.set(d, pending);
|
|
4743
5689
|
this.diagSink.push(d);
|
|
4744
5690
|
return;
|
|
4745
5691
|
}
|
|
5692
|
+
if (pending)
|
|
5693
|
+
this.commitFlush(pending.symbol, pending.source);
|
|
4746
5694
|
// One site, one report: some declarations map a type twice (module
|
|
4747
5695
|
// globals pre-register before their initializers lower) — an exact
|
|
4748
5696
|
// duplicate (code + span + message) adds noise, not information.
|
|
@@ -5060,6 +6008,9 @@ export class Lowerer {
|
|
|
5060
6008
|
if (arm !== undefined)
|
|
5061
6009
|
return arm;
|
|
5062
6010
|
}
|
|
6011
|
+
const collection = concreteCollectionNarrow(this, node, t);
|
|
6012
|
+
if (collection)
|
|
6013
|
+
return collection;
|
|
5063
6014
|
// IMPLICIT-ANY instance bodies: an identifier reference to a BOUND
|
|
5064
6015
|
// param answers the call site's concrete type wherever the checker
|
|
5065
6016
|
// still says `any` (there is no `T` for mapType to substitute — the
|
|
@@ -5383,6 +6334,29 @@ export class Lowerer {
|
|
|
5383
6334
|
this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
|
|
5384
6335
|
throw new PoisonError();
|
|
5385
6336
|
}
|
|
6337
|
+
// Instances of two unrelated classes: class identity is nominal here,
|
|
6338
|
+
// so a structurally compatible instance never becomes the other class.
|
|
6339
|
+
// An assertion (`x as Y`) names the mistake most directly.
|
|
6340
|
+
if (actual.kind === "object" &&
|
|
6341
|
+
expected.kind === "object" &&
|
|
6342
|
+
this.classes.has(actual.className) &&
|
|
6343
|
+
this.classes.has(expected.className) &&
|
|
6344
|
+
this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
|
|
6345
|
+
!this.isSubclassOf(actual.className, expected.className) &&
|
|
6346
|
+
!this.isSubclassOf(expected.className, actual.className)) {
|
|
6347
|
+
let site = node;
|
|
6348
|
+
while (ts.isParenthesizedExpression(site))
|
|
6349
|
+
site = site.expression;
|
|
6350
|
+
const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
|
|
6351
|
+
this.unsupported("SC1090", node, `${assertion ? "asserting" : "using"} a '${this.fmt(actual)}' instance as the unrelated class '${this.fmt(expected)}' (neither class extends the other, and class identity is nominal in compiled code)`, assertion
|
|
6352
|
+
? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
|
|
6353
|
+
: `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
|
|
6354
|
+
}
|
|
6355
|
+
// Iterator-typed slots hold generators and built-in collection
|
|
6356
|
+
// iterators; any other iterator object (a user class) stays fenced.
|
|
6357
|
+
if (expected.kind === "generator" && actual.kind !== "generator") {
|
|
6358
|
+
this.unsupported("SC1090", node, `'${this.fmt(actual)}' values in an iterator-typed slot (Iterator and IterableIterator slots hold generators and built-in collection iterators)`);
|
|
6359
|
+
}
|
|
5386
6360
|
// Everything else — a plain-kind mismatch like a string flowing into a
|
|
5387
6361
|
// class-instance slot — is tsc-rejected in the lowering world and only
|
|
5388
6362
|
// reaches here through preflight's project-world second chance (e.g. a
|
|
@@ -5438,6 +6412,21 @@ export class Lowerer {
|
|
|
5438
6412
|
}
|
|
5439
6413
|
return literalUnionArm(def, values, (id) => this.shapes.get(id), cached.owners);
|
|
5440
6414
|
}
|
|
6415
|
+
/** Widen only the result view; identical parameter ABIs preserve closure identity. */
|
|
6416
|
+
coerceCovariantFunction(expr, expected) {
|
|
6417
|
+
if (expr.type.kind === "func" &&
|
|
6418
|
+
expected.kind === "func" &&
|
|
6419
|
+
expr.type.rest === expected.rest &&
|
|
6420
|
+
expr.type.restAbi === expected.restAbi &&
|
|
6421
|
+
expr.type.argumentsAll === expected.argumentsAll &&
|
|
6422
|
+
expr.type.params.length === expected.params.length &&
|
|
6423
|
+
expr.type.params.every((type, i) => typeEquals(type, expected.params[i])) &&
|
|
6424
|
+
expr.type.ret.kind === "object" &&
|
|
6425
|
+
expected.ret.kind === "object" &&
|
|
6426
|
+
this.isSubclassOf(expr.type.ret.className, expected.ret.className))
|
|
6427
|
+
return { kind: "upcast", value: expr, type: expected, loc: expr.loc };
|
|
6428
|
+
return null;
|
|
6429
|
+
}
|
|
5441
6430
|
/** Implicit union construction. Wherever a value flows into a typed slot
|
|
5442
6431
|
* (initializer, assignment, call argument, return, field write, record
|
|
5443
6432
|
* literal field, ternary arm) whose expected type is a union and the
|
|
@@ -5446,6 +6435,22 @@ export class Lowerer {
|
|
|
5446
6435
|
* else (including a DIFFERENT union) is left for requireExactShape, which
|
|
5447
6436
|
* rejects union mismatches with SC2003. */
|
|
5448
6437
|
coerceToExpected(expr, expected) {
|
|
6438
|
+
// A destination that holds undefined takes an unassigned deferred-init
|
|
6439
|
+
// field's slot as is (Node passes the undefined along).
|
|
6440
|
+
if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
|
|
6441
|
+
const slot = this.deferredFieldSlot(expr);
|
|
6442
|
+
if (slot)
|
|
6443
|
+
expr = slot;
|
|
6444
|
+
}
|
|
6445
|
+
if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
|
|
6446
|
+
return this.divergentValue(expr, expected);
|
|
6447
|
+
// Iterator-typed slots: native iterators and differently typed
|
|
6448
|
+
// generators step through an adapter generator.
|
|
6449
|
+
if (expected.kind === "generator" && !typeEquals(expr.type, expected)) {
|
|
6450
|
+
const adapted = iteratorSlotAdapter(this, expr, expected);
|
|
6451
|
+
if (adapted)
|
|
6452
|
+
return adapted;
|
|
6453
|
+
}
|
|
5449
6454
|
// Island boundary, both directions. IN: any static value flowing into
|
|
5450
6455
|
// an any-typed slot marshals implicitly (tsc allows the assignment;
|
|
5451
6456
|
// the marshal is where its semantics live). OUT: an 'any' value
|
|
@@ -5595,9 +6600,18 @@ export class Lowerer {
|
|
|
5595
6600
|
};
|
|
5596
6601
|
}
|
|
5597
6602
|
if (expr.kind === "unitLit" || this.dynConvertible(expr.type)) {
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
6603
|
+
// Records and arrays keep their identity as untyped values, including
|
|
6604
|
+
// when they arrive through a union, so `unknown` views, weak
|
|
6605
|
+
// collection keys and later typed reads all see the same object.
|
|
6606
|
+
const liveReference = (type) => {
|
|
6607
|
+
if (type.kind === "array")
|
|
6608
|
+
return true;
|
|
6609
|
+
const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
|
|
6610
|
+
return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
|
|
6611
|
+
};
|
|
6612
|
+
const liveRef = liveReference(expr.type) ||
|
|
6613
|
+
(expr.type.kind === "union" &&
|
|
6614
|
+
(this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
|
|
5601
6615
|
return {
|
|
5602
6616
|
kind: "dynFrom",
|
|
5603
6617
|
value: expr,
|
|
@@ -5705,6 +6719,9 @@ export class Lowerer {
|
|
|
5705
6719
|
return { kind: "call", callee: adapter, args: [expr], type: expected, loc: expr.loc };
|
|
5706
6720
|
}
|
|
5707
6721
|
}
|
|
6722
|
+
const covariant = this.coerceCovariantFunction(expr, expected);
|
|
6723
|
+
if (covariant)
|
|
6724
|
+
return covariant;
|
|
5708
6725
|
// The GENERAL function-value adapter (funcCoerceAdapter): a function
|
|
5709
6726
|
// whose signature differs from the slot's only by coercible pieces —
|
|
5710
6727
|
// fewer parameters (JS ignores extras: `load(function () {})` into an
|
|
@@ -5728,6 +6745,31 @@ export class Lowerer {
|
|
|
5728
6745
|
this.isSubclassOf(expr.type.className, expected.className)) {
|
|
5729
6746
|
return { kind: "upcast", value: expr, type: expected, loc: expr.loc };
|
|
5730
6747
|
}
|
|
6748
|
+
// Optional subclass storage still has the same object payload. Extract
|
|
6749
|
+
// it with a checked tag test, then widen the class view without copying.
|
|
6750
|
+
if (expected.kind === "object" && expr.type.kind === "union") {
|
|
6751
|
+
const arms = this.unions.get(expr.type.unionId)?.arms;
|
|
6752
|
+
const present = arms?.filter((arm) => !isUnitType(arm)) ?? [];
|
|
6753
|
+
if (present.length === 1 &&
|
|
6754
|
+
present[0].kind === "object" &&
|
|
6755
|
+
present[0].className !== expected.className &&
|
|
6756
|
+
this.isSubclassOf(present[0].className, expected.className)) {
|
|
6757
|
+
// One class arm: the ordinary checked narrow reports a missing value
|
|
6758
|
+
// exactly like same-class storage before the prefix view widens it.
|
|
6759
|
+
return {
|
|
6760
|
+
kind: "upcast",
|
|
6761
|
+
value: this.coerceToExpected(expr, present[0]),
|
|
6762
|
+
type: expected,
|
|
6763
|
+
loc: expr.loc,
|
|
6764
|
+
};
|
|
6765
|
+
}
|
|
6766
|
+
if (present.length > 1 &&
|
|
6767
|
+
present.every((arm) => identityPreservingWidening(this, arm, expected))) {
|
|
6768
|
+
const view = checkedClassAssertion(this, expr, expected, expr.loc);
|
|
6769
|
+
if (view)
|
|
6770
|
+
return view;
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
5731
6773
|
// CLASS-VALUE widening (classval:D into a classval:C slot): the same
|
|
5732
6774
|
// pointer with only the static type changing — legal exactly when D
|
|
5733
6775
|
// strictly descends from C AND the two completed constructor ABIs
|
|
@@ -5763,7 +6805,7 @@ export class Lowerer {
|
|
|
5763
6805
|
this.armTag(expr.type.unionId, expected) >= 0) {
|
|
5764
6806
|
const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
|
|
5765
6807
|
if (helper) {
|
|
5766
|
-
return
|
|
6808
|
+
return this.checkedNarrowCall(helper, expr, expected, expr.loc);
|
|
5767
6809
|
}
|
|
5768
6810
|
}
|
|
5769
6811
|
if (!typeEquals(expr.type, expected)) {
|
|
@@ -6014,6 +7056,9 @@ export class Lowerer {
|
|
|
6014
7056
|
narrowedArmHelper(fromId, target, loc) {
|
|
6015
7057
|
return narrowedArmHelper(this, fromId, target, loc);
|
|
6016
7058
|
}
|
|
7059
|
+
checkedNarrowCall(helper, value, type, loc) {
|
|
7060
|
+
return checkedNarrowCall(this, helper, value, type, loc);
|
|
7061
|
+
}
|
|
6017
7062
|
deferredReadHelper(fromId, target, loc) {
|
|
6018
7063
|
return deferredReadHelper(this, fromId, target, loc);
|
|
6019
7064
|
}
|
|
@@ -6110,6 +7155,17 @@ export class Lowerer {
|
|
|
6110
7155
|
loc: expr.loc,
|
|
6111
7156
|
}
|
|
6112
7157
|
: this.coerceToExpected(expr, expected);
|
|
7158
|
+
// Keep storage intact when ordinary widening already accepts it. Only
|
|
7159
|
+
// a destination needing a more specific class layout uses the site's
|
|
7160
|
+
// refinement and checked extraction.
|
|
7161
|
+
if (!typeEquals(e.type, expected) &&
|
|
7162
|
+
(expected.kind === "object" || expected.kind === "union")) {
|
|
7163
|
+
const site = e.type.kind === "union" ? this.mapTypeOf(this.typeOf(node)) : null;
|
|
7164
|
+
const narrowed = (site && narrowStoredClassValue(this, e, site)) ??
|
|
7165
|
+
narrowGenericClassValue(this, e, expected);
|
|
7166
|
+
if (narrowed)
|
|
7167
|
+
e = this.coerceToExpected(narrowed, expected);
|
|
7168
|
+
}
|
|
6113
7169
|
// Existing JavaScript arrays retain mutations and identity across checked
|
|
6114
7170
|
// slots. Fresh literals have no prior identity and use checked storage
|
|
6115
7171
|
// directly, so later writes can change their inferred element type.
|
|
@@ -6698,6 +7754,42 @@ export class Lowerer {
|
|
|
6698
7754
|
wrappedUndefined(type, loc) {
|
|
6699
7755
|
return wrappedUndefined(this, type, loc);
|
|
6700
7756
|
}
|
|
7757
|
+
/** The value an OMITTED or deleted property leaves in a record field
|
|
7758
|
+
* slot of `type`: the ABSENT state for undefined-armed unions (the slot
|
|
7759
|
+
* reads as undefined but the property does not exist), the dyn
|
|
7760
|
+
* undefined for 'unknown' slots, and null when the slot cannot be
|
|
7761
|
+
* missing. Only legal as a direct field-slot write (IR fieldAbsent). */
|
|
7762
|
+
absentFieldValue(type, loc) {
|
|
7763
|
+
if (type.kind === "union" && this.armTag(type.unionId, UNDEFINED_T) >= 0) {
|
|
7764
|
+
return { kind: "fieldAbsent", unionId: type.unionId, type, loc };
|
|
7765
|
+
}
|
|
7766
|
+
return this.wrappedUndefined(type, loc);
|
|
7767
|
+
}
|
|
7768
|
+
/** A field copy that keeps absence: `value` (the converted read of
|
|
7769
|
+
* `field` from record `obj`, which must be repeatable) when the source
|
|
7770
|
+
* property exists, else the target slot's ABSENT state. Fields that are
|
|
7771
|
+
* always present, and targets that cannot be absent, copy `value`
|
|
7772
|
+
* as-is. Only legal as a direct field-slot write. */
|
|
7773
|
+
presenceKeepingCopy(obj, shapeId, field, value, loc) {
|
|
7774
|
+
const present = this.recordFieldPresent(obj, shapeId, field, loc);
|
|
7775
|
+
if (present.kind !== "recordHas")
|
|
7776
|
+
return value;
|
|
7777
|
+
const absent = this.absentFieldValue(value.type, loc);
|
|
7778
|
+
if (absent?.kind !== "fieldAbsent")
|
|
7779
|
+
return value;
|
|
7780
|
+
return { kind: "ternary", cond: present, then: value, else_: absent, type: value.type, loc };
|
|
7781
|
+
}
|
|
7782
|
+
/** Own-property presence of declared field `field` on record `obj`
|
|
7783
|
+
* (borrowed — callers pass a repeatable receiver): a runtime test for
|
|
7784
|
+
* undefined-armed union fields, which can be absent, and `true` for
|
|
7785
|
+
* every other declared field. */
|
|
7786
|
+
recordFieldPresent(obj, shapeId, field, loc) {
|
|
7787
|
+
const f = this.shapes.get(shapeId)?.fields.find((x) => x.name === field);
|
|
7788
|
+
if (f && f.type.kind === "union" && this.armTag(f.type.unionId, UNDEFINED_T) >= 0) {
|
|
7789
|
+
return { kind: "recordHas", obj, shapeId, field, type: BOOL, loc };
|
|
7790
|
+
}
|
|
7791
|
+
return { kind: "boolLit", value: true, type: BOOL, loc };
|
|
7792
|
+
}
|
|
6701
7793
|
/** The entry value of a binding JS initializes to `undefined` (an
|
|
6702
7794
|
* initializer-less declaration, a hoisted `var` before its statement):
|
|
6703
7795
|
* undefined-armed unions hold the interned undefined arm, and 'any'
|
|
@@ -6785,15 +7877,47 @@ export class Lowerer {
|
|
|
6785
7877
|
flushDeferred(symbol) {
|
|
6786
7878
|
if (this.collecting)
|
|
6787
7879
|
return;
|
|
7880
|
+
this.reportDeferred(symbol);
|
|
7881
|
+
}
|
|
7882
|
+
/** flushDeferred without the collection-pass guard: for declarations
|
|
7883
|
+
* whose diagnostics must report eagerly (class statements with
|
|
7884
|
+
* declaration-time code, extended bases — collectClassShapeDeferring). */
|
|
7885
|
+
reportDeferred(symbol) {
|
|
6788
7886
|
const diags = this.deferredDiags.get(symbol);
|
|
6789
7887
|
if (!diags)
|
|
6790
7888
|
return;
|
|
6791
|
-
this.
|
|
6792
|
-
|
|
6793
|
-
return;
|
|
6794
|
-
|
|
6795
|
-
|
|
7889
|
+
if (this.alreadyFlushed.has(symbol)) {
|
|
7890
|
+
this.deferredDiags.delete(symbol); // the emit pass reported these
|
|
7891
|
+
return;
|
|
7892
|
+
}
|
|
7893
|
+
if (this.diagSink === null) {
|
|
7894
|
+
this.deferredDiags.delete(symbol);
|
|
7895
|
+
this.flushedSymbols.add(symbol);
|
|
7896
|
+
for (const d of diags)
|
|
7897
|
+
this.pushDiag(d);
|
|
7898
|
+
return;
|
|
7899
|
+
}
|
|
7900
|
+
// A capture buffer is active (a probe, or another declaration's
|
|
7901
|
+
// deferred collection): the copies ride that buffer, and the
|
|
7902
|
+
// declaration stays deferred until one of them reaches the build.
|
|
7903
|
+
for (const d of diags) {
|
|
7904
|
+
this.pendingFlushes.set(d, { symbol, source: d });
|
|
6796
7905
|
this.pushDiag(d);
|
|
7906
|
+
}
|
|
7907
|
+
}
|
|
7908
|
+
/** A flushed copy of a deferred diagnostic reached `diags`: that
|
|
7909
|
+
* diagnostic is reported, so it leaves the declaration's deferred list
|
|
7910
|
+
* (the declaration counts as flushed for the coverage remainder). */
|
|
7911
|
+
commitFlush(symbol, source) {
|
|
7912
|
+
this.flushedSymbols.add(symbol);
|
|
7913
|
+
const list = this.deferredDiags.get(symbol);
|
|
7914
|
+
if (!list)
|
|
7915
|
+
return;
|
|
7916
|
+
const index = list.indexOf(source);
|
|
7917
|
+
if (index >= 0)
|
|
7918
|
+
list.splice(index, 1);
|
|
7919
|
+
if (list.length === 0)
|
|
7920
|
+
this.deferredDiags.delete(symbol);
|
|
6797
7921
|
}
|
|
6798
7922
|
flushDeferredClass(className) {
|
|
6799
7923
|
const symbol = this.deferredClassByName.get(className);
|
|
@@ -7102,6 +8226,32 @@ export class Lowerer {
|
|
|
7102
8226
|
declareLocal(nameNode, name, type, mutable) {
|
|
7103
8227
|
return declareContextLocal(this.ctx, name, type, mutable, this.checker.getSymbolAtLocation(nameNode), bindingSource(nameNode));
|
|
7104
8228
|
}
|
|
8229
|
+
/** A never-returning call in a value position of type `expected`: the
|
|
8230
|
+
* call runs for its effects (it throws or exits), and the expression ends
|
|
8231
|
+
* in an unreachable trap instead of producing a value. The result reads an
|
|
8232
|
+
* uninitialized hidden local that control never reaches. */
|
|
8233
|
+
divergentValue(call, expected) {
|
|
8234
|
+
const loc = call.loc;
|
|
8235
|
+
const slot = this.declareHiddenLocal("%never", expected);
|
|
8236
|
+
slot.mutable = true;
|
|
8237
|
+
return {
|
|
8238
|
+
kind: "seqExpr",
|
|
8239
|
+
stmts: [
|
|
8240
|
+
{ kind: "exprStmt", expr: call, loc },
|
|
8241
|
+
{ kind: "varDecl", localId: slot.id, init: null, loc },
|
|
8242
|
+
{
|
|
8243
|
+
kind: "runtimeFence",
|
|
8244
|
+
code: "SC9002",
|
|
8245
|
+
message: "unreachable: a 'never' call returned — please report this",
|
|
8246
|
+
loc,
|
|
8247
|
+
},
|
|
8248
|
+
],
|
|
8249
|
+
result: varRef(slot.id, expected, loc),
|
|
8250
|
+
diverges: true,
|
|
8251
|
+
type: expected,
|
|
8252
|
+
loc,
|
|
8253
|
+
};
|
|
8254
|
+
}
|
|
7105
8255
|
/** A function-scope local bound to NO ts.Symbol — the hidden ABI slot of a
|
|
7106
8256
|
* defaulted parameter (the parameter's symbol binds to the separately-
|
|
7107
8257
|
* declared body local; nothing in the source can name this one). */
|
|
@@ -7846,13 +8996,7 @@ export class Lowerer {
|
|
|
7846
8996
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
|
|
7847
8997
|
if (!helper)
|
|
7848
8998
|
return lowerForOf(this, stmt);
|
|
7849
|
-
return this.withExpressionOverride(stmt.expression,
|
|
7850
|
-
kind: "call",
|
|
7851
|
-
callee: helper,
|
|
7852
|
-
args: [optional.value],
|
|
7853
|
-
type: optional.present,
|
|
7854
|
-
loc: locOf(stmt.expression),
|
|
7855
|
-
}, () => lowerForOf(this, stmt));
|
|
8999
|
+
return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
|
|
7856
9000
|
}
|
|
7857
9001
|
lowerForStatement(stmt) {
|
|
7858
9002
|
return lowerForStatement(this, stmt);
|
|
@@ -8097,21 +9241,21 @@ export class Lowerer {
|
|
|
8097
9241
|
const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
|
|
8098
9242
|
if (!helper)
|
|
8099
9243
|
return lower();
|
|
8100
|
-
right =
|
|
8101
|
-
kind: "call",
|
|
8102
|
-
callee: helper,
|
|
8103
|
-
args: [optionalRight.value],
|
|
8104
|
-
type: optionalRight.present,
|
|
8105
|
-
loc: locOf(expr.right),
|
|
8106
|
-
};
|
|
9244
|
+
right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
|
|
8107
9245
|
}
|
|
8108
9246
|
else {
|
|
8109
9247
|
right = this.lowerExpr(expr.right);
|
|
8110
9248
|
}
|
|
8111
9249
|
if (right.type.kind !== "classval")
|
|
8112
9250
|
return lower();
|
|
8113
|
-
|
|
8114
|
-
|
|
9251
|
+
// A capture-free class reference is the immortal static class object:
|
|
9252
|
+
// re-reading it is pure, so it needs no owned temporary (whose retain
|
|
9253
|
+
// and release would be out-of-line calls on every test).
|
|
9254
|
+
const constantRight = right.kind === "classRef" && right.captures === undefined;
|
|
9255
|
+
const stableRight = constantRight
|
|
9256
|
+
? null
|
|
9257
|
+
: this.declareHiddenLocal("%instanceofRight", right.type);
|
|
9258
|
+
const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
|
|
8115
9259
|
const body = this.withExpressionOverride(expr.left, {
|
|
8116
9260
|
kind: "unionNarrow",
|
|
8117
9261
|
unionId: left.type.unionId,
|
|
@@ -8124,7 +9268,16 @@ export class Lowerer {
|
|
|
8124
9268
|
kind: "seqExpr",
|
|
8125
9269
|
stmts: [
|
|
8126
9270
|
{ kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
|
|
8127
|
-
|
|
9271
|
+
...(stableRight
|
|
9272
|
+
? [
|
|
9273
|
+
{
|
|
9274
|
+
kind: "varDecl",
|
|
9275
|
+
localId: stableRight.id,
|
|
9276
|
+
init: right,
|
|
9277
|
+
loc: locOf(expr.right),
|
|
9278
|
+
},
|
|
9279
|
+
]
|
|
9280
|
+
: []),
|
|
8128
9281
|
],
|
|
8129
9282
|
result: {
|
|
8130
9283
|
kind: "ternary",
|
|
@@ -8155,13 +9308,7 @@ export class Lowerer {
|
|
|
8155
9308
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
|
|
8156
9309
|
if (!helper)
|
|
8157
9310
|
return lower();
|
|
8158
|
-
return this.withExpressionOverride(node,
|
|
8159
|
-
kind: "call",
|
|
8160
|
-
callee: helper,
|
|
8161
|
-
args: [optional.value],
|
|
8162
|
-
type: optional.present,
|
|
8163
|
-
loc: locOf(node),
|
|
8164
|
-
}, lower);
|
|
9311
|
+
return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
|
|
8165
9312
|
}
|
|
8166
9313
|
withExpressionOverride(node, value, lower) {
|
|
8167
9314
|
const previous = this.chainRecvByNode.get(node);
|
|
@@ -8270,6 +9417,37 @@ export class Lowerer {
|
|
|
8270
9417
|
fieldGetExpr(target, loc, blame) {
|
|
8271
9418
|
return fieldGetExpr(this, target, loc, blame);
|
|
8272
9419
|
}
|
|
9420
|
+
/** The undefined-armed slot read behind a checked deferred-init field
|
|
9421
|
+
* read, or null. Consumers that observe undefined itself (equality,
|
|
9422
|
+
* typeof, truthiness, nullish defaults) test the slot instead of the
|
|
9423
|
+
* checked extraction, which throws for an unassigned field. */
|
|
9424
|
+
deferredFieldSlot(value) {
|
|
9425
|
+
// The inline form: `slot is undefined ? throw : narrow(slot)`.
|
|
9426
|
+
if (value.kind === "ternary" &&
|
|
9427
|
+
value.cond.kind === "unionIsTag" &&
|
|
9428
|
+
value.else_.kind === "unionNarrow" &&
|
|
9429
|
+
value.else_.value === value.cond.value &&
|
|
9430
|
+
value.then.kind === "libCall" &&
|
|
9431
|
+
value.then.fn === "error.nodeThrow") {
|
|
9432
|
+
const read = value.cond.value;
|
|
9433
|
+
return read.kind === "fieldGet" &&
|
|
9434
|
+
read.type.kind === "union" &&
|
|
9435
|
+
this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
|
|
9436
|
+
? read
|
|
9437
|
+
: null;
|
|
9438
|
+
}
|
|
9439
|
+
if (value.kind !== "call" || value.args.length !== 1)
|
|
9440
|
+
return null;
|
|
9441
|
+
const read = value.args[0];
|
|
9442
|
+
if (read.kind !== "fieldGet" || read.type.kind !== "union")
|
|
9443
|
+
return null;
|
|
9444
|
+
if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
|
|
9445
|
+
return null;
|
|
9446
|
+
return this.coercions.checkedNarrows.has(value.callee) ||
|
|
9447
|
+
value.callee.startsWith("%deferred.read.")
|
|
9448
|
+
? read
|
|
9449
|
+
: null;
|
|
9450
|
+
}
|
|
8273
9451
|
fieldSetStmt(target, value, loc, blame) {
|
|
8274
9452
|
return fieldSetStmt(this, target, value, loc, blame);
|
|
8275
9453
|
}
|
|
@@ -8953,8 +10131,8 @@ export class Lowerer {
|
|
|
8953
10131
|
return lowerNumberStaticProperty(this, expr);
|
|
8954
10132
|
}
|
|
8955
10133
|
/* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
|
|
8956
|
-
requireDynamicApi(feature, node) {
|
|
8957
|
-
return requireDynamicApi(this, feature, node);
|
|
10134
|
+
requireDynamicApi(feature, node, hint) {
|
|
10135
|
+
return requireDynamicApi(this, feature, node, hint);
|
|
8958
10136
|
}
|
|
8959
10137
|
lowerMathProperty(expr) {
|
|
8960
10138
|
return lowerMathProperty(this, expr);
|