mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 08:35:07 +08:00
Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- declare-erased roots make every chain rooted there a catchable ReferenceError at first touch, exactly Node's erasure semantics - Never-read unmappable bindings emit nothing; nullish-cast bindings answer Node's exact TypeError on member access - Assertion-supplied generic signatures register like annotations and monomorphize per call - Stdlib generic members take the lib fence naming the member # Conflicts: # packages/compiler/src/frontend/lowering/lower-stmts.ts # packages/compiler/test/ts7/baselines/order-parity.json
This commit is contained in:
@@ -2471,6 +2471,58 @@ export function islandPrimitiveExit(L: Lowerer, call: ts.CallExpression, result:
|
||||
export function lowerCall(L: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
const loc = locOf(expr);
|
||||
|
||||
// A call whose chain ROOTS at an ambient-undefined name (`declare
|
||||
// const value: Y | undefined; value?.foo("a")`, `declare function
|
||||
// chain...; chain(o).mapValues(f).value()`, a trap binding's read):
|
||||
// Node evaluates the root FIRST and throws the catchable
|
||||
// ReferenceError before any member, type argument, or argument runs —
|
||||
// the whole call IS that throw, typed by the use site (arguments
|
||||
// never lower; Node never evaluates them). Claimed before every
|
||||
// intrinsic and dispatch path: no lowering can answer differently
|
||||
// when the root read itself is the crash.
|
||||
{
|
||||
const root = ambientUndefVarRootOf(L, expr);
|
||||
if (root !== null) {
|
||||
const mapped = L.mapTypeOf(L.typeOf(expr));
|
||||
const t =
|
||||
mapped && mapped.kind !== "void" && !L.typeNamesUnregisteredClass(mapped)
|
||||
? mapped
|
||||
: (contextualUndefReadType(L, expr) ?? F64);
|
||||
return nsUndefRead(L, root.text, expr, t);
|
||||
}
|
||||
}
|
||||
// A method call through a NULLISH binding (`const i: I<A & B> = null
|
||||
// as any; i.fn(...)` — the receiver provably holds null/undefined
|
||||
// forever): the member READ throws Node's exact TypeError before any
|
||||
// argument evaluates — the whole call lowers to that throw. Claimed
|
||||
// when the receiver's type has no mapping (no other story exists) or
|
||||
// the member is a generic signature (the alternative is the
|
||||
// interface-dispatch fence — the runtime truth is this throw).
|
||||
if (
|
||||
ts.isPropertyAccessExpression(expr.expression) &&
|
||||
ts.isIdentifier(expr.expression.expression) &&
|
||||
expr.expression.questionDotToken === undefined
|
||||
) {
|
||||
const recvSym = L.resolveValueSymbol(expr.expression.expression);
|
||||
const unit = nullishValueUnitOf(L, recvSym);
|
||||
if (unit !== null) {
|
||||
const recvUnmappable =
|
||||
recvSym !== null && L.mapTypeOf(L.checker.getTypeOfSymbol(recvSym)) === null;
|
||||
const propSym = L.checker.getPropertyOfType(
|
||||
L.typeOf(expr.expression.expression),
|
||||
expr.expression.name.text,
|
||||
);
|
||||
const genericMember =
|
||||
propSym !== undefined && propSym !== null &&
|
||||
isGenericCallableMemberType(L.checker.getTypeOfSymbol(propSym), L.checker);
|
||||
if (recvUnmappable || genericMember) {
|
||||
const mapped = L.mapTypeOf(L.typeOf(expr));
|
||||
const t = mapped && mapped.kind !== "void" && !L.typeNamesUnregisteredClass(mapped) ? mapped : F64;
|
||||
return nodeThrowExpr(1, "", `Cannot read properties of ${unit} (reading '${expr.expression.name.text}')`, t, loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `process.getuid?.()` — intercepted BEFORE the optional-chain
|
||||
// machinery (the member always exists on a POSIX target, so the
|
||||
// optional call IS the call; `process.getuid` itself has no value
|
||||
@@ -7478,6 +7530,260 @@ export function lowerFunction(L: Lowerer, decl: ts.FunctionDeclaration): IrFunct
|
||||
return !written;
|
||||
}
|
||||
|
||||
/** Strips the value-preserving wrappers off an expression: parens,
|
||||
* non-null assertions, `as`/`satisfies`/angle-bracket casts. What
|
||||
* remains is the expression that actually evaluates. */
|
||||
export function stripValueWrappers(e: ts.Expression): ts.Expression {
|
||||
let v: ts.Expression = e;
|
||||
for (;;) {
|
||||
if (
|
||||
ts.isParenthesizedExpression(v) || ts.isNonNullExpression(v) ||
|
||||
ts.isAsExpression(v) || ts.isSatisfiesExpression(v) || ts.isTypeAssertion(v)
|
||||
) {
|
||||
v = v.expression;
|
||||
continue;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/** The nullish unit `e` provably evaluates to: a bare null/undefined
|
||||
* literal (assertion-wrapped — `null as any`, `null!`), or a read of a
|
||||
* registered NULLISH binding (nullishGenericBindingUnitOf). Null when
|
||||
* the value could be anything else. */
|
||||
export function nullishExprUnitOf(L: Lowerer, e: ts.Expression): "null" | "undefined" | null {
|
||||
const v = stripValueWrappers(e);
|
||||
if (v.kind === ts.SyntaxKind.NullKeyword) return "null";
|
||||
if (ts.isIdentifier(v)) {
|
||||
if (v.text === "undefined" && (L.typeOf(v).flags & ts.TypeFlags.Undefined) !== 0) {
|
||||
return "undefined";
|
||||
}
|
||||
return nullishValueUnitOf(L, L.resolveValueSymbol(v));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The nullish unit a binding provably holds FOREVER, by VALUE alone: its
|
||||
* initializer is nullish (`const i: I<A & B> = null as any`) and every
|
||||
* write in its declaring file is nullish too (`a = b` where b is
|
||||
* another nullish binding). No type condition — callers add their own
|
||||
* (nullishGenericBindingUnitOf gates the no-storage family on
|
||||
* unmappable types; the generic-method call path rescues its fence with
|
||||
* the value fact alone). Cached per symbol; the pre-seeded null entry
|
||||
* guards probe cycles (mutually-assigned bindings resolve link by link,
|
||||
* declaration order). */
|
||||
export function nullishValueUnitOf(L: Lowerer, sym: ts.Symbol | null): "null" | "undefined" | null {
|
||||
if (!sym) return null;
|
||||
const cached = L.nullishBindings.get(sym);
|
||||
if (cached !== undefined) return cached;
|
||||
L.nullishBindings.set(sym, null); // cycle guard: self-referential probes answer non-qualifying
|
||||
const decl = L.checker.valueDeclarationOf(sym);
|
||||
if (
|
||||
!decl || !ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name) ||
|
||||
decl.getSourceFile().isDeclarationFile || decl.initializer === undefined
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const unit = nullishExprUnitOf(L, decl.initializer);
|
||||
if (unit === null) return null;
|
||||
// Bindings with a CHECKED-DYNAMIC fallback (`const maybe: any =
|
||||
// undefined`, JS inference residue) keep that story: the dyn world
|
||||
// already holds null/undefined correctly and serves every read form
|
||||
// (optional chains included) — this family exists for types with NO
|
||||
// other home.
|
||||
if (dynFallbackType(L, decl.name, L.checker.getTypeOfSymbol(sym)) !== null) return null;
|
||||
if (!allWritesNullish(L, sym, decl)) return null;
|
||||
// A use inside a class HERITAGE clause (`class X extends Mixin(...)`)
|
||||
// declines the whole family: heritage resolution is structural (the
|
||||
// mixin machinery can pin the instantiation from the ARGUMENT class
|
||||
// expression without ever reading the callee binding), so a claimed
|
||||
// nullish callee would compile a working class where Node throws
|
||||
// "Mixin is not a function" evaluating the extends expression. The
|
||||
// declaration keeps its type fence instead.
|
||||
if (usedInHeritageClause(L, sym)) return null;
|
||||
L.nullishBindings.set(sym, unit);
|
||||
return unit;
|
||||
}
|
||||
|
||||
/** True when any identifier resolving to `sym` sits inside a class
|
||||
* heritage clause anywhere in the program. */
|
||||
function usedInHeritageClause(L: Lowerer, sym: ts.Symbol): boolean {
|
||||
const symText = sym.name;
|
||||
let found = false;
|
||||
const visit = (n: ts.Node): void => {
|
||||
if (found) return;
|
||||
if (
|
||||
ts.isIdentifier(n) && n.text === symText &&
|
||||
L.resolveValueSymbol(n) === sym
|
||||
) {
|
||||
for (let p: ts.Node | undefined = n.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
||||
if (ts.isHeritageClause(p)) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
n.forEachChild(visit);
|
||||
};
|
||||
for (const file of L.program.getSourceFiles()) {
|
||||
if (found) break;
|
||||
if (file.isDeclarationFile) continue;
|
||||
file.forEachChild(visit);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/** nullishValueUnitOf gated on a declared type that CANNOT hold the
|
||||
* value — the NO-STORAGE family: an unmappable type has no other story,
|
||||
* and a RECORD-mapped one (`const i: I<A & B> = null as any` — an
|
||||
* interface whose members are all generic signatures interns an empty
|
||||
* shape) has a slot null can never inhabit, so storing would throw the
|
||||
* representation error where Node stores null silently. Either way the
|
||||
* declaration emits nothing and reads know the value. Null-tolerant
|
||||
* mappings (unions with a null/undefined arm, dyn) keep their real
|
||||
* storage and every ordinary lowering. */
|
||||
export function nullishGenericBindingUnitOf(L: Lowerer, sym: ts.Symbol | null): "null" | "undefined" | null {
|
||||
if (!sym) return null;
|
||||
// The VALUE probe first — it is purely syntactic, so no checker type
|
||||
// query runs for the overwhelmingly common non-nullish declarations
|
||||
// (a query can even panic upstream — the 1e999 checker bug).
|
||||
const unit = nullishValueUnitOf(L, sym);
|
||||
if (unit === null) return null;
|
||||
const mapped = L.mapTypeOf(L.checker.getTypeOfSymbol(sym));
|
||||
if (mapped !== null && mapped.kind !== "record") return null;
|
||||
return unit;
|
||||
}
|
||||
|
||||
/** True when every write of `sym` in its declaring file is a plain `x =
|
||||
* <nullish>` assignment — the discipline that keeps a nullish binding's
|
||||
* value knowable. Compound assignments, ++/--, for-in/of cursors, and
|
||||
* destructuring targets all disqualify. */
|
||||
function allWritesNullish(L: Lowerer, sym: ts.Symbol, decl: ts.Node): boolean {
|
||||
const symText = sym.name;
|
||||
const namesSym = (e: ts.Node): boolean =>
|
||||
ts.isIdentifier(e) && e.text === symText && L.resolveValueSymbol(e) === sym;
|
||||
let ok = true;
|
||||
const visit = (n: ts.Node): void => {
|
||||
if (!ok) return;
|
||||
if (ts.isBinaryExpression(n)) {
|
||||
const k = n.operatorToken.kind;
|
||||
if (k >= ts.SyntaxKind.FirstAssignment && k <= ts.SyntaxKind.LastAssignment) {
|
||||
let lhs: ts.Expression = n.left;
|
||||
while (ts.isParenthesizedExpression(lhs)) lhs = lhs.expression;
|
||||
if (namesSym(lhs)) {
|
||||
if (k !== ts.SyntaxKind.EqualsToken || nullishExprUnitOf(L, n.right) === null) ok = false;
|
||||
} else if (ts.isArrayLiteralExpression(lhs) || ts.isObjectLiteralExpression(lhs)) {
|
||||
const walk = (m: ts.Node): void => {
|
||||
if (namesSym(m)) ok = false;
|
||||
else m.forEachChild(walk);
|
||||
};
|
||||
walk(lhs);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
(ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) &&
|
||||
(n.operator === ts.SyntaxKind.PlusPlusToken || n.operator === ts.SyntaxKind.MinusMinusToken)
|
||||
) {
|
||||
let op: ts.Expression = n.operand as ts.Expression;
|
||||
while (ts.isParenthesizedExpression(op)) op = op.expression;
|
||||
if (namesSym(op)) ok = false;
|
||||
} else if ((ts.isForOfStatement(n) || ts.isForInStatement(n)) && !ts.isVariableDeclarationList(n.initializer)) {
|
||||
let t: ts.Node = n.initializer;
|
||||
while (ts.isParenthesizedExpression(t as ts.Expression)) t = (t as ts.ParenthesizedExpression).expression;
|
||||
if (namesSym(t)) ok = false;
|
||||
}
|
||||
n.forEachChild(visit);
|
||||
};
|
||||
decl.getSourceFile().forEachChild(visit);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** A VALUE-ONLY expression: materializing it has no observable effect
|
||||
* beyond the value itself — function/arrow literals, class-free
|
||||
* literals, nullish units. The dead-binding rule's purity test: Node
|
||||
* builds the value and drops it, so skipping the build entirely is
|
||||
* unobservable. Bare identifier reads stay OUT (a read above a `let`
|
||||
* declaration is a TDZ throw Node WOULD serve). */
|
||||
function sideEffectFreeValueExpr(L: Lowerer, e: ts.Expression): boolean {
|
||||
const v = stripValueWrappers(e);
|
||||
if (ts.isArrowFunction(v) || ts.isFunctionExpression(v)) return true;
|
||||
if (ts.isLiteralExpression(v) || v.kind === ts.SyntaxKind.NullKeyword ||
|
||||
v.kind === ts.SyntaxKind.TrueKeyword || v.kind === ts.SyntaxKind.FalseKeyword) {
|
||||
return true;
|
||||
}
|
||||
if (ts.isIdentifier(v) && v.text === "undefined" && (L.typeOf(v).flags & ts.TypeFlags.Undefined) !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** True when `sym` — a binding whose type has NO static mapping — is DEAD:
|
||||
* never read anywhere in the program, not exported through a specifier,
|
||||
* declared with no initializer or a side-effect-free one, and written
|
||||
* (if at all) only by plain assignments of side-effect-free values. Node
|
||||
* materializes those values and drops them — zero observable effect —
|
||||
* so the declaration and its writes lower to NOTHING instead of fencing
|
||||
* on a type the program never consumes (`var xs2: typeof Array;`, the
|
||||
* write-only `var f2: { <T, U>(x: T, y: U): T }`). TS program files
|
||||
* only: JS bindings keep their checked-dynamic fallbacks. Positive
|
||||
* answers register in L.deadBindings (the assignment lowering skips
|
||||
* writes by the same set). */
|
||||
export function deadUnmappableBinding(L: Lowerer, sym: ts.Symbol | null, decl: ts.VariableDeclaration): boolean {
|
||||
if (!sym) return false;
|
||||
if (L.deadBindings.has(sym)) return true;
|
||||
if (!ts.isIdentifier(decl.name)) return false;
|
||||
const sf = decl.getSourceFile();
|
||||
if (sf.isDeclarationFile || isJsSourceFile(sf)) return false;
|
||||
if (decl.initializer !== undefined && !sideEffectFreeValueExpr(L, decl.initializer)) return false;
|
||||
// Exported bindings stay out: a library build's exports are consumed
|
||||
// from outside the graph, and export specifiers double as reads.
|
||||
if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Export) return false;
|
||||
// The type gate LAST among the cheap checks: querying the checker for
|
||||
// a type is the expensive step (and can panic upstream — the 1e999
|
||||
// bug), so only survivors of the syntactic filters pay it. Mappable
|
||||
// types keep their real storage.
|
||||
if (L.mapTypeOf(L.checker.getTypeOfSymbol(sym)) !== null) return false;
|
||||
const symText = sym.name;
|
||||
const namesSym = (e: ts.Node): boolean =>
|
||||
ts.isIdentifier(e) && e.text === symText && L.resolveValueSymbol(e) === sym;
|
||||
let dead = true;
|
||||
const visit = (n: ts.Node): void => {
|
||||
if (!dead) return;
|
||||
if (ts.isIdentifier(n) && n.text === symText) {
|
||||
// Declaration-name occurrences are not reads.
|
||||
if (n.parent !== undefined && ts.isVariableDeclaration(n.parent) && n.parent.name === n) {
|
||||
n.forEachChild(visit);
|
||||
return;
|
||||
}
|
||||
// A plain-assignment LHS is a WRITE — dead only when the RHS
|
||||
// builds no observable effect (the value is dropped with the
|
||||
// binding).
|
||||
const p = n.parent;
|
||||
if (
|
||||
p !== undefined && ts.isBinaryExpression(p) &&
|
||||
p.operatorToken.kind === ts.SyntaxKind.EqualsToken && p.left === n
|
||||
) {
|
||||
if (!namesSym(n)) return;
|
||||
if (!sideEffectFreeValueExpr(L, p.right)) dead = false;
|
||||
return;
|
||||
}
|
||||
// Import/export specifiers, and every other occurrence, count as
|
||||
// reads.
|
||||
if (namesSym(n)) dead = false;
|
||||
return;
|
||||
}
|
||||
n.forEachChild(visit);
|
||||
};
|
||||
for (const file of L.program.getSourceFiles()) {
|
||||
if (!dead) break;
|
||||
if (file.isDeclarationFile) continue;
|
||||
file.forEachChild(visit);
|
||||
}
|
||||
if (dead) L.deadBindings.add(sym);
|
||||
return dead;
|
||||
}
|
||||
|
||||
/** The generic function-like INITIALIZER behind a binding declaration —
|
||||
* `const f = <T>(x: T) => x` or `const f = function g<T>(x: T) {...}`
|
||||
* (parens stripped). Null when the declaration isn't that shape; the
|
||||
@@ -7485,8 +7791,9 @@ export function lowerFunction(L: Lowerer, decl: ts.FunctionDeclaration): IrFunct
|
||||
* reassigned) is bindingGenericFnInfoOf's business. */
|
||||
export function bindingGenericFnNodeOf(decl: ts.VariableDeclaration): ts.FunctionExpression | ts.ArrowFunction | null {
|
||||
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined) return null;
|
||||
let init: ts.Expression = decl.initializer;
|
||||
while (ts.isParenthesizedExpression(init)) init = init.expression;
|
||||
// Assertion wrappers strip like parens: `const r = (<T>(x: T) => x) as
|
||||
// Mapper` evaluates the arrow — the cast only renames its type.
|
||||
const init = stripValueWrappers(decl.initializer);
|
||||
if (
|
||||
(ts.isArrowFunction(init) || ts.isFunctionExpression(init)) &&
|
||||
init.typeParameters !== undefined && init.body !== undefined
|
||||
@@ -7505,9 +7812,15 @@ export function lowerFunction(L: Lowerer, decl: ts.FunctionDeclaration): IrFunct
|
||||
* signature. Null when the shape doesn't match (a concrete annotation, a
|
||||
* generic arrow — the syntactic probe's case, an overloaded alias). */
|
||||
export function bindingContextualGenericFnNodeOf(L: Lowerer, decl: ts.VariableDeclaration): ts.FunctionExpression | ts.ArrowFunction | null {
|
||||
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined || decl.type === undefined) return null;
|
||||
let init: ts.Expression = decl.initializer;
|
||||
while (ts.isParenthesizedExpression(init)) init = init.expression;
|
||||
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined) return null;
|
||||
// The generic signature can arrive as an ANNOTATION or as a type
|
||||
// ASSERTION on the initializer (`var r = < <T>(x: T) => T >((x) => x)`
|
||||
// — the checker contextually types the operand's parameters by the
|
||||
// asserted signature exactly like an annotation would).
|
||||
const asserted =
|
||||
ts.isAsExpression(decl.initializer) || ts.isTypeAssertion(decl.initializer);
|
||||
if (decl.type === undefined && !asserted) return null;
|
||||
const init = stripValueWrappers(decl.initializer);
|
||||
if (
|
||||
!(ts.isArrowFunction(init) || ts.isFunctionExpression(init)) ||
|
||||
init.typeParameters !== undefined || init.body === undefined
|
||||
@@ -7798,6 +8111,29 @@ export function lowerFunction(L: Lowerer, decl: ts.FunctionDeclaration): IrFunct
|
||||
}
|
||||
const found = objLitGenericFnNodeOf(L, propSym);
|
||||
if (!found) {
|
||||
// Function.prototype.apply/call/bind spelled through a FUNCTION
|
||||
// receiver: compiled functions are direct calls with no runtime
|
||||
// `this`/arguments object to re-route — name the working spelling
|
||||
// instead of a class-receiver hint (or an SC2020 recitation) no
|
||||
// function value can follow. Before the stdlib decline: these ARE
|
||||
// stdlib members (CallableFunction), but the pointed message is
|
||||
// the honest one.
|
||||
if (
|
||||
(name === "apply" || name === "call" || name === "bind") &&
|
||||
L.checker.getCallSignatures(recvT).length > 0
|
||||
) {
|
||||
L.unsupported(
|
||||
"SC1090",
|
||||
call,
|
||||
`Function.prototype.${name} on a compiled function value (compiled calls are direct — no runtime 'this' or arguments object exists to re-route; spell the call directly: '${access.expression.getText()}(...)')`,
|
||||
);
|
||||
}
|
||||
// STANDARD-LIBRARY generic members (Promise.then, Object.
|
||||
// defineProperty, Array-augmentation methods) are the lib fence's
|
||||
// story (SC2020, naming the member) — decline so the stdlib
|
||||
// chokepoint downstream reports, instead of an interface-dispatch
|
||||
// recitation about a receiver no user constructed.
|
||||
if (L.isStdlibMember(access)) return null;
|
||||
// Interface-declared generic methods dispatch statically, so the
|
||||
// receiver's runtime class must be provable — name that discipline
|
||||
// instead of the object-literal wording when the method lives on an
|
||||
|
||||
@@ -11,7 +11,7 @@ import { BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, IrExpr, IrFunction, IrJsOp, I
|
||||
import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
|
||||
import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModuleFnOf, CompoundOp, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
|
||||
import { UNSUPPORTED, blockedBindingUseDiag, recordShapeMismatchDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
||||
import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, own } from "./lowerer.js";
|
||||
import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
|
||||
import { IndexMergeContributor, lowerIndexMergeHelper, lowerNpmStaticSafeIndexRead, strCharsCall } from "./lower-containers.js";
|
||||
import { npmStaticPackageOfPath } from "../npm-static.js";
|
||||
import { unsupportedModuleFeatureOf } from "../shared.js";
|
||||
@@ -20,7 +20,7 @@ import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUn
|
||||
import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
|
||||
import { lowerSocketInstanceOf } from "./lower-server.js";
|
||||
import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
|
||||
import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
||||
import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
||||
import { mixinFnOfCallee } from "./lower-mixins.js";
|
||||
import { isConstAssertionTypeNode, isGenericCallableMemberType, underConstAssertion, unitOnlyUnion } from "../types.js";
|
||||
import { lowerYield } from "./lower-generators.js";
|
||||
@@ -896,6 +896,18 @@ function lowerExprInner(L: Lowerer, expr: ts.Expression): IrExpr {
|
||||
if (t) return nsUndefRead(L, expr.text, expr, t);
|
||||
}
|
||||
}
|
||||
// A read of a TRAP binding — a declaration whose own initializer
|
||||
// provably threw (module init unwound there), so this reference can
|
||||
// never execute: any lowering is sound, and the trap keeps the
|
||||
// shape honest. Typed by the use site when it maps, the F64 dummy
|
||||
// otherwise (never observed — never even reached).
|
||||
{
|
||||
const sym = L.resolveValueSymbol(expr);
|
||||
if (sym !== null && L.trapBindings.has(sym)) {
|
||||
const t = ambientUndefReadType(L, expr) ?? contextualUndefReadType(L, expr) ?? F64;
|
||||
return nsUndefRead(L, expr.text, expr, t);
|
||||
}
|
||||
}
|
||||
// A program CLASS NAME as a value: the classRef over the class's
|
||||
// immortal class object (member accesses and construction never
|
||||
// reach here — their hooks claim the property/new forms first).
|
||||
@@ -1616,6 +1628,23 @@ function lowerExprInner(L: Lowerer, expr: ts.Expression): IrExpr {
|
||||
if (t) return nsUndefRead(L, ambientRoot.text, expr, t);
|
||||
}
|
||||
}
|
||||
// A member read through a NULLISH generic binding (`const i: I<A &
|
||||
// B> = null as any; const _i: I<A> = i.something` — the receiver
|
||||
// provably holds null/undefined forever): the read throws Node's
|
||||
// exact TypeError at the access.
|
||||
if (ts.isIdentifier(expr.expression) && expr.questionDotToken === undefined) {
|
||||
const unit = nullishGenericBindingUnitOf(L, L.resolveValueSymbol(expr.expression));
|
||||
if (unit !== null) {
|
||||
const t = ambientUndefReadType(L, expr) ?? contextualUndefReadType(L, expr) ?? F64;
|
||||
return nodeThrowExpr(
|
||||
1,
|
||||
"",
|
||||
`Cannot read properties of ${unit} (reading '${expr.name.text}')`,
|
||||
t,
|
||||
loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
// The lib fence's PROPERTY chokepoint: a stdlib-declared member that
|
||||
// no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
|
||||
// re.exec as a value, ...) reports SC2020 here.
|
||||
|
||||
@@ -13,11 +13,11 @@ import { invalidJsonModuleDiag, npmEmbedFailedDiag, requiresDynamicImportDiag }
|
||||
import { BOOL, DYN, IrClassDef, IrExpr, IrFunction, IrGlobal, IrRecordShape, IrStmt, IrUnionDef, JSVAL, RUNTIME_ERROR_CLASSES, STRING, SrcLoc, VOID, arrayOf, canConvertToDyn, isUnitType } from "../../ir/nodes.js";
|
||||
import { ENTRY_NAME, PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, newFnCtx, uncheckedOverloadHandleCall } from "./lowerer.js";
|
||||
import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, isPromisifyCall } from "./lower-builtins.js";
|
||||
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf } from "./lower-calls.js";
|
||||
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishGenericBindingUnitOf } from "./lower-calls.js";
|
||||
import { isVarDeclared, provenanceElidedConstDecl } from "./lower-stmts.js";
|
||||
import { streamClassAliasDecl } from "./lower-stream.js";
|
||||
import { stdlibGlobalAliasDecl } from "./surfaces.js";
|
||||
import { collectNamespaceStmt, nsPathPrefix } from "./lower-namespaces.js";
|
||||
import { ambientUndefVarRootOf, collectNamespaceStmt, nsPathPrefix } from "./lower-namespaces.js";
|
||||
import { collectExpandoMembers } from "./lower-expando.js";
|
||||
import { isUnitOnlyTsType, unitOnlyUnion } from "../types.js";
|
||||
import type { ClassInfo } from "./lower-classes.js";
|
||||
@@ -1001,6 +1001,40 @@ export function collectGlobals(L: Lowerer, sf: ts.SourceFile, topStmts: ts.State
|
||||
// probes below would otherwise claim the call-initializer shape
|
||||
// and put its fences on the build.
|
||||
if (provenanceElidedConstDecl(L, decl)) continue;
|
||||
// A TRAP declaration at file scope — the initializer's chain roots
|
||||
// at an ambient-undefined name (`declare const t: Type<string>;
|
||||
// export const out = t.pipe(...)`): Node throws the root's
|
||||
// ReferenceError evaluating the initializer, so no value ever
|
||||
// exists and no reference can ever run. No global registers; the
|
||||
// binding enters trapBindings (registered HERE, before any body
|
||||
// lowers, so hoisted-function references resolve the trap), and
|
||||
// the statement lowering emits the throw at its position.
|
||||
if (decl.initializer !== undefined && ambientUndefVarRootOf(L, decl.initializer) !== null) {
|
||||
for (const nameNode of boundIdentifiersOf(decl.name)) {
|
||||
const sym = L.checker.getSymbolAtLocation(nameNode);
|
||||
if (sym) L.trapBindings.add(sym);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// A NULLISH generic binding at file scope (`export const Mixin:
|
||||
// MixinHelperFunc = null as any`): no storage — reads know the
|
||||
// value (lower-exprs/lower-calls claim them); the statement
|
||||
// lowering emits nothing by the same test.
|
||||
if (
|
||||
ts.isIdentifier(decl.name) &&
|
||||
nullishGenericBindingUnitOf(L, L.checker.getSymbolAtLocation(decl.name) ?? null) !== null
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// A DEAD unmappable binding at file scope (`var xs2: typeof
|
||||
// Array;` — never read anywhere): no global, no statement, no
|
||||
// type fence for a value the program never consumes.
|
||||
if (
|
||||
ts.isIdentifier(decl.name) &&
|
||||
deadUnmappableBinding(L, L.checker.getSymbolAtLocation(decl.name) ?? null, decl)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// `const f = <T>(x: T) => x` at file scope: a generic function
|
||||
// value binding — no global exists (the binding is never read;
|
||||
// calls and pinned references monomorphize against the
|
||||
|
||||
@@ -313,36 +313,68 @@ export function ambientNsRootOf(L: Lowerer, e: ts.Expression): ts.Identifier | n
|
||||
return root;
|
||||
}
|
||||
|
||||
/** The ROOT identifier of a property/element-access chain that names an
|
||||
* initializer-less AMBIENT variable (`declare const x: T;`, `declare var`)
|
||||
* in a user program file — the `declare const __VERSION__` stance's CHAIN
|
||||
* form. Node erases the declaration entirely, so the ROOT read throws the
|
||||
* catchable ReferenceError "<name> is not defined" BEFORE any member or
|
||||
* element key matters; callers lower the whole chain to that throw, typed
|
||||
* by the use site. Mappable roots never need this (their bare-read
|
||||
* undefRead composes through ordinary member lowerings); this serves the
|
||||
* roots whose declared type has no mapping. Null for stdlib/@types roots
|
||||
* (their own chokepoints stand), optional chains (conservative — the
|
||||
* ordinary fences keep them), and anything declared with a value. */
|
||||
/** The ROOT identifier of an expression whose FIRST runtime step is a read
|
||||
* Node cannot serve — the `declare const __VERSION__` stance's CHAIN form,
|
||||
* widened over every chain shape whose root evaluates first. Three root
|
||||
* families qualify:
|
||||
*
|
||||
* - an initializer-less AMBIENT variable (`declare const/let/var x: T;`)
|
||||
* in a user program file — Node erases the declaration entirely, so
|
||||
* the root read throws the catchable ReferenceError "<name> is not
|
||||
* defined";
|
||||
* - an ambient `declare function` nothing defines (the same erasure,
|
||||
* ambientUndefinedFnSymbolOf);
|
||||
* - a TRAP BINDING (L.trapBindings) — a binding whose own initializer
|
||||
* provably threw before producing a value, so module init unwound and
|
||||
* no reference to it can ever execute (any lowering is sound there;
|
||||
* the trap keeps the shape honest if reachability analysis is wrong).
|
||||
*
|
||||
* The walk steps through parens, non-null/as/satisfies assertions,
|
||||
* property and element accesses (OPTIONAL chains included — `?.` guards
|
||||
* null/undefined AFTER a successful read; it cannot guard the root's own
|
||||
* ReferenceError), calls and `new` (the callee evaluates before any
|
||||
* argument), instantiation expressions, and tagged templates (the tag
|
||||
* evaluates first). Callers lower the WHOLE expression to the root's
|
||||
* throw, typed by the use site — and never lower the arguments, exactly
|
||||
* the order Node dies in. Null for stdlib/@types roots (their own
|
||||
* chokepoints stand) and anything declared with a value. */
|
||||
export function ambientUndefVarRootOf(L: Lowerer, e: ts.Expression): ts.Identifier | null {
|
||||
let root: ts.Expression = e;
|
||||
for (;;) {
|
||||
if (ts.isParenthesizedExpression(root) || ts.isNonNullExpression(root)) {
|
||||
if (
|
||||
ts.isParenthesizedExpression(root) ||
|
||||
ts.isNonNullExpression(root) ||
|
||||
ts.isAsExpression(root) ||
|
||||
ts.isSatisfiesExpression(root) ||
|
||||
ts.isTypeAssertion(root)
|
||||
) {
|
||||
root = root.expression;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
(ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root)) &&
|
||||
root.questionDotToken === undefined
|
||||
) {
|
||||
if (ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root)) {
|
||||
root = root.expression;
|
||||
continue;
|
||||
}
|
||||
if (ts.isCallExpression(root) || ts.isNewExpression(root)) {
|
||||
root = root.expression;
|
||||
continue;
|
||||
}
|
||||
if (ts.isExpressionWithTypeArguments(root)) {
|
||||
root = root.expression;
|
||||
continue;
|
||||
}
|
||||
if (ts.isTaggedTemplateExpression(root)) {
|
||||
root = root.tag;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!ts.isIdentifier(root)) return null;
|
||||
const sym = L.resolveValueSymbol(root);
|
||||
if (!sym || L.isStdlibSymbol(sym)) return null;
|
||||
if (!sym) return null;
|
||||
if (L.trapBindings.has(sym)) return root;
|
||||
if (L.isStdlibSymbol(sym)) return null;
|
||||
if (ambientUndefinedFnSymbolOf(L, root) !== null) return root;
|
||||
const decls = L.checker.declarationsOf(sym);
|
||||
if (decls.length === 0) return null;
|
||||
for (const d of decls) {
|
||||
|
||||
@@ -11,10 +11,10 @@ import { enforceLibBoundary } from "./lib-boundary.js";
|
||||
import { cjsExportAssignmentOf, cjsExportDiscardReason, cjsExportTargetLiteral, isCjsJsFile, isJsSourceFile, locOf, requireSpecOf } from "../program.js";
|
||||
import { COMPOUND_ASSIGN_OPS, CompoundOp, STR_METHODS, UNSUPPORTED_STMT, isStdlibMember, sideEffectFreeOptionValue, stdlibGlobalAliasDecl, stdlibGlobalNameOf } from "./surfaces.js";
|
||||
import { isProvenanceSourceFile } from "../provenance-registry.js";
|
||||
import { lowerImportEquals, nsWritableTarget } from "./lower-namespaces.js";
|
||||
import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
|
||||
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
||||
import { ForOfIterProjection, lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
||||
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf, recordKeysArrayCall } from "./lower-calls.js";
|
||||
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
||||
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
||||
import type { ClassInfo, ClassIteratorInfo } from "./lower-classes.js";
|
||||
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
||||
@@ -2574,8 +2574,50 @@ export function lowerVarDecl(L: Lowerer, decl: ts.VariableDeclaration, isLet: bo
|
||||
// probes below would otherwise claim the call-initializer shape and
|
||||
// put its fences on the build.
|
||||
if (provenanceElidedConstDecl(L, decl)) return null;
|
||||
|
||||
// A TRAP declaration — the initializer's chain roots at an
|
||||
// ambient-undefined name (`declare function factory...; const make =
|
||||
// factory<T>()`): Node throws the root's ReferenceError while
|
||||
// evaluating the initializer, module init unwinds THERE, and nothing
|
||||
// after — including every reference to this binding — ever runs. The
|
||||
// statement lowers to exactly that throw; the binding registers as a
|
||||
// trap (references lower to the same never-reached shape), and no
|
||||
// storage or type mapping is needed for a value that never exists.
|
||||
if (decl.initializer !== undefined) {
|
||||
const root = ambientUndefVarRootOf(L, decl.initializer);
|
||||
if (root !== null) {
|
||||
for (const nameNode of boundIdentifiersOf(decl.name)) {
|
||||
const sym = L.checker.getSymbolAtLocation(nameNode);
|
||||
if (sym) L.trapBindings.add(sym);
|
||||
}
|
||||
return {
|
||||
kind: "exprStmt",
|
||||
expr: nsUndefRead(L, root.text, decl.initializer, F64),
|
||||
loc: locOf(decl),
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!ts.isIdentifier(decl.name)) L.unsupported("SC1031", decl.name);
|
||||
|
||||
// A NULLISH generic binding (`const i: I<A & B> = null as any` — the
|
||||
// declared type has no mapping, the value is provably null/undefined
|
||||
// forever): no storage exists; reads know the value (member reads and
|
||||
// method calls lower to Node's exact TypeError, nullish-to-nullish
|
||||
// flows to nothing), so the declaration emits nothing.
|
||||
if (nullishGenericBindingUnitOf(L, L.checker.getSymbolAtLocation(decl.name) ?? null) !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// A DEAD unmappable binding (`var xs2: typeof Array;`, the write-only
|
||||
// `var f2: { <T, U>(x: T, y: U): T }`): never read anywhere in the
|
||||
// program, its initializer (if any) a side-effect-free value — Node
|
||||
// materializes the value and drops it, zero observable effect — so
|
||||
// the declaration emits nothing instead of fencing on a type the
|
||||
// program never consumes.
|
||||
if (deadUnmappableBinding(L, L.checker.getSymbolAtLocation(decl.name) ?? null, decl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// An initializer-LESS declaration named `module` or `exports` at the
|
||||
// top level of an import/export-free TS file shadows the CommonJS
|
||||
// wrapper binding: Node hosts such a file as CJS, where
|
||||
@@ -3631,6 +3673,39 @@ function isEsModuleStamp(expr: ts.Expression): boolean {
|
||||
export function lowerExprStatement(L: Lowerer, expr: ts.Expression): IrStmt {
|
||||
const stmtNode = expr.parent;
|
||||
while (ts.isParenthesizedExpression(expr)) expr = expr.expression;
|
||||
// Assignment statements over the no-storage binding families:
|
||||
// - `f1 = f2` where the RHS roots at an ambient-undefined name:
|
||||
// Node evaluates the RHS first and dies on the root's
|
||||
// ReferenceError before any binding is touched — the statement IS
|
||||
// that throw;
|
||||
// - `a = b` between NULLISH generic bindings: the value is known
|
||||
// and the target has no storage — nothing happens;
|
||||
// - a write to a DEAD binding (registration proved every write's
|
||||
// RHS side-effect-free): Node builds the value and drops it —
|
||||
// nothing happens.
|
||||
if (
|
||||
ts.isBinaryExpression(expr) &&
|
||||
expr.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
||||
ts.isIdentifier(expr.left)
|
||||
) {
|
||||
const rhsRoot = ambientUndefVarRootOf(L, expr.right);
|
||||
if (rhsRoot !== null) {
|
||||
return {
|
||||
kind: "exprStmt",
|
||||
expr: nsUndefRead(L, rhsRoot.text, expr.right, F64),
|
||||
loc: locOf(expr),
|
||||
};
|
||||
}
|
||||
const targetSym = L.resolveValueSymbol(expr.left);
|
||||
if (targetSym !== null) {
|
||||
if (nullishGenericBindingUnitOf(L, targetSym) !== null && nullishExprUnitOf(L, expr.right) !== null) {
|
||||
return { kind: "block", body: [], loc: locOf(expr) };
|
||||
}
|
||||
if (L.deadBindings.has(targetSym)) {
|
||||
return { kind: "block", body: [], loc: locOf(expr) };
|
||||
}
|
||||
}
|
||||
}
|
||||
// A bare module-namespace binding in STATEMENT position (`ns;` for
|
||||
// `import * as ns from ...` — the corpus's canonical "the import
|
||||
// linked" assertion): Node evaluates an initialized binding (namespace
|
||||
|
||||
@@ -709,6 +709,29 @@ export class Lowerer {
|
||||
/** Per-symbol result of the never-reassigned file scan
|
||||
* (bindingNeverReassigned — object-literal generic-method receivers). */
|
||||
readonly neverReassignedCache = new Map<ts.Symbol, boolean>();
|
||||
/** TRAP bindings: declarations whose initializer provably throws before
|
||||
* producing a value (its chain roots at an ambient-undefined name —
|
||||
* ambientUndefVarRootOf). Module init unwinds at the declaration, so no
|
||||
* reference to the binding can ever execute; the statement lowers to the
|
||||
* root's throw, no storage exists, and references lower to the same
|
||||
* trap shape (never reached — sound whatever the type). */
|
||||
readonly trapBindings = new Set<ts.Symbol>();
|
||||
/** NULLISH bindings of unmappable (generic-signature) types: `const i:
|
||||
* I<A & B> = null as any` — the binding provably holds null/undefined
|
||||
* forever (every write's RHS is nullish too), so no storage exists and
|
||||
* each READ knows the value. Member reads and method calls through one
|
||||
* lower to Node's exact TypeError ("Cannot read properties of null
|
||||
* (reading 'fn')"); nullish-to-nullish flows lower to nothing. The map
|
||||
* answers which unit the binding holds (the TypeError names it). Null
|
||||
* entries cache probed non-qualifiers. */
|
||||
readonly nullishBindings = new Map<ts.Symbol, "null" | "undefined" | null>();
|
||||
/** DEAD bindings of unmappable types: never READ anywhere in the
|
||||
* program, declared with no initializer or a value-only one (a function
|
||||
* literal), every write's RHS side-effect-free. Node materializes the
|
||||
* value and drops it — zero observable effect — so the declaration and
|
||||
* its writes lower to nothing, and no type fence fires for a value the
|
||||
* program never consumes. */
|
||||
readonly deadBindings = new Set<ts.Symbol>();
|
||||
/** IMPLICIT-ANY function-value bindings (npm-static JS — `const knownBy
|
||||
* = (cmd) => ...`), by their VariableDeclaration: the registered info,
|
||||
* or null for probed non-qualifiers (implicitLocalFnNodeOf). */
|
||||
|
||||
@@ -4862,12 +4862,42 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2591-ambient-generic-traps.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2591-ambient-generic-traps.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2578-jsval-into-unknown-rows.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2578-jsval-into-unknown-rows.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2592-ambient-trap-uncaught.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2592-ambient-trap-uncaught.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2579-jsval-object-param-crossing.js": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2579-jsval-object-param-crossing.js"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2593-generic-inert-bindings.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2593-generic-inert-bindings.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2594-nullish-generic-bindings.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2594-nullish-generic-bindings.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2586-object-assign-spread.js": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2586-object-assign-spread.js"
|
||||
@@ -4880,12 +4910,6 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2579-jsval-object-param-crossing.js": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2579-jsval-object-param-crossing.js"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2588-array-entries-chains.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2588-array-entries-chains.ts"
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Ambient-undefined roots through GENERIC-signature chains: Node erases
|
||||
// `declare const/let/var` and body-less `declare function` entirely, so the
|
||||
// FIRST runtime step of any chain rooted there is the catchable
|
||||
// ReferenceError "<name> is not defined" — whatever the declared type, and
|
||||
// before any member, type argument, or call argument evaluates. `?.` cannot
|
||||
// guard it: the optional chain only guards null/undefined AFTER a
|
||||
// successful root read.
|
||||
|
||||
interface Y {
|
||||
foo<T>(this: T, arg: keyof T): void;
|
||||
a: number;
|
||||
}
|
||||
declare const value: Y | undefined;
|
||||
declare let TabGroup: <T>(props: { idx: number }) => T;
|
||||
declare function chain<T>(t: T): { map<U>(f: (x: T) => U): U };
|
||||
declare const o6: <T>() => undefined | { x: number };
|
||||
declare const parse: <def>(def: def) => def;
|
||||
|
||||
// Optional-chained generic method call on an ambient root.
|
||||
try {
|
||||
value?.foo("a");
|
||||
} catch (e) {
|
||||
console.log("1", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
// A generic call signature on an ambient `declare let`.
|
||||
try {
|
||||
TabGroup({ idx: 0 });
|
||||
} catch (e) {
|
||||
console.log("2", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
// A method chain THROUGH an ambient function's call result: the callee
|
||||
// read dies first; the arguments (a lambda here) never evaluate.
|
||||
try {
|
||||
chain(1).map((x) => x + 1);
|
||||
} catch (e) {
|
||||
console.log("3", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
// An instantiation-expression call plus an optional element access.
|
||||
try {
|
||||
o6<number>()?.["x"];
|
||||
} catch (e) {
|
||||
console.log("4", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
// A TRAP declaration: the initializer's root throws, so the binding never
|
||||
// holds a value and the catch owns the unwind.
|
||||
try {
|
||||
const r = parse([{ a: "foo" }]);
|
||||
console.log("never", r);
|
||||
} catch (e) {
|
||||
console.log("5", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
// An assignment whose RHS roots at an ambient name: the RHS evaluates
|
||||
// first — the statement IS the root's throw.
|
||||
declare let f1: <T>(x: Array<T>) => T[];
|
||||
declare let f2: <U>(x: Array<U>) => U[];
|
||||
try {
|
||||
f1 = f2;
|
||||
} catch (e) {
|
||||
console.log("6", (e as Error).name, (e as Error).message);
|
||||
}
|
||||
|
||||
console.log("done");
|
||||
@@ -0,0 +1,20 @@
|
||||
// @exit: 1
|
||||
// An UNCAUGHT ambient-root trap at module top level: Node prints what ran,
|
||||
// then dies with the ReferenceError — module init unwinds at the trap
|
||||
// declaration, and every later statement (including statements that would
|
||||
// fence if they lowered under a value-world lie) is dead. The binding that
|
||||
// never initialized is a TRAP binding: references to it can never execute.
|
||||
|
||||
console.log("before");
|
||||
|
||||
interface Type<t> {
|
||||
pipe<fn extends (In: t) => unknown>(fn: fn): Type<fn>;
|
||||
}
|
||||
declare const t: Type<string>;
|
||||
|
||||
export const out = t.pipe((s: string) => s.length); // ReferenceError: t is not defined
|
||||
|
||||
// Dead code past the unwind: a read of the trap binding, and another
|
||||
// ambient-rooted chain — neither runs.
|
||||
export const dead = out;
|
||||
console.log("never", dead);
|
||||
@@ -0,0 +1,35 @@
|
||||
// @transform-types
|
||||
// (strip-only Node refuses the angle-bracket type assertion below)
|
||||
// INERT generic values: bindings whose types keep type parameters but whose
|
||||
// values the program never consumes. Node materializes and drops them —
|
||||
// zero observable effect — so they compile to NOTHING instead of fencing:
|
||||
// - uninitialized, never-read declarations of generic-signature types;
|
||||
// - write-only bindings whose writes carry side-effect-free values;
|
||||
// - assertion-shaped generic bindings (`< <T>(x: T) => T >(arrow)`),
|
||||
// which register like annotated ones and monomorphize per call.
|
||||
|
||||
// Uninitialized and never read: `typeof Array` keeps ArrayConstructor's
|
||||
// type parameters, the object literals keep their own.
|
||||
var xs2: typeof Array;
|
||||
var a1: { <T, U extends T>(): void };
|
||||
var a2: { <T extends U, U extends Date>(): void };
|
||||
|
||||
// Write-only: the declaration has no mapping and no reader; the arrow the
|
||||
// write carries is a value-only expression Node drops with the binding.
|
||||
var f2: { <T, U>(x: T, y: U): T };
|
||||
f2 = (x, y) => {
|
||||
void y;
|
||||
return x;
|
||||
};
|
||||
|
||||
// A type ASSERTION supplies the generic signature exactly like an
|
||||
// annotation: the checker types the operand's parameters by it, and calls
|
||||
// monomorphize per pinned signature.
|
||||
var ident = < <T>(x: T) => T >((x) => x);
|
||||
console.log(ident(41) + 1, ident("hey").length);
|
||||
|
||||
// The angle-bracket cast's `as` twin, uncalled: registered, no instance
|
||||
// demanded, no code.
|
||||
const uncalled = ((x) => x) as <T>(x: T) => T;
|
||||
|
||||
console.log("done");
|
||||
@@ -0,0 +1,39 @@
|
||||
// NULLISH generic bindings: `null as any` / `null!` under a type that keeps
|
||||
// generic signatures. The binding provably holds its unit forever (every
|
||||
// write is nullish too), so no storage exists and each read knows the
|
||||
// value — member reads and generic-method calls compile to Node's exact
|
||||
// TypeError, nullish-to-nullish flows compile to nothing.
|
||||
|
||||
type Cond = <T>() => T extends { a?: string } ? 0 : 1;
|
||||
let a: Cond = null!;
|
||||
let b: Cond = null!;
|
||||
a = b; // nullish-to-nullish: nothing happens
|
||||
|
||||
interface I<T> {
|
||||
fn<K extends keyof T>(k: K): void;
|
||||
}
|
||||
|
||||
const i: I<{ x: number }> = null as any;
|
||||
try {
|
||||
i.fn("x");
|
||||
} catch (e) {
|
||||
console.log("1", e instanceof TypeError, (e as Error).message);
|
||||
}
|
||||
|
||||
// Propagation: a nullish binding initialized FROM a nullish binding.
|
||||
const j: I<{ y: string }> = i;
|
||||
try {
|
||||
j.fn("y");
|
||||
} catch (e) {
|
||||
console.log("2", e instanceof TypeError, (e as Error).message);
|
||||
}
|
||||
|
||||
// The undefined unit keeps its own message.
|
||||
const u: I<{ z: boolean }> = undefined!;
|
||||
try {
|
||||
u.fn("z");
|
||||
} catch (e) {
|
||||
console.log("3", e instanceof TypeError, (e as Error).message);
|
||||
}
|
||||
|
||||
console.log("done");
|
||||
@@ -28,8 +28,10 @@ const sum = (...xs: number[]): number => xs.length;
|
||||
const storedSum = sum;
|
||||
console.log(storedSum(1, 2));
|
||||
|
||||
// A function's return type carries the failure.
|
||||
declare const makeWeak: () => WeakMap<object, number>;
|
||||
// A function's return type carries the failure. The value arrives through
|
||||
// a cast, not an ambient declare (a declare-rooted chain would compile to
|
||||
// Node's ReferenceError at the root instead).
|
||||
const makeWeak = (0 as unknown) as () => WeakMap<object, number>;
|
||||
const storedMake = makeWeak;
|
||||
console.log(storedMake());
|
||||
|
||||
@@ -38,6 +40,6 @@ interface Holder {
|
||||
label: string;
|
||||
cache: WeakMap<object, number>;
|
||||
}
|
||||
declare const held: Holder;
|
||||
const held = (0 as unknown) as Holder;
|
||||
const kept = held;
|
||||
console.log(kept.label);
|
||||
|
||||
@@ -27,7 +27,9 @@ function outer(): number {
|
||||
}
|
||||
console.log(outer());
|
||||
|
||||
// An ambient binding of generic function type has no body to monomorphize.
|
||||
// An ambient binding of generic function type now compiles to Node's exact
|
||||
// ReferenceError at the access (the declare-erasure stance — no diagnostic
|
||||
// here; the optional chain cannot guard the root's own throw).
|
||||
declare const ambient: undefined | (<T>(f: (a: T) => T) => T);
|
||||
console.log(ambient?.((n: number) => n) === undefined);
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
// '%call' records — what fences is the remainder, like a primitive part
|
||||
// against an object part (inhabited only per the checker, never buildable).
|
||||
|
||||
// The producer has a BODY (an ambient `declare function` would compile to
|
||||
// Node's ReferenceError at the call instead — the declare-erasure stance).
|
||||
type Branded = number & { __brand: "id" };
|
||||
declare function mint(): Branded;
|
||||
function mint(): Branded {
|
||||
return 1 as Branded;
|
||||
}
|
||||
const kept = mint();
|
||||
console.log(kept);
|
||||
|
||||
@@ -10,7 +10,9 @@ function pick(x: string): string | number {
|
||||
const stored = pick;
|
||||
console.log(stored("a"));
|
||||
|
||||
// An overloaded TYPE LITERAL slot fences the same way.
|
||||
declare const on: { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
// An overloaded TYPE LITERAL slot fences the same way. The value arrives
|
||||
// through a cast, not an ambient declare (a declare-rooted chain would
|
||||
// compile to Node's ReferenceError at the root instead).
|
||||
const on = ((() => {}) as unknown) as { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
const listener = on;
|
||||
console.log(listener("exit", 0));
|
||||
|
||||
@@ -76,4 +76,7 @@ function pick(a: string | number, b: number | boolean): boolean {
|
||||
}
|
||||
const picked = pick(1, 2);
|
||||
|
||||
// The binding is READ so the fence stays observable (an unread bigint
|
||||
// declaration is a value Node builds and drops — it compiles to nothing).
|
||||
const big = 10n;
|
||||
console.log(big);
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
checker-panic-infinity.ts:5:1 - error SC0004: the TypeScript checker crashed answering a query here (an upstream typescript-go bug): panic: api: failed to write response: json: cannot marshal from Go float64 within "/value": unsupported value: +Inf
|
||||
|
||||
4 | type A = 1e999;
|
||||
5 | export function f(): A { throw new Error("x"); }
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
6 | export const m = f();
|
||||
|
||||
checker-panic-infinity.ts:6:1 - error SC0004: the TypeScript checker crashed answering a query here (an upstream typescript-go bug): panic: api: failed to write response: json: cannot marshal from Go float64 within "/value": unsupported value: +Inf
|
||||
|
||||
5 | export function f(): A { throw new Error("x"); }
|
||||
|
||||
@@ -152,52 +152,74 @@ component-fences.ts:29:13 - error SC2004: uses of 'storedSum' inherit the blocke
|
||||
|
||||
hint: the declaration of 'storedSum' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
component-fences.ts:33:7 - error SC2009: values of type '() => WeakMap<object, number>' cannot be compiled: the function shape is supported, but its return type 'WeakMap<object, number>' does not compile
|
||||
component-fences.ts:34:7 - error SC2009: values of type '() => WeakMap<object, number>' cannot be compiled: the function shape is supported, but its return type 'WeakMap<object, number>' does not compile
|
||||
|
||||
32 | declare const makeWeak: () => WeakMap<object, number>;
|
||||
33 | const storedMake = makeWeak;
|
||||
| ^~~~~~~~~~
|
||||
34 | console.log(storedMake());
|
||||
33 | // Node's ReferenceError at the root instead).
|
||||
34 | const makeWeak = (0 as unknown) as () => WeakMap<object, number>;
|
||||
| ^~~~~~~~
|
||||
35 | const storedMake = makeWeak;
|
||||
|
||||
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
component-fences.ts:33:20 - error SC1090: the reference to 'makeWeak' (a binding form with no lowering) is not supported yet
|
||||
component-fences.ts:35:7 - error SC2009: values of type '() => WeakMap<object, number>' cannot be compiled: the function shape is supported, but its return type 'WeakMap<object, number>' does not compile
|
||||
|
||||
32 | declare const makeWeak: () => WeakMap<object, number>;
|
||||
33 | const storedMake = makeWeak;
|
||||
34 | const makeWeak = (0 as unknown) as () => WeakMap<object, number>;
|
||||
35 | const storedMake = makeWeak;
|
||||
| ^~~~~~~~~~
|
||||
36 | console.log(storedMake());
|
||||
|
||||
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
component-fences.ts:35:20 - error SC2004: uses of 'makeWeak' inherit the blocker on its declaration
|
||||
|
||||
34 | const makeWeak = (0 as unknown) as () => WeakMap<object, number>;
|
||||
35 | const storedMake = makeWeak;
|
||||
| ^~~~~~~~
|
||||
34 | console.log(storedMake());
|
||||
36 | console.log(storedMake());
|
||||
|
||||
component-fences.ts:34:13 - error SC2004: uses of 'storedMake' inherit the blocker on its declaration
|
||||
hint: the declaration of 'makeWeak' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
33 | const storedMake = makeWeak;
|
||||
34 | console.log(storedMake());
|
||||
component-fences.ts:36:13 - error SC2004: uses of 'storedMake' inherit the blocker on its declaration
|
||||
|
||||
35 | const storedMake = makeWeak;
|
||||
36 | console.log(storedMake());
|
||||
| ^~~~~~~~~~
|
||||
35 |
|
||||
37 |
|
||||
|
||||
hint: the declaration of 'storedMake' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
component-fences.ts:42:7 - error SC2009: values of type 'Holder' cannot be compiled: the record shape is supported, but its member 'cache' has type 'WeakMap<object, number>', which does not compile
|
||||
component-fences.ts:43:7 - error SC2009: values of type 'Holder' cannot be compiled: the record shape is supported, but its member 'cache' has type 'WeakMap<object, number>', which does not compile
|
||||
|
||||
41 | declare const held: Holder;
|
||||
42 | const kept = held;
|
||||
42 | }
|
||||
43 | const held = (0 as unknown) as Holder;
|
||||
| ^~~~
|
||||
43 | console.log(kept.label);
|
||||
44 | const kept = held;
|
||||
|
||||
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
component-fences.ts:42:14 - error SC1090: the reference to 'held' (a binding form with no lowering) is not supported yet
|
||||
component-fences.ts:44:7 - error SC2009: values of type 'Holder' cannot be compiled: the record shape is supported, but its member 'cache' has type 'WeakMap<object, number>', which does not compile
|
||||
|
||||
41 | declare const held: Holder;
|
||||
42 | const kept = held;
|
||||
43 | const held = (0 as unknown) as Holder;
|
||||
44 | const kept = held;
|
||||
| ^~~~
|
||||
45 | console.log(kept.label);
|
||||
|
||||
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
component-fences.ts:44:14 - error SC2004: uses of 'held' inherit the blocker on its declaration
|
||||
|
||||
43 | const held = (0 as unknown) as Holder;
|
||||
44 | const kept = held;
|
||||
| ^~~~
|
||||
43 | console.log(kept.label);
|
||||
45 | console.log(kept.label);
|
||||
|
||||
component-fences.ts:43:13 - error SC2004: uses of 'kept' inherit the blocker on its declaration
|
||||
hint: the declaration of 'held' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
42 | const kept = held;
|
||||
43 | console.log(kept.label);
|
||||
component-fences.ts:45:13 - error SC2004: uses of 'kept' inherit the blocker on its declaration
|
||||
|
||||
44 | const kept = held;
|
||||
45 | console.log(kept.label);
|
||||
| ^~~~~~~~~~
|
||||
44 |
|
||||
46 |
|
||||
|
||||
hint: the declaration of 'kept' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
@@ -73,30 +73,23 @@ generic-value-bindings.ts:26:10 - error SC2004: uses of 'inner' inherit the bloc
|
||||
|
||||
hint: the declaration of 'inner' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
generic-value-bindings.ts:32:13 - error SC1030: the generic-signature binding 'ambient' (its type keeps type parameters and the declaration has no initializer — no function body exists to monomorphize, so nothing can pin a concrete signature) is not supported yet
|
||||
generic-value-bindings.ts:39:13 - error SC1090: results that are themselves generic functions ('<U>(y: U) => string' keeps its type parameters — no call-site instantiation pins them, and the returned value would need the producing call's frame; restructure to one generic function taking all arguments) are not supported yet
|
||||
|
||||
31 | declare const ambient: undefined | (<T>(f: (a: T) => T) => T);
|
||||
32 | console.log(ambient?.((n: number) => n) === undefined);
|
||||
| ^~~~~~~
|
||||
33 |
|
||||
|
||||
generic-value-bindings.ts:37:13 - error SC1090: results that are themselves generic functions ('<U>(y: U) => string' keeps its type parameters — no call-site instantiation pins them, and the returned value would need the producing call's frame; restructure to one generic function taking all arguments) are not supported yet
|
||||
|
||||
36 | const curried = <T>(x: T) => <U>(y: U): string => `${x}|${y}`;
|
||||
37 | console.log(curried(1)("a"));
|
||||
38 | const curried = <T>(x: T) => <U>(y: U): string => `${x}|${y}`;
|
||||
39 | console.log(curried(1)("a"));
|
||||
| ^~~~~~~~~~
|
||||
38 |
|
||||
40 |
|
||||
|
||||
generic-value-bindings.ts:49:15 - error SC1090: calls of the generic method 'm' through this receiver (no compiled declaration with a body resolves statically here — ambient 'declare class' and interface-only methods are signature-only, and only class, static, and object-literal generic methods with bodies monomorphize) is not supported yet
|
||||
generic-value-bindings.ts:51:15 - error SC1090: calls of the generic method 'm' through this receiver (no compiled declaration with a body resolves statically here — ambient 'declare class' and interface-only methods are signature-only, and only class, static, and object-literal generic methods with bodies monomorphize) is not supported yet
|
||||
|
||||
48 | function throughParam(a: Amb): void {
|
||||
49 | console.log(a.m(4));
|
||||
50 | function throughParam(a: Amb): void {
|
||||
51 | console.log(a.m(4));
|
||||
| ^~~~~~
|
||||
50 | }
|
||||
52 | }
|
||||
|
||||
generic-value-bindings.ts:54:14 - error SC1090: generic arrow/function expressions outside a never-reassigned module-scope binding (only `const f = <T>(x: T) => ...` bindings and top-level generic function declarations monomorphize) is not supported yet
|
||||
generic-value-bindings.ts:56:14 - error SC1090: generic arrow/function expressions outside a never-reassigned module-scope binding (only `const f = <T>(x: T) => ...` bindings and top-level generic function declarations monomorphize) is not supported yet
|
||||
|
||||
53 | // A generic lambda outside any monomorphizable home (an IIFE).
|
||||
54 | console.log((<T>(x: T): T => x)(5));
|
||||
55 | // A generic lambda outside any monomorphizable home (an IIFE).
|
||||
56 | console.log((<T>(x: T): T => x)(5));
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
55 |
|
||||
57 |
|
||||
@@ -1,17 +1,35 @@
|
||||
intersection-values.ts:8:7 - error SC2008: values of type 'Branded' cannot be compiled: this intersection resolves to no runtime shape
|
||||
intersection-values.ts:9:10 - error SC2008: values of type 'Branded' cannot be compiled: this intersection resolves to no runtime shape
|
||||
|
||||
7 | declare function mint(): Branded;
|
||||
8 | const kept = mint();
|
||||
| ^~~~
|
||||
9 | console.log(kept);
|
||||
8 | type Branded = number & { __brand: "id" };
|
||||
9 | function mint(): Branded {
|
||||
| ^~~~
|
||||
10 | return 1 as Branded;
|
||||
|
||||
hint: restate the intersection as a single interface or type literal with the combined members; mixin-produced intersections compile where the mixin chain pins one instantiation
|
||||
|
||||
intersection-values.ts:9:13 - error SC2004: uses of 'kept' inherit the blocker on its declaration
|
||||
intersection-values.ts:12:7 - error SC2008: values of type 'Branded' cannot be compiled: this intersection resolves to no runtime shape
|
||||
|
||||
8 | const kept = mint();
|
||||
9 | console.log(kept);
|
||||
11 | }
|
||||
12 | const kept = mint();
|
||||
| ^~~~
|
||||
13 | console.log(kept);
|
||||
|
||||
hint: restate the intersection as a single interface or type literal with the combined members; mixin-produced intersections compile where the mixin chain pins one instantiation
|
||||
|
||||
intersection-values.ts:12:14 - error SC2004: uses of 'mint' inherit the blocker on its declaration
|
||||
|
||||
11 | }
|
||||
12 | const kept = mint();
|
||||
| ^~~~
|
||||
13 | console.log(kept);
|
||||
|
||||
hint: the declaration of 'mint' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
intersection-values.ts:13:13 - error SC2004: uses of 'kept' inherit the blocker on its declaration
|
||||
|
||||
12 | const kept = mint();
|
||||
13 | console.log(kept);
|
||||
| ^~~~
|
||||
10 |
|
||||
14 |
|
||||
|
||||
hint: the declaration of 'kept' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
@@ -16,27 +16,38 @@ overloaded-function-values.ts:11:13 - error SC2004: uses of 'stored' inherit the
|
||||
|
||||
hint: the declaration of 'stored' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
overloaded-function-values.ts:15:7 - error SC2007: values of type '{ (event: "exit", code: number): void; (event: "error", err: string): void; }' cannot be compiled: the type declares multiple call signatures (overloads), and a compiled function value is always one concrete signature
|
||||
overloaded-function-values.ts:16:7 - error SC2007: values of type '{ (event: "exit", code: number): void; (event: "error", err: string): void; }' cannot be compiled: the type declares multiple call signatures (overloads), and a compiled function value is always one concrete signature
|
||||
|
||||
14 | declare const on: { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
15 | const listener = on;
|
||||
| ^~~~~~~~
|
||||
16 | console.log(listener("exit", 0));
|
||||
15 | // compile to Node's ReferenceError at the root instead).
|
||||
16 | const on = ((() => {}) as unknown) as { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
| ^~
|
||||
17 | const listener = on;
|
||||
|
||||
hint: annotate the slot with the ONE signature the program actually calls (e.g. '(x: number) => string'), or wrap the overloaded function in a single-signature arrow
|
||||
|
||||
overloaded-function-values.ts:15:18 - error SC1090: the reference to 'on' (a binding form with no lowering) is not supported yet
|
||||
overloaded-function-values.ts:17:7 - error SC2007: values of type '{ (event: "exit", code: number): void; (event: "error", err: string): void; }' cannot be compiled: the type declares multiple call signatures (overloads), and a compiled function value is always one concrete signature
|
||||
|
||||
14 | declare const on: { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
15 | const listener = on;
|
||||
16 | const on = ((() => {}) as unknown) as { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
17 | const listener = on;
|
||||
| ^~~~~~~~
|
||||
18 | console.log(listener("exit", 0));
|
||||
|
||||
hint: annotate the slot with the ONE signature the program actually calls (e.g. '(x: number) => string'), or wrap the overloaded function in a single-signature arrow
|
||||
|
||||
overloaded-function-values.ts:17:18 - error SC2004: uses of 'on' inherit the blocker on its declaration
|
||||
|
||||
16 | const on = ((() => {}) as unknown) as { (event: "exit", code: number): void; (event: "error", err: string): void };
|
||||
17 | const listener = on;
|
||||
| ^~
|
||||
16 | console.log(listener("exit", 0));
|
||||
18 | console.log(listener("exit", 0));
|
||||
|
||||
overloaded-function-values.ts:16:13 - error SC2004: uses of 'listener' inherit the blocker on its declaration
|
||||
hint: the declaration of 'on' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
|
||||
15 | const listener = on;
|
||||
16 | console.log(listener("exit", 0));
|
||||
overloaded-function-values.ts:18:13 - error SC2004: uses of 'listener' inherit the blocker on its declaration
|
||||
|
||||
17 | const listener = on;
|
||||
18 | console.log(listener("exit", 0));
|
||||
| ^~~~~~~~
|
||||
17 |
|
||||
19 |
|
||||
|
||||
hint: the declaration of 'listener' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
@@ -300,16 +300,25 @@ stdlib-fence.ts:75:10 - error SC2020: 'Object.is over these union operands' is p
|
||||
|
||||
hint: union-typed comparisons need one comparable shape (narrow first: check a discriminant field, or compare with '!== undefined'/'!== null' for unit arms)
|
||||
|
||||
stdlib-fence.ts:79:7 - error SC2001: values of type '10n' cannot be compiled yet (supported: number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
stdlib-fence.ts:81:7 - error SC2001: values of type '10n' cannot be compiled yet (supported: number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
78 |
|
||||
79 | const big = 10n;
|
||||
80 | // declaration is a value Node builds and drops — it compiles to nothing).
|
||||
81 | const big = 10n;
|
||||
| ^~~
|
||||
80 |
|
||||
82 | console.log(big);
|
||||
|
||||
stdlib-fence.ts:79:13 - error SC1090: syntax 'BigIntLiteral' is not supported yet
|
||||
stdlib-fence.ts:81:13 - error SC1090: syntax 'BigIntLiteral' is not supported yet
|
||||
|
||||
78 |
|
||||
79 | const big = 10n;
|
||||
80 | // declaration is a value Node builds and drops — it compiles to nothing).
|
||||
81 | const big = 10n;
|
||||
| ^~~
|
||||
80 |
|
||||
82 | console.log(big);
|
||||
|
||||
stdlib-fence.ts:82:13 - error SC2004: uses of 'big' inherit the blocker on its declaration
|
||||
|
||||
81 | const big = 10n;
|
||||
82 | console.log(big);
|
||||
| ^~~
|
||||
83 |
|
||||
|
||||
hint: the declaration of 'big' did not compile — fix the diagnostic reported there and these sites clear with it
|
||||
Reference in New Issue
Block a user