@scriptc/compiler 0.1.1 → 0.1.3
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 +104 -31
- package/ambient/scriptc.d.ts +2 -1
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/c/async.d.ts +5 -0
- package/dist/backend/c/async.js +81 -2
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +25 -10
- package/dist/backend/c/c-emitter.js +74 -22
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.d.ts +8 -1
- package/dist/backend/c/exprs.js +371 -18
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/island.js.map +1 -1
- package/dist/backend/c/may-throw.js.map +1 -1
- package/dist/backend/c/shapes.js +15 -13
- package/dist/backend/c/shapes.js.map +1 -1
- package/dist/backend/c/stmts.d.ts +3 -5
- package/dist/backend/c/stmts.js +58 -17
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/types.d.ts +4 -0
- package/dist/backend/c/types.js +18 -1
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/c/types.test.js +8 -1
- package/dist/backend/c/types.test.js.map +1 -1
- package/dist/backend/c/walkers.js +63 -16
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/external-c.js.map +1 -1
- package/dist/backend/json-literal.d.ts +7 -0
- package/dist/backend/json-literal.js +17 -0
- package/dist/backend/json-literal.js.map +1 -0
- package/dist/backend/json-literal.test.d.ts +1 -0
- package/dist/backend/json-literal.test.js +17 -0
- package/dist/backend/json-literal.test.js.map +1 -0
- package/dist/backend/link-plan.d.ts +1 -1
- package/dist/backend/link-plan.js +5 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/classes.js +8 -7
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +4 -0
- package/dist/backend/llvm/common.js +6 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/common.test.d.ts +1 -0
- package/dist/backend/llvm/common.test.js +9 -0
- package/dist/backend/llvm/common.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +4 -0
- package/dist/backend/llvm/dyn.js +80 -16
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +132 -25
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +85 -3
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +5 -0
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +15 -3
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +8 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +3 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +222 -10
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +53 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +24 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +50 -5
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +64 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +6 -3
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/shapes.js +10 -8
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.js +7 -5
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +2 -0
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +2 -1
- package/dist/backend/native-link-info.js +4 -0
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +24 -1
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +8 -6
- package/dist/backend/native-toolchain.js +28 -13
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +36 -1
- package/dist/backend/native-toolchain.test.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.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +1 -0
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +5 -0
- package/dist/backend/targets.js +9 -0
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +6 -1
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/compat/fetch-profile.js +4 -2
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +13 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +6 -5
- package/dist/diagnostics/diagnostic.js +10 -9
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +1 -0
- package/dist/executable/executable-cache.js +1 -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/ffi/ffi-manifest.js.map +1 -1
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +10 -0
- package/dist/frontend/input-tracker.js +26 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +8 -1
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +4 -0
- package/dist/frontend/lowering/array-values.js +18 -2
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.d.ts +31 -0
- package/dist/frontend/lowering/containers/bytes.js +1105 -0
- package/dist/frontend/lowering/containers/bytes.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
- package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
- package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +7 -2
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +16 -10
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
- package/dist/frontend/lowering/lower-async-iteration.js +657 -0
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +40 -21
- package/dist/frontend/lowering/lower-builtins.js +611 -115
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +21 -10
- package/dist/frontend/lowering/lower-calls.js +516 -75
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -13
- package/dist/frontend/lowering/lower-classes.js +128 -35
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +1 -47
- package/dist/frontend/lowering/lower-containers.js +147 -1475
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +51 -5
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +0 -78
- package/dist/frontend/lowering/lower-exprs.js +503 -2353
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +18 -3
- package/dist/frontend/lowering/lower-generators.js +145 -9
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +10 -3
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +6 -0
- package/dist/frontend/lowering/lower-island.js +37 -5
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +60 -15
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
- package/dist/frontend/lowering/lower-node-module.js +302 -0
- package/dist/frontend/lowering/lower-node-module.js.map +1 -0
- package/dist/frontend/lowering/lower-server.js +1 -1
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +10 -28
- package/dist/frontend/lowering/lower-stmts.js +542 -259
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +4 -4
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +41 -38
- package/dist/frontend/lowering/lowerer.js +148 -62
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
- package/dist/frontend/lowering/optional-arguments.js +57 -0
- package/dist/frontend/lowering/optional-arguments.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +5 -3
- package/dist/frontend/lowering/surfaces.js +33 -13
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +35 -0
- package/dist/frontend/npm-static-declarations.js +519 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -0
- package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
- package/dist/frontend/npm-static-declarations.test.js +132 -0
- package/dist/frontend/npm-static-declarations.test.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +2 -0
- package/dist/frontend/npm-static.js +26 -10
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +60 -0
- package/dist/frontend/npm.js +38 -4
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.d.ts +1 -0
- package/dist/frontend/npm.test.js +50 -0
- package/dist/frontend/npm.test.js.map +1 -0
- package/dist/frontend/program.d.ts +3 -2
- package/dist/frontend/program.js +513 -70
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +26 -3
- package/dist/frontend/resolve.js +166 -29
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +24 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/runtime-resolve.d.ts +22 -0
- package/dist/frontend/runtime-resolve.js +101 -0
- package/dist/frontend/runtime-resolve.js.map +1 -0
- package/dist/frontend/runtime-resolve.test.d.ts +1 -0
- package/dist/frontend/runtime-resolve.test.js +18 -0
- package/dist/frontend/runtime-resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.js +12 -13
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +3 -3
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +3 -0
- package/dist/frontend/ts7/checker.js +12 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/program-adapter.js +42 -3
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/type-mapper.js +171 -16
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +1 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +125 -52
- package/dist/ir/ir.js +136 -7
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +120 -63
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/cache-reset.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/library-cache.d.ts +1 -0
- package/dist/library/library-cache.js +1 -0
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +18 -5
- package/dist/library/library-cache.test.js.map +1 -1
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.js.map +1 -1
- package/dist/wasi-paths.js.map +1 -1
- package/package.json +18 -18
- package/surface-manifest.json +91 -20
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { InternalCompilerError } from "../../errors.js";
|
|
2
2
|
/* Statement lowering: the statement dispatch (lowerStmt), variable
|
|
3
3
|
* declarations including destructuring patterns, scoped blocks, control
|
|
4
|
-
* flow (if/while/for/for-of/do, switch, try/catch,
|
|
5
|
-
*
|
|
4
|
+
* flow (if/while/for/for-of/do, switch, try/catch, abrupt completions),
|
|
5
|
+
* explicit resource management, and blocked-binding poisoning. */
|
|
6
6
|
import * as ts from "../ts7/adapter.js";
|
|
7
7
|
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
8
|
-
import { lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
|
|
8
|
+
import { lowerForAwaitGenerator, lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
|
|
9
|
+
import { lowerForAwaitBuiltin } from "./lower-async-iteration.js";
|
|
9
10
|
import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arrayOf, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
|
|
10
11
|
import { PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, neverTaintedJsType, stmtUsesIsland, uncheckedOverloadHandleCall } from "./lowerer.js";
|
|
11
12
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
@@ -15,20 +16,262 @@ import { isProvenanceSourceFile } from "../provenance-registry.js";
|
|
|
15
16
|
import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget, trapDeclRootOf } from "./lower-namespaces.js";
|
|
16
17
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
17
18
|
import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, lowerSafeIndexRead, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
18
|
-
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
19
|
+
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, implicitMethodCallInfersReturn, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
19
20
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
20
21
|
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
|
21
|
-
import { lowerStreamUnderscoreAssign, streamClassAliasDecl
|
|
22
|
+
import { lowerStreamUnderscoreAssign, streamClassAliasDecl } from "./lower-stream.js";
|
|
22
23
|
import { lowerHttpResPropertyAssignment, lowerHttpServerTimeoutAssignment, lowerServerCloseOverrideAssignment } from "./lower-server.js";
|
|
23
24
|
import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, createRequireBindingDecl, createRequireCalleeFileOf, createRequireNamespaceDecl, textCodecBindingDecl } from "./lower-builtins.js";
|
|
24
25
|
import { lowerEnumDeclaration } from "./lower-enums.js";
|
|
25
|
-
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf,
|
|
26
|
+
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
|
|
27
|
+
import { isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
28
|
+
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
26
29
|
import { checkerPanicDiag, isCheckerPanic, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
27
30
|
import { isParseArgsDynTypeName, isUnitOnlyTsType, unitOnlyUnion } from "../type-mapper.js";
|
|
28
31
|
import { canonicalBuiltinModule } from "../builtin-modules.js";
|
|
29
32
|
import { isRelativeSpecifier } from "../workspace-registry.js";
|
|
30
33
|
import { probeNodeRequireRefusal } from "../npm.js";
|
|
31
34
|
import { numLit, varRef } from "../../ir/build.js";
|
|
35
|
+
import { constituentTypes } from "../ts7/checker.js";
|
|
36
|
+
import { npmStaticPackageOfPath } from "../npm-static.js";
|
|
37
|
+
import { fenceNodeModuleMutation } from "./lower-node-module.js";
|
|
38
|
+
/** An opted package's directly-returned empty array may take the function's
|
|
39
|
+
* explicit static return element type. This is the common JS accumulator
|
|
40
|
+
* shape (`const result = []; result.push(value); return result`) and is
|
|
41
|
+
* narrower than treating every evolving array in the function alike. */
|
|
42
|
+
function npmStaticReturnedEmptyArrayType(lowerer, decl) {
|
|
43
|
+
if (!ts.isIdentifier(decl.name) ||
|
|
44
|
+
decl.initializer === undefined ||
|
|
45
|
+
!ts.isArrayLiteralExpression(decl.initializer) ||
|
|
46
|
+
decl.initializer.elements.length !== 0 ||
|
|
47
|
+
!isJsSourceFile(decl.getSourceFile()) ||
|
|
48
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
|
|
49
|
+
lowerer.ctx.returnType.kind !== "array") {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
53
|
+
if (symbol === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
let owner = decl.parent;
|
|
56
|
+
while (owner !== undefined && !ts.isFunctionLike(owner))
|
|
57
|
+
owner = owner.parent;
|
|
58
|
+
const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
|
|
59
|
+
if (ownerBody === undefined || !ts.isBlock(ownerBody))
|
|
60
|
+
return null;
|
|
61
|
+
let returned = false;
|
|
62
|
+
ts.walkPreorder(ownerBody, (node) => {
|
|
63
|
+
if (node !== ownerBody && ts.isFunctionLike(node))
|
|
64
|
+
return "skip";
|
|
65
|
+
if (!ts.isReturnStatement(node) || node.expression === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
let expression = node.expression;
|
|
68
|
+
while (ts.isParenthesizedExpression(expression))
|
|
69
|
+
expression = expression.expression;
|
|
70
|
+
if (ts.isIdentifier(expression) && lowerer.checker.getSymbolAtLocation(expression) === symbol) {
|
|
71
|
+
returned = true;
|
|
72
|
+
return "stop";
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
});
|
|
76
|
+
return returned ? lowerer.ctx.returnType : null;
|
|
77
|
+
}
|
|
78
|
+
/** A JS loop cursor in opted package source can be initialized with `T`
|
|
79
|
+
* and later assigned a nullable self link (`cursor = cursor.parent`). Keep
|
|
80
|
+
* that optional value in the local so the next loop condition, rather than
|
|
81
|
+
* the assignment, narrows it. */
|
|
82
|
+
function npmStaticOptionalSelfWriteType(lowerer, decl, initial) {
|
|
83
|
+
if (!ts.isIdentifier(decl.name) ||
|
|
84
|
+
!isJsSourceFile(decl.getSourceFile()) ||
|
|
85
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
|
|
86
|
+
initial.kind === "union") {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
90
|
+
if (symbol === undefined)
|
|
91
|
+
return null;
|
|
92
|
+
let owner = decl.parent;
|
|
93
|
+
while (owner !== undefined && !ts.isFunctionLike(owner))
|
|
94
|
+
owner = owner.parent;
|
|
95
|
+
const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
|
|
96
|
+
if (ownerBody === undefined || !ts.isBlock(ownerBody))
|
|
97
|
+
return null;
|
|
98
|
+
let candidate = null;
|
|
99
|
+
let invalid = false;
|
|
100
|
+
ts.walkPreorder(ownerBody, (node) => {
|
|
101
|
+
if (node !== ownerBody && ts.isFunctionLike(node))
|
|
102
|
+
return "skip";
|
|
103
|
+
if (ts.isBinaryExpression(node)) {
|
|
104
|
+
let left = node.left;
|
|
105
|
+
while (ts.isParenthesizedExpression(left))
|
|
106
|
+
left = left.expression;
|
|
107
|
+
if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
|
|
108
|
+
if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
|
|
109
|
+
invalid = true;
|
|
110
|
+
return "stop";
|
|
111
|
+
}
|
|
112
|
+
const written = lowerer.mapTypeOf(lowerer.typeOf(node.right));
|
|
113
|
+
if (written !== null && typeEquals(written, initial))
|
|
114
|
+
return undefined;
|
|
115
|
+
const arms = written?.kind === "union" ? lowerer.unions.get(written.unionId)?.arms : undefined;
|
|
116
|
+
if (written?.kind !== "union" ||
|
|
117
|
+
arms === undefined ||
|
|
118
|
+
!arms.some((arm) => typeEquals(arm, initial)) ||
|
|
119
|
+
!arms.every((arm) => typeEquals(arm, initial) || isUnitType(arm)) ||
|
|
120
|
+
(candidate !== null && !typeEquals(candidate, written))) {
|
|
121
|
+
invalid = true;
|
|
122
|
+
return "stop";
|
|
123
|
+
}
|
|
124
|
+
candidate = written;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
128
|
+
ts.isIdentifier(node.operand) &&
|
|
129
|
+
lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
|
|
130
|
+
invalid = true;
|
|
131
|
+
return "stop";
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
});
|
|
135
|
+
return invalid ? null : candidate;
|
|
136
|
+
}
|
|
137
|
+
function isAwaitUsing(list) {
|
|
138
|
+
return (list.flags & ts.NodeFlags.AwaitUsing) === ts.NodeFlags.AwaitUsing;
|
|
139
|
+
}
|
|
140
|
+
function stdlibTypeNamed(lowerer, node, name) {
|
|
141
|
+
const visit = (t) => {
|
|
142
|
+
if (t.flags & ts.TypeFlags.Union)
|
|
143
|
+
return constituentTypes(t).some(visit);
|
|
144
|
+
const sym = t.getAliasSymbol() ?? t.getSymbol();
|
|
145
|
+
return sym?.name === name && lowerer.checker.declarationsOf(sym).some((d) => lowerer.isStdlibFile(d.getSourceFile()));
|
|
146
|
+
};
|
|
147
|
+
return visit(lowerer.typeOf(node));
|
|
148
|
+
}
|
|
149
|
+
function cleanupCall(lowerer, node, value, awaited) {
|
|
150
|
+
const loc = locOf(node);
|
|
151
|
+
if (value.type.kind === "object") {
|
|
152
|
+
const info = lowerer.classes.get(value.type.className);
|
|
153
|
+
if (!info)
|
|
154
|
+
lowerer.flushDeferredClass(value.type.className);
|
|
155
|
+
const asyncFound = awaited && info ? lowerer.findMethodOn(info, "sym:asyncDispose") : null;
|
|
156
|
+
const syncFound = info ? lowerer.findMethodOn(info, "sym:dispose") : null;
|
|
157
|
+
const member = asyncFound ? "sym:asyncDispose" : syncFound ? "sym:dispose" : null;
|
|
158
|
+
const found = asyncFound ?? syncFound;
|
|
159
|
+
if (!info || !member || !found)
|
|
160
|
+
return null;
|
|
161
|
+
if (found.sig.params.length !== 0) {
|
|
162
|
+
lowerer.unsupported("SC1090", node, `${member === "sym:asyncDispose" ? "[Symbol.asyncDispose]" : "[Symbol.dispose]"} methods with parameters`);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
call: lowerer.accessorCall(value.type.className, member, value, [], found.sig.ret, loc),
|
|
166
|
+
async: member === "sym:asyncDispose",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (value.type.kind === "fileHandle") {
|
|
170
|
+
if (!awaited)
|
|
171
|
+
lowerer.unsupported("SC1090", node, "synchronous 'using' with an async-only FileHandle (use 'await using')");
|
|
172
|
+
return {
|
|
173
|
+
call: { kind: "libCall", fn: "fileHandle.close", args: [value], type: { kind: "promise", inner: VOID }, loc },
|
|
174
|
+
async: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (value.type.kind === "child") {
|
|
178
|
+
return {
|
|
179
|
+
call: {
|
|
180
|
+
kind: "libCall",
|
|
181
|
+
fn: "child.kill",
|
|
182
|
+
args: [value, { kind: "strLit", value: "SIGTERM", type: STRING, loc }],
|
|
183
|
+
type: BOOL,
|
|
184
|
+
loc,
|
|
185
|
+
},
|
|
186
|
+
async: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (value.type.kind === "f64") {
|
|
190
|
+
if (stdlibTypeNamed(lowerer, node, "Timeout")) {
|
|
191
|
+
return { call: { kind: "libCall", fn: "timers.clearTimeout", args: [value], type: VOID, loc }, async: false };
|
|
192
|
+
}
|
|
193
|
+
if (stdlibTypeNamed(lowerer, node, "Immediate")) {
|
|
194
|
+
return { call: { kind: "libCall", fn: "timers.clearImmediate", args: [value], type: VOID, loc }, async: false };
|
|
195
|
+
}
|
|
196
|
+
if (stdlibTypeNamed(lowerer, node, "Interface")) {
|
|
197
|
+
return { call: { kind: "libCall", fn: "rl.close", args: [value], type: VOID, loc }, async: false };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
function cleanupForValue(lowerer, node, value, awaited) {
|
|
203
|
+
const loc = locOf(node);
|
|
204
|
+
if (isUnitType(value.type))
|
|
205
|
+
return [];
|
|
206
|
+
if (value.type.kind === "union") {
|
|
207
|
+
const def = lowerer.unions.get(value.type.unionId);
|
|
208
|
+
if (!def)
|
|
209
|
+
throw new InternalCompilerError(`lowerer bug: unknown using union ${value.type.unionId}`);
|
|
210
|
+
const out = [];
|
|
211
|
+
for (let tag = 0; tag < def.arms.length; tag++) {
|
|
212
|
+
const arm = def.arms[tag];
|
|
213
|
+
if (isUnitType(arm))
|
|
214
|
+
continue;
|
|
215
|
+
const narrowed = { kind: "unionNarrow", unionId: value.type.unionId, tag, value, type: arm, loc };
|
|
216
|
+
const body = cleanupForValue(lowerer, node, narrowed, awaited);
|
|
217
|
+
out.push({
|
|
218
|
+
kind: "if",
|
|
219
|
+
cond: { kind: "unionIsTag", unionId: value.type.unionId, tag, negated: false, value, type: BOOL, loc },
|
|
220
|
+
then: body,
|
|
221
|
+
else_: null,
|
|
222
|
+
loc,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
const cleanup = cleanupCall(lowerer, node, value, awaited);
|
|
228
|
+
if (!cleanup) {
|
|
229
|
+
lowerer.unsupported("SC1090", node, `disposing values of type '${lowerer.fmt(value.type)}' (use a class with a zero-parameter [Symbol.dispose] or [Symbol.asyncDispose] method, or a supported Node resource)`);
|
|
230
|
+
}
|
|
231
|
+
const call = cleanup.call;
|
|
232
|
+
if (cleanup.async) {
|
|
233
|
+
if (call.type.kind !== "promise" || call.type.inner.kind !== "void") {
|
|
234
|
+
lowerer.unsupported("SC1090", node, "[Symbol.asyncDispose] methods whose result is not Promise<void>");
|
|
235
|
+
}
|
|
236
|
+
return [{ kind: "exprStmt", expr: { kind: "awaitExpr", value: call, type: VOID, loc }, loc }];
|
|
237
|
+
}
|
|
238
|
+
const out = [{ kind: "exprStmt", expr: call, loc }];
|
|
239
|
+
if (awaited)
|
|
240
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "async.hop", args: [], type: VOID, loc }, loc });
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
function lowerUsingStatement(lowerer, stmt) {
|
|
244
|
+
if (ts.isSourceFile(stmt.parent)) {
|
|
245
|
+
lowerer.unsupported("SC1090", stmt, "top-level 'using' declarations (wrap the resource lifetime in a block or function)");
|
|
246
|
+
}
|
|
247
|
+
if (ts.isCaseClause(stmt.parent) || ts.isDefaultClause(stmt.parent)) {
|
|
248
|
+
lowerer.unsupported("SC1090", stmt, "'using' declarations directly in switch clauses (wrap the clause body in a block)");
|
|
249
|
+
}
|
|
250
|
+
const awaited = isAwaitUsing(stmt.declarationList);
|
|
251
|
+
if (awaited && !lowerer.ctx.isAsync) {
|
|
252
|
+
lowerer.unsupported("SC1090", stmt, "'await using' outside an async function or top-level-await module");
|
|
253
|
+
}
|
|
254
|
+
const registrations = [];
|
|
255
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
256
|
+
if (!ts.isIdentifier(decl.name))
|
|
257
|
+
lowerer.unsupported("SC1031", decl.name);
|
|
258
|
+
if (decl.initializer === undefined)
|
|
259
|
+
lowerer.unsupported("SC1090", decl, "a 'using' declaration without an initializer");
|
|
260
|
+
const acquire = lowerer.lowerVarDecl(decl, false);
|
|
261
|
+
if (!acquire)
|
|
262
|
+
throw new InternalCompilerError("lowerer bug: using declaration emitted no acquisition");
|
|
263
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
264
|
+
if (!symbol)
|
|
265
|
+
throw new InternalCompilerError("lowerer bug: using declaration has no symbol");
|
|
266
|
+
const binding = lowerer.bindingIn(lowerer.ctx, symbol) ?? lowerer.globalsBySymbol.get(symbol) ?? null;
|
|
267
|
+
if (!binding)
|
|
268
|
+
throw new InternalCompilerError("lowerer bug: using declaration has no runtime binding");
|
|
269
|
+
const value = varRef(binding.id, binding.type, locOf(decl.name));
|
|
270
|
+
const cleanup = cleanupForValue(lowerer, decl.name, value, awaited);
|
|
271
|
+
registrations.push({ acquire, cleanup, loc: locOf(decl) });
|
|
272
|
+
}
|
|
273
|
+
return registrations;
|
|
274
|
+
}
|
|
32
275
|
/** `const X = /* @__PURE__ *\/ makeX()` at a module's top level: every
|
|
33
276
|
* declarator initialized by a call (or `new`) carrying the bundler PURE
|
|
34
277
|
* annotation in its leading trivia — the shape --provenance-sources may
|
|
@@ -113,6 +356,7 @@ export function provenanceElidedConstDecl(lowerer, decl) {
|
|
|
113
356
|
* `let a = 1, b = a + 1;`) but is counted once. */
|
|
114
357
|
export function lowerStmts(lowerer, stmts) {
|
|
115
358
|
const out = [];
|
|
359
|
+
const usingMarkers = [];
|
|
116
360
|
// JAVASCRIPT sources defer their compile fences to runtime (the
|
|
117
361
|
// JS-input design: no annotations exist to change what lowers, so a
|
|
118
362
|
// statement whose construct has no static lowering compiles to a
|
|
@@ -144,7 +388,25 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
144
388
|
}
|
|
145
389
|
const diagsBefore = lowerer.diags.length;
|
|
146
390
|
try {
|
|
147
|
-
|
|
391
|
+
let lowered;
|
|
392
|
+
let appended = false;
|
|
393
|
+
if (ts.isVariableStatement(stmt) &&
|
|
394
|
+
(stmt.declarationList.flags & ts.NodeFlags.Using) !== 0) {
|
|
395
|
+
const registrations = lowerUsingStatement(lowerer, stmt);
|
|
396
|
+
const accounting = [];
|
|
397
|
+
for (const registration of registrations) {
|
|
398
|
+
out.push(registration.acquire);
|
|
399
|
+
accounting.push(registration.acquire, ...registration.cleanup);
|
|
400
|
+
if (registration.cleanup.length > 0) {
|
|
401
|
+
usingMarkers.push({ start: out.length, cleanup: registration.cleanup, loc: registration.loc });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
lowered = accounting;
|
|
405
|
+
appended = true;
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
lowered = lowerer.lowerStmt(stmt);
|
|
409
|
+
}
|
|
148
410
|
// The lib-boundary chokepoint (lib-boundary.ts): checked-dynamic
|
|
149
411
|
// arguments that reached builtin-call slots without the checked
|
|
150
412
|
// coercion get their dynCheck here, and the uncoercible ones
|
|
@@ -153,10 +415,12 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
153
415
|
// deferred rejection.
|
|
154
416
|
if (lowered)
|
|
155
417
|
enforceLibBoundary(lowerer, lowered);
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
418
|
+
if (!appended) {
|
|
419
|
+
if (Array.isArray(lowered))
|
|
420
|
+
out.push(...lowered);
|
|
421
|
+
else if (lowered)
|
|
422
|
+
out.push(lowered);
|
|
423
|
+
}
|
|
160
424
|
// Island accounting (--dynamic coverage): a statement that lowered
|
|
161
425
|
// but carries island constructs compiles DYNAMICALLY — its work
|
|
162
426
|
// runs in the embedded engine, and the report says so instead of
|
|
@@ -215,6 +479,23 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
215
479
|
}
|
|
216
480
|
}
|
|
217
481
|
}
|
|
482
|
+
// Each acquisition protects the remainder of its lexical statement
|
|
483
|
+
// list. Folding from the last registration outward produces the
|
|
484
|
+
// spec's LIFO nesting and ensures a later initializer failure only
|
|
485
|
+
// disposes resources acquired before it.
|
|
486
|
+
for (let i = usingMarkers.length - 1; i >= 0; i--) {
|
|
487
|
+
const marker = usingMarkers[i];
|
|
488
|
+
const body = out.splice(marker.start);
|
|
489
|
+
out.push({
|
|
490
|
+
kind: "tryCatch",
|
|
491
|
+
tryBody: body,
|
|
492
|
+
catchBody: null,
|
|
493
|
+
catchLocalId: null,
|
|
494
|
+
finallyBody: marker.cleanup,
|
|
495
|
+
suppressFinallyErrors: true,
|
|
496
|
+
loc: marker.loc,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
218
499
|
}
|
|
219
500
|
finally {
|
|
220
501
|
lowerer.activeStmtLists.pop();
|
|
@@ -237,7 +518,7 @@ const TDZ_KINDS = new Set([
|
|
|
237
518
|
// shape is the const's own initializer capturing the const:
|
|
238
519
|
// `const server = http.createServer(h).listen(0, done)` with `done`
|
|
239
520
|
// reading `server` — a TDZ read until the assign completes, JS exactly.
|
|
240
|
-
"child", "childStream", "netServer", "netSocket", "dgramSocket", "testCtx",
|
|
521
|
+
"child", "childStream", "childWriter", "netServer", "netSocket", "dgramSocket", "testCtx",
|
|
241
522
|
"httpReq", "httpRes", "httpClientReq",
|
|
242
523
|
// The h2 handles — `const client = http2.connect(url, mustCall(() =>
|
|
243
524
|
// ...client...))` is the suite's canonical self-capturing const.
|
|
@@ -313,10 +594,11 @@ export function predeclareForwardCapture(lowerer, symbol) {
|
|
|
313
594
|
/** The FUNCTION-DECLARATION twin of predeclareForwardCapture: JS hoists a
|
|
314
595
|
* nested `function f() {}` to scope entry — the binding is LIVE from the
|
|
315
596
|
* first statement, so a reference lexically above the declaration in the
|
|
316
|
-
* SAME function lowers the declaration eagerly at the reference (
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
597
|
+
* SAME function lowers the declaration eagerly at the reference (storage
|
|
598
|
+
* declaration and initializing assignment are pushed before the current
|
|
599
|
+
* statement's IR, registered in the list's scope frame) and the statement
|
|
600
|
+
* loop skips the source statement when it arrives
|
|
601
|
+
* (lowerer.hoistedFnDecls). Same-function only: the declaration's own
|
|
320
602
|
* lowering runs under the ctx that owns its statement list, and a
|
|
321
603
|
* cross-function early capture would need lowering under a DIFFERENT
|
|
322
604
|
* ctx than the one currently open — that shape keeps the honest fence. */
|
|
@@ -589,44 +871,6 @@ export function lowerScopedBlock(lowerer, stmt) {
|
|
|
589
871
|
lowerer.scopes.pop();
|
|
590
872
|
}
|
|
591
873
|
}
|
|
592
|
-
/** The finally fences. `return` crossing OUT of a try/catch body guarded
|
|
593
|
-
* by a finally is SUPPORTED now (the backend's pending-return path runs
|
|
594
|
-
* every crossed finally inner-to-outer before the function returns, the
|
|
595
|
-
* value snapshotted first — Node-exact); what stays rejected is (a) any
|
|
596
|
-
* jump out of a finally BLOCK itself (a return there would REPLACE a
|
|
597
|
-
* pending completion — clearing a still-propagating exception or
|
|
598
|
-
* abandoning a pending return, a model the emitter doesn't implement),
|
|
599
|
-
* and (b) break/continue crossing a try-with-finally (their targets bind
|
|
600
|
-
* inside the function; the pending-action plumbing exists only for
|
|
601
|
-
* return, the shape real code needs). Rejected, not miscompiled. Plain
|
|
602
|
-
* try/catch is transparent to jumps (it never appears in ctl). A LABELED
|
|
603
|
-
* jump walks outward past every construct (labeled targets can be
|
|
604
|
-
* arbitrarily far out) until the entry carrying its label, applying the
|
|
605
|
-
* same finally fences to everything crossed on the way. */
|
|
606
|
-
export function rejectJumpCrossingFinally(lowerer, kw, stmt, label) {
|
|
607
|
-
const ctl = lowerer.ctx.ctl;
|
|
608
|
-
for (let i = ctl.length - 1; i >= 0; i--) {
|
|
609
|
-
const c = ctl[i];
|
|
610
|
-
if (c.kind === "finallyBlock") {
|
|
611
|
-
lowerer.unsupported("SC1090", stmt, `'${kw}' out of a 'finally' block (it would replace the pending completion — restructure so the finally only cleans up)`);
|
|
612
|
-
}
|
|
613
|
-
if (c.kind === "tryFinally") {
|
|
614
|
-
if (kw !== "return") {
|
|
615
|
-
lowerer.unsupported("SC1090", stmt, `'${kw}' crossing a 'finally' block`);
|
|
616
|
-
}
|
|
617
|
-
continue; // return runs the finally on the way out — supported
|
|
618
|
-
}
|
|
619
|
-
if (kw === "return")
|
|
620
|
-
continue; // return crosses every construct
|
|
621
|
-
if (label !== undefined) {
|
|
622
|
-
if (c.labels?.includes(label))
|
|
623
|
-
return; // the labeled target — binds here
|
|
624
|
-
continue; // an inner construct the labeled jump exits
|
|
625
|
-
}
|
|
626
|
-
if (c.kind === "loop" || (kw === "break" && c.kind === "switch"))
|
|
627
|
-
return; // binds inside the region
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
874
|
/** `lbl: stmt` — labeled statements. A label chain collapses to one name
|
|
631
875
|
* list (`a: b: while ...` — both names target the same loop, JS-exact).
|
|
632
876
|
* Loops and switches take the labels DIRECTLY (a labeled continue needs
|
|
@@ -720,7 +964,6 @@ export function lowerStmt(lowerer, stmt) {
|
|
|
720
964
|
if (ts.isLabeledStatement(stmt))
|
|
721
965
|
return lowerLabeled(lowerer, stmt);
|
|
722
966
|
if (ts.isReturnStatement(stmt)) {
|
|
723
|
-
lowerer.rejectJumpCrossingFinally("return", stmt);
|
|
724
967
|
// Implicit-any instance RETURN INFERENCE (lower-calls'
|
|
725
968
|
// resolveInferredReturn): the value lowers BARE and the statement
|
|
726
969
|
// records itself — the post-pass settles the instance's return type
|
|
@@ -784,10 +1027,8 @@ export function lowerStmt(lowerer, stmt) {
|
|
|
784
1027
|
if (!target || (kw === "continue" && target.kind !== "loop")) {
|
|
785
1028
|
lowerer.unsupported("SC1050", stmt, `'${kw} ${label}' targeting this statement form (the labeled statement lowers through a desugar that has no label point)`);
|
|
786
1029
|
}
|
|
787
|
-
lowerer.rejectJumpCrossingFinally(kw, stmt, label);
|
|
788
1030
|
return { kind: kw, label, loc: locOf(stmt) };
|
|
789
1031
|
}
|
|
790
|
-
lowerer.rejectJumpCrossingFinally(kw, stmt);
|
|
791
1032
|
// tsc rejects break outside loops/switches and continue outside loops,
|
|
792
1033
|
// so the context is always valid here (the validator re-checks).
|
|
793
1034
|
return { kind: kw, loc: locOf(stmt) };
|
|
@@ -846,7 +1087,8 @@ export function lowerStmt(lowerer, stmt) {
|
|
|
846
1087
|
return null;
|
|
847
1088
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
848
1089
|
// Already lowered eagerly by the forward-hoisting machinery
|
|
849
|
-
// (predeclareForwardFnDecl) —
|
|
1090
|
+
// (predeclareForwardFnDecl) — its declaration and assignment are in
|
|
1091
|
+
// the list's output.
|
|
850
1092
|
if (lowerer.hoistedFnDecls.has(stmt))
|
|
851
1093
|
return null;
|
|
852
1094
|
return lowerer.lowerNestedFunctionDecl(stmt);
|
|
@@ -2904,6 +3146,95 @@ export function lowerVarDeclList(lowerer, list) {
|
|
|
2904
3146
|
throw new InternalCompilerError("lowerer bug: for-init declarator resolved to a global");
|
|
2905
3147
|
return lowered;
|
|
2906
3148
|
}
|
|
3149
|
+
/** A single static type for an initializer-less JavaScript `let` whose
|
|
3150
|
+
* later plain writes all agree. An evolving JS local reads as `undefined`
|
|
3151
|
+
* until control flow proves a write, so a scalar slot is sound only when
|
|
3152
|
+
* every ordinary use already has that concrete flow type. Any closure
|
|
3153
|
+
* capture stays checked-dynamic: it can observe the local before an outer
|
|
3154
|
+
* assignment, and the checker deliberately leaves captured evolving lets
|
|
3155
|
+
* broad. */
|
|
3156
|
+
function inferredEvolvingLetType(lowerer, decl) {
|
|
3157
|
+
if (!ts.isIdentifier(decl.name))
|
|
3158
|
+
return null;
|
|
3159
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
3160
|
+
const scope = decl.parent.parent.parent;
|
|
3161
|
+
if (symbol === undefined || (!ts.isBlock(scope) && !ts.isSourceFile(scope)))
|
|
3162
|
+
return null;
|
|
3163
|
+
const isPlainAssignmentTarget = (node) => {
|
|
3164
|
+
let target = node;
|
|
3165
|
+
while (ts.isParenthesizedExpression(target.parent) && target.parent.expression === target) {
|
|
3166
|
+
target = target.parent;
|
|
3167
|
+
}
|
|
3168
|
+
const parent = target.parent;
|
|
3169
|
+
return ts.isBinaryExpression(parent) &&
|
|
3170
|
+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
3171
|
+
parent.left === target;
|
|
3172
|
+
};
|
|
3173
|
+
const captures = (fn) => {
|
|
3174
|
+
let captured = false;
|
|
3175
|
+
ts.walkPreorder(fn, (node) => {
|
|
3176
|
+
if (node !== fn &&
|
|
3177
|
+
ts.isIdentifier(node) &&
|
|
3178
|
+
lowerer.checker.getSymbolAtLocation(node) === symbol) {
|
|
3179
|
+
captured = true;
|
|
3180
|
+
return "stop";
|
|
3181
|
+
}
|
|
3182
|
+
return undefined;
|
|
3183
|
+
});
|
|
3184
|
+
return captured;
|
|
3185
|
+
};
|
|
3186
|
+
const writes = [];
|
|
3187
|
+
const reads = [];
|
|
3188
|
+
let invalid = false;
|
|
3189
|
+
ts.walkPreorder(scope, (node) => {
|
|
3190
|
+
if (node !== scope && ts.isFunctionLike(node)) {
|
|
3191
|
+
if (captures(node))
|
|
3192
|
+
invalid = true;
|
|
3193
|
+
return "skip";
|
|
3194
|
+
}
|
|
3195
|
+
if (ts.isBinaryExpression(node)) {
|
|
3196
|
+
let left = node.left;
|
|
3197
|
+
while (ts.isParenthesizedExpression(left))
|
|
3198
|
+
left = left.expression;
|
|
3199
|
+
if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
|
|
3200
|
+
if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
|
|
3201
|
+
invalid = true;
|
|
3202
|
+
return "stop";
|
|
3203
|
+
}
|
|
3204
|
+
if (ts.isCallExpression(node.right) && implicitMethodCallInfersReturn(lowerer, node.right)) {
|
|
3205
|
+
invalid = true;
|
|
3206
|
+
return "stop";
|
|
3207
|
+
}
|
|
3208
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(node.right));
|
|
3209
|
+
if (!mapped || mapped.kind === "dyn" || mapped.kind === "jsval" || mapped.kind === "void" || isUnitType(mapped)) {
|
|
3210
|
+
invalid = true;
|
|
3211
|
+
return "stop";
|
|
3212
|
+
}
|
|
3213
|
+
writes.push(mapped);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
3217
|
+
ts.isIdentifier(node.operand) &&
|
|
3218
|
+
lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
|
|
3219
|
+
invalid = true;
|
|
3220
|
+
return "stop";
|
|
3221
|
+
}
|
|
3222
|
+
if (ts.isIdentifier(node) &&
|
|
3223
|
+
node !== decl.name &&
|
|
3224
|
+
lowerer.checker.getSymbolAtLocation(node) === symbol &&
|
|
3225
|
+
!isPlainAssignmentTarget(node)) {
|
|
3226
|
+
reads.push(node);
|
|
3227
|
+
}
|
|
3228
|
+
return undefined;
|
|
3229
|
+
});
|
|
3230
|
+
const first = writes[0];
|
|
3231
|
+
if (invalid || first === undefined || !writes.every((write) => typeEquals(write, first)))
|
|
3232
|
+
return null;
|
|
3233
|
+
return reads.every((read) => {
|
|
3234
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(read));
|
|
3235
|
+
return mapped !== null && typeEquals(mapped, first);
|
|
3236
|
+
}) ? first : null;
|
|
3237
|
+
}
|
|
2907
3238
|
export function lowerVarDecl(lowerer, decl, isLet) {
|
|
2908
3239
|
// --provenance-sources: an elided pure-annotated dead const emits
|
|
2909
3240
|
// nothing (collectGlobals registered no global by the same test —
|
|
@@ -3163,6 +3494,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3163
3494
|
// immediately (the type covers the unassigned state), so they
|
|
3164
3495
|
// initialize to the interned undefined arm like an omitted optional.
|
|
3165
3496
|
let type = lowerer.irTypeOf(decl.name);
|
|
3497
|
+
if (type.kind === "dyn" && isLet && isJsSourceFile(decl.getSourceFile())) {
|
|
3498
|
+
type = inferredEvolvingLetType(lowerer, decl) ?? type;
|
|
3499
|
+
}
|
|
3166
3500
|
// `var v: void;` / `let x: undefined;` — a unit-only binding rides
|
|
3167
3501
|
// the unit-only union (its undefined arm is the unassigned state,
|
|
3168
3502
|
// which is also the only state).
|
|
@@ -3233,6 +3567,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3233
3567
|
(lowerer.dynamic && init.type.kind === "dyn" && jsvalFlavoredType(lowerer.mapTypeOf(lowerer.typeOf(decl.name)) ?? DYN) ? DYN : null) ??
|
|
3234
3568
|
(bindingTainted ? null : lowerer.mapTypeOf(lowerer.typeOf(decl.name))) ??
|
|
3235
3569
|
(init.type.kind === "dyn" ? DYN : null);
|
|
3570
|
+
if (lowerer.implicitParamTypes !== null &&
|
|
3571
|
+
init.type.kind === "dyn" &&
|
|
3572
|
+
isJsSourceFile(decl.getSourceFile()) &&
|
|
3573
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) !== null) {
|
|
3574
|
+
type = DYN;
|
|
3575
|
+
}
|
|
3236
3576
|
// A JS `let x = {}`: TS's empty-object-literal type admits ANY later
|
|
3237
3577
|
// non-nullish assignment (`envs = {}`, later `envs =
|
|
3238
3578
|
// Object.fromEntries(...)` — tsc accepts every such write, since
|
|
@@ -3245,6 +3585,11 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3245
3585
|
if (shape && shape.fields.length === 0 && !shape.indexValue && !shape.tuple)
|
|
3246
3586
|
type = DYN;
|
|
3247
3587
|
}
|
|
3588
|
+
const returnedEmptyArrayType = npmStaticReturnedEmptyArrayType(lowerer, decl);
|
|
3589
|
+
if (returnedEmptyArrayType !== null) {
|
|
3590
|
+
type = returnedEmptyArrayType;
|
|
3591
|
+
init = lowerer.lowerExprExpecting(decl.initializer, returnedEmptyArrayType);
|
|
3592
|
+
}
|
|
3248
3593
|
// A JS `const leaked = [];` (the evolving-array idiom — test/common's
|
|
3249
3594
|
// leak ledger): tsc types the binding by its LATER pushes, but this
|
|
3250
3595
|
// frontend answers the declaration name with the uninhabited never[],
|
|
@@ -3254,7 +3599,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3254
3599
|
// checked-dynamic instead (the `let x = {}` stance, and lower-modules'
|
|
3255
3600
|
// file-scope evolving-array rule): pushes ride the dyn array, typed
|
|
3256
3601
|
// exits dynCheck.
|
|
3257
|
-
if (
|
|
3602
|
+
if (returnedEmptyArrayType === null &&
|
|
3603
|
+
(type === null || type.kind === "array") &&
|
|
3604
|
+
isJsSourceFile(decl.getSourceFile())) {
|
|
3258
3605
|
let initExpr = decl.initializer;
|
|
3259
3606
|
while (ts.isParenthesizedExpression(initExpr))
|
|
3260
3607
|
initExpr = initExpr.expression;
|
|
@@ -3316,14 +3663,15 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3316
3663
|
// their tuple record likewise). Const only — an evolving-`any`
|
|
3317
3664
|
// `let` may be reassigned a different shape later; genuine `any`
|
|
3318
3665
|
// only — every other unmappable keeps its own diagnostic.
|
|
3319
|
-
|
|
3320
|
-
|
|
3666
|
+
const checkerAny = (lowerer.typeOf(decl.name).flags & ts.TypeFlags.Any) !== 0;
|
|
3667
|
+
if ((type === null || (type.kind === "dyn" && checkerAny)) && !isLet &&
|
|
3668
|
+
(checkerAny ||
|
|
3321
3669
|
(lowerer.checkerAnyArray(decl.name) && lowerer.isArrayValueType(init.type)) ||
|
|
3322
3670
|
// JS declarations carry no annotations: an unmappable inferred
|
|
3323
3671
|
// type (a union with a fence-folded arm — the typeof-'bigint'
|
|
3324
3672
|
// dual-mode ternary) adopts the initializer's static type, the
|
|
3325
3673
|
// same rule genuine `any` consts take.
|
|
3326
|
-
isJsSourceFile(decl.getSourceFile())) &&
|
|
3674
|
+
(type === null && isJsSourceFile(decl.getSourceFile()))) &&
|
|
3327
3675
|
init.type.kind !== "void" && init.type.kind !== "caught" && init.type.kind !== "jsval" &&
|
|
3328
3676
|
!isUnitType(init.type)) {
|
|
3329
3677
|
type = init.type;
|
|
@@ -3338,6 +3686,7 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3338
3686
|
}
|
|
3339
3687
|
if (!type)
|
|
3340
3688
|
lowerer.badType(decl.name, lowerer.typeOf(decl.name));
|
|
3689
|
+
let settledType = type;
|
|
3341
3690
|
const arithmeticType = decl.initializer ? lowerer.runtimeOptionalArithmeticTypes.get(decl.initializer) : undefined;
|
|
3342
3691
|
const isStringArithmeticUnion = (t) => {
|
|
3343
3692
|
if (t.kind !== "union")
|
|
@@ -3346,35 +3695,42 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3346
3695
|
return !!arms && arms.length === 2 &&
|
|
3347
3696
|
arms.some((a) => a.kind === "f64") && arms.some((a) => a.kind === "string");
|
|
3348
3697
|
};
|
|
3349
|
-
const runtimeStringArithmetic =
|
|
3698
|
+
const runtimeStringArithmetic = settledType.kind === "string" &&
|
|
3350
3699
|
((arithmeticType && typeEquals(arithmeticType, init.type)) || isStringArithmeticUnion(init.type));
|
|
3351
3700
|
if (runtimeStringArithmetic) {
|
|
3352
|
-
|
|
3701
|
+
settledType = init.type;
|
|
3353
3702
|
}
|
|
3354
|
-
|
|
3703
|
+
const optionalSelfWriteType = isLet
|
|
3704
|
+
? npmStaticOptionalSelfWriteType(lowerer, decl, settledType)
|
|
3705
|
+
: null;
|
|
3706
|
+
settledType = lowerer.runtimeOptionalBindingType(decl.name, settledType);
|
|
3707
|
+
if (optionalSelfWriteType !== null)
|
|
3708
|
+
settledType = optionalSelfWriteType;
|
|
3709
|
+
if (returnedEmptyArrayType !== null)
|
|
3710
|
+
settledType = returnedEmptyArrayType;
|
|
3355
3711
|
// `const x: void = undefined` / `let y: undefined = undefined`: the
|
|
3356
3712
|
// unit-only union — the initializer's unit literal wraps into its arm
|
|
3357
3713
|
// like any optional completion. Non-literal void initializers (a
|
|
3358
3714
|
// void CALL's result) keep their fence at the coercion below.
|
|
3359
|
-
if (
|
|
3360
|
-
|
|
3715
|
+
if (settledType.kind === "void" && isUnitOnlyTsType(lowerer.typeOf(decl.name))) {
|
|
3716
|
+
settledType = unitOnlyUnion(lowerer.unions);
|
|
3361
3717
|
}
|
|
3362
|
-
if (
|
|
3718
|
+
if (settledType.kind === "void")
|
|
3363
3719
|
lowerer.badType(decl.name, lowerer.typeOf(decl.name));
|
|
3364
3720
|
// A PACKAGE promise stored in a local stays an island HANDLE — the
|
|
3365
3721
|
// engine's promise has no static promise value to coerce into. Awaits
|
|
3366
3722
|
// and .catch/.finally chains on the local bridge per use site (each
|
|
3367
3723
|
// bridge is an independent observer of the same settlement).
|
|
3368
|
-
if (init.type.kind === "jsval" &&
|
|
3369
|
-
|
|
3724
|
+
if (init.type.kind === "jsval" && settledType.kind === "promise")
|
|
3725
|
+
settledType = JSVAL;
|
|
3370
3726
|
// An island value under an `any[]`-declared LOCAL spelling stays an
|
|
3371
3727
|
// island HANDLE too (the runtime-world rule): the jsval-element-array
|
|
3372
3728
|
// exit is a CALL-boundary conversion (the loadPlugins param ABI) —
|
|
3373
3729
|
// converting a local would strand the engine's own prototypes
|
|
3374
3730
|
// (join/map/... on a native handle-element array), where the handle
|
|
3375
3731
|
// routes every use engine-side.
|
|
3376
|
-
if (init.type.kind === "jsval" &&
|
|
3377
|
-
|
|
3732
|
+
if (init.type.kind === "jsval" && settledType.kind === "array" && settledType.elem.kind === "jsval") {
|
|
3733
|
+
settledType = JSVAL;
|
|
3378
3734
|
}
|
|
3379
3735
|
// A STATIC array of island HANDLES under an unannotated declared type
|
|
3380
3736
|
// spelling evolved elements (`const kept = fns.filter(...)` over an
|
|
@@ -3387,8 +3743,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3387
3743
|
// validated-boundary fence.
|
|
3388
3744
|
if (!decl.type &&
|
|
3389
3745
|
init.type.kind === "array" && init.type.elem.kind === "jsval" &&
|
|
3390
|
-
|
|
3391
|
-
|
|
3746
|
+
settledType.kind === "array" && settledType.elem.kind !== "jsval") {
|
|
3747
|
+
settledType = init.type;
|
|
3392
3748
|
}
|
|
3393
3749
|
// `const runner = options.runner || defaultRunner`: the checker types
|
|
3394
3750
|
// the || as the union/merge of two structurally-compatible function
|
|
@@ -3398,13 +3754,13 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3398
3754
|
// when the parameters agree and the declared return either contains
|
|
3399
3755
|
// the init's return as a union arm or is the opaque spawnRes the
|
|
3400
3756
|
// adapter converts (nothing coerces INTO either declared form).
|
|
3401
|
-
if (
|
|
3757
|
+
if (settledType.kind === "func" && init.type.kind === "func" && !typeEquals(settledType, init.type)) {
|
|
3402
3758
|
const initFn = init.type;
|
|
3403
|
-
const paramsAgree =
|
|
3404
|
-
|
|
3405
|
-
const retUnionArm =
|
|
3406
|
-
if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(
|
|
3407
|
-
|
|
3759
|
+
const paramsAgree = settledType.params.length === initFn.params.length &&
|
|
3760
|
+
settledType.params.every((p, i) => typeEquals(p, initFn.params[i]));
|
|
3761
|
+
const retUnionArm = settledType.ret.kind === "union" && lowerer.armTag(settledType.ret.unionId, initFn.ret) >= 0;
|
|
3762
|
+
if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(settledType, initFn) !== null)) {
|
|
3763
|
+
settledType = init.type;
|
|
3408
3764
|
}
|
|
3409
3765
|
}
|
|
3410
3766
|
// The downstream twin: `const result = runner(cmd, args)` where the
|
|
@@ -3413,12 +3769,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3413
3769
|
// can narrow (spawnRes has no discriminant). The VALUE is statically
|
|
3414
3770
|
// the record arm, so the local adopts it; flows into the union slot
|
|
3415
3771
|
// re-wrap via the ordinary arm coercion.
|
|
3416
|
-
if (
|
|
3417
|
-
const arms = lowerer.unions.get(
|
|
3772
|
+
if (settledType.kind === "union" && init.type.kind === "record") {
|
|
3773
|
+
const arms = lowerer.unions.get(settledType.unionId)?.arms ?? [];
|
|
3418
3774
|
if (arms.length === 2 &&
|
|
3419
3775
|
arms.some((a) => a.kind === "spawnRes") &&
|
|
3420
3776
|
arms.some((a) => typeEquals(a, init.type))) {
|
|
3421
|
-
|
|
3777
|
+
settledType = init.type;
|
|
3422
3778
|
}
|
|
3423
3779
|
}
|
|
3424
3780
|
// `const r: Repo = new MemRepo()` over an all-generic-method
|
|
@@ -3426,8 +3782,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3426
3782
|
// (the record shape maps empty and the width copy would drop the
|
|
3427
3783
|
// class the generic-method calls monomorphize against) — see
|
|
3428
3784
|
// genericIfaceBindingKeepsClass.
|
|
3429
|
-
if (
|
|
3430
|
-
|
|
3785
|
+
if (settledType.kind === "record" && init.type.kind === "object" && genericIfaceBindingKeepsClass(lowerer, decl, settledType)) {
|
|
3786
|
+
settledType = init.type;
|
|
3431
3787
|
}
|
|
3432
3788
|
// A TDZ box minted DURING this very initializer (a callback inside it
|
|
3433
3789
|
// captured this const — predeclareForwardCapture's current-statement
|
|
@@ -3442,8 +3798,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3442
3798
|
}
|
|
3443
3799
|
// Slot coercion: `const r: A | B = bValue;` wraps implicitly; width
|
|
3444
3800
|
// subtyping (`const p: {a: number} = wider;`) is rejected, not coerced.
|
|
3445
|
-
init = lowerer.coerceInto(decl.initializer, init,
|
|
3446
|
-
const local = lowerer.declareLocal(decl.name, decl.name.text,
|
|
3801
|
+
init = lowerer.coerceInto(decl.initializer, init, settledType);
|
|
3802
|
+
const local = lowerer.declareLocal(decl.name, decl.name.text, settledType, isLet);
|
|
3447
3803
|
if (runtimeStringArithmetic && isStringArithmeticUnion(init.type)) {
|
|
3448
3804
|
lowerer.runtimeOptionalArithmeticLocals.add(lowerer.runtimeOptionalRootOf(local));
|
|
3449
3805
|
}
|
|
@@ -3545,9 +3901,8 @@ export function lowerSwitch(lowerer, stmt) {
|
|
|
3545
3901
|
const labels = lowerer.takeLabels();
|
|
3546
3902
|
const disc = lowerer.lowerExpr(stmt.expression);
|
|
3547
3903
|
const dk = disc.type.kind;
|
|
3548
|
-
if (dk === "dyn")
|
|
3549
|
-
lowerer
|
|
3550
|
-
}
|
|
3904
|
+
if (dk === "dyn")
|
|
3905
|
+
return lowerDynSwitch(lowerer, stmt, disc);
|
|
3551
3906
|
if (dk === "union")
|
|
3552
3907
|
return lowerUnionSwitch(lowerer, stmt, disc);
|
|
3553
3908
|
if (dk !== "f64" && dk !== "string" && dk !== "bool") {
|
|
@@ -3596,19 +3951,51 @@ export function lowerSwitch(lowerer, stmt) {
|
|
|
3596
3951
|
* is last).
|
|
3597
3952
|
* Case bodies share ONE lexical scope, exactly like the real switch. */
|
|
3598
3953
|
function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
3599
|
-
const loc = locOf(stmt);
|
|
3600
3954
|
if (disc.type.kind !== "union")
|
|
3601
3955
|
throw new InternalCompilerError("lowerer bug: non-union disc");
|
|
3602
3956
|
const unionType = disc.type;
|
|
3603
3957
|
if (!lowerer.eqComparableUnion(unionType.unionId)) {
|
|
3604
3958
|
lowerer.unsupported("SC1090", stmt.expression, "switch on a union whose arms do not have static equality");
|
|
3605
3959
|
}
|
|
3960
|
+
return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
|
|
3961
|
+
const unitTest = test.kind === "unitLit"
|
|
3962
|
+
? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(expression))
|
|
3963
|
+
: null;
|
|
3964
|
+
return unitTest ?? {
|
|
3965
|
+
kind: "unionEq",
|
|
3966
|
+
unionId: unionType.unionId,
|
|
3967
|
+
negated: false,
|
|
3968
|
+
sameValue: false,
|
|
3969
|
+
left: stableDisc,
|
|
3970
|
+
right: lowerer.coerceInto(expression, test, unionType),
|
|
3971
|
+
type: BOOL,
|
|
3972
|
+
loc: locOf(expression),
|
|
3973
|
+
};
|
|
3974
|
+
});
|
|
3975
|
+
}
|
|
3976
|
+
/** A checked-dynamic switch whose tests have native strict-equality
|
|
3977
|
+
* answers. Scalar tests use dynScalarEq; null/undefined use dyn kind tests.
|
|
3978
|
+
* Other case values retain the existing unknown-switch fence. */
|
|
3979
|
+
function lowerDynSwitch(lowerer, stmt, disc) {
|
|
3980
|
+
return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
|
|
3981
|
+
if (test.kind === "unitLit") {
|
|
3982
|
+
return { kind: "dynTest", test: test.unit, value: stableDisc, type: BOOL, loc: locOf(expression) };
|
|
3983
|
+
}
|
|
3984
|
+
if (test.type.kind !== "string" && test.type.kind !== "f64" &&
|
|
3985
|
+
test.type.kind !== "bool" && test.type.kind !== "dyn") {
|
|
3986
|
+
lowerer.unsupported("SC1100", expression, "switch case values that cannot compare strictly with 'unknown'");
|
|
3987
|
+
}
|
|
3988
|
+
return { kind: "dynScalarEq", left: stableDisc, right: test, type: BOOL, loc: locOf(expression) };
|
|
3989
|
+
});
|
|
3990
|
+
}
|
|
3991
|
+
function lowerBranchSwitch(lowerer, stmt, disc, compare) {
|
|
3992
|
+
const loc = locOf(stmt);
|
|
3606
3993
|
const prefix = [];
|
|
3607
3994
|
let stableDisc = disc;
|
|
3608
|
-
if (!
|
|
3609
|
-
const temp = lowerer.declareHiddenLocal("%switch",
|
|
3995
|
+
if (!isSafeToRepeat(disc)) {
|
|
3996
|
+
const temp = lowerer.declareHiddenLocal("%switch", disc.type);
|
|
3610
3997
|
prefix.push({ kind: "varDecl", localId: temp.id, init: disc, loc });
|
|
3611
|
-
stableDisc = { kind: "varRef", localId: temp.id, type:
|
|
3998
|
+
stableDisc = { kind: "varRef", localId: temp.id, type: disc.type, loc };
|
|
3612
3999
|
}
|
|
3613
4000
|
const clauses = stmt.caseBlock.clauses;
|
|
3614
4001
|
// An unlabeled break at a clause's END exits the switch — the chain's
|
|
@@ -3667,7 +4054,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3667
4054
|
// final else, so matching them changes nothing when pure).
|
|
3668
4055
|
if (test.kind !== "strLit" && test.kind !== "numLit" &&
|
|
3669
4056
|
test.kind !== "boolLit" && test.kind !== "unitLit" &&
|
|
3670
|
-
!
|
|
4057
|
+
!isSafeToRepeat(test)) {
|
|
3671
4058
|
lowerer.unsupported("SC1090", clause.expression, "effectful case tests in a union-typed switch (bind the test value to a const first)");
|
|
3672
4059
|
}
|
|
3673
4060
|
// A unit-literal test takes the unit-comparison lowering: a tag
|
|
@@ -3675,19 +4062,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3675
4062
|
// lacks it (`case null:` on a `number | undefined` — legal TS,
|
|
3676
4063
|
// never matches; coercing the literal into the union would hit
|
|
3677
4064
|
// the stranded-arm trap and throw where JS just skips the case).
|
|
3678
|
-
|
|
3679
|
-
? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(clause.expression))
|
|
3680
|
-
: null;
|
|
3681
|
-
pendingTests.push(unitTest ?? {
|
|
3682
|
-
kind: "unionEq",
|
|
3683
|
-
unionId: unionType.unionId,
|
|
3684
|
-
negated: false,
|
|
3685
|
-
sameValue: false,
|
|
3686
|
-
left: stableDisc,
|
|
3687
|
-
right: lowerer.coerceInto(clause.expression, test, unionType),
|
|
3688
|
-
type: BOOL,
|
|
3689
|
-
loc: locOf(clause.expression),
|
|
3690
|
-
});
|
|
4065
|
+
pendingTests.push(compare(stableDisc, test, clause.expression));
|
|
3691
4066
|
}
|
|
3692
4067
|
const isDefault = ts.isDefaultClause(clause);
|
|
3693
4068
|
if (clause.statements.length === 0 && !isDefault && i < clauses.length - 1) {
|
|
@@ -3730,7 +4105,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3730
4105
|
lowerer.scopes.pop();
|
|
3731
4106
|
}
|
|
3732
4107
|
}
|
|
3733
|
-
/** try/catch/finally
|
|
4108
|
+
/** try/catch/finally:
|
|
3734
4109
|
* - `catch { }` (bindingless) discards the thrown value on entry.
|
|
3735
4110
|
* - `catch (e)` binds it as a CAUGHT local — a snapshot of the exception
|
|
3736
4111
|
* cell, deliberately narrower than dyn: the supported uses are the
|
|
@@ -3741,23 +4116,14 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3741
4116
|
* assignment are fenced (SC1063 and friends); destructuring patterns
|
|
3742
4117
|
* are SC1062. The `: any` / `: unknown` annotations tsc admits both
|
|
3743
4118
|
* lower the same way (tsc narrows either through the tests).
|
|
3744
|
-
* - `finally` runs on normal completion,
|
|
3745
|
-
*
|
|
3746
|
-
*
|
|
3747
|
-
*
|
|
3748
|
-
* break/continue crossing a try-with-finally stay rejected, as does
|
|
3749
|
-
* any jump out of the finally body itself (it would replace a pending
|
|
3750
|
-
* completion — rejectJumpCrossingFinally). Plain try/catch places no
|
|
3751
|
-
* restriction on jumps.
|
|
4119
|
+
* - `finally` runs on normal completion, exception paths, and every
|
|
4120
|
+
* abrupt completion that crosses it. Return values are snapshotted
|
|
4121
|
+
* before cleanup; an abrupt completion inside the finally replaces the
|
|
4122
|
+
* pending one, matching JavaScript completion-record semantics.
|
|
3752
4123
|
* Each block is its own lexical scope (the binding lives in a scope
|
|
3753
4124
|
* WRAPPING the catch block, like the spec's catch environment). */
|
|
3754
4125
|
export function lowerTry(lowerer, stmt) {
|
|
3755
|
-
const
|
|
3756
|
-
// try/catch bodies are jump-fenced only when a finally guards them.
|
|
3757
|
-
const lowerGuarded = (block) => hasFinally
|
|
3758
|
-
? lowerer.inCtl("tryFinally", () => lowerer.lowerScopedBlock(block))
|
|
3759
|
-
: lowerer.lowerScopedBlock(block);
|
|
3760
|
-
const tryBody = lowerGuarded(stmt.tryBlock);
|
|
4126
|
+
const tryBody = lowerer.lowerScopedBlock(stmt.tryBlock);
|
|
3761
4127
|
let catchBody = null;
|
|
3762
4128
|
let catchLocalId = null;
|
|
3763
4129
|
if (stmt.catchClause) {
|
|
@@ -3770,19 +4136,17 @@ export function lowerTry(lowerer, stmt) {
|
|
|
3770
4136
|
try {
|
|
3771
4137
|
const local = lowerer.declareLocal(vd.name, vd.name.text, CAUGHT, false);
|
|
3772
4138
|
catchLocalId = local.id;
|
|
3773
|
-
catchBody =
|
|
4139
|
+
catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
|
|
3774
4140
|
}
|
|
3775
4141
|
finally {
|
|
3776
4142
|
lowerer.scopes.pop();
|
|
3777
4143
|
}
|
|
3778
4144
|
}
|
|
3779
4145
|
else {
|
|
3780
|
-
catchBody =
|
|
4146
|
+
catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
|
|
3781
4147
|
}
|
|
3782
4148
|
}
|
|
3783
|
-
const finallyBody = stmt.finallyBlock
|
|
3784
|
-
? lowerer.inCtl("finallyBlock", () => lowerer.lowerScopedBlock(stmt.finallyBlock))
|
|
3785
|
-
: null;
|
|
4149
|
+
const finallyBody = stmt.finallyBlock ? lowerer.lowerScopedBlock(stmt.finallyBlock) : null;
|
|
3786
4150
|
return {
|
|
3787
4151
|
kind: "tryCatch",
|
|
3788
4152
|
tryBody,
|
|
@@ -3806,6 +4170,7 @@ function lowerDeleteStatement(lowerer, expr) {
|
|
|
3806
4170
|
if (!ts.isElementAccessExpression(target) && !ts.isPropertyAccessExpression(target)) {
|
|
3807
4171
|
lowerer.unsupported("SC1090", expr, "'delete' of non-property expressions");
|
|
3808
4172
|
}
|
|
4173
|
+
fenceNodeModuleMutation(lowerer, target, "delete");
|
|
3809
4174
|
const lowerKey = () => {
|
|
3810
4175
|
if (ts.isPropertyAccessExpression(target)) {
|
|
3811
4176
|
return { kind: "strLit", value: target.name.text, type: STRING, loc: locOf(target.name) };
|
|
@@ -4131,6 +4496,21 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4131
4496
|
const stmtNode = expr.parent;
|
|
4132
4497
|
while (ts.isParenthesizedExpression(expr))
|
|
4133
4498
|
expr = expr.expression;
|
|
4499
|
+
// A discarded conditional has no value to merge: represent its lazy
|
|
4500
|
+
// branch selection directly as statement control flow. In particular,
|
|
4501
|
+
// both arms may be void calls (`flag ? emitA() : emitB()`), for which a
|
|
4502
|
+
// value-shaped void ternary has no IR representation. Preserve the same
|
|
4503
|
+
// narrowing environments as a source if statement, and lower only a
|
|
4504
|
+
// statically selected arm when the condition folded without effects.
|
|
4505
|
+
if (ts.isConditionalExpression(expr)) {
|
|
4506
|
+
const cond = lowerer.lowerCondition(expr.condition);
|
|
4507
|
+
if (cond.kind === "boolLit") {
|
|
4508
|
+
return lowerExprStatement(lowerer, cond.value ? expr.whenTrue : expr.whenFalse);
|
|
4509
|
+
}
|
|
4510
|
+
const then = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, true), () => withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () => [lowerExprStatement(lowerer, expr.whenTrue)]));
|
|
4511
|
+
const else_ = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, false), () => [lowerExprStatement(lowerer, expr.whenFalse)]);
|
|
4512
|
+
return { kind: "if", cond, then, else_, loc: locOf(expr) };
|
|
4513
|
+
}
|
|
4134
4514
|
// Assignment statements over the no-storage binding families:
|
|
4135
4515
|
// - `f1 = f2` where the RHS roots at an ambient-undefined name:
|
|
4136
4516
|
// Node evaluates the RHS first and dies on the root's
|
|
@@ -4299,6 +4679,7 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4299
4679
|
if (ts.isElementAccessExpression(expr.left))
|
|
4300
4680
|
return lowerer.lowerElementWrite(expr);
|
|
4301
4681
|
if (ts.isPropertyAccessExpression(expr.left)) {
|
|
4682
|
+
fenceNodeModuleMutation(lowerer, expr.left, "assignment");
|
|
4302
4683
|
// Native arrays have a writable length property. The runtime grows
|
|
4303
4684
|
// by creating holes and truncates with RC-aware removal; keep this
|
|
4304
4685
|
// as a statement operation so invalid lengths can use the normal
|
|
@@ -4451,10 +4832,10 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4451
4832
|
// chain): the dyn keyed write — the value converts INTO dyn
|
|
4452
4833
|
// (closures box), the runtime sets the member on OBJ receivers
|
|
4453
4834
|
// and throws Node's TypeErrors on the rest. The receiver is
|
|
4454
|
-
// PROBED (
|
|
4835
|
+
// PROBED (tryLowerExpression) and claimed exactly when it lowers to
|
|
4455
4836
|
// dyn; typed receivers keep their own lowerings and fences.
|
|
4456
4837
|
if (!expr.left.questionDotToken) {
|
|
4457
|
-
const recv =
|
|
4838
|
+
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
4458
4839
|
if (recv && recv.type.kind === "dyn") {
|
|
4459
4840
|
const loc = locOf(expr);
|
|
4460
4841
|
const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
|
|
@@ -5810,29 +6191,27 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5810
6191
|
// desugars drop them — a labeled jump naming those loops fences at the
|
|
5811
6192
|
// jump site (no label point exists in their desugared shape).
|
|
5812
6193
|
const labels = lowerer.takeLabels();
|
|
5813
|
-
// `for await` is ASYNC ITERATION, not the shipped async/await.
|
|
5814
|
-
// async
|
|
5815
|
-
//
|
|
5816
|
-
// named specifically (the bare SC1070 text would claim async/await
|
|
5817
|
-
// itself is missing).
|
|
6194
|
+
// `for await` is ASYNC ITERATION, not the shipped async/await. Typed
|
|
6195
|
+
// async generators use the generic request protocol; process.stdin
|
|
6196
|
+
// and readable streams retain their dedicated next-chunk paths.
|
|
5818
6197
|
if (stmt.awaitModifier) {
|
|
5819
|
-
if (ts.isPropertyAccessExpression(stmt.expression) &&
|
|
5820
|
-
lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
|
|
5821
|
-
return lowerForAwaitStdin(lowerer, stmt);
|
|
5822
|
-
}
|
|
5823
|
-
// Readable streams (the readableAsyncIterator surface): the mapped
|
|
5824
|
-
// receiver class roots at a readable-sided stream class.
|
|
5825
6198
|
{
|
|
5826
|
-
const
|
|
5827
|
-
if (
|
|
5828
|
-
const
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
return lowerForAwaitReadable(lowerer, stmt, recvT);
|
|
6199
|
+
const genT = lowerer.mapTypeOf(lowerer.typeOf(stmt.expression));
|
|
6200
|
+
if (genT?.kind === "generator" && genT.async) {
|
|
6201
|
+
const iterable = lowerer.lowerExpr(stmt.expression);
|
|
6202
|
+
if (iterable.type.kind === "generator" && iterable.type.async) {
|
|
6203
|
+
return lowerForAwaitGenerator(lowerer, stmt, iterable, labels);
|
|
5832
6204
|
}
|
|
5833
6205
|
}
|
|
5834
6206
|
}
|
|
5835
|
-
|
|
6207
|
+
if (ts.isPropertyAccessExpression(stmt.expression) &&
|
|
6208
|
+
lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
|
|
6209
|
+
return lowerForAwaitStdin(lowerer, stmt);
|
|
6210
|
+
}
|
|
6211
|
+
const builtin = lowerForAwaitBuiltin(lowerer, stmt);
|
|
6212
|
+
if (builtin)
|
|
6213
|
+
return builtin;
|
|
6214
|
+
lowerer.unsupported("SC1070", stmt, "'for await' over this value (supported: typed async generators, represented class async iterators, process.stdin, and Node/Web readable streams)");
|
|
5836
6215
|
}
|
|
5837
6216
|
lowerer.fenceStaticHeadersIteration(stmt.expression);
|
|
5838
6217
|
// A stored numeric value iterator declared in this function keeps its
|
|
@@ -6051,7 +6430,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6051
6430
|
elemT = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
6052
6431
|
}
|
|
6053
6432
|
}
|
|
6054
|
-
if (elemT !== null &&
|
|
6433
|
+
if (elemT !== null && isSafeToRepeat(iterable)) {
|
|
6055
6434
|
const loc = locOf(stmt.expression);
|
|
6056
6435
|
const shapeId = iterable.type.shapeId;
|
|
6057
6436
|
const snapshotElem = elemT;
|
|
@@ -6161,9 +6540,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6161
6540
|
lowerer.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
|
|
6162
6541
|
}
|
|
6163
6542
|
const list = stmt.initializer;
|
|
6164
|
-
|
|
6165
|
-
lowerer.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
|
|
6166
|
-
}
|
|
6543
|
+
const resourceLoop = (list.flags & ts.NodeFlags.Using) !== 0;
|
|
6167
6544
|
const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || (list.flags & ts.NodeFlags.BlockScoped) === 0;
|
|
6168
6545
|
const decl = list.declarations[0]; // the grammar allows exactly one
|
|
6169
6546
|
// `for (const [k, v] of pairs)` — a destructuring loop variable: the
|
|
@@ -6216,7 +6593,19 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6216
6593
|
lowerer.runtimeOptionalStorageLocals.add(root);
|
|
6217
6594
|
}
|
|
6218
6595
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
6219
|
-
|
|
6596
|
+
if (!resourceLoop)
|
|
6597
|
+
return forValues(local.id, body);
|
|
6598
|
+
const resource = varRef(local.id, local.type, locOf(decl.name));
|
|
6599
|
+
const cleanup = cleanupForValue(lowerer, decl.name, resource, isAwaitUsing(list));
|
|
6600
|
+
return forValues(local.id, [{
|
|
6601
|
+
kind: "tryCatch",
|
|
6602
|
+
tryBody: body,
|
|
6603
|
+
catchBody: null,
|
|
6604
|
+
catchLocalId: null,
|
|
6605
|
+
finallyBody: cleanup,
|
|
6606
|
+
suppressFinallyErrors: true,
|
|
6607
|
+
loc: locOf(decl),
|
|
6608
|
+
}]);
|
|
6220
6609
|
}
|
|
6221
6610
|
finally {
|
|
6222
6611
|
lowerer.scopes.pop();
|
|
@@ -7372,112 +7761,6 @@ function lowerForAwaitStdin(lowerer, stmt) {
|
|
|
7372
7761
|
lowerer.scopes.pop();
|
|
7373
7762
|
}
|
|
7374
7763
|
}
|
|
7375
|
-
/** `for await (const chunk of readable)` — the stream async iterator
|
|
7376
|
-
* (the stdin desugar's sibling): every pass awaits the runtime's
|
|
7377
|
-
* next-chunk promise — buffered content, or a REJECTION carrying the
|
|
7378
|
-
* stream's error (the await rethrows it, Node's iterator contract) —
|
|
7379
|
-
* and exits on the EOF sentinel. Chunks are Buffers in typed code
|
|
7380
|
-
* (encoded streams fence at the runtime entry); in the JS lane the
|
|
7381
|
-
* chunk is checked-dynamic and boxes by runtime tag (dyn strings once
|
|
7382
|
-
* an encoding applies, dyn undefined as the sentinel — chunks are
|
|
7383
|
-
* never undefined). Early exit leaves the stream alive (Node's
|
|
7384
|
-
* iterator return() would destroy it — a documented divergence). */
|
|
7385
|
-
function lowerForAwaitReadable(lowerer, stmt, recvT) {
|
|
7386
|
-
if (!lowerer.ctx.isAsync) {
|
|
7387
|
-
lowerer.unsupported("SC1090", stmt, "top-level 'for await' (await outside async functions)");
|
|
7388
|
-
}
|
|
7389
|
-
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
7390
|
-
lowerer.unsupported("SC1090", stmt.initializer, "for-await over a pre-declared variable (declare the loop variable in the loop: for await (const chunk of ...))");
|
|
7391
|
-
}
|
|
7392
|
-
const list = stmt.initializer;
|
|
7393
|
-
const isConst = (list.flags & ts.NodeFlags.Const) !== 0;
|
|
7394
|
-
const isLet = (list.flags & ts.NodeFlags.Let) !== 0;
|
|
7395
|
-
// `for await (var chunk of ...)`: the chunk binding is per-await
|
|
7396
|
-
// machinery (a fresh value each pass, fiber-parked in between) —
|
|
7397
|
-
// threading it through a shared hoisted slot has no user yet.
|
|
7398
|
-
if (!isConst && !isLet)
|
|
7399
|
-
lowerer.unsupported("SC1030", list, "'var' loop bindings in 'for await' (use const)");
|
|
7400
|
-
const decl = list.declarations[0];
|
|
7401
|
-
if (!ts.isIdentifier(decl.name))
|
|
7402
|
-
lowerer.unsupported("SC1031", decl.name);
|
|
7403
|
-
const loc = locOf(stmt);
|
|
7404
|
-
// The chunk is CHECKED-DYNAMIC in both lanes (the shim types the
|
|
7405
|
-
// iterator's element as any): the runtime boxes by tag — Buffers
|
|
7406
|
-
// normally, strings once an encoding applies — and the dyn surface
|
|
7407
|
-
// carries the common consumptions (.length, [i], toString, +=,
|
|
7408
|
-
// typeof). A statically-typed chunk would have to pick one side of
|
|
7409
|
-
// the encoding question at compile time.
|
|
7410
|
-
const dynLane = true;
|
|
7411
|
-
const chunkT = DYN;
|
|
7412
|
-
const promiseT = { kind: "promise", inner: chunkT };
|
|
7413
|
-
lowerer.scopes.push(new Map());
|
|
7414
|
-
try {
|
|
7415
|
-
// The receiver evaluates ONCE, before the loop.
|
|
7416
|
-
const recvLocal = lowerer.declareHiddenLocal("%faStream", recvT);
|
|
7417
|
-
const recvDecl = {
|
|
7418
|
-
kind: "varDecl",
|
|
7419
|
-
localId: recvLocal.id,
|
|
7420
|
-
init: lowerer.lowerExpr(stmt.expression),
|
|
7421
|
-
loc,
|
|
7422
|
-
};
|
|
7423
|
-
const p = lowerer.declareHiddenLocal("%streamNext", promiseT);
|
|
7424
|
-
const chunk = lowerer.declareLocal(decl.name, decl.name.text, chunkT, isLet);
|
|
7425
|
-
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement));
|
|
7426
|
-
const chunkRef = { kind: "varRef", localId: chunk.id, type: chunkT, loc };
|
|
7427
|
-
const eofCond = dynLane
|
|
7428
|
-
? { kind: "dynTest", test: "undefined", value: chunkRef, type: BOOL, loc }
|
|
7429
|
-
: {
|
|
7430
|
-
kind: "bin",
|
|
7431
|
-
op: "===",
|
|
7432
|
-
left: { kind: "bytesIntrinsic", method: "length", receiver: chunkRef, args: [], type: F64, loc },
|
|
7433
|
-
right: { kind: "numLit", value: 0, type: F64, loc },
|
|
7434
|
-
type: BOOL,
|
|
7435
|
-
loc,
|
|
7436
|
-
};
|
|
7437
|
-
const head = [
|
|
7438
|
-
{
|
|
7439
|
-
kind: "varDecl",
|
|
7440
|
-
localId: p.id,
|
|
7441
|
-
init: {
|
|
7442
|
-
kind: "libCall",
|
|
7443
|
-
fn: dynLane ? "readable.nextChunkDyn" : "readable.nextChunk",
|
|
7444
|
-
args: [{ kind: "varRef", localId: recvLocal.id, type: recvT, loc }],
|
|
7445
|
-
type: promiseT,
|
|
7446
|
-
loc,
|
|
7447
|
-
},
|
|
7448
|
-
loc,
|
|
7449
|
-
},
|
|
7450
|
-
{
|
|
7451
|
-
kind: "varDecl",
|
|
7452
|
-
localId: chunk.id,
|
|
7453
|
-
init: {
|
|
7454
|
-
kind: "awaitExpr",
|
|
7455
|
-
value: { kind: "varRef", localId: p.id, type: promiseT, loc },
|
|
7456
|
-
type: chunkT,
|
|
7457
|
-
loc,
|
|
7458
|
-
},
|
|
7459
|
-
loc,
|
|
7460
|
-
},
|
|
7461
|
-
{ kind: "if", cond: eofCond, then: [{ kind: "break", loc }], else_: null, loc },
|
|
7462
|
-
];
|
|
7463
|
-
return {
|
|
7464
|
-
kind: "block",
|
|
7465
|
-
body: [
|
|
7466
|
-
recvDecl,
|
|
7467
|
-
{
|
|
7468
|
-
kind: "while",
|
|
7469
|
-
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
7470
|
-
body: [...head, ...body],
|
|
7471
|
-
loc,
|
|
7472
|
-
},
|
|
7473
|
-
],
|
|
7474
|
-
loc,
|
|
7475
|
-
};
|
|
7476
|
-
}
|
|
7477
|
-
finally {
|
|
7478
|
-
lowerer.scopes.pop();
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
7764
|
export function lowerForStatement(lowerer, stmt) {
|
|
7482
7765
|
const labels = lowerer.takeLabels();
|
|
7483
7766
|
lowerer.scopes.push(new Map());
|