mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-05 08:13:06 +08:00
Merge remote-tracking branch 'origin/lf/l4-expressions' into lf/n2-fallback
This commit is contained in:
@@ -223,8 +223,10 @@ function slotCause(lowerer: Lowerer, n: ts.Node): UntypedCause | null {
|
||||
? lowerer.checker.getTypeOfSymbol(symbol)
|
||||
: lowerer.checker.getTypeAtLocation(n);
|
||||
const id = (type as { id?: number }).id ?? -1;
|
||||
// A whole degraded type, or a dynamic piece of a pushed-down one.
|
||||
const owner = lowerer.lenientPieces.get(id);
|
||||
// A whole degraded type, or a dynamic piece of a pushed-down one. A type
|
||||
// that is also a slot of its own belongs to that slot, not to a container
|
||||
// it is a piece of.
|
||||
const owner = lowerer.degradedTypes.has(id) ? undefined : lowerer.lenientPieces.get(id);
|
||||
if (!lowerer.degradedTypes.has(id) && owner === undefined) return null;
|
||||
const text = ts.getTextOfNode(n);
|
||||
const declaredType = lowerer.checker.typeToString(type);
|
||||
|
||||
@@ -758,6 +758,11 @@ export function lowerStmts(lowerer: Lowerer, stmts: readonly ts.Statement[]): Ir
|
||||
// out of tdzPredeclared; a retry after a later declarator degrades
|
||||
// must initialize the same box again.
|
||||
const tdzBefore = new Map(lowerer.ctx.tdzPredeclared);
|
||||
// The first attempt's diagnostics: a statement that still fails
|
||||
// after degrading reports them, since a retry does not repeat what
|
||||
// lowered once (a poisoned generic instance) and its own diagnostics
|
||||
// can miss the failure's cause.
|
||||
let firstAttempt: ScrDiagnostic[] | null = null;
|
||||
try {
|
||||
for (let attempt = 0; ; attempt++) {
|
||||
const hits = new Set<number>();
|
||||
@@ -772,7 +777,12 @@ export function lowerStmts(lowerer: Lowerer, stmts: readonly ts.Statement[]): Ir
|
||||
// global degrades that type and the statement lowers again.
|
||||
// A declaration this statement flushed inside a probe (where
|
||||
// no retry runs) can retry here too.
|
||||
if (attempt >= MAX_DEGRADE_RETRIES || deferFences) throw e;
|
||||
firstAttempt ??= lowerer.diags.slice(diagsBefore);
|
||||
const giveUp = (): unknown => {
|
||||
if (attempt > 0) lowerer.restoreFirstAttemptDiags(diagsBefore, firstAttempt!);
|
||||
return e;
|
||||
};
|
||||
if (attempt >= MAX_DEGRADE_RETRIES || deferFences) throw giveUp();
|
||||
const slot = lowerer.degradeAtCommit(e, STATEMENT_SLOTS);
|
||||
// Pushed-down types the failed attempt mapped widen to the
|
||||
// whole slot before anything else is given up.
|
||||
@@ -781,7 +791,7 @@ export function lowerStmts(lowerer: Lowerer, stmts: readonly ts.Statement[]): Ir
|
||||
!lowerer.reopenFlushedAtCommit(diagsBefore) &&
|
||||
!lowerer.widenPartials(hits)
|
||||
)
|
||||
throw e;
|
||||
throw giveUp();
|
||||
// A failed attempt adds nothing to `out` but the TDZ boxes
|
||||
// forward captures declared (they stay declared), so the
|
||||
// retry appends after them.
|
||||
|
||||
@@ -9921,6 +9921,19 @@ export class Lowerer {
|
||||
this.diags.push(...kept);
|
||||
}
|
||||
|
||||
/** A unit that still fails after its retries commits its first
|
||||
* attempt's own diagnostics (`first`, taken before the first
|
||||
* degradation) in place of the last attempt's. The diagnostics the
|
||||
* retries carry (dropAttemptDiags) stay as they are now: a declaration
|
||||
* reopened since may have registered. */
|
||||
restoreFirstAttemptDiags(diagsBefore: number, first: readonly ScrDiagnostic[]): void {
|
||||
const carried = (d: ScrDiagnostic): boolean =>
|
||||
d.rejects !== undefined || this.loweredOnceDiags.has(d);
|
||||
const kept = this.diags.slice(diagsBefore).filter(carried);
|
||||
this.diags.length = diagsBefore;
|
||||
this.diags.push(...first.filter((d) => !carried(d)), ...kept);
|
||||
}
|
||||
|
||||
/** Lowers one unit whose body-level poison skips it (a generic
|
||||
* instance), with localized failure's commit point: a degradable type
|
||||
* failure at one of its declaration slots degrades the type and the unit
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// A type that degrades as a piece of a container (`Plugin.hook`) and as a
|
||||
// slot of its own (`current`): the operations on `current` belong to
|
||||
// `current`, not to the container.
|
||||
type Hook = (() => number) | AbortSignal | Map<string, (x: number) => string>;
|
||||
|
||||
interface Plugin {
|
||||
name: string;
|
||||
hook: Hook;
|
||||
}
|
||||
|
||||
const plugins: Plugin[] = [{ name: "one", hook: () => 1 }];
|
||||
|
||||
let current: Hook = () => 2;
|
||||
|
||||
function run(rounds: number): number {
|
||||
let total = 0;
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
if (typeof current === "function") total += current();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
console.log(run(10), plugins.length);
|
||||
@@ -496,6 +496,15 @@ test("dynamic slots rank by their dynamic operations and name themselves in SC61
|
||||
expect(hot?.message).toContain("`hook` is a dynamic piece of `Plugin[]` (declared `Hook`");
|
||||
});
|
||||
|
||||
test("a type that is a dynamic piece and a slot of its own counts toward its own slot", () => {
|
||||
const { coverage } = analyze(fixture("dynamic-slot-shared.ts"));
|
||||
const slots = coverage.dynamicSlots ?? [];
|
||||
expect(slots.find((s) => s.name === "current")?.operations).toBeGreaterThan(0);
|
||||
expect(slots.find((s) => s.name === "plugins")?.operations ?? 0).toBe(0);
|
||||
const hot = coverage.performance?.find((d) => d.code === "SC6101");
|
||||
expect(hot?.message).toContain("`current` is a dynamic slot (declared `Hook`");
|
||||
});
|
||||
|
||||
test("CLI: --tier and --explain-dynamic", () => {
|
||||
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
|
||||
const outDir = mkdtempSync(join(tmpdir(), "scriptc-tier-"));
|
||||
|
||||
Reference in New Issue
Block a user