@scriptc/compiler 0.2.8 → 0.2.10
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 +1 -0
- package/dist/backend/cycle-analysis.d.ts +56 -0
- package/dist/backend/cycle-analysis.js +222 -28
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +232 -15
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +27 -6
- package/dist/backend/cycle-mutability.js +110 -17
- package/dist/backend/cycle-mutability.js.map +1 -1
- package/dist/backend/immutability.d.ts +57 -0
- package/dist/backend/immutability.js +178 -0
- package/dist/backend/immutability.js.map +1 -0
- package/dist/backend/immutability.test.d.ts +1 -0
- package/dist/backend/immutability.test.js +131 -0
- package/dist/backend/immutability.test.js.map +1 -0
- package/dist/backend/llvm/alloc.d.ts +13 -3
- package/dist/backend/llvm/alloc.js +24 -10
- package/dist/backend/llvm/alloc.js.map +1 -1
- package/dist/backend/llvm/alloc.test.js +5 -5
- package/dist/backend/llvm/alloc.test.js.map +1 -1
- package/dist/backend/llvm/borrowed-inputs.d.ts +9 -2
- package/dist/backend/llvm/borrowed-inputs.js +31 -3
- package/dist/backend/llvm/borrowed-inputs.js.map +1 -1
- package/dist/backend/llvm/byte-numbers.js +2 -3
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +28 -0
- package/dist/backend/llvm/classes.js +51 -10
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +129 -1
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +30 -0
- package/dist/backend/llvm/common.js +41 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.js +6 -5
- package/dist/backend/llvm/dense-array-access.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +10 -2
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +6 -4
- package/dist/backend/llvm/dyn.js +30 -14
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +109 -5
- package/dist/backend/llvm/emitter.js +716 -112
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +54 -3
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +35 -28
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +14 -6
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +31 -8
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +46 -5
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +3 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +30 -3
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +53 -0
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-bridges.js +76 -14
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +18 -2
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +17 -6
- package/dist/backend/llvm/local-array-reads.js +157 -45
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/loop-array-borrows.d.ts +5 -2
- package/dist/backend/llvm/loop-array-borrows.js +27 -8
- package/dist/backend/llvm/loop-array-borrows.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.d.ts +7 -0
- package/dist/backend/llvm/map-number-lookup.js +18 -1
- package/dist/backend/llvm/map-number-lookup.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
- package/dist/backend/llvm/map-read-lifetimes.js +14 -4
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +5 -0
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.js +34 -1
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-writes.d.ts +124 -0
- package/dist/backend/llvm/reference-writes.js +1090 -0
- package/dist/backend/llvm/reference-writes.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +22 -8
- package/dist/backend/llvm/shapes.js +79 -23
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +5 -5
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +39 -10
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-reads.d.ts +3 -0
- package/dist/backend/llvm/string-reads.js +45 -11
- package/dist/backend/llvm/string-reads.js.map +1 -1
- package/dist/backend/llvm/string-slices.js +1 -1
- package/dist/backend/llvm/switch-dispatch.d.ts +2 -2
- package/dist/backend/llvm/switch-dispatch.js +35 -22
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +28 -10
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/trunc.d.ts +8 -0
- package/dist/backend/llvm/trunc.js +28 -0
- package/dist/backend/llvm/trunc.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.d.ts +8 -1
- package/dist/backend/llvm/walk-borrows.js +15 -4
- package/dist/backend/llvm/walk-borrows.js.map +1 -1
- package/dist/backend/loop-polls.d.ts +14 -0
- package/dist/backend/loop-polls.js +100 -0
- package/dist/backend/loop-polls.js.map +1 -0
- package/dist/backend/loop-polls.test.d.ts +1 -0
- package/dist/backend/loop-polls.test.js +86 -0
- package/dist/backend/loop-polls.test.js.map +1 -0
- package/dist/backend/may-throw.d.ts +36 -2
- package/dist/backend/may-throw.js +328 -13
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +169 -1
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +1 -3
- package/dist/backend/native-codegen.js +4 -32
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/publication.d.ts +5 -0
- package/dist/backend/publication.js +10 -0
- package/dist/backend/publication.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-abi.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/cli/command.js +8 -1
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/output-options.d.ts +1 -0
- package/dist/cli/output-options.js +3 -0
- package/dist/cli/output-options.js.map +1 -1
- package/dist/cli/usage.d.ts +5 -1
- package/dist/cli/usage.js +6 -0
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +5 -0
- package/dist/contracts/check.d.ts +8 -0
- package/dist/contracts/check.js +1577 -0
- package/dist/contracts/check.js.map +1 -0
- package/dist/executable/executable-cache.d.ts +2 -0
- package/dist/executable/executable-cache.js +3 -0
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +1 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +9 -0
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/contract-types.d.ts +50 -0
- package/dist/frontend/contract-types.js +232 -0
- package/dist/frontend/contract-types.js.map +1 -0
- package/dist/frontend/lowering/array-values.js +37 -1
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/strings.js +4 -1
- package/dist/frontend/lowering/builtins/strings.js.map +1 -1
- package/dist/frontend/lowering/call-arguments.js +96 -4
- package/dist/frontend/lowering/call-arguments.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.js +88 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
- package/dist/frontend/lowering/coercions/unions.d.ts +12 -0
- package/dist/frontend/lowering/coercions/unions.js +63 -0
- package/dist/frontend/lowering/coercions/unions.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +15 -4
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +4 -1
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-fields.d.ts +19 -0
- package/dist/frontend/lowering/expressions/union-fields.js +236 -0
- package/dist/frontend/lowering/expressions/union-fields.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.js +21 -4
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +6 -0
- package/dist/frontend/lowering/lower-exprs.js +245 -4
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +5 -19
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +11 -1
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +14 -0
- package/dist/frontend/lowering/lowerer.js +221 -8
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/nan-coded-reads.d.ts +161 -0
- package/dist/frontend/lowering/nan-coded-reads.js +1342 -0
- package/dist/frontend/lowering/nan-coded-reads.js.map +1 -0
- package/dist/frontend/lowering/narrowing-stability.d.ts +100 -0
- package/dist/frontend/lowering/narrowing-stability.js +1174 -0
- package/dist/frontend/lowering/narrowing-stability.js.map +1 -0
- package/dist/frontend/lowering/narrowing-stability.test.d.ts +1 -0
- package/dist/frontend/lowering/narrowing-stability.test.js +150 -0
- package/dist/frontend/lowering/narrowing-stability.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +14 -2
- package/dist/frontend/lowering/runtime-optional-bounds.js +17 -11
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +28 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +8 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.js +1 -1
- package/dist/frontend/type-mapper.js +43 -0
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +20 -8
- package/dist/index.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +2 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-globals.d.ts +14 -0
- package/dist/ir/constant-globals.js +81 -0
- package/dist/ir/constant-globals.js.map +1 -0
- package/dist/ir/constant-globals.test.d.ts +1 -0
- package/dist/ir/constant-globals.test.js +97 -0
- package/dist/ir/constant-globals.test.js.map +1 -0
- package/dist/ir/final-fields.d.ts +35 -0
- package/dist/ir/final-fields.js +187 -0
- package/dist/ir/final-fields.js.map +1 -0
- package/dist/ir/final-fields.test.d.ts +1 -0
- package/dist/ir/final-fields.test.js +106 -0
- package/dist/ir/final-fields.test.js.map +1 -0
- package/dist/ir/int32-slots.d.ts +35 -0
- package/dist/ir/int32-slots.js +217 -104
- package/dist/ir/int32-slots.js.map +1 -1
- package/dist/ir/int32-slots.test.js +27 -0
- package/dist/ir/int32-slots.test.js.map +1 -1
- package/dist/ir/integer-loops.d.ts +5 -0
- package/dist/ir/integer-loops.js +23 -4
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +43 -1
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/integer-ranges.d.ts +20 -0
- package/dist/ir/integer-ranges.js +178 -21
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +148 -3
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +38 -1
- package/dist/ir/ir.js +4 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +17 -3
- package/dist/ir/serialize.js +198 -39
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +65 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/unshare.d.ts +14 -0
- package/dist/ir/unshare.js +60 -0
- package/dist/ir/unshare.js.map +1 -0
- package/dist/ir/unshare.test.d.ts +1 -0
- package/dist/ir/unshare.test.js +115 -0
- package/dist/ir/unshare.test.js.map +1 -0
- package/dist/library/prepare.js +9 -0
- package/dist/library/prepare.js.map +1 -1
- package/dist/native/cache.d.ts +8 -1
- package/dist/native/cache.js +38 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +11 -0
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/compiler.js +4 -3
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/prepare.d.ts +3 -1
- package/dist/native/prepare.js +63 -28
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/prepare.test.js +23 -1
- package/dist/native/prepare.test.js.map +1 -1
- package/dist/native/text-file.d.ts +10 -0
- package/dist/native/text-file.js +41 -0
- package/dist/native/text-file.js.map +1 -0
- package/dist/text-parts.d.ts +5 -0
- package/dist/text-parts.js +36 -0
- package/dist/text-parts.js.map +1 -0
- package/package.json +20 -20
- package/surface-manifest.json +7 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +0 -8
- package/dist/frontend/lowering/expressions/union-field-write.js +0 -108
- package/dist/frontend/lowering/expressions/union-field-write.js.map +0 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type CycleInitFlag } from "./cycle-initialization.js";
|
|
2
2
|
import type { FieldLift } from "./coercions/structural-plans.js";
|
|
3
3
|
import { CoercionState } from "./coercions/state.js";
|
|
4
|
+
import { NarrowingStability } from "./narrowing-stability.js";
|
|
4
5
|
import { StringIndexBounds } from "./string-index-bounds.js";
|
|
6
|
+
import { NanCodedReads } from "./nan-coded-reads.js";
|
|
5
7
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
8
|
+
import { type ContractAnnotations } from "../contract-types.js";
|
|
6
9
|
import type { WidthLift } from "./width-lift.js";
|
|
7
10
|
import { type FnCtx } from "./function-context.js";
|
|
8
11
|
import type { FrontendServices } from "../services.js";
|
|
@@ -87,6 +90,9 @@ export interface LowerResult {
|
|
|
87
90
|
/** Present iff diagnostics is empty. */
|
|
88
91
|
module: IrModule | null;
|
|
89
92
|
diagnostics: ScrDiagnostic[];
|
|
93
|
+
/** The program's written contract types (@scriptc/types), keyed by the
|
|
94
|
+
* source locations the IR carries; absent when it writes none. */
|
|
95
|
+
contracts?: ContractAnnotations;
|
|
90
96
|
/** JS statements whose compile fences DEFERRED to runtime (runtimeFence
|
|
91
97
|
* statements in the module) — off the build, on the coverage report. */
|
|
92
98
|
runtimeFences: ScrDiagnostic[];
|
|
@@ -361,6 +367,10 @@ export declare class Lowerer {
|
|
|
361
367
|
/** Per-symbol result of the never-reassigned file scan
|
|
362
368
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
363
369
|
readonly neverReassignedCache: Map<ts.Symbol, boolean>;
|
|
370
|
+
/** Typed-array element reads whose every consumer treats undefined like
|
|
371
|
+
* NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
|
|
372
|
+
* analyzeRuntimeOptionalArrayReads before any body lowers. */
|
|
373
|
+
nanCodedReads: NanCodedReads | null;
|
|
364
374
|
private stringIndexBoundsCache;
|
|
365
375
|
/** Proofs that a string element read names an existing code unit. The
|
|
366
376
|
* indexed-read analysis and expression lowering share one instance, so a
|
|
@@ -471,6 +481,10 @@ export declare class Lowerer {
|
|
|
471
481
|
* discriminant was captured. A case match narrows the captured value,
|
|
472
482
|
* not that mutable binding; preserve its tagged representation on reads. */
|
|
473
483
|
readonly unstableSwitchBindings: Set<ts.Symbol>;
|
|
484
|
+
/** Whether a checker narrowing of a mutable member or closure-assigned
|
|
485
|
+
* binding still holds at its use (calls do not reset TypeScript's
|
|
486
|
+
* narrowing). Stale ones read the declared storage or check it. */
|
|
487
|
+
readonly narrowing: NarrowingStability;
|
|
474
488
|
/** All storage slots widened for runtime absence, including slots whose
|
|
475
489
|
* current control-flow branch has temporarily narrowed the value. */
|
|
476
490
|
readonly runtimeOptionalStorageLocals: Set<IrLocal>;
|
|
@@ -19,11 +19,14 @@ import { lowerUtilTypeValue, pathModuleValue, utilTypesModuleValue, } from "./lo
|
|
|
19
19
|
import { everyExprChild, everyStmtChild, transformStmtList } from "../../ir/traverse.js";
|
|
20
20
|
import { RuntimeOptionalProvenance } from "./runtime-optional-provenance.js";
|
|
21
21
|
import { presentAtUse, reassignedBindings } from "./runtime-optional-guards.js";
|
|
22
|
+
import { NarrowingStability } from "./narrowing-stability.js";
|
|
22
23
|
import { ArrayElementStates } from "./runtime-optional-elements.js";
|
|
23
24
|
import { indexReadInBounds } from "./runtime-optional-bounds.js";
|
|
24
25
|
import { ArrayOwnership } from "./runtime-optional-ownership.js";
|
|
25
26
|
import { StringIndexBounds } from "./string-index-bounds.js";
|
|
27
|
+
import { NanCodedReads } from "./nan-coded-reads.js";
|
|
26
28
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
29
|
+
import { collectContractAnnotations } from "../contract-types.js";
|
|
27
30
|
import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
|
|
28
31
|
import { UnregisteredClassTypes } from "./unregistered-class-types.js";
|
|
29
32
|
import { bindingInContext, captureContextBinding, declareContextLocal, declareContextThis, } from "./function-context.js";
|
|
@@ -394,6 +397,39 @@ export function lowerToIr(program, entry, moduleOrder, options = {}) {
|
|
|
394
397
|
timing("divergence-scan", { found: divergences.length });
|
|
395
398
|
if (divergences.length > 0)
|
|
396
399
|
result = { ...result, divergences };
|
|
400
|
+
// Contract types (@scriptc/types): written annotations, checked against
|
|
401
|
+
// the IR by the compile pipeline (contracts/check.ts).
|
|
402
|
+
// Acyclic object types name the IR unit the program lowered them to; a
|
|
403
|
+
// type the module never uses has no unit (mapping it now only touches the
|
|
404
|
+
// lowerer's tables, never the finished module).
|
|
405
|
+
const lowered = result.module;
|
|
406
|
+
const contracts = collectContractAnnotations(program.getTypeChecker(), moduleOrder, (type) => {
|
|
407
|
+
if (lowered === null)
|
|
408
|
+
return null;
|
|
409
|
+
const mapped = panicSafe(() => resultLowerer.mapTypeOf(type), null);
|
|
410
|
+
if (mapped?.kind === "object")
|
|
411
|
+
return lowered.classes?.some((c) => c.name === mapped.className)
|
|
412
|
+
? `object:${mapped.className}`
|
|
413
|
+
: null;
|
|
414
|
+
if (mapped?.kind === "record")
|
|
415
|
+
return lowered.records?.some((r) => r.id === mapped.shapeId)
|
|
416
|
+
? `record:${mapped.shapeId}`
|
|
417
|
+
: null;
|
|
418
|
+
if (mapped?.kind === "union")
|
|
419
|
+
return lowered.unions?.some((u) => u.id === mapped.unionId)
|
|
420
|
+
? `union:${mapped.unionId}`
|
|
421
|
+
: null;
|
|
422
|
+
return null;
|
|
423
|
+
});
|
|
424
|
+
timing("contract-annotations", { found: contracts.count });
|
|
425
|
+
if (contracts.diagnostics.length > 0)
|
|
426
|
+
result = {
|
|
427
|
+
...result,
|
|
428
|
+
module: null,
|
|
429
|
+
diagnostics: [...result.diagnostics, ...contracts.diagnostics],
|
|
430
|
+
};
|
|
431
|
+
else if (contracts.count > 0)
|
|
432
|
+
result = { ...result, contracts };
|
|
397
433
|
if (options.coverage !== true)
|
|
398
434
|
return result;
|
|
399
435
|
const remainder = new Lowerer(program, entry, moduleOrder, dynamic, {
|
|
@@ -976,6 +1012,10 @@ export class Lowerer {
|
|
|
976
1012
|
/** Per-symbol result of the never-reassigned file scan
|
|
977
1013
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
978
1014
|
neverReassignedCache = new Map();
|
|
1015
|
+
/** Typed-array element reads whose every consumer treats undefined like
|
|
1016
|
+
* NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
|
|
1017
|
+
* analyzeRuntimeOptionalArrayReads before any body lowers. */
|
|
1018
|
+
nanCodedReads = null;
|
|
979
1019
|
stringIndexBoundsCache = null;
|
|
980
1020
|
/** Proofs that a string element read names an existing code unit. The
|
|
981
1021
|
* indexed-read analysis and expression lowering share one instance, so a
|
|
@@ -1214,6 +1254,10 @@ export class Lowerer {
|
|
|
1214
1254
|
* discriminant was captured. A case match narrows the captured value,
|
|
1215
1255
|
* not that mutable binding; preserve its tagged representation on reads. */
|
|
1216
1256
|
unstableSwitchBindings = new Set();
|
|
1257
|
+
/** Whether a checker narrowing of a mutable member or closure-assigned
|
|
1258
|
+
* binding still holds at its use (calls do not reset TypeScript's
|
|
1259
|
+
* narrowing). Stale ones read the declared storage or check it. */
|
|
1260
|
+
narrowing = new NarrowingStability(this);
|
|
1217
1261
|
/** All storage slots widened for runtime absence, including slots whose
|
|
1218
1262
|
* current control-flow branch has temporarily narrowed the value. */
|
|
1219
1263
|
runtimeOptionalStorageLocals = new Set();
|
|
@@ -3133,6 +3177,32 @@ export class Lowerer {
|
|
|
3133
3177
|
}
|
|
3134
3178
|
}
|
|
3135
3179
|
}
|
|
3180
|
+
const mappedOrNull = (node) => panicSafe(() => this.mapTypeOf(this.typeOf(node)), null);
|
|
3181
|
+
const nanCoded = new NanCodedReads({
|
|
3182
|
+
symbolOf,
|
|
3183
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3184
|
+
bytesElemOf: (receiver) => {
|
|
3185
|
+
const t = mappedOrNull(receiver);
|
|
3186
|
+
return t?.kind === "bytes" ? t.elem : null;
|
|
3187
|
+
},
|
|
3188
|
+
isNumber: (node) => mappedOrNull(node)?.kind === "f64",
|
|
3189
|
+
isString: (node) => mappedOrNull(node)?.kind === "string",
|
|
3190
|
+
hasLength: (node) => {
|
|
3191
|
+
const kind = mappedOrNull(node)?.kind;
|
|
3192
|
+
return kind === "array" || kind === "bytes";
|
|
3193
|
+
},
|
|
3194
|
+
isStdlibGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3195
|
+
paramIsNumber: (fn, index) => {
|
|
3196
|
+
const param = signatureBySymbol.get(fn)?.params[index];
|
|
3197
|
+
return (param !== undefined &&
|
|
3198
|
+
param.type.kind === "f64" &&
|
|
3199
|
+
(param.mode === "required" || param.mode === "omittable"));
|
|
3200
|
+
},
|
|
3201
|
+
returnIsNumber: (fn) => signatureBySymbol.get(fn)?.returnType.kind === "f64",
|
|
3202
|
+
methodOverridden: (fn) => (familyBySymbol.get(fn)?.length ?? 1) > 1,
|
|
3203
|
+
}, sourceFiles);
|
|
3204
|
+
nanCoded.analyze();
|
|
3205
|
+
this.nanCodedReads = nanCoded;
|
|
3136
3206
|
const peel = (node) => {
|
|
3137
3207
|
let e = node;
|
|
3138
3208
|
while (ts.isParenthesizedExpression(e) ||
|
|
@@ -3252,8 +3322,11 @@ export class Lowerer {
|
|
|
3252
3322
|
const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
|
|
3253
3323
|
if (kind === "string")
|
|
3254
3324
|
return !this.stringIndexBounds.inBounds(e);
|
|
3255
|
-
// Typed-array reads answer undefined for an invalid index too
|
|
3256
|
-
|
|
3325
|
+
// Typed-array reads answer undefined for an invalid index too, unless
|
|
3326
|
+
// every consumer treats undefined like NaN.
|
|
3327
|
+
if (kind === "bytes")
|
|
3328
|
+
return !nanCoded.isNumericRead(e);
|
|
3329
|
+
return kind === "array";
|
|
3257
3330
|
};
|
|
3258
3331
|
const isDynamicObjectEntryRead = (node) => {
|
|
3259
3332
|
const read = peel(node);
|
|
@@ -3337,6 +3410,13 @@ export class Lowerer {
|
|
|
3337
3410
|
this.mapTypeOf(this.typeOf(e))?.kind === "union" &&
|
|
3338
3411
|
this.armTag(this.mapTypeOf(this.typeOf(e)).unionId, UNDEFINED_T) >= 0)
|
|
3339
3412
|
return true;
|
|
3413
|
+
// A NaN-coded value that reaches a consumer able to observe undefined
|
|
3414
|
+
// is rebuilt as the ordinary optional value there; every other read
|
|
3415
|
+
// of a NaN-coded slot is a plain number.
|
|
3416
|
+
if (nanCoded.reboxes(e))
|
|
3417
|
+
return true;
|
|
3418
|
+
if (nanCoded.readsNanCodedSlot(e))
|
|
3419
|
+
return false;
|
|
3340
3420
|
if (ts.isIdentifier(e)) {
|
|
3341
3421
|
const symbol = symbolOf(e);
|
|
3342
3422
|
return (symbol !== null &&
|
|
@@ -3429,6 +3509,25 @@ export class Lowerer {
|
|
|
3429
3509
|
return false;
|
|
3430
3510
|
return noteFieldSymbol(symbol, name);
|
|
3431
3511
|
};
|
|
3512
|
+
/** An instance field of a TypeScript class whose declared storage is
|
|
3513
|
+
* exactly `T | undefined`: an assignment from a missing read stores the
|
|
3514
|
+
* undefined arm while the checker narrows later reads to `T`. */
|
|
3515
|
+
const optionalInstanceField = (symbol) => {
|
|
3516
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
3517
|
+
if (!decl || !ts.isPropertyDeclaration(decl) || !ts.isIdentifier(decl.name))
|
|
3518
|
+
return false;
|
|
3519
|
+
if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Static)
|
|
3520
|
+
return false;
|
|
3521
|
+
const sf = decl.getSourceFile();
|
|
3522
|
+
if (isJsSourceFile(sf) || sf.isDeclarationFile)
|
|
3523
|
+
return false;
|
|
3524
|
+
const declared = typeOrNull(decl.name);
|
|
3525
|
+
const mapped = declared ? this.mapTypeOf(declared) : null;
|
|
3526
|
+
if (mapped?.kind !== "union" || this.armTag(mapped.unionId, UNDEFINED_T) < 0)
|
|
3527
|
+
return false;
|
|
3528
|
+
const arms = this.unions.get(mapped.unionId)?.arms ?? [];
|
|
3529
|
+
return (arms.length === 2 && arms.every((arm) => arm.kind === "undefinedT" || !isUnitType(arm)));
|
|
3530
|
+
};
|
|
3432
3531
|
const noteFieldSymbol = (symbol, name) => {
|
|
3433
3532
|
const set = optionalFields.get(symbol) ?? new Set();
|
|
3434
3533
|
const before = set.size;
|
|
@@ -3567,14 +3666,31 @@ export class Lowerer {
|
|
|
3567
3666
|
return found;
|
|
3568
3667
|
};
|
|
3569
3668
|
const jsLambdas = [];
|
|
3669
|
+
// TypeScript lambdas keep the checker's inferred return type, which
|
|
3670
|
+
// spells a missing read as its bare element type and `read ?? "text"`
|
|
3671
|
+
// as a number. Their closures return the runtime result instead.
|
|
3672
|
+
const tsLambdas = [];
|
|
3570
3673
|
for (const sf of sourceFiles) {
|
|
3571
|
-
|
|
3674
|
+
const js = isJsSourceFile(sf);
|
|
3675
|
+
if (!js && sf.isDeclarationFile)
|
|
3572
3676
|
continue;
|
|
3573
3677
|
ts.walkPreorder(sf, (node) => {
|
|
3574
3678
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
3575
|
-
jsLambdas.push(node);
|
|
3679
|
+
(js ? jsLambdas : tsLambdas).push(node);
|
|
3576
3680
|
});
|
|
3577
3681
|
}
|
|
3682
|
+
/** The primitive result union of a lambda's returns (see
|
|
3683
|
+
* optionalPrimitiveResultType), as declared functions record it. */
|
|
3684
|
+
const lambdaArithmeticReturn = (fn) => {
|
|
3685
|
+
if (fn.body && !ts.isBlock(fn.body))
|
|
3686
|
+
return optionalPrimitiveResultType(fn.body);
|
|
3687
|
+
for (const expression of returnsOf(fn)) {
|
|
3688
|
+
const result = optionalPrimitiveResultType(expression);
|
|
3689
|
+
if (result)
|
|
3690
|
+
return result;
|
|
3691
|
+
}
|
|
3692
|
+
return null;
|
|
3693
|
+
};
|
|
3578
3694
|
for (const [symbol, signature] of signatureBySymbol) {
|
|
3579
3695
|
const declaration = functionDeclBySymbol.get(symbol);
|
|
3580
3696
|
if (!declaration || !isJsSourceFile(declaration.getSourceFile()))
|
|
@@ -4262,6 +4378,14 @@ export class Lowerer {
|
|
|
4262
4378
|
provenance?.note("static-field", node.left, node.right);
|
|
4263
4379
|
changed = true;
|
|
4264
4380
|
}
|
|
4381
|
+
else if (field && !optionalSymbols.has(field) && optionalInstanceField(field)) {
|
|
4382
|
+
// The checker narrows the field to the assigned read's bare
|
|
4383
|
+
// type, but the stored value can be undefined: reads keep the
|
|
4384
|
+
// stored union (runtimeOptionalClassField).
|
|
4385
|
+
optionalSymbols.add(field);
|
|
4386
|
+
provenance?.note("assigned-field", node.left, node.right);
|
|
4387
|
+
changed = true;
|
|
4388
|
+
}
|
|
4265
4389
|
else if (ts.isIdentifier(node.left.expression)) {
|
|
4266
4390
|
const symbol = symbolOf(node.left.expression);
|
|
4267
4391
|
if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
|
|
@@ -4575,6 +4699,52 @@ export class Lowerer {
|
|
|
4575
4699
|
for (const [symbol, decl] of functionDeclBySymbol)
|
|
4576
4700
|
if (scanReturns(symbol, decl))
|
|
4577
4701
|
changed = true;
|
|
4702
|
+
for (const fn of tsLambdas) {
|
|
4703
|
+
// An annotated return is the author's contract, as for parameters.
|
|
4704
|
+
if (fn.type)
|
|
4705
|
+
continue;
|
|
4706
|
+
const mapped = this.mapTypeOf(this.typeOf(fn));
|
|
4707
|
+
if (mapped?.kind !== "func")
|
|
4708
|
+
continue;
|
|
4709
|
+
const previous = this.runtimeOptionalFunctionReturnType(fn, mapped.ret);
|
|
4710
|
+
// Async, generator and void bodies keep their own result shapes.
|
|
4711
|
+
if (previous.kind === "promise" ||
|
|
4712
|
+
previous.kind === "generator" ||
|
|
4713
|
+
previous.kind === "void")
|
|
4714
|
+
continue;
|
|
4715
|
+
const arithmetic = lambdaArithmeticReturn(fn);
|
|
4716
|
+
const next = arithmetic &&
|
|
4717
|
+
(previous.kind === "f64" || previous.kind === "string" || previous.kind === "bool")
|
|
4718
|
+
? arithmetic
|
|
4719
|
+
: bodyReturnsOptional(fn)
|
|
4720
|
+
? this.runtimeOptionalType(previous)
|
|
4721
|
+
: previous;
|
|
4722
|
+
if (!typeEquals(previous, next)) {
|
|
4723
|
+
this.runtimeOptionalFunctionReturns.set(fn, next);
|
|
4724
|
+
provenance?.note("lambda-return", fn, fn.body);
|
|
4725
|
+
changed = true;
|
|
4726
|
+
}
|
|
4727
|
+
// Calls through the lambda's own binding (`const get = () => xs[i];
|
|
4728
|
+
// get()`) produce the same result as calls of a declared function.
|
|
4729
|
+
const binding = fn.parent;
|
|
4730
|
+
if (next !== previous &&
|
|
4731
|
+
ts.isVariableDeclaration(binding) &&
|
|
4732
|
+
binding.initializer === fn &&
|
|
4733
|
+
!binding.type &&
|
|
4734
|
+
ts.isIdentifier(binding.name)) {
|
|
4735
|
+
const symbol = symbolOf(binding.name);
|
|
4736
|
+
if (symbol && next.kind === "union" && this.armTag(next.unionId, UNDEFINED_T) >= 0) {
|
|
4737
|
+
if (!optionalReturns.has(symbol)) {
|
|
4738
|
+
optionalReturns.add(symbol);
|
|
4739
|
+
changed = true;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
else if (symbol && !arithmeticReturns.has(symbol)) {
|
|
4743
|
+
arithmeticReturns.set(symbol, next);
|
|
4744
|
+
changed = true;
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4578
4748
|
for (const fn of jsLambdas) {
|
|
4579
4749
|
if (!bodyReturnsOptional(fn))
|
|
4580
4750
|
continue;
|
|
@@ -4606,6 +4776,25 @@ export class Lowerer {
|
|
|
4606
4776
|
}
|
|
4607
4777
|
}
|
|
4608
4778
|
}
|
|
4779
|
+
// NaN-coded slots convert optional values on entry instead of holding
|
|
4780
|
+
// the union, so promotion never applies to them.
|
|
4781
|
+
for (const symbol of [...optionalSymbols])
|
|
4782
|
+
if (nanCoded.nanCodedBinding(symbol))
|
|
4783
|
+
optionalSymbols.delete(symbol);
|
|
4784
|
+
for (const symbol of [...optionalReturns])
|
|
4785
|
+
if (nanCoded.nanCodedReturn(symbol))
|
|
4786
|
+
optionalReturns.delete(symbol);
|
|
4787
|
+
for (const [symbol, params] of optionalParams) {
|
|
4788
|
+
const decl = functionDeclBySymbol.get(symbol);
|
|
4789
|
+
for (const index of [...params]) {
|
|
4790
|
+
const name = decl?.parameters[index]?.name;
|
|
4791
|
+
const param = name && ts.isIdentifier(name) ? symbolOf(name) : null;
|
|
4792
|
+
if (param && nanCoded.nanCodedBinding(param))
|
|
4793
|
+
params.delete(index);
|
|
4794
|
+
}
|
|
4795
|
+
if (params.size === 0)
|
|
4796
|
+
optionalParams.delete(symbol);
|
|
4797
|
+
}
|
|
4609
4798
|
for (const [site, present] of presentSites)
|
|
4610
4799
|
if (present)
|
|
4611
4800
|
this.presentElementCalls.add(site);
|
|
@@ -4678,11 +4867,11 @@ export class Lowerer {
|
|
|
4678
4867
|
const symbol = symbolOf(node.name);
|
|
4679
4868
|
if (!symbol)
|
|
4680
4869
|
return;
|
|
4681
|
-
if (
|
|
4682
|
-
!node.type &&
|
|
4870
|
+
if (!node.type &&
|
|
4683
4871
|
!hasJsTypeAnnotation(node) &&
|
|
4684
4872
|
node.initializer &&
|
|
4685
|
-
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
|
|
4873
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) &&
|
|
4874
|
+
(isJsSourceFile(sf) || this.runtimeOptionalFunctionReturns.has(node.initializer))) {
|
|
4686
4875
|
const global = this.globalsBySymbol.get(symbol);
|
|
4687
4876
|
if (global?.type.kind === "func")
|
|
4688
4877
|
global.type = {
|
|
@@ -4791,6 +4980,7 @@ export class Lowerer {
|
|
|
4791
4980
|
}
|
|
4792
4981
|
run() {
|
|
4793
4982
|
const parts = this.splitFiles();
|
|
4983
|
+
this.narrowing.setFiles(parts.map((fp) => fp.sf));
|
|
4794
4984
|
// The coverage remainder deliberately visits every body that reachable
|
|
4795
4985
|
// emit skipped. Those files are already phase-managed, so an ordinary
|
|
4796
4986
|
// checker miss would stay a one-node IPC query instead of falling back
|
|
@@ -5170,6 +5360,7 @@ export class Lowerer {
|
|
|
5170
5360
|
emitReachable(extraRoots) {
|
|
5171
5361
|
this.deferredModuleInitializers = new DeferredModuleInitializers();
|
|
5172
5362
|
const parts = this.splitFiles();
|
|
5363
|
+
this.narrowing.setFiles(parts.map((fp) => fp.sf));
|
|
5173
5364
|
// Direct lowering callers do not necessarily run program preflight.
|
|
5174
5365
|
// Establish the same managed header/top-level batch here before
|
|
5175
5366
|
// collection, while the production path simply finds warm memos.
|
|
@@ -5995,6 +6186,12 @@ export class Lowerer {
|
|
|
5995
6186
|
const narrowed = this.chainNarrowedType.get(node);
|
|
5996
6187
|
if (narrowed)
|
|
5997
6188
|
return narrowed;
|
|
6189
|
+
// A stale narrowing of a mutable member or closure-assigned binding in
|
|
6190
|
+
// an absence/type test (or a slot accepting the declared type) reads
|
|
6191
|
+
// as declared, so the test inspects the stored value.
|
|
6192
|
+
const widened = this.narrowing.widenedType(node);
|
|
6193
|
+
if (widened)
|
|
6194
|
+
return widened;
|
|
5998
6195
|
const t = this.checker.getTypeAtLocation(node);
|
|
5999
6196
|
// ALIASED-TYPEOF narrowing: inside a branch a `type === 'string'`
|
|
6000
6197
|
// test proves (type = typeof val, both never reassigned), references
|
|
@@ -8224,7 +8421,23 @@ export class Lowerer {
|
|
|
8224
8421
|
}
|
|
8225
8422
|
/* ── scoping and captures ─────────────────────────────────────────── */
|
|
8226
8423
|
declareLocal(nameNode, name, type, mutable) {
|
|
8227
|
-
|
|
8424
|
+
const symbol = this.checker.getSymbolAtLocation(nameNode);
|
|
8425
|
+
const local = declareContextLocal(this.ctx, name, type, mutable, symbol, bindingSource(nameNode));
|
|
8426
|
+
// A binding declared `T | undefined` that receives a missing read: the
|
|
8427
|
+
// checker narrows it to `T` after the assignment, but the stored value
|
|
8428
|
+
// can be undefined. Track it like widened storage so bare reads keep
|
|
8429
|
+
// the union until a guard proves presence.
|
|
8430
|
+
if (symbol &&
|
|
8431
|
+
this.runtimeOptionalAssignedSymbols.has(symbol) &&
|
|
8432
|
+
type.kind === "union" &&
|
|
8433
|
+
this.armTag(type.unionId, UNDEFINED_T) >= 0 &&
|
|
8434
|
+
this.unions.get(type.unionId)?.arms.length === 2 &&
|
|
8435
|
+
!isUnitType(this.stripUndefinedArm(type))) {
|
|
8436
|
+
const root = this.runtimeOptionalRootOf(local);
|
|
8437
|
+
this.runtimeOptionalStorageLocals.add(root);
|
|
8438
|
+
this.runtimeOptionalLocals.add(root);
|
|
8439
|
+
}
|
|
8440
|
+
return local;
|
|
8228
8441
|
}
|
|
8229
8442
|
/** A never-returning call in a value position of type `expected`: the
|
|
8230
8443
|
* call runs for its effects (it throws or exits), and the expression ends
|