@scriptc/compiler 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +2 -0
- package/dist/backend/llvm/alloc.d.ts +3 -0
- package/dist/backend/llvm/alloc.js +54 -13
- package/dist/backend/llvm/alloc.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/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 +26 -18
- 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/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.test.js +89 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +4 -0
- package/dist/backend/llvm/dyn.js +31 -20
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +184 -11
- package/dist/backend/llvm/emitter.js +1189 -125
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +21 -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 +85 -8
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +29 -21
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.d.ts +5 -0
- package/dist/backend/llvm/expr-calls.js +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 +230 -27
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +25 -10
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +72 -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.js +41 -31
- 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 +7 -11
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +8 -8
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.js +73 -13
- 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 +106 -16
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-effects.test.js +52 -1
- package/dist/backend/llvm/reference-effects.test.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +45 -17
- package/dist/backend/llvm/shapes.js +131 -53
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +33 -8
- 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 +76 -0
- package/dist/backend/llvm/string-reads.js.map +1 -0
- 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/union-repr.d.ts +15 -0
- package/dist/backend/llvm/union-repr.js +89 -0
- package/dist/backend/llvm/union-repr.js.map +1 -0
- package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
- package/dist/backend/llvm/virtual-borrows.js +82 -0
- package/dist/backend/llvm/virtual-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.d.ts +43 -0
- package/dist/backend/llvm/walk-borrows.js +150 -0
- package/dist/backend/llvm/walk-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
- package/dist/backend/llvm/walk-borrows.test.js +119 -0
- package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +5 -1
- package/dist/backend/llvm/walkers.js +24 -21
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/may-throw.d.ts +29 -1
- package/dist/backend/may-throw.js +346 -3
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +113 -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 +4 -0
- package/dist/backend/native-codegen.js +49 -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-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/cli/command.js +70 -7
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/usage.d.ts +4 -1
- package/dist/cli/usage.js +8 -1
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +12 -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/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 +120 -4
- 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/workers.js +4 -2
- package/dist/frontend/lowering/builtins/workers.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/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/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/lower-calls.d.ts +5 -0
- package/dist/frontend/lowering/lower-calls.js +226 -157
- 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 +13 -0
- package/dist/frontend/lowering/lower-exprs.js +431 -107
- 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 +24 -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 +36 -6
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +78 -2
- package/dist/frontend/lowering/lowerer.js +986 -73
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
- package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
- package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
- package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
- package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-provenance.d.ts +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/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 +6 -1
- package/dist/index.js +47 -9
- 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 +3 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/int32-slots.d.ts +72 -0
- package/dist/ir/int32-slots.js +837 -0
- package/dist/ir/int32-slots.js.map +1 -0
- package/dist/ir/int32-slots.test.d.ts +1 -0
- package/dist/ir/int32-slots.test.js +222 -0
- package/dist/ir/int32-slots.test.js.map +1 -0
- package/dist/ir/integer-ranges.d.ts +19 -3
- package/dist/ir/integer-ranges.js +168 -23
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +74 -2
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +24 -6
- package/dist/ir/ir.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 +3 -0
- package/dist/native/cache.js +49 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +21 -1
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/cli.js +1 -1
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.d.ts +5 -0
- package/dist/native/compiler.js +51 -4
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/prepare.d.ts +4 -0
- package/dist/native/prepare.js +2 -1
- package/dist/native/prepare.js.map +1 -1
- package/dist/startup-cache.d.ts +2 -0
- package/dist/startup-cache.js +2 -0
- package/dist/startup-cache.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +1 -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,16 @@ 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";
|
|
20
26
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
21
27
|
import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
|
|
22
28
|
import { UnregisteredClassTypes } from "./unregistered-class-types.js";
|
|
@@ -52,7 +58,7 @@ import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, comp
|
|
|
52
58
|
import { arrayOf, BOOL, canAdaptDynFuncTo, canDynCheckTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, DYN_HANDLE_KINDS, F64, isDynTypedRefType, isJsonSafeType, isJsonStringifySafeType, isPrimitiveCollectionKey, isSupportedMapKey, isSupportedMapValue, isUndefinedArmedUnion, isUnitType, JSVAL, NULL_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID, } from "../../ir/ir.js";
|
|
53
59
|
import { provenanceActive } from "../provenance-registry.js";
|
|
54
60
|
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";
|
|
61
|
+
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, isWorkerHandleType, jsOpenObjectType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
56
62
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalNameOf, stdlibGlobalMember, nodeTypesOnlySymbol, } from "./surfaces.js";
|
|
57
63
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules, appendForkModules, appendWorkerModules, } from "./lower-modules.js";
|
|
58
64
|
import { prepareCjsModuleGraph } from "./lower-node-module.js";
|
|
@@ -197,6 +203,39 @@ export function stmtUsesIsland(stmts) {
|
|
|
197
203
|
return !stmts.every((stmt) => everyStmtChild(stmt, expr, nested));
|
|
198
204
|
return !everyStmtChild(stmts, expr, nested);
|
|
199
205
|
}
|
|
206
|
+
/** Statements a never-lowered body would have contributed to the coverage
|
|
207
|
+
* totals: every element of a statement list (blocks, case clauses), plus
|
|
208
|
+
* the single-statement bodies of control flow, nested functions included —
|
|
209
|
+
* the same accounting lowerStmts applies to bodies that do lower. */
|
|
210
|
+
export function countBodyStatements(root) {
|
|
211
|
+
let n = 0;
|
|
212
|
+
const visit = (node) => {
|
|
213
|
+
if (ts.isBlock(node) || ts.isCaseClause(node) || ts.isDefaultClause(node)) {
|
|
214
|
+
n += node.statements.length;
|
|
215
|
+
}
|
|
216
|
+
else if ((ts.isIfStatement(node) ||
|
|
217
|
+
ts.isForStatement(node) ||
|
|
218
|
+
ts.isForInStatement(node) ||
|
|
219
|
+
ts.isForOfStatement(node) ||
|
|
220
|
+
ts.isWhileStatement(node) ||
|
|
221
|
+
ts.isDoStatement(node) ||
|
|
222
|
+
ts.isLabeledStatement(node)) &&
|
|
223
|
+
node.parent !== undefined) {
|
|
224
|
+
const bodies = ts.isIfStatement(node)
|
|
225
|
+
? [node.thenStatement, node.elseStatement]
|
|
226
|
+
: ts.isLabeledStatement(node)
|
|
227
|
+
? [node.statement]
|
|
228
|
+
: [node.statement];
|
|
229
|
+
for (const body of bodies) {
|
|
230
|
+
if (body !== undefined && !ts.isBlock(body) && !ts.isIfStatement(body))
|
|
231
|
+
n++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
// Iterative: absurdly deep nesting must not overflow the stack here.
|
|
236
|
+
ts.walkPreorder(root, visit);
|
|
237
|
+
return n;
|
|
238
|
+
}
|
|
200
239
|
function directExternalTypeSpecifiersByFile(externalTypes) {
|
|
201
240
|
const out = new Map();
|
|
202
241
|
for (const [specifier, file] of externalTypes) {
|
|
@@ -349,6 +388,12 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
349
388
|
resultLowerer = emit;
|
|
350
389
|
timing("historical-order-relower");
|
|
351
390
|
}
|
|
391
|
+
// Divergence warnings over the program's own modules: computed once,
|
|
392
|
+
// after lowering, from the same checker (never part of a failure).
|
|
393
|
+
const divergences = scanDivergences(resultLowerer, moduleOrder);
|
|
394
|
+
timing("divergence-scan", { found: divergences.length });
|
|
395
|
+
if (divergences.length > 0)
|
|
396
|
+
result = { ...result, divergences };
|
|
352
397
|
if (options.coverage !== true)
|
|
353
398
|
return result;
|
|
354
399
|
const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
@@ -366,7 +411,17 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
366
411
|
externalTypeSpecifiersByFile,
|
|
367
412
|
});
|
|
368
413
|
const rem = remainder.run();
|
|
369
|
-
|
|
414
|
+
// Unreached JS statements whose fences deferred to runtime count as
|
|
415
|
+
// failed statements in the remainder's stats, so their diagnostics must
|
|
416
|
+
// travel with them: a failed statement is never left unexplained.
|
|
417
|
+
return {
|
|
418
|
+
...result,
|
|
419
|
+
unreached: {
|
|
420
|
+
diagnostics: rem.diagnostics,
|
|
421
|
+
stats: rem.stats,
|
|
422
|
+
...(rem.runtimeFences.length > 0 ? { runtimeFences: rem.runtimeFences } : {}),
|
|
423
|
+
},
|
|
424
|
+
};
|
|
370
425
|
}
|
|
371
426
|
/** The island-handle type a `import(...)` initializer gives a binding
|
|
372
427
|
* whose DECLARED type has no static mapping (`Promise<typeof
|
|
@@ -882,6 +937,17 @@ export function jsFuncNameOf(node, lowerer) {
|
|
|
882
937
|
}
|
|
883
938
|
return null;
|
|
884
939
|
}
|
|
940
|
+
/** Answers `fallback` when a checker query panics inside the type checker. */
|
|
941
|
+
function panicSafe(query, fallback) {
|
|
942
|
+
try {
|
|
943
|
+
return query();
|
|
944
|
+
}
|
|
945
|
+
catch (e) {
|
|
946
|
+
if (isCheckerPanic(e))
|
|
947
|
+
return fallback;
|
|
948
|
+
throw e;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
885
951
|
export class Lowerer {
|
|
886
952
|
program;
|
|
887
953
|
entry;
|
|
@@ -910,6 +976,14 @@ export class Lowerer {
|
|
|
910
976
|
/** Per-symbol result of the never-reassigned file scan
|
|
911
977
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
912
978
|
neverReassignedCache = new Map();
|
|
979
|
+
stringIndexBoundsCache = null;
|
|
980
|
+
/** Proofs that a string element read names an existing code unit. The
|
|
981
|
+
* indexed-read analysis and expression lowering share one instance, so a
|
|
982
|
+
* read's representation and its callers' signatures always agree. */
|
|
983
|
+
get stringIndexBounds() {
|
|
984
|
+
this.stringIndexBoundsCache ??= new StringIndexBounds((node) => this.checker.getSymbolAtLocation(node) ?? null, (symbol) => this.checker.valueDeclarationOf(symbol), (node) => (this.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0);
|
|
985
|
+
return this.stringIndexBoundsCache;
|
|
986
|
+
}
|
|
913
987
|
/** Syntactic writes indexed once per JS scope. Symbol and RHS type
|
|
914
988
|
* checks remain contextual to each binding probe. */
|
|
915
989
|
jsBindingWritesByOwner = new Map();
|
|
@@ -1156,6 +1230,11 @@ export class Lowerer {
|
|
|
1156
1230
|
* prepass. This includes explicit annotations: unchecked TS annotations do
|
|
1157
1231
|
* not prove that an array property read produced a value. */
|
|
1158
1232
|
runtimeOptionalBindingTypes = new Map();
|
|
1233
|
+
/** Bindings that receive an unchecked indexed read (or a value derived
|
|
1234
|
+
* from one) through an initializer or assignment. The checker narrows a
|
|
1235
|
+
* declared optional binding to that read's bare type, so presence tests
|
|
1236
|
+
* on these bindings consult the stored tag instead. */
|
|
1237
|
+
runtimeOptionalAssignedSymbols = new Set();
|
|
1159
1238
|
/** Concise callback/lambda returns promoted by the HOF callback prepass;
|
|
1160
1239
|
* arrows have no declaration symbol to key in fnSigsBySymbol. */
|
|
1161
1240
|
runtimeOptionalFunctionReturns = new Map();
|
|
@@ -1173,6 +1252,17 @@ export class Lowerer {
|
|
|
1173
1252
|
* every name destructured from a present source optional. */
|
|
1174
1253
|
runtimeOptionalPatternTypes = new Map();
|
|
1175
1254
|
runtimeOptionalReduceTypes = new Map();
|
|
1255
|
+
/** Non-null assertions whose value is forwarded into a slot promoted to
|
|
1256
|
+
* hold undefined. `x!` has no runtime effect, so these keep the stored
|
|
1257
|
+
* undefined state instead of trapping before any dereference. */
|
|
1258
|
+
runtimeOptionalAssertions = new Set();
|
|
1259
|
+
/** Array callback calls whose receiver the optional-read analysis proved
|
|
1260
|
+
* never holds an explicit undefined (or a hole, for the find family):
|
|
1261
|
+
* their callbacks take the element type itself. */
|
|
1262
|
+
presentElementCalls = new Set();
|
|
1263
|
+
/** for-of loops over arrays that can hold neither holes nor undefined:
|
|
1264
|
+
* each pass reads the element payload directly. */
|
|
1265
|
+
presentElementLoops = new Set();
|
|
1176
1266
|
/** Capture entries and their origin share one mutable box. Normalize each
|
|
1177
1267
|
* entry to the origin so writes and flow proofs stay synchronized. */
|
|
1178
1268
|
runtimeOptionalRoots = new Map();
|
|
@@ -1247,6 +1337,9 @@ export class Lowerer {
|
|
|
1247
1337
|
* optional-read analysis, so ordinary assertions keep their generic
|
|
1248
1338
|
* checked-narrow behavior. */
|
|
1249
1339
|
runtimeOptionalSourceValue(node, value) {
|
|
1340
|
+
const slot = this.deferredFieldSlot(value);
|
|
1341
|
+
if (slot)
|
|
1342
|
+
return slot;
|
|
1250
1343
|
let origin = node;
|
|
1251
1344
|
while (ts.isParenthesizedExpression(origin))
|
|
1252
1345
|
origin = origin.expression;
|
|
@@ -1281,12 +1374,9 @@ export class Lowerer {
|
|
|
1281
1374
|
value.value.type.kind === "dyn")
|
|
1282
1375
|
return value.value;
|
|
1283
1376
|
let source = value;
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
value.args[0]?.type.kind === "union") {
|
|
1288
|
-
source = value.args[0];
|
|
1289
|
-
}
|
|
1377
|
+
const narrowed = checkedNarrowOperand(this, value);
|
|
1378
|
+
if (narrowed?.type.kind === "union")
|
|
1379
|
+
source = narrowed;
|
|
1290
1380
|
return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
|
|
1291
1381
|
? source
|
|
1292
1382
|
: null;
|
|
@@ -1399,6 +1489,41 @@ export class Lowerer {
|
|
|
1399
1489
|
runtimeOptionalFunctionReturnType(node, fallback) {
|
|
1400
1490
|
return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
|
|
1401
1491
|
}
|
|
1492
|
+
/** True when `actual` is `declared` with some record fields (directly,
|
|
1493
|
+
* in nested records, or in an array's element records) widened to runtime-optional unions:
|
|
1494
|
+
* fields filled from unchecked indexed reads the checker calls present.
|
|
1495
|
+
* Inferred bindings and destructured parameters keep that layout instead
|
|
1496
|
+
* of narrowing it away. */
|
|
1497
|
+
isRuntimeOptionalRecordVariant(actual, declared) {
|
|
1498
|
+
if (actual.kind === "array" && declared.kind === "array")
|
|
1499
|
+
return this.isRuntimeOptionalRecordVariant(actual.elem, declared.elem);
|
|
1500
|
+
if (actual.kind !== "record" || declared.kind !== "record")
|
|
1501
|
+
return false;
|
|
1502
|
+
if (actual.shapeId === declared.shapeId)
|
|
1503
|
+
return false;
|
|
1504
|
+
const a = this.shapes.get(actual.shapeId);
|
|
1505
|
+
const d = this.shapes.get(declared.shapeId);
|
|
1506
|
+
if (!a || !d || a.fields.length !== d.fields.length || !!a.tuple !== !!d.tuple)
|
|
1507
|
+
return false;
|
|
1508
|
+
if (a.indexValue || d.indexValue)
|
|
1509
|
+
return false;
|
|
1510
|
+
let widened = false;
|
|
1511
|
+
for (const field of d.fields) {
|
|
1512
|
+
const source = a.fields.find((f) => f.name === field.name);
|
|
1513
|
+
if (!source)
|
|
1514
|
+
return false;
|
|
1515
|
+
if (typeEquals(source.type, field.type))
|
|
1516
|
+
continue;
|
|
1517
|
+
if (this.isRuntimeOptionalField(actual.shapeId, field.name) &&
|
|
1518
|
+
(this.runtimeOptionalWidening(source.type, field.type) !== null ||
|
|
1519
|
+
this.isRuntimeOptionalRecordVariant(source.type, field.type))) {
|
|
1520
|
+
widened = true;
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
return widened;
|
|
1526
|
+
}
|
|
1402
1527
|
/** Promote one field of a record storage shape to the value's runtime
|
|
1403
1528
|
* type. Shapes are immutable by identity, so this interns a sibling shape
|
|
1404
1529
|
* and records the field as runtime-optional for property-read narrowing. */
|
|
@@ -1699,6 +1824,9 @@ export class Lowerer {
|
|
|
1699
1824
|
* and therefore becomes the cycle's evaluation root. Dynamic imports
|
|
1700
1825
|
* wait on this shared verdict rather than a build-time-selected member. */
|
|
1701
1826
|
asyncCyclePromiseOf = new Map();
|
|
1827
|
+
/** Modules whose evaluation an import() can request again after it threw,
|
|
1828
|
+
* computed on first use (lower-modules.ts importRetryModules). */
|
|
1829
|
+
importRetryModules = null;
|
|
1702
1830
|
/** Record-shape interner: canonical (name-sorted) field list → shapeId.
|
|
1703
1831
|
* Threaded into every mapType call; its `shapes` array becomes
|
|
1704
1832
|
* IrModule.records. */
|
|
@@ -1795,6 +1923,13 @@ export class Lowerer {
|
|
|
1795
1923
|
* coverage remainder as alreadyFlushed). */
|
|
1796
1924
|
flushedSymbols = new Set();
|
|
1797
1925
|
alreadyFlushed;
|
|
1926
|
+
/** Deferred diagnostics a flush handed to a capture buffer (diagSink)
|
|
1927
|
+
* instead of the build, keyed by the copy in that buffer. A buffer can
|
|
1928
|
+
* be discarded (a declined probe, a collection that succeeded) or
|
|
1929
|
+
* re-deferred under another declaration, so the declaration stays
|
|
1930
|
+
* deferred until one of these copies reaches `diags` (pushDiag commits
|
|
1931
|
+
* it); until then a later flush reports the diagnostics again. */
|
|
1932
|
+
pendingFlushes = new Map();
|
|
1798
1933
|
/** The build's target platform ("win32" | "darwin" | "linux" | ...):
|
|
1799
1934
|
* selects the platform-keyed builtin surfaces (builtinModuleFnsOf /
|
|
1800
1935
|
* builtinModuleConstOf in surfaces.ts). */
|
|
@@ -1973,6 +2108,12 @@ export class Lowerer {
|
|
|
1973
2108
|
this.classProtocols ??= new ClassProtocols(this.checker, this.moduleOrder);
|
|
1974
2109
|
return this.classProtocols.usesCheckedIdentity(type);
|
|
1975
2110
|
}
|
|
2111
|
+
/** Declared class fields may store a DYN value only when its static type
|
|
2112
|
+
* names checked protocol identity or an owned runtime handle (a Worker
|
|
2113
|
+
* or MessagePort); `unknown` and `any` fields stay refused. */
|
|
2114
|
+
classFieldStoresDyn(type) {
|
|
2115
|
+
return this.classImplementedProtocol(type) || isWorkerHandleType(type, this.typeCtx);
|
|
2116
|
+
}
|
|
1976
2117
|
/** The nominal identity of a checker module-namespace type. Program
|
|
1977
2118
|
* modules use their normalized absolute source name; supported builtins
|
|
1978
2119
|
* use the lowering tables' canonical bare name. */
|
|
@@ -3043,12 +3184,76 @@ export class Lowerer {
|
|
|
3043
3184
|
}
|
|
3044
3185
|
return false;
|
|
3045
3186
|
};
|
|
3187
|
+
// `parent!` passed to a call or bound to a name forwards the value
|
|
3188
|
+
// without reading it: a callee that never dereferences it observes
|
|
3189
|
+
// undefined, as in JavaScript. Only a class value with one undefined
|
|
3190
|
+
// arm qualifies; element reads keep the established bounds trap.
|
|
3191
|
+
const forwardedAssertion = (node) => {
|
|
3192
|
+
let e = node;
|
|
3193
|
+
while (ts.isParenthesizedExpression(e))
|
|
3194
|
+
e = e.expression;
|
|
3195
|
+
if (!ts.isNonNullExpression(e))
|
|
3196
|
+
return null;
|
|
3197
|
+
let operand = e.expression;
|
|
3198
|
+
while (ts.isParenthesizedExpression(operand))
|
|
3199
|
+
operand = operand.expression;
|
|
3200
|
+
if (ts.isElementAccessExpression(operand) || ts.isNonNullExpression(operand))
|
|
3201
|
+
return null;
|
|
3202
|
+
// A guard that already proves the binding present makes the
|
|
3203
|
+
// assertion true at runtime: nothing is forwarded as undefined.
|
|
3204
|
+
if (ts.isIdentifier(operand)) {
|
|
3205
|
+
const symbol = symbolOf(operand);
|
|
3206
|
+
if (symbol && provenPresent(operand, symbol))
|
|
3207
|
+
return null;
|
|
3208
|
+
}
|
|
3209
|
+
const stored = this.mapTypeOf(this.typeOf(e.expression));
|
|
3210
|
+
const asserted = this.mapTypeOf(this.typeOf(e));
|
|
3211
|
+
if (stored?.kind !== "union" || asserted?.kind !== "object")
|
|
3212
|
+
return null;
|
|
3213
|
+
const arms = this.unions.get(stored.unionId)?.arms;
|
|
3214
|
+
if (arms?.length !== 2 ||
|
|
3215
|
+
this.armTag(stored.unionId, UNDEFINED_T) < 0 ||
|
|
3216
|
+
!arms.some((arm) => typeEquals(arm, asserted)))
|
|
3217
|
+
return null;
|
|
3218
|
+
return e;
|
|
3219
|
+
};
|
|
3220
|
+
// `decode(xs[i]!)`: a missing element is passed as undefined in
|
|
3221
|
+
// JavaScript, and the callee may never read it.
|
|
3222
|
+
const forwardedElementAssertion = (node) => {
|
|
3223
|
+
let e = node;
|
|
3224
|
+
while (ts.isParenthesizedExpression(e))
|
|
3225
|
+
e = e.expression;
|
|
3226
|
+
if (!ts.isNonNullExpression(e))
|
|
3227
|
+
return null;
|
|
3228
|
+
let read = e.expression;
|
|
3229
|
+
while (ts.isParenthesizedExpression(read))
|
|
3230
|
+
read = read.expression;
|
|
3231
|
+
if (!ts.isElementAccessExpression(read))
|
|
3232
|
+
return null;
|
|
3233
|
+
if (this.mapTypeOf(this.typeOf(read.expression))?.kind !== "array")
|
|
3234
|
+
return null;
|
|
3235
|
+
// An in-bounds read of an array that never holds undefined or holes
|
|
3236
|
+
// always produces a present element.
|
|
3237
|
+
if (indexReadInBounds(read) && elementStates.elementsPresent(read.expression, "find"))
|
|
3238
|
+
return null;
|
|
3239
|
+
const element = this.mapTypeOf(this.typeOf(e));
|
|
3240
|
+
if (!element ||
|
|
3241
|
+
element.kind === "void" ||
|
|
3242
|
+
element.kind === "dyn" ||
|
|
3243
|
+
element.kind === "jsval" ||
|
|
3244
|
+
element.kind === "generator")
|
|
3245
|
+
return null;
|
|
3246
|
+
return e;
|
|
3247
|
+
};
|
|
3046
3248
|
const isIndexedRead = (node) => {
|
|
3047
3249
|
const e = peel(node);
|
|
3048
3250
|
if (!ts.isElementAccessExpression(e))
|
|
3049
3251
|
return false;
|
|
3050
3252
|
const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
|
|
3051
|
-
|
|
3253
|
+
if (kind === "string")
|
|
3254
|
+
return !this.stringIndexBounds.inBounds(e);
|
|
3255
|
+
// Typed-array reads answer undefined for an invalid index too.
|
|
3256
|
+
return kind === "array" || kind === "bytes";
|
|
3052
3257
|
};
|
|
3053
3258
|
const isDynamicObjectEntryRead = (node) => {
|
|
3054
3259
|
const read = peel(node);
|
|
@@ -3076,6 +3281,37 @@ export class Lowerer {
|
|
|
3076
3281
|
return this.withUndefinedArm(t);
|
|
3077
3282
|
};
|
|
3078
3283
|
const fieldName = (node) => node.name.text;
|
|
3284
|
+
// The precision facts below ask about nodes ordinary lowering may never
|
|
3285
|
+
// query. A checker panic on one of them answers null, and each caller
|
|
3286
|
+
// falls back to its conservative fact.
|
|
3287
|
+
const typeOrNull = (node) => panicSafe(() => this.typeOf(node), null);
|
|
3288
|
+
const contextualOrNull = (node) => panicSafe(() => this.checker.getContextualType(node), null);
|
|
3289
|
+
// Presence proofs only hold for a binding that keeps its one value for
|
|
3290
|
+
// its whole lifetime.
|
|
3291
|
+
const reassigned = reassignedBindings(sourceFiles, symbolOf, isJsSourceFile);
|
|
3292
|
+
const presenceProofs = new Map();
|
|
3293
|
+
const provenPresent = (use, symbol) => {
|
|
3294
|
+
if (reassigned.has(symbol))
|
|
3295
|
+
return false;
|
|
3296
|
+
const cached = presenceProofs.get(use);
|
|
3297
|
+
if (cached !== undefined)
|
|
3298
|
+
return cached;
|
|
3299
|
+
const proven = presentAtUse(use, {
|
|
3300
|
+
sameBinding: (node) => node.text === use.text && symbolOf(node) === symbol,
|
|
3301
|
+
isUndefined: (node) => node.text === "undefined" &&
|
|
3302
|
+
((typeOrNull(node)?.flags ?? 0) & ts.TypeFlags.Undefined) !== 0,
|
|
3303
|
+
});
|
|
3304
|
+
presenceProofs.set(use, proven);
|
|
3305
|
+
return proven;
|
|
3306
|
+
};
|
|
3307
|
+
// Bindings of a for-of loop over an array that can hold holes or
|
|
3308
|
+
// undefined. Lowering keeps such a binding's runtime union, so storing
|
|
3309
|
+
// it into another array can store undefined.
|
|
3310
|
+
const iterationSymbols = new Set();
|
|
3311
|
+
// The subset whose iterated array's own facts show a hole or undefined.
|
|
3312
|
+
// Such a binding reads `T | undefined`, so a typed parameter, local or
|
|
3313
|
+
// field it flows into widens like any other missing element read.
|
|
3314
|
+
const holeyIterationSymbols = new Set();
|
|
3079
3315
|
const mayBeOptional = (node) => {
|
|
3080
3316
|
// `xs[i]!` is the explicit proven-present form. Its array read keeps
|
|
3081
3317
|
// the established dense bounds trap and must not promote the enclosing
|
|
@@ -3103,7 +3339,9 @@ export class Lowerer {
|
|
|
3103
3339
|
return true;
|
|
3104
3340
|
if (ts.isIdentifier(e)) {
|
|
3105
3341
|
const symbol = symbolOf(e);
|
|
3106
|
-
return symbol !== null &&
|
|
3342
|
+
return (symbol !== null &&
|
|
3343
|
+
(optionalSymbols.has(symbol) || holeyIterationSymbols.has(symbol)) &&
|
|
3344
|
+
!provenPresent(e, symbol));
|
|
3107
3345
|
}
|
|
3108
3346
|
if (ts.isPropertyAccessExpression(e)) {
|
|
3109
3347
|
const symbol = symbolOf(e.name);
|
|
@@ -3169,7 +3407,9 @@ export class Lowerer {
|
|
|
3169
3407
|
if (t?.kind !== "string")
|
|
3170
3408
|
return false;
|
|
3171
3409
|
const recv = this.mapTypeOf(this.typeOf(p.expression));
|
|
3172
|
-
|
|
3410
|
+
if (recv?.kind === "string")
|
|
3411
|
+
return !this.stringIndexBounds.inBounds(p);
|
|
3412
|
+
return recv?.kind === "array" && recv.elem.kind === "string";
|
|
3173
3413
|
};
|
|
3174
3414
|
const primitive = (part) => {
|
|
3175
3415
|
const t = this.mapTypeOf(this.typeOf(part));
|
|
@@ -3196,6 +3436,43 @@ export class Lowerer {
|
|
|
3196
3436
|
optionalFields.set(symbol, set);
|
|
3197
3437
|
return set.size !== before;
|
|
3198
3438
|
};
|
|
3439
|
+
// Runtime-optional record fields travel with the record value: a copy,
|
|
3440
|
+
// a spread or a parameter receives the source's optional fields.
|
|
3441
|
+
const fieldsOfValue = (node) => {
|
|
3442
|
+
const e = peel(node);
|
|
3443
|
+
if (!ts.isIdentifier(e))
|
|
3444
|
+
return undefined;
|
|
3445
|
+
const symbol = symbolOf(e);
|
|
3446
|
+
return symbol === null ? undefined : optionalFields.get(symbol);
|
|
3447
|
+
};
|
|
3448
|
+
const noteFieldsFrom = (symbol, source) => {
|
|
3449
|
+
const fields = fieldsOfValue(source);
|
|
3450
|
+
if (fields === undefined)
|
|
3451
|
+
return false;
|
|
3452
|
+
let changed = false;
|
|
3453
|
+
for (const field of fields)
|
|
3454
|
+
if (noteFieldSymbol(symbol, field))
|
|
3455
|
+
changed = true;
|
|
3456
|
+
return changed;
|
|
3457
|
+
};
|
|
3458
|
+
/** Optional fields an object literal takes from its spreads, minus
|
|
3459
|
+
* the properties a later member writes explicitly. */
|
|
3460
|
+
const spreadFieldsOf = (object) => {
|
|
3461
|
+
const out = new Set();
|
|
3462
|
+
for (const prop of object.properties) {
|
|
3463
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
3464
|
+
const fields = fieldsOfValue(prop.expression);
|
|
3465
|
+
if (fields !== undefined)
|
|
3466
|
+
for (const field of fields)
|
|
3467
|
+
out.add(field);
|
|
3468
|
+
}
|
|
3469
|
+
else if (prop.name !== undefined &&
|
|
3470
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
|
|
3471
|
+
out.delete(prop.name.text);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
return out;
|
|
3475
|
+
};
|
|
3199
3476
|
const scanPattern = (name, init) => {
|
|
3200
3477
|
if (!ts.isArrayBindingPattern(name))
|
|
3201
3478
|
return false;
|
|
@@ -3462,6 +3739,70 @@ export class Lowerer {
|
|
|
3462
3739
|
}
|
|
3463
3740
|
return changed;
|
|
3464
3741
|
};
|
|
3742
|
+
// A reduce callback over present elements keeps the reduce helper's
|
|
3743
|
+
// optional element ABI for its parameter storage, so the helper still
|
|
3744
|
+
// splits each visit into a plain-element call. Its value is never
|
|
3745
|
+
// absent, so nothing it flows into is widened.
|
|
3746
|
+
const retypeInlineCallback = (callback, parameterIndices) => {
|
|
3747
|
+
const fn = peel(callback);
|
|
3748
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
3749
|
+
return false;
|
|
3750
|
+
let changed = false;
|
|
3751
|
+
for (const parameterIndex of parameterIndices) {
|
|
3752
|
+
const parameter = fn.parameters[parameterIndex];
|
|
3753
|
+
if (!parameter)
|
|
3754
|
+
continue;
|
|
3755
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3756
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3757
|
+
continue;
|
|
3758
|
+
const current = this.mapTypeOf(checkerType);
|
|
3759
|
+
if (!current)
|
|
3760
|
+
continue;
|
|
3761
|
+
const widened = this.runtimeOptionalType(current);
|
|
3762
|
+
if (!ts.isIdentifier(parameter.name)) {
|
|
3763
|
+
const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
|
|
3764
|
+
this.runtimeOptionalPatternTypes.set(parameter.name, widened);
|
|
3765
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3766
|
+
changed = true;
|
|
3767
|
+
continue;
|
|
3768
|
+
}
|
|
3769
|
+
const symbol = symbolOf(parameter.name);
|
|
3770
|
+
if (!symbol)
|
|
3771
|
+
continue;
|
|
3772
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
3773
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
3774
|
+
if (!previous || !typeEquals(previous, widened))
|
|
3775
|
+
changed = true;
|
|
3776
|
+
}
|
|
3777
|
+
return changed;
|
|
3778
|
+
};
|
|
3779
|
+
// A present-element helper passes the stored element itself, so the
|
|
3780
|
+
// callback must accept that exact value through a mechanical adapter.
|
|
3781
|
+
// A parameter spelled as a structural supertype (`(r: { pos: number })`
|
|
3782
|
+
// over a wider record) only converts through the optional element
|
|
3783
|
+
// ABI's union retag; keep that site on the optional visit. Unannotated
|
|
3784
|
+
// inline parameters take the element's contextual type, and generic
|
|
3785
|
+
// parameters specialize later, so neither restricts the site.
|
|
3786
|
+
const callbackTakesPresentElement = (callback, elem, index) => {
|
|
3787
|
+
const fn = peel(callback);
|
|
3788
|
+
let param;
|
|
3789
|
+
if (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) {
|
|
3790
|
+
const parameter = fn.parameters[index];
|
|
3791
|
+
if (!parameter?.type || parameter.dotDotDotToken)
|
|
3792
|
+
return true;
|
|
3793
|
+
const checkerType = this.typeOf(parameter.name);
|
|
3794
|
+
if ((checkerType.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
3795
|
+
return true;
|
|
3796
|
+
param = this.mapTypeOf(checkerType);
|
|
3797
|
+
}
|
|
3798
|
+
else {
|
|
3799
|
+
const value = this.mapTypeOf(this.typeOf(fn));
|
|
3800
|
+
if (value?.kind !== "func" || value.rest === true)
|
|
3801
|
+
return true;
|
|
3802
|
+
param = value.params[index] ?? null;
|
|
3803
|
+
}
|
|
3804
|
+
return param === null || this.coercibleValue(elem, param);
|
|
3805
|
+
};
|
|
3465
3806
|
const callbackReturnsOptional = (callback, seen = new Set()) => {
|
|
3466
3807
|
const node = peel(callback);
|
|
3467
3808
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
@@ -3479,6 +3820,253 @@ export class Lowerer {
|
|
|
3479
3820
|
!!decl.initializer &&
|
|
3480
3821
|
callbackReturnsOptional(decl.initializer, seen));
|
|
3481
3822
|
};
|
|
3823
|
+
// Element ABIs whose own type admits undefined (`(T | undefined)[]`,
|
|
3824
|
+
// or a rest array widened for an absent argument).
|
|
3825
|
+
const optionalElementKeys = new Set();
|
|
3826
|
+
// Checker answers for the fixed point's repeated element-ABI queries.
|
|
3827
|
+
// Only a binding's widened storage can change between passes.
|
|
3828
|
+
const checkerTypes = new Map();
|
|
3829
|
+
const arrayKeysOf = (node) => panicSafe(() => arrayKeysUnsafe(node), null);
|
|
3830
|
+
const arrayKeysUnsafe = (node) => {
|
|
3831
|
+
let checkerType = checkerTypes.get(node);
|
|
3832
|
+
if (checkerType === undefined) {
|
|
3833
|
+
checkerType = this.typeOf(node);
|
|
3834
|
+
checkerTypes.set(node, checkerType);
|
|
3835
|
+
}
|
|
3836
|
+
const e = peel(node);
|
|
3837
|
+
// A binding the prepass widened stores its widened array ABI.
|
|
3838
|
+
const widened = ts.isIdentifier(e)
|
|
3839
|
+
? (() => {
|
|
3840
|
+
const symbol = symbolOf(e);
|
|
3841
|
+
return symbol ? this.runtimeOptionalBindingTypes.get(symbol) : undefined;
|
|
3842
|
+
})()
|
|
3843
|
+
: undefined;
|
|
3844
|
+
let mapped = widened ?? this.mapTypeOf(checkerType);
|
|
3845
|
+
if (mapped?.kind === "promise")
|
|
3846
|
+
mapped = mapped.inner;
|
|
3847
|
+
const anyElements = mapped?.kind === "array"
|
|
3848
|
+
? mapped.elem.kind === "dyn" || mapped.elem.kind === "jsval"
|
|
3849
|
+
: mapped === null &&
|
|
3850
|
+
this.checker.isArrayType(checkerType) &&
|
|
3851
|
+
!this.checker.isTupleType(checkerType) &&
|
|
3852
|
+
((this.checker.getTypeArguments(checkerType)[0]?.flags ?? 0) &
|
|
3853
|
+
(ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !==
|
|
3854
|
+
0;
|
|
3855
|
+
if (anyElements) {
|
|
3856
|
+
// `new Array(n)` has an `any` element type; its storage takes the
|
|
3857
|
+
// ABI of the slot it initializes.
|
|
3858
|
+
const contextual = this.checker.getContextualType(node);
|
|
3859
|
+
const target = contextual ? this.mapTypeOf(contextual) : null;
|
|
3860
|
+
return target?.kind === "array" ? [typeKey(target.elem)] : [];
|
|
3861
|
+
}
|
|
3862
|
+
if (mapped === null) {
|
|
3863
|
+
// An array whose element type is a type parameter: each
|
|
3864
|
+
// instantiation picks its own element ABI.
|
|
3865
|
+
return this.checker.isArrayType(checkerType) ? null : [];
|
|
3866
|
+
}
|
|
3867
|
+
const arms = mapped.kind === "union" ? (this.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
|
|
3868
|
+
const keys = [];
|
|
3869
|
+
for (const arm of arms) {
|
|
3870
|
+
if (arm.kind !== "array")
|
|
3871
|
+
continue;
|
|
3872
|
+
const key = typeKey(arm.elem);
|
|
3873
|
+
if (arm.elem.kind === "union"
|
|
3874
|
+
? this.armTag(arm.elem.unionId, UNDEFINED_T) >= 0
|
|
3875
|
+
: isUnitType(arm.elem) || arm.elem.kind === "void")
|
|
3876
|
+
optionalElementKeys.add(key);
|
|
3877
|
+
keys.push(key);
|
|
3878
|
+
}
|
|
3879
|
+
return keys;
|
|
3880
|
+
};
|
|
3881
|
+
const ownership = new ArrayOwnership(sourceFiles, {
|
|
3882
|
+
symbolOf,
|
|
3883
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3884
|
+
typeOf: typeOrNull,
|
|
3885
|
+
instanceAssignableTo: (owner, target) => panicSafe(() => this.checker.isTypeAssignableTo(this.checker.getTypeAtLocation(owner), target), true),
|
|
3886
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3887
|
+
isArray: (node) => {
|
|
3888
|
+
const type = typeOrNull(node);
|
|
3889
|
+
return type !== null && this.checker.isArrayType(type);
|
|
3890
|
+
},
|
|
3891
|
+
fieldKeys: (declaration) => arrayKeysOf(declaration.name) ?? [],
|
|
3892
|
+
dynamicViewKeys: () => (dynamicViews ??= collectDynamicViews()),
|
|
3893
|
+
});
|
|
3894
|
+
let dynamicViews = null;
|
|
3895
|
+
// Element ABIs of the native arrays that an `any` or `unknown` value is
|
|
3896
|
+
// converted to, including arrays nested in the converted value.
|
|
3897
|
+
const collectDynamicViews = () => {
|
|
3898
|
+
const keys = new Set();
|
|
3899
|
+
const dynamicValue = (node) => {
|
|
3900
|
+
// A binding's declared type says whether it holds dynamic data; a
|
|
3901
|
+
// flow type such as `Array.isArray(x)`'s `any[]` only narrows it.
|
|
3902
|
+
const e = peel(node);
|
|
3903
|
+
const symbol = ts.isIdentifier(e) ? symbolOf(e) : null;
|
|
3904
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
3905
|
+
const declared = declaration &&
|
|
3906
|
+
(ts.isVariableDeclaration(declaration) || ts.isParameter(declaration)) &&
|
|
3907
|
+
ts.isIdentifier(declaration.name)
|
|
3908
|
+
? declaration.name
|
|
3909
|
+
: null;
|
|
3910
|
+
const type = typeOrNull(declared ?? node);
|
|
3911
|
+
if (type === null)
|
|
3912
|
+
return true;
|
|
3913
|
+
if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
|
|
3914
|
+
return true;
|
|
3915
|
+
if (!this.checker.isArrayType(type) || this.checker.isTupleType(type))
|
|
3916
|
+
return false;
|
|
3917
|
+
const element = this.checker.getTypeArguments(type)[0];
|
|
3918
|
+
return (element !== undefined && (element.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0);
|
|
3919
|
+
};
|
|
3920
|
+
const collect = (type, depth) => {
|
|
3921
|
+
if (!type || depth > 4)
|
|
3922
|
+
return;
|
|
3923
|
+
if (type.kind === "array") {
|
|
3924
|
+
keys.add(typeKey(type.elem));
|
|
3925
|
+
collect(type.elem, depth + 1);
|
|
3926
|
+
}
|
|
3927
|
+
else if (type.kind === "union") {
|
|
3928
|
+
for (const arm of this.unions.get(type.unionId)?.arms ?? [])
|
|
3929
|
+
collect(arm, depth + 1);
|
|
3930
|
+
}
|
|
3931
|
+
else if (type.kind === "record") {
|
|
3932
|
+
for (const field of this.shapes.get(type.shapeId)?.fields ?? [])
|
|
3933
|
+
collect(field.type, depth + 1);
|
|
3934
|
+
}
|
|
3935
|
+
else if (type.kind === "promise")
|
|
3936
|
+
collect(type.inner, depth + 1);
|
|
3937
|
+
};
|
|
3938
|
+
const target = (type) => {
|
|
3939
|
+
// An unanswerable target may be any array.
|
|
3940
|
+
if (type === null)
|
|
3941
|
+
keys.add("*");
|
|
3942
|
+
else if (type)
|
|
3943
|
+
collect(this.mapTypeOf(type), 0);
|
|
3944
|
+
};
|
|
3945
|
+
const visit = (node) => {
|
|
3946
|
+
if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && dynamicValue(node.expression))
|
|
3947
|
+
target(typeOrNull(node));
|
|
3948
|
+
else if (ts.isVariableDeclaration(node) &&
|
|
3949
|
+
node.type &&
|
|
3950
|
+
node.initializer &&
|
|
3951
|
+
dynamicValue(node.initializer))
|
|
3952
|
+
target(typeOrNull(node.name));
|
|
3953
|
+
else if (ts.isBinaryExpression(node) &&
|
|
3954
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
3955
|
+
dynamicValue(node.right))
|
|
3956
|
+
target(typeOrNull(node.left));
|
|
3957
|
+
else if (ts.isReturnStatement(node) && node.expression && dynamicValue(node.expression))
|
|
3958
|
+
target(contextualOrNull(node.expression));
|
|
3959
|
+
else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
3960
|
+
for (const arg of node.arguments ?? [])
|
|
3961
|
+
if (!ts.isSpreadElement(arg) && dynamicValue(arg))
|
|
3962
|
+
target(contextualOrNull(arg));
|
|
3963
|
+
}
|
|
3964
|
+
else if (ts.isPropertyAssignment(node) && dynamicValue(node.initializer))
|
|
3965
|
+
target(contextualOrNull(node.initializer));
|
|
3966
|
+
else if (ts.isArrayLiteralExpression(node)) {
|
|
3967
|
+
for (const element of node.elements)
|
|
3968
|
+
if (!ts.isSpreadElement(element) &&
|
|
3969
|
+
!ts.isOmittedExpression(element) &&
|
|
3970
|
+
dynamicValue(element))
|
|
3971
|
+
target(contextualOrNull(element));
|
|
3972
|
+
}
|
|
3973
|
+
else if (ts.isArrowFunction(node) &&
|
|
3974
|
+
!ts.isBlock(node.body) &&
|
|
3975
|
+
dynamicValue(node.body))
|
|
3976
|
+
target(contextualOrNull(node.body));
|
|
3977
|
+
};
|
|
3978
|
+
for (const sf of sourceFiles)
|
|
3979
|
+
ts.walkPreorder(sf, (node) => {
|
|
3980
|
+
// A checker panic hides this conversion's target: any array.
|
|
3981
|
+
if (!panicSafe(() => (visit(node), true), false))
|
|
3982
|
+
keys.add("*");
|
|
3983
|
+
return undefined;
|
|
3984
|
+
});
|
|
3985
|
+
return keys;
|
|
3986
|
+
};
|
|
3987
|
+
const elementStates = new ArrayElementStates({
|
|
3988
|
+
elementAdmitsUndefined: (key) => optionalElementKeys.has(key),
|
|
3989
|
+
ownerOf: (node) => ownership.ownerOf(node),
|
|
3990
|
+
allocationOwner: (node) => ownership.allocationOwner(node),
|
|
3991
|
+
arrayKeys: arrayKeysOf,
|
|
3992
|
+
mayBeUndefined: (node) => {
|
|
3993
|
+
if (explicitlyNonNull(node))
|
|
3994
|
+
return false;
|
|
3995
|
+
if (mayBeOptional(node))
|
|
3996
|
+
return true;
|
|
3997
|
+
// `undefined as unknown as T` keeps the undefined value.
|
|
3998
|
+
for (let inner = node; ts.isParenthesizedExpression(inner) ||
|
|
3999
|
+
ts.isAsExpression(inner) ||
|
|
4000
|
+
ts.isTypeAssertion(inner) ||
|
|
4001
|
+
ts.isSatisfiesExpression(inner);) {
|
|
4002
|
+
inner = inner.expression;
|
|
4003
|
+
const type = typeOrNull(inner);
|
|
4004
|
+
if (type === null || (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0)
|
|
4005
|
+
return true;
|
|
4006
|
+
}
|
|
4007
|
+
const e = peel(node);
|
|
4008
|
+
if (!ts.isIdentifier(e))
|
|
4009
|
+
return false;
|
|
4010
|
+
const symbol = symbolOf(e);
|
|
4011
|
+
return symbol !== null && iterationSymbols.has(symbol) && !provenPresent(e, symbol);
|
|
4012
|
+
},
|
|
4013
|
+
callbackReturnsUndefined: (node) => callbackReturnsOptional(node),
|
|
4014
|
+
isGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
4015
|
+
isString: (node) => {
|
|
4016
|
+
const type = typeOrNull(node);
|
|
4017
|
+
return type !== null && this.mapTypeOf(type)?.kind === "string";
|
|
4018
|
+
},
|
|
4019
|
+
isArrayLikeObject: (node) => {
|
|
4020
|
+
if (ts.isObjectLiteralExpression(peel(node)))
|
|
4021
|
+
return true;
|
|
4022
|
+
const type = typeOrNull(node);
|
|
4023
|
+
const mapped = type === null ? null : this.mapTypeOf(type);
|
|
4024
|
+
return (mapped === null ||
|
|
4025
|
+
mapped.kind === "record" ||
|
|
4026
|
+
mapped.kind === "object" ||
|
|
4027
|
+
mapped.kind === "dyn" ||
|
|
4028
|
+
mapped.kind === "jsval" ||
|
|
4029
|
+
mapped.kind === "union");
|
|
4030
|
+
},
|
|
4031
|
+
restKeys: (call) => {
|
|
4032
|
+
const callee = peel(call.expression);
|
|
4033
|
+
if (!ts.isIdentifier(callee) && !ts.isPropertyAccessExpression(callee))
|
|
4034
|
+
return null;
|
|
4035
|
+
const symbol = callableSymbolOf(callee);
|
|
4036
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4037
|
+
if (!sig)
|
|
4038
|
+
return ts.isPropertyAccessExpression(callee) ? [] : null;
|
|
4039
|
+
const rest = sig.params.find((shape) => shape.mode === "rest");
|
|
4040
|
+
return rest?.type.kind === "array" ? [typeKey(rest.type.elem)] : [];
|
|
4041
|
+
},
|
|
4042
|
+
markIterationBinding: (node, known) => {
|
|
4043
|
+
const symbol = symbolOf(node);
|
|
4044
|
+
if (!symbol)
|
|
4045
|
+
return false;
|
|
4046
|
+
let changed = false;
|
|
4047
|
+
if (!iterationSymbols.has(symbol)) {
|
|
4048
|
+
iterationSymbols.add(symbol);
|
|
4049
|
+
changed = true;
|
|
4050
|
+
}
|
|
4051
|
+
if (known && !holeyIterationSymbols.has(symbol)) {
|
|
4052
|
+
holeyIterationSymbols.add(symbol);
|
|
4053
|
+
// `for (name of xs)` assigns an existing binding, whose own
|
|
4054
|
+
// storage must then hold the loop's undefined too.
|
|
4055
|
+
if (!ts.isVariableDeclaration(node.parent) && !ts.isBindingElement(node.parent))
|
|
4056
|
+
optionalSymbols.add(symbol);
|
|
4057
|
+
changed = true;
|
|
4058
|
+
}
|
|
4059
|
+
return changed;
|
|
4060
|
+
},
|
|
4061
|
+
note: provenance
|
|
4062
|
+
? (fact, key, site) => provenance.note(`element-${fact}`, site, site, key)
|
|
4063
|
+
: null,
|
|
4064
|
+
});
|
|
4065
|
+
// Each pass revisits every array callback site; the last visit holds
|
|
4066
|
+
// the decision at the fixed point.
|
|
4067
|
+
const presentSites = new Map();
|
|
4068
|
+
// The same for each for-of loop over an array.
|
|
4069
|
+
const presentLoops = new Map();
|
|
3482
4070
|
const scanSites = new Map();
|
|
3483
4071
|
const sitesOf = (sf) => {
|
|
3484
4072
|
const cached = scanSites.get(sf);
|
|
@@ -3488,7 +4076,14 @@ export class Lowerer {
|
|
|
3488
4076
|
ts.walkPreorder(sf, (node) => {
|
|
3489
4077
|
if (ts.isVariableDeclaration(node) ||
|
|
3490
4078
|
ts.isBinaryExpression(node) ||
|
|
3491
|
-
ts.isCallExpression(node)
|
|
4079
|
+
ts.isCallExpression(node) ||
|
|
4080
|
+
ts.isNewExpression(node) ||
|
|
4081
|
+
ts.isArrayLiteralExpression(node) ||
|
|
4082
|
+
ts.isDeleteExpression(node) ||
|
|
4083
|
+
ts.isForOfStatement(node) ||
|
|
4084
|
+
ts.isArrayBindingPattern(node) ||
|
|
4085
|
+
ts.isPrefixUnaryExpression(node) ||
|
|
4086
|
+
ts.isPostfixUnaryExpression(node))
|
|
3492
4087
|
nodes.push(node);
|
|
3493
4088
|
});
|
|
3494
4089
|
scanSites.set(sf, nodes);
|
|
@@ -3497,6 +4092,10 @@ export class Lowerer {
|
|
|
3497
4092
|
const scanFile = (sf) => {
|
|
3498
4093
|
let changed = false;
|
|
3499
4094
|
const scan = (node) => {
|
|
4095
|
+
if (elementStates.scan(node))
|
|
4096
|
+
changed = true;
|
|
4097
|
+
if (ts.isForOfStatement(node))
|
|
4098
|
+
presentLoops.set(node, !elementStates.loopMayYieldUndefined(node));
|
|
3500
4099
|
if (ts.isVariableDeclaration(node)) {
|
|
3501
4100
|
if (node.initializer) {
|
|
3502
4101
|
const result = optionalPrimitiveResultType(node.initializer);
|
|
@@ -3524,6 +4123,16 @@ export class Lowerer {
|
|
|
3524
4123
|
changed = true;
|
|
3525
4124
|
}
|
|
3526
4125
|
}
|
|
4126
|
+
else if (ts.isIdentifier(node.name)) {
|
|
4127
|
+
const assertion = forwardedAssertion(node.initializer);
|
|
4128
|
+
const symbol = assertion ? symbolOf(node.name) : null;
|
|
4129
|
+
if (assertion && symbol && !optionalSymbols.has(symbol)) {
|
|
4130
|
+
optionalSymbols.add(symbol);
|
|
4131
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4132
|
+
provenance?.note("asserted-binding", node.name, node.initializer);
|
|
4133
|
+
changed = true;
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
3527
4136
|
if (ts.isIdentifier(node.name)) {
|
|
3528
4137
|
const valueSymbol = ts.isIdentifier(peel(node.initializer))
|
|
3529
4138
|
? symbolOf(peel(node.initializer))
|
|
@@ -3565,6 +4174,22 @@ export class Lowerer {
|
|
|
3565
4174
|
}
|
|
3566
4175
|
if (scanPattern(node.name, node.initializer))
|
|
3567
4176
|
changed = true;
|
|
4177
|
+
if (ts.isIdentifier(node.name)) {
|
|
4178
|
+
const symbol = symbolOf(node.name);
|
|
4179
|
+
const init = peel(node.initializer);
|
|
4180
|
+
if (symbol && noteFieldsFrom(symbol, init)) {
|
|
4181
|
+
provenance?.note("record-copy", node.name, node.initializer);
|
|
4182
|
+
changed = true;
|
|
4183
|
+
}
|
|
4184
|
+
if (symbol && ts.isObjectLiteralExpression(init)) {
|
|
4185
|
+
for (const field of spreadFieldsOf(init)) {
|
|
4186
|
+
if (noteFieldSymbol(symbol, field)) {
|
|
4187
|
+
provenance?.note("record-spread", node.name, node.initializer);
|
|
4188
|
+
changed = true;
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
3568
4193
|
if (ts.isIdentifier(node.name) &&
|
|
3569
4194
|
ts.isObjectLiteralExpression(peel(node.initializer))) {
|
|
3570
4195
|
const object = peel(node.initializer);
|
|
@@ -3586,6 +4211,36 @@ export class Lowerer {
|
|
|
3586
4211
|
}
|
|
3587
4212
|
}
|
|
3588
4213
|
}
|
|
4214
|
+
if (ts.isBinaryExpression(node) &&
|
|
4215
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4216
|
+
ts.isIdentifier(node.left)) {
|
|
4217
|
+
const symbol = symbolOf(node.left);
|
|
4218
|
+
if (symbol && noteFieldsFrom(symbol, node.right)) {
|
|
4219
|
+
provenance?.note("record-copy", node.left, node.right);
|
|
4220
|
+
changed = true;
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
if (ts.isBinaryExpression(node) &&
|
|
4224
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
4225
|
+
ts.isIdentifier(node.left)) {
|
|
4226
|
+
const assertion = forwardedAssertion(node.right);
|
|
4227
|
+
const symbol = assertion ? symbolOf(node.left) : null;
|
|
4228
|
+
// Only a variable's storage is promoted (see the final pass).
|
|
4229
|
+
const declaration = symbol ? this.checker.valueDeclarationOf(symbol) : undefined;
|
|
4230
|
+
if (assertion &&
|
|
4231
|
+
symbol &&
|
|
4232
|
+
declaration &&
|
|
4233
|
+
ts.isVariableDeclaration(declaration) &&
|
|
4234
|
+
ts.isIdentifier(declaration.name) &&
|
|
4235
|
+
!this.runtimeOptionalAssertions.has(assertion)) {
|
|
4236
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4237
|
+
if (!optionalSymbols.has(symbol)) {
|
|
4238
|
+
optionalSymbols.add(symbol);
|
|
4239
|
+
provenance?.note("asserted-binding", node.left, node.right);
|
|
4240
|
+
}
|
|
4241
|
+
changed = true;
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
3589
4244
|
if (ts.isBinaryExpression(node) &&
|
|
3590
4245
|
(node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
|
|
3591
4246
|
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
@@ -3655,11 +4310,25 @@ export class Lowerer {
|
|
|
3655
4310
|
}
|
|
3656
4311
|
if (nativeArrayReceiver) {
|
|
3657
4312
|
provenanceSite = node;
|
|
3658
|
-
if (promoteHofCallback(callback, callbackIndices))
|
|
3659
|
-
changed = true;
|
|
3660
4313
|
const method = node.expression.name.text;
|
|
4314
|
+
// Callbacks skip holes (except the find family), so an array
|
|
4315
|
+
// that never holds undefined only passes present elements.
|
|
4316
|
+
const reduce = method === "reduce" || method === "reduceRight";
|
|
4317
|
+
const elementsPresent = receiver?.kind === "array" &&
|
|
4318
|
+
elementStates.elementsPresent(receiverNode, method) &&
|
|
4319
|
+
callbackTakesPresentElement(callback, receiver.elem, reduce ? 1 : 0);
|
|
4320
|
+
// A callback returning a runtime-absent value still needs its
|
|
4321
|
+
// widened return, whatever its elements.
|
|
4322
|
+
if (promoteHofCallback(callback, elementsPresent ? [] : callbackIndices))
|
|
4323
|
+
changed = true;
|
|
4324
|
+
// Element-typed callback helpers cover every method but
|
|
4325
|
+
// reduce, whose accumulator keeps the split visit.
|
|
4326
|
+
presentSites.set(node, elementsPresent && !reduce);
|
|
4327
|
+
if (elementsPresent && reduce && retypeInlineCallback(callback, callbackIndices))
|
|
4328
|
+
changed = true;
|
|
3661
4329
|
if ((method === "reduce" || method === "reduceRight") &&
|
|
3662
|
-
(node.arguments.length < 2 ||
|
|
4330
|
+
((!elementsPresent && node.arguments.length < 2) ||
|
|
4331
|
+
callbackReturnsOptional(callback))) {
|
|
3663
4332
|
if (promoteHofCallback(callback, [0, 1]))
|
|
3664
4333
|
changed = true;
|
|
3665
4334
|
const result = this.runtimeOptionalType(this.irTypeOf(node));
|
|
@@ -3678,8 +4347,34 @@ export class Lowerer {
|
|
|
3678
4347
|
if (!symbol)
|
|
3679
4348
|
return;
|
|
3680
4349
|
const sig = signatureBySymbol.get(symbol);
|
|
3681
|
-
if (!sig)
|
|
4350
|
+
if (!sig) {
|
|
4351
|
+
// A direct call of a function-valued const (`const show = (n:
|
|
4352
|
+
// number) => ...`): a possibly missing argument widens that
|
|
4353
|
+
// parameter, as for a callback the same value is passed as.
|
|
4354
|
+
if (!ts.isIdentifier(node.expression) || reassigned.has(symbol))
|
|
4355
|
+
return;
|
|
4356
|
+
const declaration = this.checker.valueDeclarationOf(symbol);
|
|
4357
|
+
const value = declaration && ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
4358
|
+
? peel(declaration.initializer)
|
|
4359
|
+
: null;
|
|
4360
|
+
if (!value || !(ts.isArrowFunction(value) || ts.isFunctionExpression(value)))
|
|
4361
|
+
return;
|
|
4362
|
+
// Optional, defaulted and rest parameters keep their own ABI.
|
|
4363
|
+
if (value.parameters.some((parameter) => parameter.questionToken !== undefined ||
|
|
4364
|
+
parameter.initializer !== undefined ||
|
|
4365
|
+
parameter.dotDotDotToken !== undefined))
|
|
4366
|
+
return;
|
|
4367
|
+
const indices = node.arguments.flatMap((arg, i) => !ts.isSpreadElement(arg) &&
|
|
4368
|
+
value.parameters[i] !== undefined &&
|
|
4369
|
+
!value.parameters[i].dotDotDotToken &&
|
|
4370
|
+
mayBeOptional(arg)
|
|
4371
|
+
? [i]
|
|
4372
|
+
: []);
|
|
4373
|
+
provenanceSite = node;
|
|
4374
|
+
if (indices.length > 0 && promoteHofCallback(node.expression, indices))
|
|
4375
|
+
changed = true;
|
|
3682
4376
|
return;
|
|
4377
|
+
}
|
|
3683
4378
|
// JSDoc describes the usual value, not a runtime arity check.
|
|
3684
4379
|
// A short JavaScript call still passes undefined. Widen only the
|
|
3685
4380
|
// observed slots before bodies lower so both caller and callee
|
|
@@ -3736,8 +4431,57 @@ export class Lowerer {
|
|
|
3736
4431
|
changed = true;
|
|
3737
4432
|
}
|
|
3738
4433
|
}
|
|
3739
|
-
if (ts.isSpreadElement(arg)
|
|
4434
|
+
if (!ts.isSpreadElement(arg) && shape && i !== restAt && fieldsOfValue(arg)) {
|
|
4435
|
+
const fields = fieldsOfValue(arg);
|
|
4436
|
+
for (const target of familyBySymbol.get(symbol) ?? [symbol]) {
|
|
4437
|
+
const parameterShape = signatureBySymbol.get(target)?.params[i];
|
|
4438
|
+
const parameter = functionDeclBySymbol.get(target)?.parameters[i];
|
|
4439
|
+
if (!parameterShape || parameterShape.type.kind !== "record")
|
|
4440
|
+
continue;
|
|
4441
|
+
if (!parameter || !ts.isIdentifier(parameter.name))
|
|
4442
|
+
continue;
|
|
4443
|
+
const bound = symbolOf(parameter.name);
|
|
4444
|
+
if (!bound)
|
|
4445
|
+
continue;
|
|
4446
|
+
let promoted = parameterShape.type;
|
|
4447
|
+
for (const field of fields) {
|
|
4448
|
+
const source = promoted.kind === "record"
|
|
4449
|
+
? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
|
|
4450
|
+
: undefined;
|
|
4451
|
+
if (source) {
|
|
4452
|
+
promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
|
|
4453
|
+
}
|
|
4454
|
+
if (noteFieldSymbol(bound, field))
|
|
4455
|
+
changed = true;
|
|
4456
|
+
}
|
|
4457
|
+
if (!typeEquals(promoted, parameterShape.type)) {
|
|
4458
|
+
parameterShape.type = promoted;
|
|
4459
|
+
this.runtimeOptionalBindingTypes.set(bound, promoted);
|
|
4460
|
+
provenance?.note("record-parameter", parameter, arg);
|
|
4461
|
+
changed = true;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
if (ts.isSpreadElement(arg) || !shape)
|
|
4466
|
+
return;
|
|
4467
|
+
const assertion = forwardedAssertion(arg) ?? forwardedElementAssertion(arg);
|
|
4468
|
+
if (!assertion && !mayBeOptional(arg))
|
|
3740
4469
|
return;
|
|
4470
|
+
if (assertion) {
|
|
4471
|
+
if (i === restAt)
|
|
4472
|
+
return;
|
|
4473
|
+
// An omittable slot's ABI already holds undefined (an
|
|
4474
|
+
// optional or defaulted parameter applies its own default).
|
|
4475
|
+
if (shape.mode === "omittable") {
|
|
4476
|
+
if (shape.type.kind === "union" &&
|
|
4477
|
+
this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4478
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4479
|
+
return;
|
|
4480
|
+
}
|
|
4481
|
+
if (shape.mode !== "required")
|
|
4482
|
+
return;
|
|
4483
|
+
this.runtimeOptionalAssertions.add(assertion);
|
|
4484
|
+
}
|
|
3741
4485
|
if (i === restAt) {
|
|
3742
4486
|
// A rest binding always receives an array. An unchecked
|
|
3743
4487
|
// argument can make its elements undefined, not the pack.
|
|
@@ -3862,6 +4606,12 @@ export class Lowerer {
|
|
|
3862
4606
|
}
|
|
3863
4607
|
}
|
|
3864
4608
|
}
|
|
4609
|
+
for (const [site, present] of presentSites)
|
|
4610
|
+
if (present)
|
|
4611
|
+
this.presentElementCalls.add(site);
|
|
4612
|
+
for (const [loop, present] of presentLoops)
|
|
4613
|
+
if (present)
|
|
4614
|
+
this.presentElementLoops.add(loop);
|
|
3865
4615
|
const globalsById = new Map(this.globalsList.map((global) => [global.id, global]));
|
|
3866
4616
|
for (const [symbol, field] of staticFieldsBySymbol) {
|
|
3867
4617
|
if (!optionalSymbols.has(symbol))
|
|
@@ -4008,6 +4758,35 @@ export class Lowerer {
|
|
|
4008
4758
|
}
|
|
4009
4759
|
});
|
|
4010
4760
|
}
|
|
4761
|
+
for (const symbol of optionalSymbols)
|
|
4762
|
+
this.runtimeOptionalAssignedSymbols.add(symbol);
|
|
4763
|
+
// `decode(xs[i]!)` into a parameter whose final ABI already holds
|
|
4764
|
+
// undefined: the assertion has no runtime effect, so a missing element
|
|
4765
|
+
// reaches the callee as undefined instead of trapping at the read.
|
|
4766
|
+
for (const sf of sourceFiles) {
|
|
4767
|
+
for (const node of sitesOf(sf)) {
|
|
4768
|
+
if (!ts.isCallExpression(node))
|
|
4769
|
+
continue;
|
|
4770
|
+
if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression))
|
|
4771
|
+
continue;
|
|
4772
|
+
const symbol = callableSymbolOf(node.expression);
|
|
4773
|
+
const sig = symbol ? signatureBySymbol.get(symbol) : undefined;
|
|
4774
|
+
if (!sig || node.arguments.some(ts.isSpreadElement))
|
|
4775
|
+
continue;
|
|
4776
|
+
node.arguments.forEach((arg, i) => {
|
|
4777
|
+
let e = arg;
|
|
4778
|
+
while (ts.isParenthesizedExpression(e))
|
|
4779
|
+
e = e.expression;
|
|
4780
|
+
if (!ts.isNonNullExpression(e) || !isIndexedRead(e.expression))
|
|
4781
|
+
return;
|
|
4782
|
+
const shape = sig.params[i];
|
|
4783
|
+
if (shape?.mode !== "required" && shape?.mode !== "omittable")
|
|
4784
|
+
return;
|
|
4785
|
+
if (shape.type.kind === "union" && this.armTag(shape.type.unionId, UNDEFINED_T) >= 0)
|
|
4786
|
+
this.runtimeOptionalAssertions.add(e);
|
|
4787
|
+
});
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4011
4790
|
provenance?.flush();
|
|
4012
4791
|
}
|
|
4013
4792
|
run() {
|
|
@@ -4058,14 +4837,14 @@ export class Lowerer {
|
|
|
4058
4837
|
if (fn)
|
|
4059
4838
|
functions.push(fn);
|
|
4060
4839
|
else if (this.countsSkips())
|
|
4061
|
-
this.
|
|
4840
|
+
this.countSkippedBody(decl);
|
|
4062
4841
|
}
|
|
4063
4842
|
for (const decl of fp.classDecls) {
|
|
4064
4843
|
const info = this.classes.get(this.classNamer(decl));
|
|
4065
4844
|
if (info)
|
|
4066
4845
|
functions.push(...this.lowerClassMembers(info));
|
|
4067
4846
|
else if (this.countsSkips())
|
|
4068
|
-
this.
|
|
4847
|
+
this.countSkippedBody(decl);
|
|
4069
4848
|
}
|
|
4070
4849
|
}
|
|
4071
4850
|
// Each file's top-level statements form its run-once init function.
|
|
@@ -4153,10 +4932,10 @@ export class Lowerer {
|
|
|
4153
4932
|
// Deferred collection diagnostics nothing flushed — declarations no
|
|
4154
4933
|
// reference ever made relevant. They belong to the unreached group
|
|
4155
4934
|
// (collection order keeps them deterministic).
|
|
4156
|
-
for (const [symbol, diags] of this.deferredDiags) {
|
|
4935
|
+
for (const [symbol, diags] of [...this.deferredDiags]) {
|
|
4157
4936
|
if (this.alreadyFlushed.has(symbol))
|
|
4158
4937
|
continue;
|
|
4159
|
-
for (const d of diags)
|
|
4938
|
+
for (const d of [...diags])
|
|
4160
4939
|
this.pushDiag(d);
|
|
4161
4940
|
}
|
|
4162
4941
|
return {
|
|
@@ -4277,6 +5056,7 @@ export class Lowerer {
|
|
|
4277
5056
|
...(this.npmEmbedded ? { embedded: this.npmEmbedded } : {}),
|
|
4278
5057
|
entry: ENTRY_NAME,
|
|
4279
5058
|
...(this.usesWorkers ? { workers: true } : {}),
|
|
5059
|
+
...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }),
|
|
4280
5060
|
...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}),
|
|
4281
5061
|
};
|
|
4282
5062
|
if (module)
|
|
@@ -4298,41 +5078,72 @@ export class Lowerer {
|
|
|
4298
5078
|
* registered ClassInfo — the shape of a JS class whose collection fenced.
|
|
4299
5079
|
* Used by run()'s global pruning; shapes/unions recurse with a seen-set
|
|
4300
5080
|
* (interned ids can nest). */
|
|
4301
|
-
typeNamesUnregisteredClass(t
|
|
5081
|
+
typeNamesUnregisteredClass(t) {
|
|
5082
|
+
return this.unregisteredClassOf(t, new Set()) !== null;
|
|
5083
|
+
}
|
|
5084
|
+
/** The first class name typeNamesUnregisteredClass finds in `t`. */
|
|
5085
|
+
unregisteredClassOf(t, seen) {
|
|
5086
|
+
const first = (types) => {
|
|
5087
|
+
for (const type of types) {
|
|
5088
|
+
const name = this.unregisteredClassOf(type, seen);
|
|
5089
|
+
if (name !== null)
|
|
5090
|
+
return name;
|
|
5091
|
+
}
|
|
5092
|
+
return null;
|
|
5093
|
+
};
|
|
4302
5094
|
switch (t.kind) {
|
|
4303
5095
|
case "object":
|
|
4304
|
-
return
|
|
5096
|
+
return this.classes.has(t.className) ? null : t.className;
|
|
4305
5097
|
case "array":
|
|
4306
5098
|
case "set":
|
|
4307
|
-
return
|
|
5099
|
+
return first([t.elem]);
|
|
4308
5100
|
case "map":
|
|
4309
|
-
return (
|
|
4310
|
-
this.typeNamesUnregisteredClass(t.value, seen));
|
|
5101
|
+
return first([t.key, t.value]);
|
|
4311
5102
|
case "promise":
|
|
4312
|
-
return
|
|
5103
|
+
return first([t.inner]);
|
|
4313
5104
|
case "func":
|
|
4314
|
-
return (t.params
|
|
4315
|
-
this.typeNamesUnregisteredClass(t.ret, seen));
|
|
5105
|
+
return first([...t.params, t.ret]);
|
|
4316
5106
|
case "record": {
|
|
4317
5107
|
if (seen.has(t.shapeId))
|
|
4318
|
-
return
|
|
5108
|
+
return null;
|
|
4319
5109
|
seen.add(t.shapeId);
|
|
4320
5110
|
const shape = this.shapes.get(t.shapeId);
|
|
4321
5111
|
if (!shape)
|
|
4322
|
-
return
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
5112
|
+
return null;
|
|
5113
|
+
return first([
|
|
5114
|
+
...(shape.indexValue ? [shape.indexValue] : []),
|
|
5115
|
+
...shape.fields.map((f) => f.type),
|
|
5116
|
+
]);
|
|
4326
5117
|
}
|
|
4327
5118
|
case "union": {
|
|
4328
5119
|
if (seen.has(t.unionId))
|
|
4329
|
-
return
|
|
5120
|
+
return null;
|
|
4330
5121
|
seen.add(t.unionId);
|
|
4331
5122
|
const def = this.unions.get(t.unionId);
|
|
4332
|
-
return
|
|
5123
|
+
return def ? first(def.arms) : null;
|
|
4333
5124
|
}
|
|
4334
5125
|
default:
|
|
4335
|
-
return
|
|
5126
|
+
return null;
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
/** A declaration whose signature blocked never lowers its body, so its
|
|
5130
|
+
* statements would otherwise vanish from the coverage totals and the
|
|
5131
|
+
* percentage would overstate how much of the program compiles. Count
|
|
5132
|
+
* every statement the body holds as attempted and failed, in addition to
|
|
5133
|
+
* the skipped-function tally. */
|
|
5134
|
+
countSkippedBody(decl) {
|
|
5135
|
+
this.stats.functionsSkipped++;
|
|
5136
|
+
if (this.suppressStats)
|
|
5137
|
+
return;
|
|
5138
|
+
const n = countBodyStatements(decl);
|
|
5139
|
+
if (n === 0)
|
|
5140
|
+
return;
|
|
5141
|
+
this.stats.statementsTotal += n;
|
|
5142
|
+
this.stats.statementsFailed += n;
|
|
5143
|
+
const file = decl.getSourceFile().fileName;
|
|
5144
|
+
for (let i = 0; i < n; i++) {
|
|
5145
|
+
this.bumpFileStat(file, "total");
|
|
5146
|
+
this.bumpFileStat(file, "failed");
|
|
4336
5147
|
}
|
|
4337
5148
|
}
|
|
4338
5149
|
/** Whether run() counts a signature-blocked declaration in
|
|
@@ -4868,13 +5679,18 @@ export class Lowerer {
|
|
|
4868
5679
|
const d = this.instantiationContext
|
|
4869
5680
|
? { ...diag, message: `${diag.message} (${this.instantiationContext})` }
|
|
4870
5681
|
: diag;
|
|
5682
|
+
const pending = this.pendingFlushes.get(diag);
|
|
4871
5683
|
// Deferred collection: the wrapper decides whether these ever report
|
|
4872
5684
|
// (a reference flushes them; unreferenced declarations stay silent in
|
|
4873
5685
|
// builds and report under coverage's unreached group).
|
|
4874
5686
|
if (this.diagSink) {
|
|
5687
|
+
if (pending && d !== diag)
|
|
5688
|
+
this.pendingFlushes.set(d, pending);
|
|
4875
5689
|
this.diagSink.push(d);
|
|
4876
5690
|
return;
|
|
4877
5691
|
}
|
|
5692
|
+
if (pending)
|
|
5693
|
+
this.commitFlush(pending.symbol, pending.source);
|
|
4878
5694
|
// One site, one report: some declarations map a type twice (module
|
|
4879
5695
|
// globals pre-register before their initializers lower) — an exact
|
|
4880
5696
|
// duplicate (code + span + message) adds noise, not information.
|
|
@@ -5518,6 +6334,24 @@ export class Lowerer {
|
|
|
5518
6334
|
this.pushDiag(recordShapeMismatchDiag(this.fmt(expected), this.fmt(actual), locOf(node), detail));
|
|
5519
6335
|
throw new PoisonError();
|
|
5520
6336
|
}
|
|
6337
|
+
// Instances of two unrelated classes: class identity is nominal here,
|
|
6338
|
+
// so a structurally compatible instance never becomes the other class.
|
|
6339
|
+
// An assertion (`x as Y`) names the mistake most directly.
|
|
6340
|
+
if (actual.kind === "object" &&
|
|
6341
|
+
expected.kind === "object" &&
|
|
6342
|
+
this.classes.has(actual.className) &&
|
|
6343
|
+
this.classes.has(expected.className) &&
|
|
6344
|
+
this.classes.get(actual.className).decl !== this.classes.get(expected.className).decl &&
|
|
6345
|
+
!this.isSubclassOf(actual.className, expected.className) &&
|
|
6346
|
+
!this.isSubclassOf(expected.className, actual.className)) {
|
|
6347
|
+
let site = node;
|
|
6348
|
+
while (ts.isParenthesizedExpression(site))
|
|
6349
|
+
site = site.expression;
|
|
6350
|
+
const assertion = ts.isAsExpression(site) || ts.isTypeAssertion(site);
|
|
6351
|
+
this.unsupported("SC1090", node, `${assertion ? "asserting" : "using"} a '${this.fmt(actual)}' instance as the unrelated class '${this.fmt(expected)}' (neither class extends the other, and class identity is nominal in compiled code)`, assertion
|
|
6352
|
+
? `narrow with \`instanceof ${this.fmt(expected)}\` where the value can really be one, or declare a shared base class and assert through it`
|
|
6353
|
+
: `make one class extend the other, or construct a '${this.fmt(expected)}' from the value's fields`);
|
|
6354
|
+
}
|
|
5521
6355
|
// Iterator-typed slots hold generators and built-in collection
|
|
5522
6356
|
// iterators; any other iterator object (a user class) stays fenced.
|
|
5523
6357
|
if (expected.kind === "generator" && actual.kind !== "generator") {
|
|
@@ -5601,6 +6435,13 @@ export class Lowerer {
|
|
|
5601
6435
|
* else (including a DIFFERENT union) is left for requireExactShape, which
|
|
5602
6436
|
* rejects union mismatches with SC2003. */
|
|
5603
6437
|
coerceToExpected(expr, expected) {
|
|
6438
|
+
// A destination that holds undefined takes an unassigned deferred-init
|
|
6439
|
+
// field's slot as is (Node passes the undefined along).
|
|
6440
|
+
if (expected.kind === "union" && this.armTag(expected.unionId, UNDEFINED_T) >= 0) {
|
|
6441
|
+
const slot = this.deferredFieldSlot(expr);
|
|
6442
|
+
if (slot)
|
|
6443
|
+
expr = slot;
|
|
6444
|
+
}
|
|
5604
6445
|
if (expr.type.kind === "void" && expected.kind !== "void" && this.neverValued.has(expr))
|
|
5605
6446
|
return this.divergentValue(expr, expected);
|
|
5606
6447
|
// Iterator-typed slots: native iterators and differently typed
|
|
@@ -5759,9 +6600,18 @@ export class Lowerer {
|
|
|
5759
6600
|
};
|
|
5760
6601
|
}
|
|
5761
6602
|
if (expr.kind === "unitLit" || this.dynConvertible(expr.type)) {
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
6603
|
+
// Records and arrays keep their identity as untyped values, including
|
|
6604
|
+
// when they arrive through a union, so `unknown` views, weak
|
|
6605
|
+
// collection keys and later typed reads all see the same object.
|
|
6606
|
+
const liveReference = (type) => {
|
|
6607
|
+
if (type.kind === "array")
|
|
6608
|
+
return true;
|
|
6609
|
+
const shape = type.kind === "record" ? this.shapes.get(type.shapeId) : undefined;
|
|
6610
|
+
return !!shape && (shape.fields.length > 0 || shape.indexValue !== undefined);
|
|
6611
|
+
};
|
|
6612
|
+
const liveRef = liveReference(expr.type) ||
|
|
6613
|
+
(expr.type.kind === "union" &&
|
|
6614
|
+
(this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
|
|
5765
6615
|
return {
|
|
5766
6616
|
kind: "dynFrom",
|
|
5767
6617
|
value: expr,
|
|
@@ -5955,7 +6805,7 @@ export class Lowerer {
|
|
|
5955
6805
|
this.armTag(expr.type.unionId, expected) >= 0) {
|
|
5956
6806
|
const helper = this.narrowedArmHelper(expr.type.unionId, expected, expr.loc);
|
|
5957
6807
|
if (helper) {
|
|
5958
|
-
return
|
|
6808
|
+
return this.checkedNarrowCall(helper, expr, expected, expr.loc);
|
|
5959
6809
|
}
|
|
5960
6810
|
}
|
|
5961
6811
|
if (!typeEquals(expr.type, expected)) {
|
|
@@ -6206,6 +7056,9 @@ export class Lowerer {
|
|
|
6206
7056
|
narrowedArmHelper(fromId, target, loc) {
|
|
6207
7057
|
return narrowedArmHelper(this, fromId, target, loc);
|
|
6208
7058
|
}
|
|
7059
|
+
checkedNarrowCall(helper, value, type, loc) {
|
|
7060
|
+
return checkedNarrowCall(this, helper, value, type, loc);
|
|
7061
|
+
}
|
|
6209
7062
|
deferredReadHelper(fromId, target, loc) {
|
|
6210
7063
|
return deferredReadHelper(this, fromId, target, loc);
|
|
6211
7064
|
}
|
|
@@ -7024,15 +7877,47 @@ export class Lowerer {
|
|
|
7024
7877
|
flushDeferred(symbol) {
|
|
7025
7878
|
if (this.collecting)
|
|
7026
7879
|
return;
|
|
7880
|
+
this.reportDeferred(symbol);
|
|
7881
|
+
}
|
|
7882
|
+
/** flushDeferred without the collection-pass guard: for declarations
|
|
7883
|
+
* whose diagnostics must report eagerly (class statements with
|
|
7884
|
+
* declaration-time code, extended bases — collectClassShapeDeferring). */
|
|
7885
|
+
reportDeferred(symbol) {
|
|
7027
7886
|
const diags = this.deferredDiags.get(symbol);
|
|
7028
7887
|
if (!diags)
|
|
7029
7888
|
return;
|
|
7030
|
-
this.
|
|
7031
|
-
|
|
7032
|
-
return;
|
|
7033
|
-
|
|
7034
|
-
|
|
7889
|
+
if (this.alreadyFlushed.has(symbol)) {
|
|
7890
|
+
this.deferredDiags.delete(symbol); // the emit pass reported these
|
|
7891
|
+
return;
|
|
7892
|
+
}
|
|
7893
|
+
if (this.diagSink === null) {
|
|
7894
|
+
this.deferredDiags.delete(symbol);
|
|
7895
|
+
this.flushedSymbols.add(symbol);
|
|
7896
|
+
for (const d of diags)
|
|
7897
|
+
this.pushDiag(d);
|
|
7898
|
+
return;
|
|
7899
|
+
}
|
|
7900
|
+
// A capture buffer is active (a probe, or another declaration's
|
|
7901
|
+
// deferred collection): the copies ride that buffer, and the
|
|
7902
|
+
// declaration stays deferred until one of them reaches the build.
|
|
7903
|
+
for (const d of diags) {
|
|
7904
|
+
this.pendingFlushes.set(d, { symbol, source: d });
|
|
7035
7905
|
this.pushDiag(d);
|
|
7906
|
+
}
|
|
7907
|
+
}
|
|
7908
|
+
/** A flushed copy of a deferred diagnostic reached `diags`: that
|
|
7909
|
+
* diagnostic is reported, so it leaves the declaration's deferred list
|
|
7910
|
+
* (the declaration counts as flushed for the coverage remainder). */
|
|
7911
|
+
commitFlush(symbol, source) {
|
|
7912
|
+
this.flushedSymbols.add(symbol);
|
|
7913
|
+
const list = this.deferredDiags.get(symbol);
|
|
7914
|
+
if (!list)
|
|
7915
|
+
return;
|
|
7916
|
+
const index = list.indexOf(source);
|
|
7917
|
+
if (index >= 0)
|
|
7918
|
+
list.splice(index, 1);
|
|
7919
|
+
if (list.length === 0)
|
|
7920
|
+
this.deferredDiags.delete(symbol);
|
|
7036
7921
|
}
|
|
7037
7922
|
flushDeferredClass(className) {
|
|
7038
7923
|
const symbol = this.deferredClassByName.get(className);
|
|
@@ -8111,13 +8996,7 @@ export class Lowerer {
|
|
|
8111
8996
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
|
|
8112
8997
|
if (!helper)
|
|
8113
8998
|
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));
|
|
8999
|
+
return this.withExpressionOverride(stmt.expression, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(stmt.expression)), () => lowerForOf(this, stmt));
|
|
8121
9000
|
}
|
|
8122
9001
|
lowerForStatement(stmt) {
|
|
8123
9002
|
return lowerForStatement(this, stmt);
|
|
@@ -8362,21 +9241,21 @@ export class Lowerer {
|
|
|
8362
9241
|
const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
|
|
8363
9242
|
if (!helper)
|
|
8364
9243
|
return lower();
|
|
8365
|
-
right =
|
|
8366
|
-
kind: "call",
|
|
8367
|
-
callee: helper,
|
|
8368
|
-
args: [optionalRight.value],
|
|
8369
|
-
type: optionalRight.present,
|
|
8370
|
-
loc: locOf(expr.right),
|
|
8371
|
-
};
|
|
9244
|
+
right = this.checkedNarrowCall(helper, optionalRight.value, optionalRight.present, locOf(expr.right));
|
|
8372
9245
|
}
|
|
8373
9246
|
else {
|
|
8374
9247
|
right = this.lowerExpr(expr.right);
|
|
8375
9248
|
}
|
|
8376
9249
|
if (right.type.kind !== "classval")
|
|
8377
9250
|
return lower();
|
|
8378
|
-
|
|
8379
|
-
|
|
9251
|
+
// A capture-free class reference is the immortal static class object:
|
|
9252
|
+
// re-reading it is pure, so it needs no owned temporary (whose retain
|
|
9253
|
+
// and release would be out-of-line calls on every test).
|
|
9254
|
+
const constantRight = right.kind === "classRef" && right.captures === undefined;
|
|
9255
|
+
const stableRight = constantRight
|
|
9256
|
+
? null
|
|
9257
|
+
: this.declareHiddenLocal("%instanceofRight", right.type);
|
|
9258
|
+
const rightValue = stableRight ? varRef(stableRight.id, right.type, locOf(expr.right)) : right;
|
|
8380
9259
|
const body = this.withExpressionOverride(expr.left, {
|
|
8381
9260
|
kind: "unionNarrow",
|
|
8382
9261
|
unionId: left.type.unionId,
|
|
@@ -8389,7 +9268,16 @@ export class Lowerer {
|
|
|
8389
9268
|
kind: "seqExpr",
|
|
8390
9269
|
stmts: [
|
|
8391
9270
|
{ kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
|
|
8392
|
-
|
|
9271
|
+
...(stableRight
|
|
9272
|
+
? [
|
|
9273
|
+
{
|
|
9274
|
+
kind: "varDecl",
|
|
9275
|
+
localId: stableRight.id,
|
|
9276
|
+
init: right,
|
|
9277
|
+
loc: locOf(expr.right),
|
|
9278
|
+
},
|
|
9279
|
+
]
|
|
9280
|
+
: []),
|
|
8393
9281
|
],
|
|
8394
9282
|
result: {
|
|
8395
9283
|
kind: "ternary",
|
|
@@ -8420,13 +9308,7 @@ export class Lowerer {
|
|
|
8420
9308
|
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
|
|
8421
9309
|
if (!helper)
|
|
8422
9310
|
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);
|
|
9311
|
+
return this.withExpressionOverride(node, this.checkedNarrowCall(helper, optional.value, optional.present, locOf(node)), lower);
|
|
8430
9312
|
}
|
|
8431
9313
|
withExpressionOverride(node, value, lower) {
|
|
8432
9314
|
const previous = this.chainRecvByNode.get(node);
|
|
@@ -8535,6 +9417,37 @@ export class Lowerer {
|
|
|
8535
9417
|
fieldGetExpr(target, loc, blame) {
|
|
8536
9418
|
return fieldGetExpr(this, target, loc, blame);
|
|
8537
9419
|
}
|
|
9420
|
+
/** The undefined-armed slot read behind a checked deferred-init field
|
|
9421
|
+
* read, or null. Consumers that observe undefined itself (equality,
|
|
9422
|
+
* typeof, truthiness, nullish defaults) test the slot instead of the
|
|
9423
|
+
* checked extraction, which throws for an unassigned field. */
|
|
9424
|
+
deferredFieldSlot(value) {
|
|
9425
|
+
// The inline form: `slot is undefined ? throw : narrow(slot)`.
|
|
9426
|
+
if (value.kind === "ternary" &&
|
|
9427
|
+
value.cond.kind === "unionIsTag" &&
|
|
9428
|
+
value.else_.kind === "unionNarrow" &&
|
|
9429
|
+
value.else_.value === value.cond.value &&
|
|
9430
|
+
value.then.kind === "libCall" &&
|
|
9431
|
+
value.then.fn === "error.nodeThrow") {
|
|
9432
|
+
const read = value.cond.value;
|
|
9433
|
+
return read.kind === "fieldGet" &&
|
|
9434
|
+
read.type.kind === "union" &&
|
|
9435
|
+
this.classes.get(read.className)?.deferredInitFields?.has(read.field) === true
|
|
9436
|
+
? read
|
|
9437
|
+
: null;
|
|
9438
|
+
}
|
|
9439
|
+
if (value.kind !== "call" || value.args.length !== 1)
|
|
9440
|
+
return null;
|
|
9441
|
+
const read = value.args[0];
|
|
9442
|
+
if (read.kind !== "fieldGet" || read.type.kind !== "union")
|
|
9443
|
+
return null;
|
|
9444
|
+
if (this.classes.get(read.className)?.deferredInitFields?.has(read.field) !== true)
|
|
9445
|
+
return null;
|
|
9446
|
+
return this.coercions.checkedNarrows.has(value.callee) ||
|
|
9447
|
+
value.callee.startsWith("%deferred.read.")
|
|
9448
|
+
? read
|
|
9449
|
+
: null;
|
|
9450
|
+
}
|
|
8538
9451
|
fieldSetStmt(target, value, loc, blame) {
|
|
8539
9452
|
return fieldSetStmt(this, target, value, loc, blame);
|
|
8540
9453
|
}
|
|
@@ -9218,8 +10131,8 @@ export class Lowerer {
|
|
|
9218
10131
|
return lowerNumberStaticProperty(this, expr);
|
|
9219
10132
|
}
|
|
9220
10133
|
/* ── the island-backed ambient surface (ISLAND_SURFACE) ───────────── */
|
|
9221
|
-
requireDynamicApi(feature, node) {
|
|
9222
|
-
return requireDynamicApi(this, feature, node);
|
|
10134
|
+
requireDynamicApi(feature, node, hint) {
|
|
10135
|
+
return requireDynamicApi(this, feature, node, hint);
|
|
9223
10136
|
}
|
|
9224
10137
|
lowerMathProperty(expr) {
|
|
9225
10138
|
return lowerMathProperty(this, expr);
|