@scriptc/compiler 0.2.7 → 0.2.9
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 +3 -0
- package/dist/backend/cycle-analysis.js +50 -17
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +138 -14
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +31 -6
- package/dist/backend/cycle-mutability.js +120 -18
- package/dist/backend/cycle-mutability.js.map +1 -1
- package/dist/backend/llvm/alloc.d.ts +16 -3
- package/dist/backend/llvm/alloc.js +78 -23
- package/dist/backend/llvm/alloc.js.map +1 -1
- package/dist/backend/llvm/alloc.test.js +5 -5
- package/dist/backend/llvm/alloc.test.js.map +1 -1
- 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/byte-numbers.js +2 -3
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.d.ts +4 -1
- package/dist/backend/llvm/call-lifetimes.js +14 -3
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +8 -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 +13 -1
- package/dist/backend/llvm/classes.js +32 -23
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +32 -3
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +17 -0
- package/dist/backend/llvm/common.js +28 -0
- package/dist/backend/llvm/common.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.js +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +89 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +8 -4
- package/dist/backend/llvm/dyn.js +58 -31
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +227 -12
- package/dist/backend/llvm/emitter.js +1492 -169
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +54 -1
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +4 -4
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +116 -32
- 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 +55 -10
- 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 +231 -28
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +70 -14
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +75 -21
- 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.js +41 -12
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +50 -2
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +28 -14
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-bridges.js +114 -42
- package/dist/backend/llvm/expr-stream-bridges.js.map +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/layouts.d.ts +2 -1
- package/dist/backend/llvm/layouts.js +2 -2
- package/dist/backend/llvm/layouts.js.map +1 -1
- 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 +25 -13
- 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 +2 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +17 -6
- package/dist/backend/llvm/local-array-reads.js +223 -51
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +7 -3
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.js +10 -6
- package/dist/backend/llvm/local-union-storage.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +3 -2
- 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 +38 -19
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- 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 +140 -17
- 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/shapes.d.ts +58 -19
- package/dist/backend/llvm/shapes.js +171 -62
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +36 -11
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +5 -2
- 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-key-hash.d.ts +9 -2
- package/dist/backend/llvm/string-key-hash.js +20 -3
- package/dist/backend/llvm/string-key-hash.js.map +1 -1
- package/dist/backend/llvm/string-key-hash.test.js +14 -3
- package/dist/backend/llvm/string-key-hash.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +24 -11
- 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 +79 -0
- package/dist/backend/llvm/string-reads.js.map +1 -0
- package/dist/backend/llvm/string-slices.js +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +1 -1
- 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/trunc.d.ts +8 -0
- package/dist/backend/llvm/trunc.js +28 -0
- package/dist/backend/llvm/trunc.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/loop-polls.d.ts +14 -0
- package/dist/backend/loop-polls.js +100 -0
- package/dist/backend/loop-polls.js.map +1 -0
- package/dist/backend/loop-polls.test.d.ts +1 -0
- package/dist/backend/loop-polls.test.js +86 -0
- package/dist/backend/loop-polls.test.js.map +1 -0
- package/dist/backend/may-throw.d.ts +29 -1
- package/dist/backend/may-throw.js +365 -3
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +136 -1
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native/runtime-inputs.d.ts +1 -1
- package/dist/backend/native/runtime-inputs.js +1 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +7 -5
- 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 +2 -0
- package/dist/backend/native-codegen.js +21 -2
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +41 -10
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +2 -0
- package/dist/backend/native-tools.js +3 -0
- package/dist/backend/native-tools.js.map +1 -1
- 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-abi.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/cli/command.js +78 -8
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/output-options.d.ts +1 -0
- package/dist/cli/output-options.js +3 -0
- package/dist/cli/output-options.js.map +1 -1
- package/dist/cli/usage.d.ts +8 -1
- package/dist/cli/usage.js +14 -1
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +17 -3
- 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 +74 -4
- 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 -0
- package/dist/executable/executable-cache.js +3 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +1 -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/array-values.d.ts +7 -0
- package/dist/frontend/lowering/array-values.js +157 -5
- package/dist/frontend/lowering/array-values.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 +4 -0
- package/dist/frontend/lowering/builtins/process.js +10 -0
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/builtins/strings.js +4 -1
- package/dist/frontend/lowering/builtins/strings.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/call-arguments.js +96 -4
- package/dist/frontend/lowering/call-arguments.js.map +1 -1
- package/dist/frontend/lowering/class-assertions.d.ts +8 -0
- package/dist/frontend/lowering/class-assertions.js +217 -1
- package/dist/frontend/lowering/class-assertions.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +8 -5
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.js +88 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
- 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/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/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-callbacks.js +81 -44
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-construction.js +15 -2
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.js +231 -12
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +15 -4
- package/dist/frontend/lowering/containers/bytes.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/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 +6 -1
- package/dist/frontend/lowering/cycle-initialization.js +59 -33
- package/dist/frontend/lowering/cycle-initialization.js.map +1 -1
- 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/object-literals.js +40 -2
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-fields.d.ts +19 -0
- package/dist/frontend/lowering/expressions/union-fields.js +236 -0
- package/dist/frontend/lowering/expressions/union-fields.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.d.ts +5 -0
- package/dist/frontend/lowering/lower-calls.js +237 -158
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +4 -0
- package/dist/frontend/lowering/lower-classes.js +72 -22
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +19 -0
- package/dist/frontend/lowering/lower-exprs.js +592 -109
- package/dist/frontend/lowering/lower-exprs.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 +3 -2
- 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 +10 -7
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +90 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +47 -7
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +83 -2
- package/dist/frontend/lowering/lowerer.js +1158 -79
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/nan-coded-reads.d.ts +161 -0
- package/dist/frontend/lowering/nan-coded-reads.js +1342 -0
- package/dist/frontend/lowering/nan-coded-reads.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +31 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +360 -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 +94 -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 +2 -1
- package/dist/frontend/lowering/runtime-optional-provenance.js +2 -1
- package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -1
- 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 +8 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- 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 +25 -0
- package/dist/frontend/program.js +613 -17
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +64 -1
- 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 +3 -0
- package/dist/frontend/type-mapper.js +72 -6
- package/dist/frontend/type-mapper.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 +7 -2
- package/dist/index.js +67 -17
- package/dist/index.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 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-globals.d.ts +14 -0
- package/dist/ir/constant-globals.js +81 -0
- package/dist/ir/constant-globals.js.map +1 -0
- package/dist/ir/constant-globals.test.d.ts +1 -0
- package/dist/ir/constant-globals.test.js +97 -0
- package/dist/ir/constant-globals.test.js.map +1 -0
- package/dist/ir/final-fields.d.ts +35 -0
- package/dist/ir/final-fields.js +187 -0
- package/dist/ir/final-fields.js.map +1 -0
- package/dist/ir/final-fields.test.d.ts +1 -0
- package/dist/ir/final-fields.test.js +106 -0
- package/dist/ir/final-fields.test.js.map +1 -0
- package/dist/ir/int32-slots.d.ts +81 -0
- package/dist/ir/int32-slots.js +850 -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 +249 -0
- package/dist/ir/int32-slots.test.js.map +1 -0
- package/dist/ir/integer-loops.d.ts +5 -0
- package/dist/ir/integer-loops.js +20 -4
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +17 -0
- package/dist/ir/integer-loops.test.js.map +1 -1
- 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 +30 -7
- package/dist/ir/ir.js +4 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +12 -2
- package/dist/ir/serialize.js +190 -39
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +65 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/validate.js +21 -2
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +39 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/native/cache.d.ts +11 -1
- package/dist/native/cache.js +87 -2
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +32 -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 +55 -7
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/prepare.d.ts +7 -1
- package/dist/native/prepare.js +65 -29
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/prepare.test.js +23 -1
- package/dist/native/prepare.test.js.map +1 -1
- package/dist/native/text-file.d.ts +10 -0
- package/dist/native/text-file.js +41 -0
- package/dist/native/text-file.js.map +1 -0
- package/dist/startup-cache.d.ts +2 -0
- package/dist/startup-cache.js +2 -0
- package/dist/startup-cache.js.map +1 -1
- package/dist/text-parts.d.ts +5 -0
- package/dist/text-parts.js +36 -0
- package/dist/text-parts.js.map +1 -0
- package/package.json +20 -20
- package/surface-manifest.json +7 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +0 -8
- package/dist/frontend/lowering/expressions/union-field-write.js +0 -108
- package/dist/frontend/lowering/expressions/union-field-write.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkCycleBindingAccesses, refuseUncheckedCycleReads, } from "./cycle-initialization.js";
|
|
2
2
|
import { identityPreservingWidening } from "./coercions/identity.js";
|
|
3
|
+
import { scanDivergences } from "./divergences.js";
|
|
3
4
|
import { checkedClassAssertion } from "./class-assertions.js";
|
|
4
5
|
import { narrowStoredClassValue, narrowGenericClassValue } from "./class-unions.js";
|
|
5
6
|
import { isOptionalProcessStreamProperty } from "./builtins/process.js";
|
|
@@ -12,11 +13,17 @@ import { CoercionState } from "./coercions/state.js";
|
|
|
12
13
|
import { widthLiftPlan, unitOnlyElem, recordWidthPlan, describeRecordWidthBlocker, objToRecordPlan, recordToClassPlan, } from "./coercions/structural-plans.js";
|
|
13
14
|
import { widthCoerce, applyWidthLift, unionRecordWidthHelper, recordWidthHelper, tupleArrayWidthHelper, emptyArrayLiftHelper, arrayWidthHelper, objRecordWidthHelper, recordClassWidthHelper, classStaticsProjection, } from "./coercions/structural.js";
|
|
14
15
|
import { funcReturnWidthAdapter, coercibleValue, cleanFuncAdaptable, funcCoerceAdapter, spawnResFnAdapterPlan, spawnResFnAdapter, } from "./coercions/function-adapters.js";
|
|
15
|
-
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";
|
|
16
17
|
import { jsvalLiftable, jsvalLiftExpr, unionToJsvalHelper, recordToJsvalHelper, arrayToJsvalHelper, arrayToJsvalArrayHelper, } from "./coercions/island.js";
|
|
17
18
|
import { lowerUtilTypeValue, pathModuleValue, utilTypesModuleValue, } from "./lower-builtin-values.js";
|
|
18
19
|
import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
|
|
19
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";
|
|
26
|
+
import { NanCodedReads } from "./nan-coded-reads.js";
|
|
20
27
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
21
28
|
import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
|
|
22
29
|
import { UnregisteredClassTypes } from "./unregistered-class-types.js";
|
|
@@ -52,7 +59,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
|
|
|
52
59
|
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";
|
|
53
60
|
import { provenanceActive } from "../provenance-registry.js";
|
|
54
61
|
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
55
|
-
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
62
|
+
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
56
63
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
|
|
57
64
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
|
|
58
65
|
import { prepareCjsModuleGraph } from "./lower-node-module.js";
|
|
@@ -197,6 +204,39 @@ export function stmtUsesIsland(stmts) {
|
|
|
197
204
|
return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
|
|
198
205
|
return !everyStmtChild(stmts, expr, nested);
|
|
199
206
|
}
|
|
207
|
+
/** Statements a never-lowered body would have contributed to the coverage
|
|
208
|
+
* totals: every element of a statement list (blocks, case clauses), plus
|
|
209
|
+
* the single-statement bodies of control flow, nested functions included —
|
|
210
|
+
* the same accounting lowerStmts applies to bodies that do lower. */
|
|
211
|
+
export function countBodyStatements(root) {
|
|
212
|
+
let n = 0;
|
|
213
|
+
const visit = (node) => {
|
|
214
|
+
if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
|
|
215
|
+
n += node.statements.length;
|
|
216
|
+
}
|
|
217
|
+
else if ((ts.isIfStatement(node) ||
|
|
218
|
+
ts.isForStatement(node) ||
|
|
219
|
+
ts.isForInStatement(node) ||
|
|
220
|
+
ts.isForOfStatement(node) ||
|
|
221
|
+
ts.isWhileStatement(node) ||
|
|
222
|
+
ts.isDoStatement(node) ||
|
|
223
|
+
ts.isLabeledStatement(node)) &&
|
|
224
|
+
node.parent !== undefined) {
|
|
225
|
+
const bodies = ts.isIfStatement(node)
|
|
226
|
+
? [node.thenStatement, node.elseStatement]
|
|
227
|
+
: ts.isLabeledStatement(node)
|
|
228
|
+
? [node.statement]
|
|
229
|
+
: [node.statement];
|
|
230
|
+
for (const body of bodies) {
|
|
231
|
+
if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
|
|
232
|
+
n++;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
// Iterative: absurdly deep nesting must not overflow the stack here.
|
|
237
|
+
ts.walkPreorder(root, visit);
|
|
238
|
+
return n;
|
|
239
|
+
}
|
|
200
240
|
function directExternalTypeSpecifiersByFile(externalTypes) {
|
|
201
241
|
const out = new Map();
|
|
202
242
|
for (const [specifier, file] of externalTypes) {
|
|
@@ -349,6 +389,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
349
389
|
resultLowerer = emit;
|
|
350
390
|
timing("historical-order-relower");
|
|
351
391
|
}
|
|
392
|
+
// Divergence warnings over the program's own modules: computed once,
|
|
393
|
+
// after lowering, from the same checker (never part of a failure).
|
|
394
|
+
const divergences = scanDivergences(resultLowerer, moduleOrder);
|
|
395
|
+
timing("divergence-scan", { found: divergences.length });
|
|
396
|
+
if (divergences.length > 0)
|
|
397
|
+
result = { ...result, divergences };
|
|
352
398
|
if (options.coverage !== true)
|
|
353
399
|
return result;
|
|
354
400
|
const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
@@ -366,7 +412,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
366
412
|
externalTypeSpecifiersByFile,
|
|
367
413
|
});
|
|
368
414
|
const rem = remainder.run();
|
|
369
|
-
|
|
415
|
+
// Unreached JS statements whose fences deferred to runtime count as
|
|
416
|
+
// failed statements in the remainder's stats, so their diagnostics must
|
|
417
|
+
// travel with them: a failed statement is never left unexplained.
|
|
418
|
+
return {
|
|
419
|
+
...result,
|
|
420
|
+
unreached: {
|
|
421
|
+
diagnostics: rem.diagnostics,
|
|
422
|
+
stats: rem.stats,
|
|
423
|
+
...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
|
|
424
|
+
},
|
|
425
|
+
};
|
|
370
426
|
}
|
|
371
427
|
/** The island-handle type a `import(...)` initializer gives a binding
|
|
372
428
|
* whose DECLARED type has no static mapping (`Promise<typeof
|
|
@@ -882,6 +938,17 @@ export function jsFuncNameOf(node, lowerer) {
|
|
|
882
938
|
}
|
|
883
939
|
return null;
|
|
884
940
|
}
|
|
941
|
+
/** Answers `fallback` when a checker query panics inside the type checker. */
|
|
942
|
+
function panicSafe(query, fallback) {
|
|
943
|
+
try {
|
|
944
|
+
return query();
|
|
945
|
+
}
|
|
946
|
+
catch (e) {
|
|
947
|
+
if (isCheckerPanic(e))
|
|
948
|
+
return fallback;
|
|
949
|
+
throw e;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
885
952
|
export class Lowerer {
|
|
886
953
|
program;
|
|
887
954
|
entry;
|
|
@@ -910,6 +977,18 @@ export class Lowerer {
|
|
|
910
977
|
/** Per-symbol result of the never-reassigned file scan
|
|
911
978
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
912
979
|
neverReassignedCache = new Map();
|
|
980
|
+
/** Typed-array element reads whose every consumer treats undefined like
|
|
981
|
+
* NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
|
|
982
|
+
* analyzeRuntimeOptionalArrayReads before any body lowers. */
|
|
983
|
+
nanCodedReads = null;
|
|
984
|
+
stringIndexBoundsCache = null;
|
|
985
|
+
/** Proofs that a string element read names an existing code unit. The
|
|
986
|
+
* indexed-read analysis and expression lowering share one instance, so a
|
|
987
|
+
* read's representation and its callers' signatures always agree. */
|
|
988
|
+
get stringIndexBounds() {
|
|
989
|
+
this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
|
|
990
|
+
return this.stringIndexBoundsCache;
|
|
991
|
+
}
|
|
913
992
|
/** Syntactic writes indexed once per JS scope. Symbol and RHS type
|
|
914
993
|
* checks remain contextual to each binding probe. */
|
|
915
994
|
jsBindingWritesByOwner = new Map();
|
|
@@ -1156,6 +1235,11 @@ export class Lowerer {
|
|
|
1156
1235
|
* prepass. This includes explicit annotations: unchecked TS annotations do
|
|
1157
1236
|
* not prove that an array property read produced a value. */
|
|
1158
1237
|
runtimeOptionalBindingTypes = new Map();
|
|
1238
|
+
/** Bindings that receive an unchecked indexed read (or a value derived
|
|
1239
|
+
* from one) through an initializer or assignment. The checker narrows a
|
|
1240
|
+
* declared optional binding to that read's bare type, so presence tests
|
|
1241
|
+
* on these bindings consult the stored tag instead. */
|
|
1242
|
+
runtimeOptionalAssignedSymbols = new Set();
|
|
1159
1243
|
/** Concise callback/lambda returns promoted by the HOF callback prepass;
|
|
1160
1244
|
* arrows have no declaration symbol to key in fnSigsBySymbol. */
|
|
1161
1245
|
runtimeOptionalFunctionReturns = new Map();
|
|
@@ -1173,6 +1257,17 @@ export class Lowerer {
|
|
|
1173
1257
|
* every name destructured from a present source optional. */
|
|
1174
1258
|
runtimeOptionalPatternTypes = new Map();
|
|
1175
1259
|
runtimeOptionalReduceTypes = new Map();
|
|
1260
|
+
/** Non-null assertions whose value is forwarded into a slot promoted to
|
|
1261
|
+
* hold undefined. `x!` has no runtime effect, so these keep the stored
|
|
1262
|
+
* undefined state instead of trapping before any dereference. */
|
|
1263
|
+
runtimeOptionalAssertions = new Set();
|
|
1264
|
+
/** Array callback calls whose receiver the optional-read analysis proved
|
|
1265
|
+
* never holds an explicit undefined (or a hole, for the find family):
|
|
1266
|
+
* their callbacks take the element type itself. */
|
|
1267
|
+
presentElementCalls = new Set();
|
|
1268
|
+
/** for-of loops over arrays that can hold neither holes nor undefined:
|
|
1269
|
+
* each pass reads the element payload directly. */
|
|
1270
|
+
presentElementLoops = new Set();
|
|
1176
1271
|
/** Capture entries and their origin share one mutable box. Normalize each
|
|
1177
1272
|
* entry to the origin so writes and flow proofs stay synchronized. */
|
|
1178
1273
|
runtimeOptionalRoots = new Map();
|
|
@@ -1247,6 +1342,9 @@ export class Lowerer {
|
|
|
1247
1342
|
* optional-read analysis, so ordinary assertions keep their generic
|
|
1248
1343
|
* checked-narrow behavior. */
|
|
1249
1344
|
runtimeOptionalSourceValue(node, value) {
|
|
1345
|
+
const slot = this.deferredFieldSlot(value);
|
|
1346
|
+
if (slot)
|
|
1347
|
+
return slot;
|
|
1250
1348
|
let origin = node;
|
|
1251
1349
|
while (ts.isParenthesizedExpression(origin))
|
|
1252
1350
|
origin = origin.expression;
|
|
@@ -1281,12 +1379,9 @@ export class Lowerer {
|
|
|
1281
1379
|
value.value.type.kind === "dyn")
|
|
1282
1380
|
return value.value;
|
|
1283
1381
|
let source = value;
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
value.args[0]?.type.kind === "union") {
|
|
1288
|
-
source = value.args[0];
|
|
1289
|
-
}
|
|
1382
|
+
const narrowed = checkedNarrowOperand(this, value);
|
|
1383
|
+
if (narrowed?.type.kind === "union")
|
|
1384
|
+
source = narrowed;
|
|
1290
1385
|
return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
|
|
1291
1386
|
? source
|
|
1292
1387
|
: null;
|
|
@@ -1399,6 +1494,41 @@ export class Lowerer {
|
|
|
1399
1494
|
runtimeOptionalFunctionReturnType(node, fallback) {
|
|
1400
1495
|
return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
|
|
1401
1496
|
}
|
|
1497
|
+
/** True when `actual` is `declared` with some record fields (directly,
|
|
1498
|
+
* in nested records, or in an array's element records) widened to runtime-optional unions:
|
|
1499
|
+
* fields filled from unchecked indexed reads the checker calls present.
|
|
1500
|
+
* Inferred bindings and destructured parameters keep that layout instead
|
|
1501
|
+
* of narrowing it away. */
|
|
1502
|
+
isRuntimeOptionalRecordVariant(actual, declared) {
|
|
1503
|
+
if (actual.kind === "array" && declared.kind === "array")
|
|
1504
|
+
return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
|
|
1505
|
+
if (actual.kind !== "record" || declared.kind !== "record")
|
|
1506
|
+
return false;
|
|
1507
|
+
if (actual.shapeId === declared.shapeId)
|
|
1508
|
+
return false;
|
|
1509
|
+
const a = this.shapes.get(actual.shapeId);
|
|
1510
|
+
const d = this.shapes.get(declared.shapeId);
|
|
1511
|
+
if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
|
|
1512
|
+
return false;
|
|
1513
|
+
if (a.indexValue || d.indexValue)
|
|
1514
|
+
return false;
|
|
1515
|
+
let widened = false;
|
|
1516
|
+
for (const field of d.fields) {
|
|
1517
|
+
const source = a.fields.find((f) => f.name === field.name);
|
|
1518
|
+
if (!source)
|
|
1519
|
+
return false;
|
|
1520
|
+
if (typeEquals(source.type, field.type))
|
|
1521
|
+
continue;
|
|
1522
|
+
if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
|
|
1523
|
+
(this.runtimeOptionalWidening(source.type, field.type) !== null ||
|
|
1524
|
+
this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
|
|
1525
|
+
widened = true;
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
return false;
|
|
1529
|
+
}
|
|
1530
|
+
return widened;
|
|
1531
|
+
}
|
|
1402
1532
|
/** Promote one field of a record storage shape to the value's runtime
|
|
1403
1533
|
* type. Shapes are immutable by identity, so this interns a sibling shape
|
|
1404
1534
|
* and records the field as runtime-optional for property-read narrowing. */
|
|
@@ -1699,6 +1829,9 @@ export class Lowerer {
|
|
|
1699
1829
|
* and therefore becomes the cycle's evaluation root. Dynamic imports
|
|
1700
1830
|
* wait on this shared verdict rather than a build-time-selected member. */
|
|
1701
1831
|
asyncCyclePromiseOf = new Map();
|
|
1832
|
+
/** Modules whose evaluation an import() can request again after it threw,
|
|
1833
|
+
* computed on first use (lower-modules.ts importRetryModules). */
|
|
1834
|
+
importRetryModules = null;
|
|
1702
1835
|
/** Record-shape interner: canonical (name-sorted) field list → shapeId.
|
|
1703
1836
|
* Threaded into every mapType call; its `shapes` array becomes
|
|
1704
1837
|
* IrModule.records. */
|
|
@@ -1795,6 +1928,13 @@ export class Lowerer {
|
|
|
1795
1928
|
* coverage remainder as alreadyFlushed). */
|
|
1796
1929
|
flushedSymbols = new Set();
|
|
1797
1930
|
alreadyFlushed;
|
|
1931
|
+
/** Deferred diagnostics a flush handed to a capture buffer (diagSink)
|
|
1932
|
+
* instead of the build, keyed by the copy in that buffer. A buffer can
|
|
1933
|
+
* be discarded (a declined probe, a collection that succeeded) or
|
|
1934
|
+
* re-deferred under another declaration, so the declaration stays
|
|
1935
|
+
* deferred until one of these copies reaches `diags` (pushDiag commits
|
|
1936
|
+
* it); until then a later flush reports the diagnostics again. */
|
|
1937
|
+
pendingFlushes = new Map();
|
|
1798
1938
|
/** The build's target platform ("win32" | "darwin" | "linux" | ...):
|
|
1799
1939
|
* selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
|
|
1800
1940
|
* builtinModuleConstOf in surfaces.ts). */
|
|
@@ -1973,6 +2113,12 @@ export class Lowerer {
|
|
|
1973
2113
|
this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
|
|
1974
2114
|
return this.classProtocols.usesCheckedIdentity(type);
|
|
1975
2115
|
}
|
|
2116
|
+
/** Declared class fields may store a DYN value only when its static type
|
|
2117
|
+
* names checked protocol identity or an owned runtime handle (a Worker
|
|
2118
|
+
* or MessagePort); `unknown` and `any` fields stay refused. */
|
|
2119
|
+
classFieldStoresDyn(type) {
|
|
2120
|
+
return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
|
|
2121
|
+
}
|
|
1976
2122
|
/** The nominal identity of a checker module-namespace type. Program
|
|
1977
2123
|
* modules use their normalized absolute source name; supported builtins
|
|
1978
2124
|
* use the lowering tables' canonical bare name. */
|
|
@@ -2992,6 +3138,32 @@ export class Lowerer {
|
|
|
2992
3138
|
}
|
|
2993
3139
|
}
|
|
2994
3140
|
}
|
|
3141
|
+
const mappedOrNull = (node) => panicSafe(() => this.mapTypeOf(this.typeOf(node)), null);
|
|
3142
|
+
const nanCoded = new NanCodedReads({
|
|
3143
|
+
symbolOf,
|
|
3144
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3145
|
+
bytesElemOf: (receiver) => {
|
|
3146
|
+
const t = mappedOrNull(receiver);
|
|
3147
|
+
return t?.kind === "bytes" ? t.elem : null;
|
|
3148
|
+
},
|
|
3149
|
+
isNumber: (node) => mappedOrNull(node)?.kind === "f64",
|
|
3150
|
+
isString: (node) => mappedOrNull(node)?.kind === "string",
|
|
3151
|
+
hasLength: (node) => {
|
|
3152
|
+
const kind = mappedOrNull(node)?.kind;
|
|
3153
|
+
return kind === "array" || kind === "bytes";
|
|
3154
|
+
},
|
|
3155
|
+
isStdlibGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3156
|
+
paramIsNumber: (fn, index) => {
|
|
3157
|
+
const param = signatureBySymbol.get(fn)?.params[index];
|
|
3158
|
+
return (param !== undefined &&
|
|
3159
|
+
param.type.kind === "f64" &&
|
|
3160
|
+
(param.mode === "required" || param.mode === "omittable"));
|
|
3161
|
+
},
|
|
3162
|
+
returnIsNumber: (fn) => signatureBySymbol.get(fn)?.returnType.kind === "f64",
|
|
3163
|
+
methodOverridden: (fn) => (familyBySymbol.get(fn)?.length ?? 1) > 1,
|
|
3164
|
+
}, sourceFiles);
|
|
3165
|
+
nanCoded.analyze();
|
|
3166
|
+
this.nanCodedReads = nanCoded;
|
|
2995
3167
|
const peel = (node) => {
|
|
2996
3168
|
let e = node;
|
|
2997
3169
|
while (ts.isParenthesizedExpression(e) ||
|
|
@@ -3043,12 +3215,79 @@ export class Lowerer {
|
|
|
3043
3215
|
}
|
|
3044
3216
|
return false;
|
|
3045
3217
|
};
|
|
3218
|
+
// `parent!` passed to a call or bound to a name forwards the value
|
|
3219
|
+
// without reading it: a callee that never dereferences it observes
|
|
3220
|
+
// undefined, as in JavaScript. Only a class value with one undefined
|
|
3221
|
+
// arm qualifies; element reads keep the established bounds trap.
|
|
3222
|
+
const forwardedAssertion = (node) => {
|
|
3223
|
+
let e = node;
|
|
3224
|
+
while (ts.isParenthesizedExpression(e))
|
|
3225
|
+
e = e.expression;
|
|
3226
|
+
if (!ts.isNonNullExpression(e))
|
|
3227
|
+
return null;
|
|
3228
|
+
let operand = e.expression;
|
|
3229
|
+
while (ts.isParenthesizedExpression(operand))
|
|
3230
|
+
operand = operand.expression;
|
|
3231
|
+
if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
|
|
3232
|
+
return null;
|
|
3233
|
+
// A guard that already proves the binding present makes the
|
|
3234
|
+
// assertion true at runtime: nothing is forwarded as undefined.
|
|
3235
|
+
if (ts.isIdentifier(operand)) {
|
|
3236
|
+
const symbol = symbolOf(operand);
|
|
3237
|
+
if (symbol && provenPresent(operand, symbol))
|
|
3238
|
+
return null;
|
|
3239
|
+
}
|
|
3240
|
+
const stored = this.mapTypeOf(this.typeOf(e.expression));
|
|
3241
|
+
const asserted = this.mapTypeOf(this.typeOf(e));
|
|
3242
|
+
if (stored?.kind !== "union" || asserted?.kind !== "object")
|
|
3243
|
+
return null;
|
|
3244
|
+
const arms = this.unions.get(stored.unionId)?.arms;
|
|
3245
|
+
if (arms?.length !== 2 ||
|
|
3246
|
+
this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
|
|
3247
|
+
!arms.some((arm) => typeEquals(arm, asserted)))
|
|
3248
|
+
return null;
|
|
3249
|
+
return e;
|
|
3250
|
+
};
|
|
3251
|
+
// `decode(xs[i]!)`: a missing element is passed as undefined in
|
|
3252
|
+
// JavaScript, and the callee may never read it.
|
|
3253
|
+
const forwardedElementAssertion = (node) => {
|
|
3254
|
+
let e = node;
|
|
3255
|
+
while (ts.isParenthesizedExpression(e))
|
|
3256
|
+
e = e.expression;
|
|
3257
|
+
if (!ts.isNonNullExpression(e))
|
|
3258
|
+
return null;
|
|
3259
|
+
let read = e.expression;
|
|
3260
|
+
while (ts.isParenthesizedExpression(read))
|
|
3261
|
+
read = read.expression;
|
|
3262
|
+
if (!ts.isElementAccessExpression(read))
|
|
3263
|
+
return null;
|
|
3264
|
+
if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
|
|
3265
|
+
return null;
|
|
3266
|
+
// An in-bounds read of an array that never holds undefined or holes
|
|
3267
|
+
// always produces a present element.
|
|
3268
|
+
if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
|
|
3269
|
+
return null;
|
|
3270
|
+
const element = this.mapTypeOf(this.typeOf(e));
|
|
3271
|
+
if (!element ||
|
|
3272
|
+
element.kind === "void" ||
|
|
3273
|
+
element.kind === "dyn" ||
|
|
3274
|
+
element.kind === "jsval" ||
|
|
3275
|
+
element.kind === "generator")
|
|
3276
|
+
return null;
|
|
3277
|
+
return e;
|
|
3278
|
+
};
|
|
3046
3279
|
const isIndexedRead = (node) => {
|
|
3047
3280
|
const e = peel(node);
|
|
3048
3281
|
if (!ts.isElementAccessExpression(e))
|
|
3049
3282
|
return false;
|
|
3050
3283
|
const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
|
|
3051
|
-
|
|
3284
|
+
if (kind === "string")
|
|
3285
|
+
return !this.stringIndexBounds.inBounds(e);
|
|
3286
|
+
// Typed-array reads answer undefined for an invalid index too, unless
|
|
3287
|
+
// every consumer treats undefined like NaN.
|
|
3288
|
+
if (kind === "bytes")
|
|
3289
|
+
return !nanCoded.isNumericRead(e);
|
|
3290
|
+
return kind === "array";
|
|
3052
3291
|
};
|
|
3053
3292
|
const isDynamicObjectEntryRead = (node) => {
|
|
3054
3293
|
const read = peel(node);
|
|
@@ -3076,6 +3315,37 @@ export class Lowerer {
|
|
|
3076
3315
|
return this.withUndefinedArm(t);
|
|
3077
3316
|
};
|
|
3078
3317
|
const fieldName = (node) => node.name.text;
|
|
3318
|
+
// The precision facts below ask about nodes ordinary lowering may never
|
|
3319
|
+
// query. A checker panic on one of them answers null, and each caller
|
|
3320
|
+
// falls back to its conservative fact.
|
|
3321
|
+
const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
|
|
3322
|
+
const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
|
|
3323
|
+
// Presence proofs only hold for a binding that keeps its one value for
|
|
3324
|
+
// its whole lifetime.
|
|
3325
|
+
const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
|
|
3326
|
+
const presenceProofs = new Map();
|
|
3327
|
+
const provenPresent = (use, symbol) => {
|
|
3328
|
+
if (reassigned.has(symbol))
|
|
3329
|
+
return false;
|
|
3330
|
+
const cached = presenceProofs.get(use);
|
|
3331
|
+
if (cached !== undefined)
|
|
3332
|
+
return cached;
|
|
3333
|
+
const proven = presentAtUse(use, {
|
|
3334
|
+
sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
|
|
3335
|
+
isUndefined: (node) => node.text === "undefined" &&
|
|
3336
|
+
((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
|
|
3337
|
+
});
|
|
3338
|
+
presenceProofs.set(use, proven);
|
|
3339
|
+
return proven;
|
|
3340
|
+
};
|
|
3341
|
+
// Bindings of a for-of loop over an array that can hold holes or
|
|
3342
|
+
// undefined. Lowering keeps such a binding's runtime union, so storing
|
|
3343
|
+
// it into another array can store undefined.
|
|
3344
|
+
const iterationSymbols = new Set();
|
|
3345
|
+
// The subset whose iterated array's own facts show a hole or undefined.
|
|
3346
|
+
// Such a binding reads `T | undefined`, so a typed parameter, local or
|
|
3347
|
+
// field it flows into widens like any other missing element read.
|
|
3348
|
+
const holeyIterationSymbols = new Set();
|
|
3079
3349
|
const mayBeOptional = (node) => {
|
|
3080
3350
|
// `xs[i]!` is the explicit proven-present form. Its array read keeps
|
|
3081
3351
|
// the established dense bounds trap and must not promote the enclosing
|
|
@@ -3101,9 +3371,18 @@ export class Lowerer {
|
|
|
3101
3371
|
this.mapTypeOf(this.typeOf(e))?.kind === "union" &&
|
|
3102
3372
|
this.armTag(this.mapTypeOf(this.typeOf(e)).unionId, UNDEFINED_T) >= 0)
|
|
3103
3373
|
return true;
|
|
3374
|
+
// A NaN-coded value that reaches a consumer able to observe undefined
|
|
3375
|
+
// is rebuilt as the ordinary optional value there; every other read
|
|
3376
|
+
// of a NaN-coded slot is a plain number.
|
|
3377
|
+
if (nanCoded.reboxes(e))
|
|
3378
|
+
return true;
|
|
3379
|
+
if (nanCoded.readsNanCodedSlot(e))
|
|
3380
|
+
return false;
|
|
3104
3381
|
if (ts.isIdentifier(e)) {
|
|
3105
3382
|
const symbol = symbolOf(e);
|
|
3106
|
-
return symbol !== null &&
|
|
3383
|
+
return (symbol !== null &&
|
|
3384
|
+
(optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
|
|
3385
|
+
!provenPresent(e, symbol));
|
|
3107
3386
|
}
|
|
3108
3387
|
if (ts.isPropertyAccessExpression(e)) {
|
|
3109
3388
|
const symbol = symbolOf(e.name);
|
|
@@ -3169,7 +3448,9 @@ export class Lowerer {
|
|
|
3169
3448
|
if (t?.kind !== "string")
|
|
3170
3449
|
return false;
|
|
3171
3450
|
const recv = this.mapTypeOf(this.typeOf(p.expression));
|
|
3172
|
-
|
|
3451
|
+
if (recv?.kind === "string")
|
|
3452
|
+
return !this.stringIndexBounds.inBounds(p);
|
|
3453
|
+
return recv?.kind === "array" && recv.elem.kind === "string";
|
|
3173
3454
|
};
|
|
3174
3455
|
const primitive = (part) => {
|
|
3175
3456
|
const t = this.mapTypeOf(this.typeOf(part));
|
|
@@ -3189,6 +3470,25 @@ export class Lowerer {
|
|
|
3189
3470
|
return false;
|
|
3190
3471
|
return noteFieldSymbol(symbol, name);
|
|
3191
3472
|
};
|
|
3473
|
+
/** An instance field of a TypeScript class whose declared storage is
|
|
3474
|
+
* exactly `T | undefined`: an assignment from a missing read stores the
|
|
3475
|
+
* undefined arm while the checker narrows later reads to `T`. */
|
|
3476
|
+
const optionalInstanceField = (symbol) => {
|
|
3477
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
3478
|
+
if (!decl || !ts.isPropertyDeclaration(decl) || !ts.isIdentifier(decl.name))
|
|
3479
|
+
return false;
|
|
3480
|
+
if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Static)
|
|
3481
|
+
return false;
|
|
3482
|
+
const sf = decl.getSourceFile();
|
|
3483
|
+
if (isJsSourceFile(sf) || sf.isDeclarationFile)
|
|
3484
|
+
return false;
|
|
3485
|
+
const declared = typeOrNull(decl.name);
|
|
3486
|
+
const mapped = declared ? this.mapTypeOf(declared) : null;
|
|
3487
|
+
if (mapped?.kind !== "union" || this.armTag(mapped.unionId, UNDEFINED_T) < 0)
|
|
3488
|
+
return false;
|
|
3489
|
+
const arms = this.unions.get(mapped.unionId)?.arms ?? [];
|
|
3490
|
+
return (arms.length === 2 && arms.every((arm) => arm.kind === "undefinedT" || !isUnitType(arm)));
|
|
3491
|
+
};
|
|
3192
3492
|
const noteFieldSymbol = (symbol, name) => {
|
|
3193
3493
|
const set = optionalFields.get(symbol) ?? new Set();
|
|
3194
3494
|
const before = set.size;
|
|
@@ -3196,6 +3496,43 @@ export class Lowerer {
|
|
|
3196
3496
|
optionalFields.set(symbol, set);
|
|
3197
3497
|
return set.size !== before;
|
|
3198
3498
|
};
|
|
3499
|
+
// Runtime-optional record fields travel with the record value: a copy,
|
|
3500
|
+
// a spread or a parameter receives the source's optional fields.
|
|
3501
|
+
const fieldsOfValue = (node) => {
|
|
3502
|
+
const e = peel(node);
|
|
3503
|
+
if (!ts.isIdentifier(e))
|
|
3504
|
+
return undefined;
|
|
3505
|
+
const symbol = symbolOf(e);
|
|
3506
|
+
return symbol === null ? undefined : optionalFields.get(symbol);
|
|
3507
|
+
};
|
|
3508
|
+
const noteFieldsFrom = (symbol, source) => {
|
|
3509
|
+
const fields = fieldsOfValue(source);
|
|
3510
|
+
if (fields === undefined)
|
|
3511
|
+
return false;
|
|
3512
|
+
let changed = false;
|
|
3513
|
+
for (const field of fields)
|
|
3514
|
+
if (noteFieldSymbol(symbol, field))
|
|
3515
|
+
changed = true;
|
|
3516
|
+
return changed;
|
|
3517
|
+
};
|
|
3518
|
+
/** Optional fields an object literal takes from its spreads, minus
|
|
3519
|
+
* the properties a later member writes explicitly. */
|
|
3520
|
+
const spreadFieldsOf = (object) => {
|
|
3521
|
+
const out = new Set();
|
|
3522
|
+
for (const prop of object.properties) {
|
|
3523
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
3524
|
+
const fields = fieldsOfValue(prop.expression);
|
|
3525
|
+
if (fields !== undefined)
|
|
3526
|
+
for (const field of fields)
|
|
3527
|
+
out.add(field);
|
|
3528
|
+
}
|
|
3529
|
+
else if (prop.name !== undefined &&
|
|
3530
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
|
|
3531
|
+
out.delete(prop.name.text);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
return out;
|
|
3535
|
+
};
|
|
3199
3536
|
const scanPattern = (name, init) => {
|
|
3200
3537
|
if (!ts.isArrayBindingPattern(name))
|
|
3201
3538
|
return false;
|
|
@@ -3290,14 +3627,31 @@ export class Lowerer {
|
|
|
3290
3627
|
return found;
|
|
3291
3628
|
};
|
|
3292
3629
|
const jsLambdas = [];
|
|
3630
|
+
// TypeScript lambdas keep the checker's inferred return type, which
|
|
3631
|
+
// spells a missing read as its bare element type and `read ?? "text"`
|
|
3632
|
+
// as a number. Their closures return the runtime result instead.
|
|
3633
|
+
const tsLambdas = [];
|
|
3293
3634
|
for (const sf of sourceFiles) {
|
|
3294
|
-
|
|
3635
|
+
const js = isJsSourceFile(sf);
|
|
3636
|
+
if (!js && sf.isDeclarationFile)
|
|
3295
3637
|
continue;
|
|
3296
3638
|
ts.walkPreorder(sf, (node) => {
|
|
3297
3639
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
3298
|
-
jsLambdas.push(node);
|
|
3640
|
+
(js ? jsLambdas : tsLambdas).push(node);
|
|
3299
3641
|
});
|
|
3300
3642
|
}
|
|
3643
|
+
/** The primitive result union of a lambda's returns (see
|
|
3644
|
+
* optionalPrimitiveResultType), as declared functions record it. */
|
|
3645
|
+
const lambdaArithmeticReturn = (fn) => {
|
|
3646
|
+
if (fn.body && !ts.isBlock(fn.body))
|
|
3647
|
+
return optionalPrimitiveResultType(fn.body);
|
|
3648
|
+
for (const expression of returnsOf(fn)) {
|
|
3649
|
+
const result = optionalPrimitiveResultType(expression);
|
|
3650
|
+
if (result)
|
|
3651
|
+
return result;
|
|
3652
|
+
}
|
|
3653
|
+
return null;
|
|
3654
|
+
};
|
|
3301
3655
|
for (const [symbol, signature] of signatureBySymbol) {
|
|
3302
3656
|
const declaration = functionDeclBySymbol.get(symbol);
|
|
3303
3657
|
if (!declaration || !isJsSourceFile(declaration.getSourceFile()))
|
|
@@ -3462,6 +3816,70 @@ export class Lowerer {
|
|
|
3462
3816
|
}
|
|
3463
3817
|
return changed;
|
|
3464
3818
|
};
|
|
3819
|
+
// A reduce callback over present elements keeps the reduce helper's
|
|
3820
|
+
// optional element ABI for its parameter storage, so the helper still
|
|
3821
|
+
// splits each visit into a plain-element call. Its value is never
|
|
3822
|
+
// absent, so nothing it flows into is widened.
|
|
3823
|
+
const retypeInlineCallback = (callback, parameterIndices) => {
|
|
3824
|
+
const fn = peel(callback);
|
|
3825
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
3826
|
+
return false;
|
|
3827
|
+
let changed = false;
|
|
3828
|
+
for (const parameterIndex of parameterIndices) {
|
|
3829
|
+
const parameter = fn.parameters[parameterIndex];
|
|
3830
|
+
if (!parameter)
|
|
3831
|
+
continue;
|
|
3832
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3833
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3834
|
+
continue;
|
|
3835
|
+
const current = this.mapTypeOf(checkerType);
|
|
3836
|
+
if (!current)
|
|
3837
|
+
continue;
|
|
3838
|
+
const widened = this.runtimeOptionalType(current);
|
|
3839
|
+
if (!ts.isIdentifier(parameter.name)) {
|
|
3840
|
+
const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
|
|
3841
|
+
this.runtimeOptionalPatternTypes.set(parameter.name, widened);
|
|
3842
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3843
|
+
changed = true;
|
|
3844
|
+
continue;
|
|
3845
|
+
}
|
|
3846
|
+
const symbol = symbolOf(parameter.name);
|
|
3847
|
+
if (!symbol)
|
|
3848
|
+
continue;
|
|
3849
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
3850
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
3851
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3852
|
+
changed = true;
|
|
3853
|
+
}
|
|
3854
|
+
return changed;
|
|
3855
|
+
};
|
|
3856
|
+
// A present-element helper passes the stored element itself, so the
|
|
3857
|
+
// callback must accept that exact value through a mechanical adapter.
|
|
3858
|
+
// A parameter spelled as a structural supertype (`(r: { pos: number })`
|
|
3859
|
+
// over a wider record) only converts through the optional element
|
|
3860
|
+
// ABI's union retag; keep that site on the optional visit. Unannotated
|
|
3861
|
+
// inline parameters take the element's contextual type, and generic
|
|
3862
|
+
// parameters specialize later, so neither restricts the site.
|
|
3863
|
+
const callbackTakesPresentElement = (callback, elem, index) => {
|
|
3864
|
+
const fn = peel(callback);
|
|
3865
|
+
let param;
|
|
3866
|
+
if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
|
|
3867
|
+
const parameter = fn.parameters[index];
|
|
3868
|
+
if (!parameter?.type || parameter.dotDotDotToken)
|
|
3869
|
+
return true;
|
|
3870
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3871
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3872
|
+
return true;
|
|
3873
|
+
param = this.mapTypeOf(checkerType);
|
|
3874
|
+
}
|
|
3875
|
+
else {
|
|
3876
|
+
const value = this.mapTypeOf(this.typeOf(fn));
|
|
3877
|
+
if (value?.kind !== "func" || value.rest === true)
|
|
3878
|
+
return true;
|
|
3879
|
+
param = value.params[index] ?? null;
|
|
3880
|
+
}
|
|
3881
|
+
return param === null || this.coercibleValue(elem, param);
|
|
3882
|
+
};
|
|
3465
3883
|
const callbackReturnsOptional = (callback, seen = new Set()) => {
|
|
3466
3884
|
const node = peel(callback);
|
|
3467
3885
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
@@ -3479,6 +3897,253 @@ export class Lowerer {
|
|
|
3479
3897
|
!!decl.initializer &&
|
|
3480
3898
|
callbackReturnsOptional(decl.initializer, seen));
|
|
3481
3899
|
};
|
|
3900
|
+
// Element ABIs whose own type admits undefined (`(T | undefined)[]`,
|
|
3901
|
+
// or a rest array widened for an absent argument).
|
|
3902
|
+
const optionalElementKeys = new Set();
|
|
3903
|
+
// Checker answers for the fixed point's repeated element-ABI queries.
|
|
3904
|
+
// Only a binding's widened storage can change between passes.
|
|
3905
|
+
const checkerTypes = new Map();
|
|
3906
|
+
const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
|
|
3907
|
+
const arrayKeysUnsafe = (node) => {
|
|
3908
|
+
let checkerType = checkerTypes.get(node);
|
|
3909
|
+
if (checkerType === undefined) {
|
|
3910
|
+
checkerType = this.typeOf(node);
|
|
3911
|
+
checkerTypes.set(node, checkerType);
|
|
3912
|
+
}
|
|
3913
|
+
const e = peel(node);
|
|
3914
|
+
// A binding the prepass widened stores its widened array ABI.
|
|
3915
|
+
const widened = ts.isIdentifier(e)
|
|
3916
|
+
? (() => {
|
|
3917
|
+
const symbol = symbolOf(e);
|
|
3918
|
+
return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
|
|
3919
|
+
})()
|
|
3920
|
+
: undefined;
|
|
3921
|
+
let mapped = widened ?? this.mapTypeOf(checkerType);
|
|
3922
|
+
if (mapped?.kind === "promise")
|
|
3923
|
+
mapped = mapped.inner;
|
|
3924
|
+
const anyElements = mapped?.kind === "array"
|
|
3925
|
+
? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
|
|
3926
|
+
: mapped === null &&
|
|
3927
|
+
this.checker.isArrayType(checkerType) &&
|
|
3928
|
+
!this.checker.isTupleType(checkerType) &&
|
|
3929
|
+
((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
|
|
3930
|
+
(ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
|
|
3931
|
+
0;
|
|
3932
|
+
if (anyElements) {
|
|
3933
|
+
// `new Array(n)` has an `any` element type; its storage takes the
|
|
3934
|
+
// ABI of the slot it initializes.
|
|
3935
|
+
const contextual = this.checker.getContextualType(node);
|
|
3936
|
+
const target = contextual ? this.mapTypeOf(contextual) : null;
|
|
3937
|
+
return target?.kind === "array" ? [typeKey(target.elem)] : [];
|
|
3938
|
+
}
|
|
3939
|
+
if (mapped === null) {
|
|
3940
|
+
// An array whose element type is a type parameter: each
|
|
3941
|
+
// instantiation picks its own element ABI.
|
|
3942
|
+
return this.checker.isArrayType(checkerType) ? null : [];
|
|
3943
|
+
}
|
|
3944
|
+
const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
|
|
3945
|
+
const keys = [];
|
|
3946
|
+
for (const arm of arms) {
|
|
3947
|
+
if (arm.kind !== "array")
|
|
3948
|
+
continue;
|
|
3949
|
+
const key = typeKey(arm.elem);
|
|
3950
|
+
if (arm.elem.kind === "union"
|
|
3951
|
+
? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
|
|
3952
|
+
: isUnitType(arm.elem) || arm.elem.kind === "void")
|
|
3953
|
+
optionalElementKeys.add(key);
|
|
3954
|
+
keys.push(key);
|
|
3955
|
+
}
|
|
3956
|
+
return keys;
|
|
3957
|
+
};
|
|
3958
|
+
const ownership = new ArrayOwnership(sourceFiles, {
|
|
3959
|
+
symbolOf,
|
|
3960
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3961
|
+
typeOf: typeOrNull,
|
|
3962
|
+
instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
|
|
3963
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3964
|
+
isArray: (node) => {
|
|
3965
|
+
const type = typeOrNull(node);
|
|
3966
|
+
return type !== null && this.checker.isArrayType(type);
|
|
3967
|
+
},
|
|
3968
|
+
fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
|
|
3969
|
+
dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
|
|
3970
|
+
});
|
|
3971
|
+
let dynamicViews = null;
|
|
3972
|
+
// Element ABIs of the native arrays that an `any` or `unknown` value is
|
|
3973
|
+
// converted to, including arrays nested in the converted value.
|
|
3974
|
+
const collectDynamicViews = () => {
|
|
3975
|
+
const keys = new Set();
|
|
3976
|
+
const dynamicValue = (node) => {
|
|
3977
|
+
// A binding's declared type says whether it holds dynamic data; a
|
|
3978
|
+
// flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
|
|
3979
|
+
const e = peel(node);
|
|
3980
|
+
const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
|
|
3981
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
3982
|
+
const declared = declaration &&
|
|
3983
|
+
(ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
|
|
3984
|
+
ts.isIdentifier(declaration.name)
|
|
3985
|
+
? declaration.name
|
|
3986
|
+
: null;
|
|
3987
|
+
const type = typeOrNull(declared ?? node);
|
|
3988
|
+
if (type === null)
|
|
3989
|
+
return true;
|
|
3990
|
+
if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
|
|
3991
|
+
return true;
|
|
3992
|
+
if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
|
|
3993
|
+
return false;
|
|
3994
|
+
const element = this.checker.getTypeArguments(type)[0];
|
|
3995
|
+
return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
|
|
3996
|
+
};
|
|
3997
|
+
const collect = (type, depth) => {
|
|
3998
|
+
if (!type || depth > 4)
|
|
3999
|
+
return;
|
|
4000
|
+
if (type.kind === "array") {
|
|
4001
|
+
keys.add(typeKey(type.elem));
|
|
4002
|
+
collect(type.elem, depth + 1);
|
|
4003
|
+
}
|
|
4004
|
+
else if (type.kind === "union") {
|
|
4005
|
+
for (const arm of this.unions.get(type.unionId)?.arms ?? [])
|
|
4006
|
+
collect(arm, depth + 1);
|
|
4007
|
+
}
|
|
4008
|
+
else if (type.kind === "record") {
|
|
4009
|
+
for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
|
|
4010
|
+
collect(field.type, depth + 1);
|
|
4011
|
+
}
|
|
4012
|
+
else if (type.kind === "promise")
|
|
4013
|
+
collect(type.inner, depth + 1);
|
|
4014
|
+
};
|
|
4015
|
+
const target = (type) => {
|
|
4016
|
+
// An unanswerable target may be any array.
|
|
4017
|
+
if (type === null)
|
|
4018
|
+
keys.add("*");
|
|
4019
|
+
else if (type)
|
|
4020
|
+
collect(this.mapTypeOf(type), 0);
|
|
4021
|
+
};
|
|
4022
|
+
const visit = (node) => {
|
|
4023
|
+
if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
|
|
4024
|
+
target(typeOrNull(node));
|
|
4025
|
+
else if (ts.isVariableDeclaration(node) &&
|
|
4026
|
+
node.type &&
|
|
4027
|
+
node.initializer &&
|
|
4028
|
+
dynamicValue(node.initializer))
|
|
4029
|
+
target(typeOrNull(node.name));
|
|
4030
|
+
else if (ts.isBinaryExpression(node) &&
|
|
4031
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4032
|
+
dynamicValue(node.right))
|
|
4033
|
+
target(typeOrNull(node.left));
|
|
4034
|
+
else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
|
|
4035
|
+
target(contextualOrNull(node.expression));
|
|
4036
|
+
else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
4037
|
+
for (const arg of node.arguments ?? [])
|
|
4038
|
+
if (!ts.isSpreadElement(arg) && dynamicValue(arg))
|
|
4039
|
+
target(contextualOrNull(arg));
|
|
4040
|
+
}
|
|
4041
|
+
else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
|
|
4042
|
+
target(contextualOrNull(node.initializer));
|
|
4043
|
+
else if (ts.isArrayLiteralExpression(node)) {
|
|
4044
|
+
for (const element of node.elements)
|
|
4045
|
+
if (!ts.isSpreadElement(element) &&
|
|
4046
|
+
!ts.isOmittedExpression(element) &&
|
|
4047
|
+
dynamicValue(element))
|
|
4048
|
+
target(contextualOrNull(element));
|
|
4049
|
+
}
|
|
4050
|
+
else if (ts.isArrowFunction(node) &&
|
|
4051
|
+
!ts.isBlock(node.body) &&
|
|
4052
|
+
dynamicValue(node.body))
|
|
4053
|
+
target(contextualOrNull(node.body));
|
|
4054
|
+
};
|
|
4055
|
+
for (const sf of sourceFiles)
|
|
4056
|
+
ts.walkPreorder(sf, (node) => {
|
|
4057
|
+
// A checker panic hides this conversion's target: any array.
|
|
4058
|
+
if (!panicSafe(() => (visit(node), true), false))
|
|
4059
|
+
keys.add("*");
|
|
4060
|
+
return undefined;
|
|
4061
|
+
});
|
|
4062
|
+
return keys;
|
|
4063
|
+
};
|
|
4064
|
+
const elementStates = new ArrayElementStates({
|
|
4065
|
+
elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
|
|
4066
|
+
ownerOf: (node) => ownership.ownerOf(node),
|
|
4067
|
+
allocationOwner: (node) => ownership.allocationOwner(node),
|
|
4068
|
+
arrayKeys: arrayKeysOf,
|
|
4069
|
+
mayBeUndefined: (node) => {
|
|
4070
|
+
if (explicitlyNonNull(node))
|
|
4071
|
+
return false;
|
|
4072
|
+
if (mayBeOptional(node))
|
|
4073
|
+
return true;
|
|
4074
|
+
// `undefined as unknown as T` keeps the undefined value.
|
|
4075
|
+
for (let inner = node; ts.isParenthesizedExpression(inner) ||
|
|
4076
|
+
ts.isAsExpression(inner) ||
|
|
4077
|
+
ts.isTypeAssertion(inner) ||
|
|
4078
|
+
ts.isSatisfiesExpression(inner);) {
|
|
4079
|
+
inner = inner.expression;
|
|
4080
|
+
const type = typeOrNull(inner);
|
|
4081
|
+
if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
|
|
4082
|
+
return true;
|
|
4083
|
+
}
|
|
4084
|
+
const e = peel(node);
|
|
4085
|
+
if (!ts.isIdentifier(e))
|
|
4086
|
+
return false;
|
|
4087
|
+
const symbol = symbolOf(e);
|
|
4088
|
+
return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
|
|
4089
|
+
},
|
|
4090
|
+
callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
|
|
4091
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
4092
|
+
isString: (node) => {
|
|
4093
|
+
const type = typeOrNull(node);
|
|
4094
|
+
return type !== null && this.mapTypeOf(type)?.kind === "string";
|
|
4095
|
+
},
|
|
4096
|
+
isArrayLikeObject: (node) => {
|
|
4097
|
+
if (ts.isObjectLiteralExpression(peel(node)))
|
|
4098
|
+
return true;
|
|
4099
|
+
const type = typeOrNull(node);
|
|
4100
|
+
const mapped = type === null ? null : this.mapTypeOf(type);
|
|
4101
|
+
return (mapped === null ||
|
|
4102
|
+
mapped.kind === "record" ||
|
|
4103
|
+
mapped.kind === "object" ||
|
|
4104
|
+
mapped.kind === "dyn" ||
|
|
4105
|
+
mapped.kind === "jsval" ||
|
|
4106
|
+
mapped.kind === "union");
|
|
4107
|
+
},
|
|
4108
|
+
restKeys: (call) => {
|
|
4109
|
+
const callee = peel(call.expression);
|
|
4110
|
+
if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
|
|
4111
|
+
return null;
|
|
4112
|
+
const symbol = callableSymbolOf(callee);
|
|
4113
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4114
|
+
if (!sig)
|
|
4115
|
+
return ts.isPropertyAccessExpression(callee) ? [] : null;
|
|
4116
|
+
const rest = sig.params.find((shape) => shape.mode === "rest");
|
|
4117
|
+
return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
|
|
4118
|
+
},
|
|
4119
|
+
markIterationBinding: (node, known) => {
|
|
4120
|
+
const symbol = symbolOf(node);
|
|
4121
|
+
if (!symbol)
|
|
4122
|
+
return false;
|
|
4123
|
+
let changed = false;
|
|
4124
|
+
if (!iterationSymbols.has(symbol)) {
|
|
4125
|
+
iterationSymbols.add(symbol);
|
|
4126
|
+
changed = true;
|
|
4127
|
+
}
|
|
4128
|
+
if (known && !holeyIterationSymbols.has(symbol)) {
|
|
4129
|
+
holeyIterationSymbols.add(symbol);
|
|
4130
|
+
// `for (name of xs)` assigns an existing binding, whose own
|
|
4131
|
+
// storage must then hold the loop's undefined too.
|
|
4132
|
+
if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
|
|
4133
|
+
optionalSymbols.add(symbol);
|
|
4134
|
+
changed = true;
|
|
4135
|
+
}
|
|
4136
|
+
return changed;
|
|
4137
|
+
},
|
|
4138
|
+
note: provenance
|
|
4139
|
+
? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
|
|
4140
|
+
: null,
|
|
4141
|
+
});
|
|
4142
|
+
// Each pass revisits every array callback site; the last visit holds
|
|
4143
|
+
// the decision at the fixed point.
|
|
4144
|
+
const presentSites = new Map();
|
|
4145
|
+
// The same for each for-of loop over an array.
|
|
4146
|
+
const presentLoops = new Map();
|
|
3482
4147
|
const scanSites = new Map();
|
|
3483
4148
|
const sitesOf = (sf) => {
|
|
3484
4149
|
const cached = scanSites.get(sf);
|
|
@@ -3488,7 +4153,14 @@ export class Lowerer {
|
|
|
3488
4153
|
ts.walkPreorder(sf, (node) => {
|
|
3489
4154
|
if (ts.isVariableDeclaration(node) ||
|
|
3490
4155
|
ts.isBinaryExpression(node) ||
|
|
3491
|
-
ts.isCallExpression(node)
|
|
4156
|
+
ts.isCallExpression(node) ||
|
|
4157
|
+
ts.isNewExpression(node) ||
|
|
4158
|
+
ts.isArrayLiteralExpression(node) ||
|
|
4159
|
+
ts.isDeleteExpression(node) ||
|
|
4160
|
+
ts.isForOfStatement(node) ||
|
|
4161
|
+
ts.isArrayBindingPattern(node) ||
|
|
4162
|
+
ts.isPrefixUnaryExpression(node) ||
|
|
4163
|
+
ts.isPostfixUnaryExpression(node))
|
|
3492
4164
|
nodes.push(node);
|
|
3493
4165
|
});
|
|
3494
4166
|
scanSites.set(sf, nodes);
|
|
@@ -3497,6 +4169,10 @@ export class Lowerer {
|
|
|
3497
4169
|
const scanFile = (sf) => {
|
|
3498
4170
|
let changed = false;
|
|
3499
4171
|
const scan = (node) => {
|
|
4172
|
+
if (elementStates.scan(node))
|
|
4173
|
+
changed = true;
|
|
4174
|
+
if (ts.isForOfStatement(node))
|
|
4175
|
+
presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
|
|
3500
4176
|
if (ts.isVariableDeclaration(node)) {
|
|
3501
4177
|
if (node.initializer) {
|
|
3502
4178
|
const result = optionalPrimitiveResultType(node.initializer);
|
|
@@ -3524,6 +4200,16 @@ export class Lowerer {
|
|
|
3524
4200
|
changed = true;
|
|
3525
4201
|
}
|
|
3526
4202
|
}
|
|
4203
|
+
else if (ts.isIdentifier(node.name)) {
|
|
4204
|
+
const assertion = forwardedAssertion(node.initializer);
|
|
4205
|
+
const symbol = assertion ? symbolOf(node.name) : null;
|
|
4206
|
+
if (assertion && symbol && !optionalSymbols.has(symbol)) {
|
|
4207
|
+
optionalSymbols.add(symbol);
|
|
4208
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4209
|
+
provenance?.note("asserted-binding", node.name, node.initializer);
|
|
4210
|
+
changed = true;
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
3527
4213
|
if (ts.isIdentifier(node.name)) {
|
|
3528
4214
|
const valueSymbol = ts.isIdentifier(peel(node.initializer))
|
|
3529
4215
|
? symbolOf(peel(node.initializer))
|
|
@@ -3565,6 +4251,22 @@ export class Lowerer {
|
|
|
3565
4251
|
}
|
|
3566
4252
|
if (scanPattern(node.name, node.initializer))
|
|
3567
4253
|
changed = true;
|
|
4254
|
+
if (ts.isIdentifier(node.name)) {
|
|
4255
|
+
const symbol = symbolOf(node.name);
|
|
4256
|
+
const init = peel(node.initializer);
|
|
4257
|
+
if (symbol && noteFieldsFrom(symbol, init)) {
|
|
4258
|
+
provenance?.note("record-copy", node.name, node.initializer);
|
|
4259
|
+
changed = true;
|
|
4260
|
+
}
|
|
4261
|
+
if (symbol && ts.isObjectLiteralExpression(init)) {
|
|
4262
|
+
for (const field of spreadFieldsOf(init)) {
|
|
4263
|
+
if (noteFieldSymbol(symbol, field)) {
|
|
4264
|
+
provenance?.note("record-spread", node.name, node.initializer);
|
|
4265
|
+
changed = true;
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
3568
4270
|
if (ts.isIdentifier(node.name) &&
|
|
3569
4271
|
ts.isObjectLiteralExpression(peel(node.initializer))) {
|
|
3570
4272
|
const object = peel(node.initializer);
|
|
@@ -3586,6 +4288,36 @@ export class Lowerer {
|
|
|
3586
4288
|
}
|
|
3587
4289
|
}
|
|
3588
4290
|
}
|
|
4291
|
+
if (ts.isBinaryExpression(node) &&
|
|
4292
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4293
|
+
ts.isIdentifier(node.left)) {
|
|
4294
|
+
const symbol = symbolOf(node.left);
|
|
4295
|
+
if (symbol && noteFieldsFrom(symbol, node.right)) {
|
|
4296
|
+
provenance?.note("record-copy", node.left, node.right);
|
|
4297
|
+
changed = true;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
if (ts.isBinaryExpression(node) &&
|
|
4301
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4302
|
+
ts.isIdentifier(node.left)) {
|
|
4303
|
+
const assertion = forwardedAssertion(node.right);
|
|
4304
|
+
const symbol = assertion ? symbolOf(node.left) : null;
|
|
4305
|
+
// Only a variable's storage is promoted (see the final pass).
|
|
4306
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
4307
|
+
if (assertion &&
|
|
4308
|
+
symbol &&
|
|
4309
|
+
declaration &&
|
|
4310
|
+
ts.isVariableDeclaration(declaration) &&
|
|
4311
|
+
ts.isIdentifier(declaration.name) &&
|
|
4312
|
+
!this.runtimeOptionalAssertions.has(assertion)) {
|
|
4313
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4314
|
+
if (!optionalSymbols.has(symbol)) {
|
|
4315
|
+
optionalSymbols.add(symbol);
|
|
4316
|
+
provenance?.note("asserted-binding", node.left, node.right);
|
|
4317
|
+
}
|
|
4318
|
+
changed = true;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
3589
4321
|
if (ts.isBinaryExpression(node) &&
|
|
3590
4322
|
(node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
|
|
3591
4323
|
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
@@ -3607,6 +4339,14 @@ export class Lowerer {
|
|
|
3607
4339
|
provenance?.note("static-field", node.left, node.right);
|
|
3608
4340
|
changed = true;
|
|
3609
4341
|
}
|
|
4342
|
+
else if (field && !optionalSymbols.has(field) && optionalInstanceField(field)) {
|
|
4343
|
+
// The checker narrows the field to the assigned read's bare
|
|
4344
|
+
// type, but the stored value can be undefined: reads keep the
|
|
4345
|
+
// stored union (runtimeOptionalClassField).
|
|
4346
|
+
optionalSymbols.add(field);
|
|
4347
|
+
provenance?.note("assigned-field", node.left, node.right);
|
|
4348
|
+
changed = true;
|
|
4349
|
+
}
|
|
3610
4350
|
else if (ts.isIdentifier(node.left.expression)) {
|
|
3611
4351
|
const symbol = symbolOf(node.left.expression);
|
|
3612
4352
|
if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
|
|
@@ -3655,11 +4395,25 @@ export class Lowerer {
|
|
|
3655
4395
|
}
|
|
3656
4396
|
if (nativeArrayReceiver) {
|
|
3657
4397
|
provenanceSite = node;
|
|
3658
|
-
if (promoteHofCallback(callback, callbackIndices))
|
|
3659
|
-
changed = true;
|
|
3660
4398
|
const method = node.expression.name.text;
|
|
4399
|
+
// Callbacks skip holes (except the find family), so an array
|
|
4400
|
+
// that never holds undefined only passes present elements.
|
|
4401
|
+
const reduce = method === "reduce" || method === "reduceRight";
|
|
4402
|
+
const elementsPresent = receiver?.kind === "array" &&
|
|
4403
|
+
elementStates.elementsPresent(receiverNode, method) &&
|
|
4404
|
+
callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
|
|
4405
|
+
// A callback returning a runtime-absent value still needs its
|
|
4406
|
+
// widened return, whatever its elements.
|
|
4407
|
+
if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
|
|
4408
|
+
changed = true;
|
|
4409
|
+
// Element-typed callback helpers cover every method but
|
|
4410
|
+
// reduce, whose accumulator keeps the split visit.
|
|
4411
|
+
presentSites.set(node, elementsPresent && !reduce);
|
|
4412
|
+
if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
|
|
4413
|
+
changed = true;
|
|
3661
4414
|
if ((method === "reduce" || method === "reduceRight") &&
|
|
3662
|
-
(node.arguments.length < 2 ||
|
|
4415
|
+
((!elementsPresent && node.arguments.length < 2) ||
|
|
4416
|
+
callbackReturnsOptional(callback))) {
|
|
3663
4417
|
if (promoteHofCallback(callback, [0, 1]))
|
|
3664
4418
|
changed = true;
|
|
3665
4419
|
const result = this.runtimeOptionalType(this.irTypeOf(node));
|
|
@@ -3678,8 +4432,34 @@ export class Lowerer {
|
|
|
3678
4432
|
if (!symbol)
|
|
3679
4433
|
return;
|
|
3680
4434
|
const sig = signatureBySymbol.get(symbol);
|
|
3681
|
-
if (!sig)
|
|
4435
|
+
if (!sig) {
|
|
4436
|
+
// A direct call of a function-valued const (`const show = (n:
|
|
4437
|
+
// number) => ...`): a possibly missing argument widens that
|
|
4438
|
+
// parameter, as for a callback the same value is passed as.
|
|
4439
|
+
if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
|
|
4440
|
+
return;
|
|
4441
|
+
const declaration = this.checker.valueDeclarationOf(symbol);
|
|
4442
|
+
const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
4443
|
+
? peel(declaration.initializer)
|
|
4444
|
+
: null;
|
|
4445
|
+
if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
|
|
4446
|
+
return;
|
|
4447
|
+
// Optional, defaulted and rest parameters keep their own ABI.
|
|
4448
|
+
if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
|
|
4449
|
+
parameter.initializer !== undefined ||
|
|
4450
|
+
parameter.dotDotDotToken !== undefined))
|
|
4451
|
+
return;
|
|
4452
|
+
const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
|
|
4453
|
+
value.parameters[i] !== undefined &&
|
|
4454
|
+
!value.parameters[i].dotDotDotToken &&
|
|
4455
|
+
mayBeOptional(arg)
|
|
4456
|
+
? [i]
|
|
4457
|
+
: []);
|
|
4458
|
+
provenanceSite = node;
|
|
4459
|
+
if (indices.length > 0 && promoteHofCallback(node.expression, indices))
|
|
4460
|
+
changed = true;
|
|
3682
4461
|
return;
|
|
4462
|
+
}
|
|
3683
4463
|
// JSDoc describes the usual value, not a runtime arity check.
|
|
3684
4464
|
// A short JavaScript call still passes undefined. Widen only the
|
|
3685
4465
|
// observed slots before bodies lower so both caller and callee
|
|
@@ -3736,8 +4516,57 @@ export class Lowerer {
|
|
|
3736
4516
|
changed = true;
|
|
3737
4517
|
}
|
|
3738
4518
|
}
|
|
3739
|
-
if (ts.isSpreadElement(arg)
|
|
4519
|
+
if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
|
|
4520
|
+
const fields = fieldsOfValue(arg);
|
|
4521
|
+
for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
|
|
4522
|
+
const parameterShape = signatureBySymbol.get(target)?.params[i];
|
|
4523
|
+
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4524
|
+
if (!parameterShape || parameterShape.type.kind !== "record")
|
|
4525
|
+
continue;
|
|
4526
|
+
if (!parameter || !ts.isIdentifier(parameter.name))
|
|
4527
|
+
continue;
|
|
4528
|
+
const bound = symbolOf(parameter.name);
|
|
4529
|
+
if (!bound)
|
|
4530
|
+
continue;
|
|
4531
|
+
let promoted = parameterShape.type;
|
|
4532
|
+
for (const field of fields) {
|
|
4533
|
+
const source = promoted.kind === "record"
|
|
4534
|
+
? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
|
|
4535
|
+
: undefined;
|
|
4536
|
+
if (source) {
|
|
4537
|
+
promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
|
|
4538
|
+
}
|
|
4539
|
+
if (noteFieldSymbol(bound, field))
|
|
4540
|
+
changed = true;
|
|
4541
|
+
}
|
|
4542
|
+
if (!typeEquals(promoted, parameterShape.type)) {
|
|
4543
|
+
parameterShape.type = promoted;
|
|
4544
|
+
this.runtimeOptionalBindingTypes.set(bound, promoted);
|
|
4545
|
+
provenance?.note("record-parameter", parameter, arg);
|
|
4546
|
+
changed = true;
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
}
|
|
4550
|
+
if (ts.isSpreadElement(arg) || !shape)
|
|
3740
4551
|
return;
|
|
4552
|
+
const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
|
|
4553
|
+
if (!assertion && !mayBeOptional(arg))
|
|
4554
|
+
return;
|
|
4555
|
+
if (assertion) {
|
|
4556
|
+
if (i === restAt)
|
|
4557
|
+
return;
|
|
4558
|
+
// An omittable slot's ABI already holds undefined (an
|
|
4559
|
+
// optional or defaulted parameter applies its own default).
|
|
4560
|
+
if (shape.mode === "omittable") {
|
|
4561
|
+
if (shape.type.kind === "union" &&
|
|
4562
|
+
this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4563
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4566
|
+
if (shape.mode !== "required")
|
|
4567
|
+
return;
|
|
4568
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4569
|
+
}
|
|
3741
4570
|
if (i === restAt) {
|
|
3742
4571
|
// A rest binding always receives an array. An unchecked
|
|
3743
4572
|
// argument can make its elements undefined, not the pack.
|
|
@@ -3831,6 +4660,52 @@ export class Lowerer {
|
|
|
3831
4660
|
for (const [symbol, decl] of functionDeclBySymbol)
|
|
3832
4661
|
if (scanReturns(symbol, decl))
|
|
3833
4662
|
changed = true;
|
|
4663
|
+
for (const fn of tsLambdas) {
|
|
4664
|
+
// An annotated return is the author's contract, as for parameters.
|
|
4665
|
+
if (fn.type)
|
|
4666
|
+
continue;
|
|
4667
|
+
const mapped = this.mapTypeOf(this.typeOf(fn));
|
|
4668
|
+
if (mapped?.kind !== "func")
|
|
4669
|
+
continue;
|
|
4670
|
+
const previous = this.runtimeOptionalFunctionReturnType(fn, mapped.ret);
|
|
4671
|
+
// Async, generator and void bodies keep their own result shapes.
|
|
4672
|
+
if (previous.kind === "promise" ||
|
|
4673
|
+
previous.kind === "generator" ||
|
|
4674
|
+
previous.kind === "void")
|
|
4675
|
+
continue;
|
|
4676
|
+
const arithmetic = lambdaArithmeticReturn(fn);
|
|
4677
|
+
const next = arithmetic &&
|
|
4678
|
+
(previous.kind === "f64" || previous.kind === "string" || previous.kind === "bool")
|
|
4679
|
+
? arithmetic
|
|
4680
|
+
: bodyReturnsOptional(fn)
|
|
4681
|
+
? this.runtimeOptionalType(previous)
|
|
4682
|
+
: previous;
|
|
4683
|
+
if (!typeEquals(previous, next)) {
|
|
4684
|
+
this.runtimeOptionalFunctionReturns.set(fn, next);
|
|
4685
|
+
provenance?.note("lambda-return", fn, fn.body);
|
|
4686
|
+
changed = true;
|
|
4687
|
+
}
|
|
4688
|
+
// Calls through the lambda's own binding (`const get = () => xs[i];
|
|
4689
|
+
// get()`) produce the same result as calls of a declared function.
|
|
4690
|
+
const binding = fn.parent;
|
|
4691
|
+
if (next !== previous &&
|
|
4692
|
+
ts.isVariableDeclaration(binding) &&
|
|
4693
|
+
binding.initializer === fn &&
|
|
4694
|
+
!binding.type &&
|
|
4695
|
+
ts.isIdentifier(binding.name)) {
|
|
4696
|
+
const symbol = symbolOf(binding.name);
|
|
4697
|
+
if (symbol && next.kind === "union" && this.armTag(next.unionId, UNDEFINED_T) >= 0) {
|
|
4698
|
+
if (!optionalReturns.has(symbol)) {
|
|
4699
|
+
optionalReturns.add(symbol);
|
|
4700
|
+
changed = true;
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
else if (symbol && !arithmeticReturns.has(symbol)) {
|
|
4704
|
+
arithmeticReturns.set(symbol, next);
|
|
4705
|
+
changed = true;
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
3834
4709
|
for (const fn of jsLambdas) {
|
|
3835
4710
|
if (!bodyReturnsOptional(fn))
|
|
3836
4711
|
continue;
|
|
@@ -3862,6 +4737,31 @@ export class Lowerer {
|
|
|
3862
4737
|
}
|
|
3863
4738
|
}
|
|
3864
4739
|
}
|
|
4740
|
+
// NaN-coded slots convert optional values on entry instead of holding
|
|
4741
|
+
// the union, so promotion never applies to them.
|
|
4742
|
+
for (const symbol of [...optionalSymbols])
|
|
4743
|
+
if (nanCoded.nanCodedBinding(symbol))
|
|
4744
|
+
optionalSymbols.delete(symbol);
|
|
4745
|
+
for (const symbol of [...optionalReturns])
|
|
4746
|
+
if (nanCoded.nanCodedReturn(symbol))
|
|
4747
|
+
optionalReturns.delete(symbol);
|
|
4748
|
+
for (const [symbol, params] of optionalParams) {
|
|
4749
|
+
const decl = functionDeclBySymbol.get(symbol);
|
|
4750
|
+
for (const index of [...params]) {
|
|
4751
|
+
const name = decl?.parameters[index]?.name;
|
|
4752
|
+
const param = name && ts.isIdentifier(name) ? symbolOf(name) : null;
|
|
4753
|
+
if (param && nanCoded.nanCodedBinding(param))
|
|
4754
|
+
params.delete(index);
|
|
4755
|
+
}
|
|
4756
|
+
if (params.size === 0)
|
|
4757
|
+
optionalParams.delete(symbol);
|
|
4758
|
+
}
|
|
4759
|
+
for (const [site, present] of presentSites)
|
|
4760
|
+
if (present)
|
|
4761
|
+
this.presentElementCalls.add(site);
|
|
4762
|
+
for (const [loop, present] of presentLoops)
|
|
4763
|
+
if (present)
|
|
4764
|
+
this.presentElementLoops.add(loop);
|
|
3865
4765
|
const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
|
|
3866
4766
|
for (const [symbol, field] of staticFieldsBySymbol) {
|
|
3867
4767
|
if (!optionalSymbols.has(symbol))
|
|
@@ -3928,11 +4828,11 @@ export class Lowerer {
|
|
|
3928
4828
|
const symbol = symbolOf(node.name);
|
|
3929
4829
|
if (!symbol)
|
|
3930
4830
|
return;
|
|
3931
|
-
if (
|
|
3932
|
-
!node.type &&
|
|
4831
|
+
if (!node.type &&
|
|
3933
4832
|
!hasJsTypeAnnotation(node) &&
|
|
3934
4833
|
node.initializer &&
|
|
3935
|
-
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
|
|
4834
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) &&
|
|
4835
|
+
(isJsSourceFile(sf) || this.runtimeOptionalFunctionReturns.has(node.initializer))) {
|
|
3936
4836
|
const global = this.globalsBySymbol.get(symbol);
|
|
3937
4837
|
if (global?.type.kind === "func")
|
|
3938
4838
|
global.type = {
|
|
@@ -4008,6 +4908,35 @@ export class Lowerer {
|
|
|
4008
4908
|
}
|
|
4009
4909
|
});
|
|
4010
4910
|
}
|
|
4911
|
+
for (const symbol of optionalSymbols)
|
|
4912
|
+
this.runtimeOptionalAssignedSymbols.add(symbol);
|
|
4913
|
+
// `decode(xs[i]!)` into a parameter whose final ABI already holds
|
|
4914
|
+
// undefined: the assertion has no runtime effect, so a missing element
|
|
4915
|
+
// reaches the callee as undefined instead of trapping at the read.
|
|
4916
|
+
for (const sf of sourceFiles) {
|
|
4917
|
+
for (const node of sitesOf(sf)) {
|
|
4918
|
+
if (!ts.isCallExpression(node))
|
|
4919
|
+
continue;
|
|
4920
|
+
if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
|
|
4921
|
+
continue;
|
|
4922
|
+
const symbol = callableSymbolOf(node.expression);
|
|
4923
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4924
|
+
if (!sig || node.arguments.some(ts.isSpreadElement))
|
|
4925
|
+
continue;
|
|
4926
|
+
node.arguments.forEach((arg, i) => {
|
|
4927
|
+
let e = arg;
|
|
4928
|
+
while (ts.isParenthesizedExpression(e))
|
|
4929
|
+
e = e.expression;
|
|
4930
|
+
if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
|
|
4931
|
+
return;
|
|
4932
|
+
const shape = sig.params[i];
|
|
4933
|
+
if (shape?.mode !== "required" && shape?.mode !== "omittable")
|
|
4934
|
+
return;
|
|
4935
|
+
if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4936
|
+
this.runtimeOptionalAssertions.add(e);
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4011
4940
|
provenance?.flush();
|
|
4012
4941
|
}
|
|
4013
4942
|
run() {
|
|
@@ -4058,14 +4987,14 @@ export class Lowerer {
|
|
|
4058
4987
|
if (fn)
|
|
4059
4988
|
functions.push(fn);
|
|
4060
4989
|
else if (this.countsSkips())
|
|
4061
|
-
this.
|
|
4990
|
+
this.countSkippedBody(decl);
|
|
4062
4991
|
}
|
|
4063
4992
|
for (const decl of fp.classDecls) {
|
|
4064
4993
|
const info = this.classes.get(this.classNamer(decl));
|
|
4065
4994
|
if (info)
|
|
4066
4995
|
functions.push(...this.lowerClassMembers(info));
|
|
4067
4996
|
else if (this.countsSkips())
|
|
4068
|
-
this.
|
|
4997
|
+
this.countSkippedBody(decl);
|
|
4069
4998
|
}
|
|
4070
4999
|
}
|
|
4071
5000
|
// Each file's top-level statements form its run-once init function.
|
|
@@ -4153,10 +5082,10 @@ export class Lowerer {
|
|
|
4153
5082
|
// Deferred collection diagnostics nothing flushed — declarations no
|
|
4154
5083
|
// reference ever made relevant. They belong to the unreached group
|
|
4155
5084
|
// (collection order keeps them deterministic).
|
|
4156
|
-
for (const [symbol, diags] of this.deferredDiags) {
|
|
5085
|
+
for (const [symbol, diags] of [...this.deferredDiags]) {
|
|
4157
5086
|
if (this.alreadyFlushed.has(symbol))
|
|
4158
5087
|
continue;
|
|
4159
|
-
for (const d of diags)
|
|
5088
|
+
for (const d of [...diags])
|
|
4160
5089
|
this.pushDiag(d);
|
|
4161
5090
|
}
|
|
4162
5091
|
return {
|
|
@@ -4277,6 +5206,7 @@ export class Lowerer {
|
|
|
4277
5206
|
...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
|
|
4278
5207
|
entry: ENTRY_NAME,
|
|
4279
5208
|
...(this.usesWorkers ? { workers: true } : {}),
|
|
5209
|
+
...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
|
|
4280
5210
|
...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
|
|
4281
5211
|
};
|
|
4282
5212
|
if (module)
|
|
@@ -4298,41 +5228,72 @@ export class Lowerer {
|
|
|
4298
5228
|
* registered ClassInfo — the shape of a JS class whose collection fenced.
|
|
4299
5229
|
* Used by run()'s global pruning; shapes/unions recurse with a seen-set
|
|
4300
5230
|
* (interned ids can nest). */
|
|
4301
|
-
typeNamesUnregisteredClass(t
|
|
5231
|
+
typeNamesUnregisteredClass(t) {
|
|
5232
|
+
return this.unregisteredClassOf(t, new Set()) !== null;
|
|
5233
|
+
}
|
|
5234
|
+
/** The first class name typeNamesUnregisteredClass finds in `t`. */
|
|
5235
|
+
unregisteredClassOf(t, seen) {
|
|
5236
|
+
const first = (types) => {
|
|
5237
|
+
for (const type of types) {
|
|
5238
|
+
const name = this.unregisteredClassOf(type, seen);
|
|
5239
|
+
if (name !== null)
|
|
5240
|
+
return name;
|
|
5241
|
+
}
|
|
5242
|
+
return null;
|
|
5243
|
+
};
|
|
4302
5244
|
switch (t.kind) {
|
|
4303
5245
|
case "object":
|
|
4304
|
-
return
|
|
5246
|
+
return this.classes.has(t.className) ? null : t.className;
|
|
4305
5247
|
case "array":
|
|
4306
5248
|
case "set":
|
|
4307
|
-
return
|
|
5249
|
+
return first([t.elem]);
|
|
4308
5250
|
case "map":
|
|
4309
|
-
return (
|
|
4310
|
-
this.typeNamesUnregisteredClass(t.value, seen));
|
|
5251
|
+
return first([t.key, t.value]);
|
|
4311
5252
|
case "promise":
|
|
4312
|
-
return
|
|
5253
|
+
return first([t.inner]);
|
|
4313
5254
|
case "func":
|
|
4314
|
-
return (t.params
|
|
4315
|
-
this.typeNamesUnregisteredClass(t.ret, seen));
|
|
5255
|
+
return first([...t.params, t.ret]);
|
|
4316
5256
|
case "record": {
|
|
4317
5257
|
if (seen.has(t.shapeId))
|
|
4318
|
-
return
|
|
5258
|
+
return null;
|
|
4319
5259
|
seen.add(t.shapeId);
|
|
4320
5260
|
const shape = this.shapes.get(t.shapeId);
|
|
4321
5261
|
if (!shape)
|
|
4322
|
-
return
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
5262
|
+
return null;
|
|
5263
|
+
return first([
|
|
5264
|
+
...(shape.indexValue ? [shape.indexValue] : []),
|
|
5265
|
+
...shape.fields.map((f) => f.type),
|
|
5266
|
+
]);
|
|
4326
5267
|
}
|
|
4327
5268
|
case "union": {
|
|
4328
5269
|
if (seen.has(t.unionId))
|
|
4329
|
-
return
|
|
5270
|
+
return null;
|
|
4330
5271
|
seen.add(t.unionId);
|
|
4331
5272
|
const def = this.unions.get(t.unionId);
|
|
4332
|
-
return
|
|
5273
|
+
return def ? first(def.arms) : null;
|
|
4333
5274
|
}
|
|
4334
5275
|
default:
|
|
4335
|
-
return
|
|
5276
|
+
return null;
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
5279
|
+
/** A declaration whose signature blocked never lowers its body, so its
|
|
5280
|
+
* statements would otherwise vanish from the coverage totals and the
|
|
5281
|
+
* percentage would overstate how much of the program compiles. Count
|
|
5282
|
+
* every statement the body holds as attempted and failed, in addition to
|
|
5283
|
+
* the skipped-function tally. */
|
|
5284
|
+
countSkippedBody(decl) {
|
|
5285
|
+
this.stats.functionsSkipped++;
|
|
5286
|
+
if (this.suppressStats)
|
|
5287
|
+
return;
|
|
5288
|
+
const n = countBodyStatements(decl);
|
|
5289
|
+
if (n === 0)
|
|
5290
|
+
return;
|
|
5291
|
+
this.stats.statementsTotal += n;
|
|
5292
|
+
this.stats.statementsFailed += n;
|
|
5293
|
+
const file = decl.getSourceFile().fileName;
|
|
5294
|
+
for (let i = 0; i < n; i++) {
|
|
5295
|
+
this.bumpFileStat(file, "total");
|
|
5296
|
+
this.bumpFileStat(file, "failed");
|
|
4336
5297
|
}
|
|
4337
5298
|
}
|
|
4338
5299
|
/** Whether run() counts a signature-blocked declaration in
|
|
@@ -4868,13 +5829,18 @@ export class Lowerer {
|
|
|
4868
5829
|
const d = this.instantiationContext
|
|
4869
5830
|
? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
|
|
4870
5831
|
: diag;
|
|
5832
|
+
const pending = this.pendingFlushes.get(diag);
|
|
4871
5833
|
// Deferred collection: the wrapper decides whether these ever report
|
|
4872
5834
|
// (a reference flushes them; unreferenced declarations stay silent in
|
|
4873
5835
|
// builds and report under coverage's unreached group).
|
|
4874
5836
|
if (this.diagSink) {
|
|
5837
|
+
if (pending && d !== diag)
|
|
5838
|
+
this.pendingFlushes.set(d, pending);
|
|
4875
5839
|
this.diagSink.push(d);
|
|
4876
5840
|
return;
|
|
4877
5841
|
}
|
|
5842
|
+
if (pending)
|
|
5843
|
+
this.commitFlush(pending.symbol, pending.source);
|
|
4878
5844
|
// One site, one report: some declarations map a type twice (module
|
|
4879
5845
|
// globals pre-register before their initializers lower) — an exact
|
|
4880
5846
|
// duplicate (code + span + message) adds noise, not information.
|
|
@@ -5518,6 +6484,24 @@ export class Lowerer {
|
|
|
5518
6484
|
this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
|
|
5519
6485
|
throw new PoisonError();
|
|
5520
6486
|
}
|
|
6487
|
+
// Instances of two unrelated classes: class identity is nominal here,
|
|
6488
|
+
// so a structurally compatible instance never becomes the other class.
|
|
6489
|
+
// An assertion (`x as Y`) names the mistake most directly.
|
|
6490
|
+
if (actual.kind === "object" &&
|
|
6491
|
+
expected.kind === "object" &&
|
|
6492
|
+
this.classes.has(actual.className) &&
|
|
6493
|
+
this.classes.has(expected.className) &&
|
|
6494
|
+
this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
|
|
6495
|
+
!this.isSubclassOf(actual.className, expected.className) &&
|
|
6496
|
+
!this.isSubclassOf(expected.className, actual.className)) {
|
|
6497
|
+
let site = node;
|
|
6498
|
+
while (ts.isParenthesizedExpression(site))
|
|
6499
|
+
site = site.expression;
|
|
6500
|
+
const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
|
|
6501
|
+
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
|
|
6502
|
+
? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
|
|
6503
|
+
: `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
|
|
6504
|
+
}
|
|
5521
6505
|
// Iterator-typed slots hold generators and built-in collection
|
|
5522
6506
|
// iterators; any other iterator object (a user class) stays fenced.
|
|
5523
6507
|
if (expected.kind === "generator" && actual.kind !== "generator") {
|
|
@@ -5601,6 +6585,13 @@ export class Lowerer {
|
|
|
5601
6585
|
* else (including a DIFFERENT union) is left for requireExactShape, which
|
|
5602
6586
|
* rejects union mismatches with SC2003. */
|
|
5603
6587
|
coerceToExpected(expr, expected) {
|
|
6588
|
+
// A destination that holds undefined takes an unassigned deferred-init
|
|
6589
|
+
// field's slot as is (Node passes the undefined along).
|
|
6590
|
+
if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
|
|
6591
|
+
const slot = this.deferredFieldSlot(expr);
|
|
6592
|
+
if (slot)
|
|
6593
|
+
expr = slot;
|
|
6594
|
+
}
|
|
5604
6595
|
if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
|
|
5605
6596
|
return this.divergentValue(expr, expected);
|
|
5606
6597
|
// Iterator-typed slots: native iterators and differently typed
|
|
@@ -5759,9 +6750,18 @@ export class Lowerer {
|
|
|
5759
6750
|
};
|
|
5760
6751
|
}
|
|
5761
6752
|
if (expr.kind === "unitLit" || this.dynConvertible(expr.type)) {
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
6753
|
+
// Records and arrays keep their identity as untyped values, including
|
|
6754
|
+
// when they arrive through a union, so `unknown` views, weak
|
|
6755
|
+
// collection keys and later typed reads all see the same object.
|
|
6756
|
+
const liveReference = (type) => {
|
|
6757
|
+
if (type.kind === "array")
|
|
6758
|
+
return true;
|
|
6759
|
+
const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
|
|
6760
|
+
return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
|
|
6761
|
+
};
|
|
6762
|
+
const liveRef = liveReference(expr.type) ||
|
|
6763
|
+
(expr.type.kind === "union" &&
|
|
6764
|
+
(this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
|
|
5765
6765
|
return {
|
|
5766
6766
|
kind: "dynFrom",
|
|
5767
6767
|
value: expr,
|
|
@@ -5955,7 +6955,7 @@ export class Lowerer {
|
|
|
5955
6955
|
this.armTag(expr.type.unionId, expected) >= 0) {
|
|
5956
6956
|
const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
|
|
5957
6957
|
if (helper) {
|
|
5958
|
-
return
|
|
6958
|
+
return this.checkedNarrowCall(helper, expr, expected, expr.loc);
|
|
5959
6959
|
}
|
|
5960
6960
|
}
|
|
5961
6961
|
if (!typeEquals(expr.type, expected)) {
|
|
@@ -6206,6 +7206,9 @@ export class Lowerer {
|
|
|
6206
7206
|
narrowedArmHelper(fromId, target, loc) {
|
|
6207
7207
|
return narrowedArmHelper(this, fromId, target, loc);
|
|
6208
7208
|
}
|
|
7209
|
+
checkedNarrowCall(helper, value, type, loc) {
|
|
7210
|
+
return checkedNarrowCall(this, helper, value, type, loc);
|
|
7211
|
+
}
|
|
6209
7212
|
deferredReadHelper(fromId, target, loc) {
|
|
6210
7213
|
return deferredReadHelper(this, fromId, target, loc);
|
|
6211
7214
|
}
|
|
@@ -7024,15 +8027,47 @@ export class Lowerer {
|
|
|
7024
8027
|
flushDeferred(symbol) {
|
|
7025
8028
|
if (this.collecting)
|
|
7026
8029
|
return;
|
|
8030
|
+
this.reportDeferred(symbol);
|
|
8031
|
+
}
|
|
8032
|
+
/** flushDeferred without the collection-pass guard: for declarations
|
|
8033
|
+
* whose diagnostics must report eagerly (class statements with
|
|
8034
|
+
* declaration-time code, extended bases — collectClassShapeDeferring). */
|
|
8035
|
+
reportDeferred(symbol) {
|
|
7027
8036
|
const diags = this.deferredDiags.get(symbol);
|
|
7028
8037
|
if (!diags)
|
|
7029
8038
|
return;
|
|
7030
|
-
this.
|
|
7031
|
-
|
|
7032
|
-
return;
|
|
7033
|
-
|
|
7034
|
-
|
|
8039
|
+
if (this.alreadyFlushed.has(symbol)) {
|
|
8040
|
+
this.deferredDiags.delete(symbol); // the emit pass reported these
|
|
8041
|
+
return;
|
|
8042
|
+
}
|
|
8043
|
+
if (this.diagSink === null) {
|
|
8044
|
+
this.deferredDiags.delete(symbol);
|
|
8045
|
+
this.flushedSymbols.add(symbol);
|
|
8046
|
+
for (const d of diags)
|
|
8047
|
+
this.pushDiag(d);
|
|
8048
|
+
return;
|
|
8049
|
+
}
|
|
8050
|
+
// A capture buffer is active (a probe, or another declaration's
|
|
8051
|
+
// deferred collection): the copies ride that buffer, and the
|
|
8052
|
+
// declaration stays deferred until one of them reaches the build.
|
|
8053
|
+
for (const d of diags) {
|
|
8054
|
+
this.pendingFlushes.set(d, { symbol, source: d });
|
|
7035
8055
|
this.pushDiag(d);
|
|
8056
|
+
}
|
|
8057
|
+
}
|
|
8058
|
+
/** A flushed copy of a deferred diagnostic reached `diags`: that
|
|
8059
|
+
* diagnostic is reported, so it leaves the declaration's deferred list
|
|
8060
|
+
* (the declaration counts as flushed for the coverage remainder). */
|
|
8061
|
+
commitFlush(symbol, source) {
|
|
8062
|
+
this.flushedSymbols.add(symbol);
|
|
8063
|
+
const list = this.deferredDiags.get(symbol);
|
|
8064
|
+
if (!list)
|
|
8065
|
+
return;
|
|
8066
|
+
const index = list.indexOf(source);
|
|
8067
|
+
if (index >= 0)
|
|
8068
|
+
list.splice(index, 1);
|
|
8069
|
+
if (list.length === 0)
|
|
8070
|
+
this.deferredDiags.delete(symbol);
|
|
7036
8071
|
}
|
|
7037
8072
|
flushDeferredClass(className) {
|
|
7038
8073
|
const symbol = this.deferredClassByName.get(className);
|
|
@@ -7339,7 +8374,23 @@ export class Lowerer {
|
|
|
7339
8374
|
}
|
|
7340
8375
|
/* ── scoping and captures ─────────────────────────────────────────── */
|
|
7341
8376
|
declareLocal(nameNode, name, type, mutable) {
|
|
7342
|
-
|
|
8377
|
+
const symbol = this.checker.getSymbolAtLocation(nameNode);
|
|
8378
|
+
const local = declareContextLocal(this.ctx, name, type, mutable, symbol, bindingSource(nameNode));
|
|
8379
|
+
// A binding declared `T | undefined` that receives a missing read: the
|
|
8380
|
+
// checker narrows it to `T` after the assignment, but the stored value
|
|
8381
|
+
// can be undefined. Track it like widened storage so bare reads keep
|
|
8382
|
+
// the union until a guard proves presence.
|
|
8383
|
+
if (symbol &&
|
|
8384
|
+
this.runtimeOptionalAssignedSymbols.has(symbol) &&
|
|
8385
|
+
type.kind === "union" &&
|
|
8386
|
+
this.armTag(type.unionId, UNDEFINED_T) >= 0 &&
|
|
8387
|
+
this.unions.get(type.unionId)?.arms.length === 2 &&
|
|
8388
|
+
!isUnitType(this.stripUndefinedArm(type))) {
|
|
8389
|
+
const root = this.runtimeOptionalRootOf(local);
|
|
8390
|
+
this.runtimeOptionalStorageLocals.add(root);
|
|
8391
|
+
this.runtimeOptionalLocals.add(root);
|
|
8392
|
+
}
|
|
8393
|
+
return local;
|
|
7343
8394
|
}
|
|
7344
8395
|
/** A never-returning call in a value position of type `expected`: the
|
|
7345
8396
|
* call runs for its effects (it throws or exits), and the expression ends
|
|
@@ -8111,13 +9162,7 @@ export class Lowerer {
|
|
|
8111
9162
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
|
|
8112
9163
|
if (!helper)
|
|
8113
9164
|
return lowerForOf(this, stmt);
|
|
8114
|
-
return this.withExpressionOverride(stmt.expression,
|
|
8115
|
-
kind: "call",
|
|
8116
|
-
callee: helper,
|
|
8117
|
-
args: [optional.value],
|
|
8118
|
-
type: optional.present,
|
|
8119
|
-
loc: locOf(stmt.expression),
|
|
8120
|
-
}, () => lowerForOf(this, stmt));
|
|
9165
|
+
return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
|
|
8121
9166
|
}
|
|
8122
9167
|
lowerForStatement(stmt) {
|
|
8123
9168
|
return lowerForStatement(this, stmt);
|
|
@@ -8362,21 +9407,21 @@ export class Lowerer {
|
|
|
8362
9407
|
const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
|
|
8363
9408
|
if (!helper)
|
|
8364
9409
|
return lower();
|
|
8365
|
-
right =
|
|
8366
|
-
kind: "call",
|
|
8367
|
-
callee: helper,
|
|
8368
|
-
args: [optionalRight.value],
|
|
8369
|
-
type: optionalRight.present,
|
|
8370
|
-
loc: locOf(expr.right),
|
|
8371
|
-
};
|
|
9410
|
+
right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
|
|
8372
9411
|
}
|
|
8373
9412
|
else {
|
|
8374
9413
|
right = this.lowerExpr(expr.right);
|
|
8375
9414
|
}
|
|
8376
9415
|
if (right.type.kind !== "classval")
|
|
8377
9416
|
return lower();
|
|
8378
|
-
|
|
8379
|
-
|
|
9417
|
+
// A capture-free class reference is the immortal static class object:
|
|
9418
|
+
// re-reading it is pure, so it needs no owned temporary (whose retain
|
|
9419
|
+
// and release would be out-of-line calls on every test).
|
|
9420
|
+
const constantRight = right.kind === "classRef" && right.captures === undefined;
|
|
9421
|
+
const stableRight = constantRight
|
|
9422
|
+
? null
|
|
9423
|
+
: this.declareHiddenLocal("%instanceofRight", right.type);
|
|
9424
|
+
const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
|
|
8380
9425
|
const body = this.withExpressionOverride(expr.left, {
|
|
8381
9426
|
kind: "unionNarrow",
|
|
8382
9427
|
unionId: left.type.unionId,
|
|
@@ -8389,7 +9434,16 @@ export class Lowerer {
|
|
|
8389
9434
|
kind: "seqExpr",
|
|
8390
9435
|
stmts: [
|
|
8391
9436
|
{ kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
|
|
8392
|
-
|
|
9437
|
+
...(stableRight
|
|
9438
|
+
? [
|
|
9439
|
+
{
|
|
9440
|
+
kind: "varDecl",
|
|
9441
|
+
localId: stableRight.id,
|
|
9442
|
+
init: right,
|
|
9443
|
+
loc: locOf(expr.right),
|
|
9444
|
+
},
|
|
9445
|
+
]
|
|
9446
|
+
: []),
|
|
8393
9447
|
],
|
|
8394
9448
|
result: {
|
|
8395
9449
|
kind: "ternary",
|
|
@@ -8420,13 +9474,7 @@ export class Lowerer {
|
|
|
8420
9474
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
|
|
8421
9475
|
if (!helper)
|
|
8422
9476
|
return lower();
|
|
8423
|
-
return this.withExpressionOverride(node,
|
|
8424
|
-
kind: "call",
|
|
8425
|
-
callee: helper,
|
|
8426
|
-
args: [optional.value],
|
|
8427
|
-
type: optional.present,
|
|
8428
|
-
loc: locOf(node),
|
|
8429
|
-
}, lower);
|
|
9477
|
+
return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
|
|
8430
9478
|
}
|
|
8431
9479
|
withExpressionOverride(node, value, lower) {
|
|
8432
9480
|
const previous = this.chainRecvByNode.get(node);
|
|
@@ -8535,6 +9583,37 @@ export class Lowerer {
|
|
|
8535
9583
|
fieldGetExpr(target, loc, blame) {
|
|
8536
9584
|
return fieldGetExpr(this, target, loc, blame);
|
|
8537
9585
|
}
|
|
9586
|
+
/** The undefined-armed slot read behind a checked deferred-init field
|
|
9587
|
+
* read, or null. Consumers that observe undefined itself (equality,
|
|
9588
|
+
* typeof, truthiness, nullish defaults) test the slot instead of the
|
|
9589
|
+
* checked extraction, which throws for an unassigned field. */
|
|
9590
|
+
deferredFieldSlot(value) {
|
|
9591
|
+
// The inline form: `slot is undefined ? throw : narrow(slot)`.
|
|
9592
|
+
if (value.kind === "ternary" &&
|
|
9593
|
+
value.cond.kind === "unionIsTag" &&
|
|
9594
|
+
value.else_.kind === "unionNarrow" &&
|
|
9595
|
+
value.else_.value === value.cond.value &&
|
|
9596
|
+
value.then.kind === "libCall" &&
|
|
9597
|
+
value.then.fn === "error.nodeThrow") {
|
|
9598
|
+
const read = value.cond.value;
|
|
9599
|
+
return read.kind === "fieldGet" &&
|
|
9600
|
+
read.type.kind === "union" &&
|
|
9601
|
+
this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
|
|
9602
|
+
? read
|
|
9603
|
+
: null;
|
|
9604
|
+
}
|
|
9605
|
+
if (value.kind !== "call" || value.args.length !== 1)
|
|
9606
|
+
return null;
|
|
9607
|
+
const read = value.args[0];
|
|
9608
|
+
if (read.kind !== "fieldGet" || read.type.kind !== "union")
|
|
9609
|
+
return null;
|
|
9610
|
+
if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
|
|
9611
|
+
return null;
|
|
9612
|
+
return this.coercions.checkedNarrows.has(value.callee) ||
|
|
9613
|
+
value.callee.startsWith("%deferred.read.")
|
|
9614
|
+
? read
|
|
9615
|
+
: null;
|
|
9616
|
+
}
|
|
8538
9617
|
fieldSetStmt(target, value, loc, blame) {
|
|
8539
9618
|
return fieldSetStmt(this, target, value, loc, blame);
|
|
8540
9619
|
}
|
|
@@ -9218,8 +10297,8 @@ export class Lowerer {
|
|
|
9218
10297
|
return lowerNumberStaticProperty(this, expr);
|
|
9219
10298
|
}
|
|
9220
10299
|
/* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
|
|
9221
|
-
requireDynamicApi(feature, node) {
|
|
9222
|
-
return requireDynamicApi(this, feature, node);
|
|
10300
|
+
requireDynamicApi(feature, node, hint) {
|
|
10301
|
+
return requireDynamicApi(this, feature, node, hint);
|
|
9223
10302
|
}
|
|
9224
10303
|
lowerMathProperty(expr) {
|
|
9225
10304
|
return lowerMathProperty(this, expr);
|