mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-06 08:23:19 +08:00
Refuse values of generic bindings declared inside functions
Each call of a function makes a new function object for a generic arrow or function expression bound inside it, so `make() !== make()` when make returns the binding, and such values are distinct Map and Set keys. The compiled instances are shared module functions, which cannot give that identity, so a value use of such a binding keeps an SC1090 fence that names the reason. Calls of the binding still compile.
This commit is contained in:
@@ -87,6 +87,10 @@ export interface GenericFnInfo {
|
||||
* defining literal must sit at module scope (no enclosing frame to
|
||||
* capture). */
|
||||
objectLiteral?: true;
|
||||
/** A binding declared inside a function: each evaluation of the
|
||||
* declaration makes a new function object in JavaScript, while the
|
||||
* compiled instances are shared module functions, so only calls lower. */
|
||||
functionScoped?: true;
|
||||
/** IMPLICIT-ANY monomorphization (npm-static JS): parallel to
|
||||
* decl.parameters — the param's own symbol when the slot is a BINDABLE
|
||||
* implicit-any parameter (untyped, identifier-named, never written in
|
||||
@@ -1184,6 +1188,16 @@ export function lowerGenericFnValue(
|
||||
lowerer.noteEdge(inst.name);
|
||||
return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
|
||||
}
|
||||
// Each call of the enclosing function makes a new function object for a
|
||||
// binding declared inside it (`make() !== make()` when make returns
|
||||
// it), which one shared compiled instance cannot give.
|
||||
if (info.functionScoped) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
ref,
|
||||
`generic arrow/function-expression bindings declared inside functions as values (each call of the enclosing function makes a new '${info.baseName}' function object, while its compiled instances are shared module functions — call '${info.baseName}' directly, or declare it at module scope)`,
|
||||
);
|
||||
}
|
||||
const fenceUnpinned: () => never = () =>
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
@@ -2489,6 +2503,7 @@ export function bindingGenericFnInfoOf(
|
||||
if (fnNode.asteriskToken) lowerer.unsupported("SC1071", fnNode);
|
||||
// Inside a function, the compiled instances are module functions, so
|
||||
// the initializer may not use the enclosing frame.
|
||||
let functionScoped = false;
|
||||
for (
|
||||
let n: ts.Node | undefined = decl.parent;
|
||||
n !== undefined && !ts.isSourceFile(n);
|
||||
@@ -2502,6 +2517,7 @@ export function bindingGenericFnInfoOf(
|
||||
`generic arrow/function-expression bindings that use the frames of their enclosing functions (the compiled instantiations of '${name}' are module functions and cannot capture its locals, parameters, type parameters or 'this' — pass them as arguments, or declare the binding at module scope)`,
|
||||
);
|
||||
}
|
||||
functionScoped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2548,6 +2564,7 @@ export function bindingGenericFnInfoOf(
|
||||
),
|
||||
typeParams,
|
||||
instances: new Map(),
|
||||
...(functionScoped ? { functionScoped: true as const } : {}),
|
||||
};
|
||||
lowerer.bindingGenericFns.set(fnNode, info);
|
||||
lowerer.genericFnsBySymbol.set(sym, info);
|
||||
|
||||
@@ -27,6 +27,15 @@ function outer(base: number): number {
|
||||
}
|
||||
console.log(outer(4));
|
||||
|
||||
// Used as a value inside a function: each call of the enclosing function
|
||||
// makes a new function object (`make() !== make()`), and the compiled
|
||||
// instances are shared module functions.
|
||||
function make(): (x: number) => number {
|
||||
const same = <T>(x: T): T => x;
|
||||
return same<number>;
|
||||
}
|
||||
console.log(make() === make());
|
||||
|
||||
// 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).
|
||||
|
||||
@@ -73,16 +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: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
|
||||
generic-value-bindings.ts:35:10 - error SC1090: generic arrow/function-expression bindings declared inside functions as values (each call of the enclosing function makes a new 'same' function object, while its compiled instances are shared module functions — call 'same' directly, or declare it at module scope) are not supported yet
|
||||
|
||||
38 | const curried = <T>(x: T) => <U>(y: U): string => `${x}|${y}`;
|
||||
39 | console.log(curried(1)("a"));
|
||||
34 | const same = <T>(x: T): T => x;
|
||||
35 | return same<number>;
|
||||
| ^~~~~~~~~~~~
|
||||
36 | }
|
||||
|
||||
generic-value-bindings.ts:48: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
|
||||
|
||||
47 | const curried = <T>(x: T) => <U>(y: U): string => `${x}|${y}`;
|
||||
48 | console.log(curried(1)("a"));
|
||||
| ^~~~~~~~~~
|
||||
40 |
|
||||
49 |
|
||||
|
||||
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
|
||||
generic-value-bindings.ts:60: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
|
||||
|
||||
50 | function throughParam(a: Amb): void {
|
||||
51 | console.log(a.m(4));
|
||||
59 | function throughParam(a: Amb): void {
|
||||
60 | console.log(a.m(4));
|
||||
| ^~~~~~
|
||||
52 | }
|
||||
61 | }
|
||||
Reference in New Issue
Block a user