Files
scriptc/tests/diagnostics/generic-value-bindings.ts
Chris Tate e81e4f5ab5 Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- ambientUndefVarRootOf walks calls, `new`, optional chains, assertions, instantiation expressions, and tagged templates, and accepts `declare function` and trap-binding roots: Node erases the declaration, so the root read IS the crash — lowerCall, member reads, assignments, and variable declarations lower the whole statement to the catchable ReferenceError, arguments never evaluate, and the never-initialized binding registers as a trap whose references can never run
- never-read unmappable bindings with value-only initializers and writes emit nothing: Node builds the value and drops it, so `var xs2: typeof Array;` and the write-only `var f2: { <T, U>(x: T, y: U): T }` compile instead of fencing on a type the program never consumes
- null!/as-any bindings whose types keep generic signatures (or intern empty record shapes) register no storage; generic-method calls and member reads through them compile to Node's exact TypeError, nullish-to-nullish flows to nothing, heritage-clause uses decline the family so mixin resolution keeps its fence, and checked-dynamic (`any`) bindings keep their dyn story untouched
- assertion-shaped generic arrows (`< <T>(x: T) => T >((x) => x)`) register like annotated bindings and monomorphize per call; stdlib generic members (Promise.then, Object.defineProperty) take the SC2020 lib fence, and Function.prototype.apply/call/bind on compiled function values name the direct-call spelling
- diagnostics fixtures that used `declare` to conjure unconstructible values now conjure them by cast so each fence family keeps its pin; snapshots re-recorded, the deferred checker-panic diagnostic now flushes at its first reference as designed
2026-07-24 02:29:48 -05:00

57 lines
2.1 KiB
TypeScript

// Generic function VALUE fences, named: every shape here keeps the generic
// signature alive where static monomorphization cannot follow.
// A reassigned binding cannot resolve statically — a later write could hold
// a structurally identical function with a different body.
let flip = <T>(x: T): T => x;
flip = <T>(x: T): T => x;
console.log(flip(1));
// An ALIAS binding registers its target (calls through it monomorphize),
// but a USE with no pinning context still keeps the generic signature —
// the value fences at the reference, not the binding.
const id = <T>(x: T): T => x;
const stored = id;
console.log(typeof stored);
// An alias of a REASSIGNED binding never registers: the target's own
// fence reports, and the alias falls back to the ordinary binding story.
const flipAlias = flip;
console.log(flipAlias(5));
// Declared inside a function: instances are module functions and cannot
// capture the enclosing frame.
function outer(): number {
const inner = <T>(x: T): T => x;
return inner(3);
}
console.log(outer());
// 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);
// A call whose RESULT is itself a generic function: the returned signature
// keeps its type parameters and would need the producing call's frame.
const curried = <T>(x: T) => <U>(y: U): string => `${x}|${y}`;
console.log(curried(1)("a"));
// Ambient `declare class` generic methods are signature-only. Through an
// AMBIENT-ROOTED receiver the call now compiles to Node's ReferenceError
// at the root (the declare-const chain stance — no diagnostic here); a
// receiver that exists at runtime keeps the fence.
declare class Amb {
m<T>(x: T): T;
}
declare const amb: Amb;
console.log(amb.m(4));
function throughParam(a: Amb): void {
console.log(a.m(4));
}
throughParam(amb);
// A generic lambda outside any monomorphizable home (an IIFE).
console.log((<T>(x: T): T => x)(5));