@scriptc/compiler 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +1 -0
- package/dist/backend/cycle-analysis.js +50 -17
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +138 -14
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +31 -6
- package/dist/backend/cycle-mutability.js +120 -18
- package/dist/backend/cycle-mutability.js.map +1 -1
- package/dist/backend/llvm/alloc.d.ts +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/byte-numbers.js +2 -3
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/classes.js +7 -6
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +17 -0
- package/dist/backend/llvm/common.js +28 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.js +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +1 -1
- package/dist/backend/llvm/dyn.d.ts +4 -4
- package/dist/backend/llvm/dyn.js +27 -11
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +45 -3
- package/dist/backend/llvm/emitter.js +333 -74
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +36 -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-containers.js +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-stream-bridges.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-bridges.js +73 -11
- 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 +155 -43
- package/dist/backend/llvm/local-array-reads.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/shapes.d.ts +14 -3
- package/dist/backend/llvm/shapes.js +43 -12
- 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-reads.js +29 -26
- package/dist/backend/llvm/string-reads.js.map +1 -1
- package/dist/backend/llvm/string-slices.js +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/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.js +20 -1
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +23 -0
- 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/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/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/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/containers/bytes.js +15 -4
- package/dist/frontend/lowering/containers/bytes.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 +12 -2
- 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 +162 -3
- 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 +5 -0
- package/dist/frontend/lowering/lowerer.js +174 -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/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/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 +9 -0
- package/dist/ir/int32-slots.js +70 -57
- 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 +20 -4
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +17 -0
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/ir.d.ts +6 -1
- package/dist/ir/ir.js +4 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +12 -2
- package/dist/ir/serialize.js +190 -39
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +65 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/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
|
@@ -2,6 +2,7 @@ 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
4
|
import { StringIndexBounds } from "./string-index-bounds.js";
|
|
5
|
+
import { NanCodedReads } from "./nan-coded-reads.js";
|
|
5
6
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
6
7
|
import type { WidthLift } from "./width-lift.js";
|
|
7
8
|
import { type FnCtx } from "./function-context.js";
|
|
@@ -361,6 +362,10 @@ export declare class Lowerer {
|
|
|
361
362
|
/** Per-symbol result of the never-reassigned file scan
|
|
362
363
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
363
364
|
readonly neverReassignedCache: Map<ts.Symbol, boolean>;
|
|
365
|
+
/** Typed-array element reads whose every consumer treats undefined like
|
|
366
|
+
* NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
|
|
367
|
+
* analyzeRuntimeOptionalArrayReads before any body lowers. */
|
|
368
|
+
nanCodedReads: NanCodedReads | null;
|
|
364
369
|
private stringIndexBoundsCache;
|
|
365
370
|
/** Proofs that a string element read names an existing code unit. The
|
|
366
371
|
* indexed-read analysis and expression lowering share one instance, so a
|
|
@@ -23,6 +23,7 @@ import { ArrayElementStates } from "./runtime-optional-elements.js";
|
|
|
23
23
|
import { indexReadInBounds } from "./runtime-optional-bounds.js";
|
|
24
24
|
import { ArrayOwnership } from "./runtime-optional-ownership.js";
|
|
25
25
|
import { StringIndexBounds } from "./string-index-bounds.js";
|
|
26
|
+
import { NanCodedReads } from "./nan-coded-reads.js";
|
|
26
27
|
import { RuntimeOptionalLocals } from "./runtime-optional-locals.js";
|
|
27
28
|
import { sanitizeUnregisteredClassTypes } from "./sanitize-class-types.js";
|
|
28
29
|
import { UnregisteredClassTypes } from "./unregistered-class-types.js";
|
|
@@ -976,6 +977,10 @@ export class Lowerer {
|
|
|
976
977
|
/** Per-symbol result of the never-reassigned file scan
|
|
977
978
|
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
|
978
979
|
neverReassignedCache = new Map();
|
|
980
|
+
/** Typed-array element reads whose every consumer treats undefined like
|
|
981
|
+
* NaN, which lower to plain doubles (see nan-coded-reads.ts). Settled by
|
|
982
|
+
* analyzeRuntimeOptionalArrayReads before any body lowers. */
|
|
983
|
+
nanCodedReads = null;
|
|
979
984
|
stringIndexBoundsCache = null;
|
|
980
985
|
/** Proofs that a string element read names an existing code unit. The
|
|
981
986
|
* indexed-read analysis and expression lowering share one instance, so a
|
|
@@ -3133,6 +3138,32 @@ export class Lowerer {
|
|
|
3133
3138
|
}
|
|
3134
3139
|
}
|
|
3135
3140
|
}
|
|
3141
|
+
const mappedOrNull = (node) => panicSafe(() => this.mapTypeOf(this.typeOf(node)), null);
|
|
3142
|
+
const nanCoded = new NanCodedReads({
|
|
3143
|
+
symbolOf,
|
|
3144
|
+
declarationOf: (symbol) => this.checker.valueDeclarationOf(symbol),
|
|
3145
|
+
bytesElemOf: (receiver) => {
|
|
3146
|
+
const t = mappedOrNull(receiver);
|
|
3147
|
+
return t?.kind === "bytes" ? t.elem : null;
|
|
3148
|
+
},
|
|
3149
|
+
isNumber: (node) => mappedOrNull(node)?.kind === "f64",
|
|
3150
|
+
isString: (node) => mappedOrNull(node)?.kind === "string",
|
|
3151
|
+
hasLength: (node) => {
|
|
3152
|
+
const kind = mappedOrNull(node)?.kind;
|
|
3153
|
+
return kind === "array" || kind === "bytes";
|
|
3154
|
+
},
|
|
3155
|
+
isStdlibGlobal: (node, name) => this.isStdlibGlobal(node, name),
|
|
3156
|
+
paramIsNumber: (fn, index) => {
|
|
3157
|
+
const param = signatureBySymbol.get(fn)?.params[index];
|
|
3158
|
+
return (param !== undefined &&
|
|
3159
|
+
param.type.kind === "f64" &&
|
|
3160
|
+
(param.mode === "required" || param.mode === "omittable"));
|
|
3161
|
+
},
|
|
3162
|
+
returnIsNumber: (fn) => signatureBySymbol.get(fn)?.returnType.kind === "f64",
|
|
3163
|
+
methodOverridden: (fn) => (familyBySymbol.get(fn)?.length ?? 1) > 1,
|
|
3164
|
+
}, sourceFiles);
|
|
3165
|
+
nanCoded.analyze();
|
|
3166
|
+
this.nanCodedReads = nanCoded;
|
|
3136
3167
|
const peel = (node) => {
|
|
3137
3168
|
let e = node;
|
|
3138
3169
|
while (ts.isParenthesizedExpression(e) ||
|
|
@@ -3252,8 +3283,11 @@ export class Lowerer {
|
|
|
3252
3283
|
const kind = this.mapTypeOf(this.typeOf(e.expression))?.kind;
|
|
3253
3284
|
if (kind === "string")
|
|
3254
3285
|
return !this.stringIndexBounds.inBounds(e);
|
|
3255
|
-
// Typed-array reads answer undefined for an invalid index too
|
|
3256
|
-
|
|
3286
|
+
// Typed-array reads answer undefined for an invalid index too, unless
|
|
3287
|
+
// every consumer treats undefined like NaN.
|
|
3288
|
+
if (kind === "bytes")
|
|
3289
|
+
return !nanCoded.isNumericRead(e);
|
|
3290
|
+
return kind === "array";
|
|
3257
3291
|
};
|
|
3258
3292
|
const isDynamicObjectEntryRead = (node) => {
|
|
3259
3293
|
const read = peel(node);
|
|
@@ -3337,6 +3371,13 @@ export class Lowerer {
|
|
|
3337
3371
|
this.mapTypeOf(this.typeOf(e))?.kind === "union" &&
|
|
3338
3372
|
this.armTag(this.mapTypeOf(this.typeOf(e)).unionId, UNDEFINED_T) >= 0)
|
|
3339
3373
|
return true;
|
|
3374
|
+
// A NaN-coded value that reaches a consumer able to observe undefined
|
|
3375
|
+
// is rebuilt as the ordinary optional value there; every other read
|
|
3376
|
+
// of a NaN-coded slot is a plain number.
|
|
3377
|
+
if (nanCoded.reboxes(e))
|
|
3378
|
+
return true;
|
|
3379
|
+
if (nanCoded.readsNanCodedSlot(e))
|
|
3380
|
+
return false;
|
|
3340
3381
|
if (ts.isIdentifier(e)) {
|
|
3341
3382
|
const symbol = symbolOf(e);
|
|
3342
3383
|
return (symbol !== null &&
|
|
@@ -3429,6 +3470,25 @@ export class Lowerer {
|
|
|
3429
3470
|
return false;
|
|
3430
3471
|
return noteFieldSymbol(symbol, name);
|
|
3431
3472
|
};
|
|
3473
|
+
/** An instance field of a TypeScript class whose declared storage is
|
|
3474
|
+
* exactly `T | undefined`: an assignment from a missing read stores the
|
|
3475
|
+
* undefined arm while the checker narrows later reads to `T`. */
|
|
3476
|
+
const optionalInstanceField = (symbol) => {
|
|
3477
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
3478
|
+
if (!decl || !ts.isPropertyDeclaration(decl) || !ts.isIdentifier(decl.name))
|
|
3479
|
+
return false;
|
|
3480
|
+
if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Static)
|
|
3481
|
+
return false;
|
|
3482
|
+
const sf = decl.getSourceFile();
|
|
3483
|
+
if (isJsSourceFile(sf) || sf.isDeclarationFile)
|
|
3484
|
+
return false;
|
|
3485
|
+
const declared = typeOrNull(decl.name);
|
|
3486
|
+
const mapped = declared ? this.mapTypeOf(declared) : null;
|
|
3487
|
+
if (mapped?.kind !== "union" || this.armTag(mapped.unionId, UNDEFINED_T) < 0)
|
|
3488
|
+
return false;
|
|
3489
|
+
const arms = this.unions.get(mapped.unionId)?.arms ?? [];
|
|
3490
|
+
return (arms.length === 2 && arms.every((arm) => arm.kind === "undefinedT" || !isUnitType(arm)));
|
|
3491
|
+
};
|
|
3432
3492
|
const noteFieldSymbol = (symbol, name) => {
|
|
3433
3493
|
const set = optionalFields.get(symbol) ?? new Set();
|
|
3434
3494
|
const before = set.size;
|
|
@@ -3567,14 +3627,31 @@ export class Lowerer {
|
|
|
3567
3627
|
return found;
|
|
3568
3628
|
};
|
|
3569
3629
|
const jsLambdas = [];
|
|
3630
|
+
// TypeScript lambdas keep the checker's inferred return type, which
|
|
3631
|
+
// spells a missing read as its bare element type and `read ?? "text"`
|
|
3632
|
+
// as a number. Their closures return the runtime result instead.
|
|
3633
|
+
const tsLambdas = [];
|
|
3570
3634
|
for (const sf of sourceFiles) {
|
|
3571
|
-
|
|
3635
|
+
const js = isJsSourceFile(sf);
|
|
3636
|
+
if (!js && sf.isDeclarationFile)
|
|
3572
3637
|
continue;
|
|
3573
3638
|
ts.walkPreorder(sf, (node) => {
|
|
3574
3639
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
3575
|
-
jsLambdas.push(node);
|
|
3640
|
+
(js ? jsLambdas : tsLambdas).push(node);
|
|
3576
3641
|
});
|
|
3577
3642
|
}
|
|
3643
|
+
/** The primitive result union of a lambda's returns (see
|
|
3644
|
+
* optionalPrimitiveResultType), as declared functions record it. */
|
|
3645
|
+
const lambdaArithmeticReturn = (fn) => {
|
|
3646
|
+
if (fn.body && !ts.isBlock(fn.body))
|
|
3647
|
+
return optionalPrimitiveResultType(fn.body);
|
|
3648
|
+
for (const expression of returnsOf(fn)) {
|
|
3649
|
+
const result = optionalPrimitiveResultType(expression);
|
|
3650
|
+
if (result)
|
|
3651
|
+
return result;
|
|
3652
|
+
}
|
|
3653
|
+
return null;
|
|
3654
|
+
};
|
|
3578
3655
|
for (const [symbol, signature] of signatureBySymbol) {
|
|
3579
3656
|
const declaration = functionDeclBySymbol.get(symbol);
|
|
3580
3657
|
if (!declaration || !isJsSourceFile(declaration.getSourceFile()))
|
|
@@ -4262,6 +4339,14 @@ export class Lowerer {
|
|
|
4262
4339
|
provenance?.note("static-field", node.left, node.right);
|
|
4263
4340
|
changed = true;
|
|
4264
4341
|
}
|
|
4342
|
+
else if (field && !optionalSymbols.has(field) && optionalInstanceField(field)) {
|
|
4343
|
+
// The checker narrows the field to the assigned read's bare
|
|
4344
|
+
// type, but the stored value can be undefined: reads keep the
|
|
4345
|
+
// stored union (runtimeOptionalClassField).
|
|
4346
|
+
optionalSymbols.add(field);
|
|
4347
|
+
provenance?.note("assigned-field", node.left, node.right);
|
|
4348
|
+
changed = true;
|
|
4349
|
+
}
|
|
4265
4350
|
else if (ts.isIdentifier(node.left.expression)) {
|
|
4266
4351
|
const symbol = symbolOf(node.left.expression);
|
|
4267
4352
|
if (symbol && noteFieldSymbol(symbol, node.left.name.text)) {
|
|
@@ -4575,6 +4660,52 @@ export class Lowerer {
|
|
|
4575
4660
|
for (const [symbol, decl] of functionDeclBySymbol)
|
|
4576
4661
|
if (scanReturns(symbol, decl))
|
|
4577
4662
|
changed = true;
|
|
4663
|
+
for (const fn of tsLambdas) {
|
|
4664
|
+
// An annotated return is the author's contract, as for parameters.
|
|
4665
|
+
if (fn.type)
|
|
4666
|
+
continue;
|
|
4667
|
+
const mapped = this.mapTypeOf(this.typeOf(fn));
|
|
4668
|
+
if (mapped?.kind !== "func")
|
|
4669
|
+
continue;
|
|
4670
|
+
const previous = this.runtimeOptionalFunctionReturnType(fn, mapped.ret);
|
|
4671
|
+
// Async, generator and void bodies keep their own result shapes.
|
|
4672
|
+
if (previous.kind === "promise" ||
|
|
4673
|
+
previous.kind === "generator" ||
|
|
4674
|
+
previous.kind === "void")
|
|
4675
|
+
continue;
|
|
4676
|
+
const arithmetic = lambdaArithmeticReturn(fn);
|
|
4677
|
+
const next = arithmetic &&
|
|
4678
|
+
(previous.kind === "f64" || previous.kind === "string" || previous.kind === "bool")
|
|
4679
|
+
? arithmetic
|
|
4680
|
+
: bodyReturnsOptional(fn)
|
|
4681
|
+
? this.runtimeOptionalType(previous)
|
|
4682
|
+
: previous;
|
|
4683
|
+
if (!typeEquals(previous, next)) {
|
|
4684
|
+
this.runtimeOptionalFunctionReturns.set(fn, next);
|
|
4685
|
+
provenance?.note("lambda-return", fn, fn.body);
|
|
4686
|
+
changed = true;
|
|
4687
|
+
}
|
|
4688
|
+
// Calls through the lambda's own binding (`const get = () => xs[i];
|
|
4689
|
+
// get()`) produce the same result as calls of a declared function.
|
|
4690
|
+
const binding = fn.parent;
|
|
4691
|
+
if (next !== previous &&
|
|
4692
|
+
ts.isVariableDeclaration(binding) &&
|
|
4693
|
+
binding.initializer === fn &&
|
|
4694
|
+
!binding.type &&
|
|
4695
|
+
ts.isIdentifier(binding.name)) {
|
|
4696
|
+
const symbol = symbolOf(binding.name);
|
|
4697
|
+
if (symbol && next.kind === "union" && this.armTag(next.unionId, UNDEFINED_T) >= 0) {
|
|
4698
|
+
if (!optionalReturns.has(symbol)) {
|
|
4699
|
+
optionalReturns.add(symbol);
|
|
4700
|
+
changed = true;
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
else if (symbol && !arithmeticReturns.has(symbol)) {
|
|
4704
|
+
arithmeticReturns.set(symbol, next);
|
|
4705
|
+
changed = true;
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4578
4709
|
for (const fn of jsLambdas) {
|
|
4579
4710
|
if (!bodyReturnsOptional(fn))
|
|
4580
4711
|
continue;
|
|
@@ -4606,6 +4737,25 @@ export class Lowerer {
|
|
|
4606
4737
|
}
|
|
4607
4738
|
}
|
|
4608
4739
|
}
|
|
4740
|
+
// NaN-coded slots convert optional values on entry instead of holding
|
|
4741
|
+
// the union, so promotion never applies to them.
|
|
4742
|
+
for (const symbol of [...optionalSymbols])
|
|
4743
|
+
if (nanCoded.nanCodedBinding(symbol))
|
|
4744
|
+
optionalSymbols.delete(symbol);
|
|
4745
|
+
for (const symbol of [...optionalReturns])
|
|
4746
|
+
if (nanCoded.nanCodedReturn(symbol))
|
|
4747
|
+
optionalReturns.delete(symbol);
|
|
4748
|
+
for (const [symbol, params] of optionalParams) {
|
|
4749
|
+
const decl = functionDeclBySymbol.get(symbol);
|
|
4750
|
+
for (const index of [...params]) {
|
|
4751
|
+
const name = decl?.parameters[index]?.name;
|
|
4752
|
+
const param = name && ts.isIdentifier(name) ? symbolOf(name) : null;
|
|
4753
|
+
if (param && nanCoded.nanCodedBinding(param))
|
|
4754
|
+
params.delete(index);
|
|
4755
|
+
}
|
|
4756
|
+
if (params.size === 0)
|
|
4757
|
+
optionalParams.delete(symbol);
|
|
4758
|
+
}
|
|
4609
4759
|
for (const [site, present] of presentSites)
|
|
4610
4760
|
if (present)
|
|
4611
4761
|
this.presentElementCalls.add(site);
|
|
@@ -4678,11 +4828,11 @@ export class Lowerer {
|
|
|
4678
4828
|
const symbol = symbolOf(node.name);
|
|
4679
4829
|
if (!symbol)
|
|
4680
4830
|
return;
|
|
4681
|
-
if (
|
|
4682
|
-
!node.type &&
|
|
4831
|
+
if (!node.type &&
|
|
4683
4832
|
!hasJsTypeAnnotation(node) &&
|
|
4684
4833
|
node.initializer &&
|
|
4685
|
-
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
|
|
4834
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) &&
|
|
4835
|
+
(isJsSourceFile(sf) || this.runtimeOptionalFunctionReturns.has(node.initializer))) {
|
|
4686
4836
|
const global = this.globalsBySymbol.get(symbol);
|
|
4687
4837
|
if (global?.type.kind === "func")
|
|
4688
4838
|
global.type = {
|
|
@@ -8224,7 +8374,23 @@ export class Lowerer {
|
|
|
8224
8374
|
}
|
|
8225
8375
|
/* ── scoping and captures ─────────────────────────────────────────── */
|
|
8226
8376
|
declareLocal(nameNode, name, type, mutable) {
|
|
8227
|
-
|
|
8377
|
+
const symbol = this.checker.getSymbolAtLocation(nameNode);
|
|
8378
|
+
const local = declareContextLocal(this.ctx, name, type, mutable, symbol, bindingSource(nameNode));
|
|
8379
|
+
// A binding declared `T | undefined` that receives a missing read: the
|
|
8380
|
+
// checker narrows it to `T` after the assignment, but the stored value
|
|
8381
|
+
// can be undefined. Track it like widened storage so bare reads keep
|
|
8382
|
+
// the union until a guard proves presence.
|
|
8383
|
+
if (symbol &&
|
|
8384
|
+
this.runtimeOptionalAssignedSymbols.has(symbol) &&
|
|
8385
|
+
type.kind === "union" &&
|
|
8386
|
+
this.armTag(type.unionId, UNDEFINED_T) >= 0 &&
|
|
8387
|
+
this.unions.get(type.unionId)?.arms.length === 2 &&
|
|
8388
|
+
!isUnitType(this.stripUndefinedArm(type))) {
|
|
8389
|
+
const root = this.runtimeOptionalRootOf(local);
|
|
8390
|
+
this.runtimeOptionalStorageLocals.add(root);
|
|
8391
|
+
this.runtimeOptionalLocals.add(root);
|
|
8392
|
+
}
|
|
8393
|
+
return local;
|
|
8228
8394
|
}
|
|
8229
8395
|
/** A never-returning call in a value position of type `expected`: the
|
|
8230
8396
|
* call runs for its effects (it throws or exits), and the expression ends
|