Use a kept layout only for the binding the current lowering resolves

A const that kept its initializer's layout records the initializer's
checker type by declaration symbol. A function body lowers once per
instance, and only an instance for a wider argument keeps the layout, so
the record from that instance made the function's own lowering bind the
wider layout for `grow(alias)`: the argument converted into a copy and the
callee's writes did not reach the caller. The recorded type now applies
only while it maps to the layout of the binding the current lowering
resolves.
This commit is contained in:
Chris Tate
2026-10-26 07:18:25 -05:00
parent 28b61086c3
commit e9977107fe
2 changed files with 22 additions and 2 deletions
@@ -249,13 +249,20 @@ export function layoutReturnType(
/** The initializer type of a const the argument names when the const kept
* that initializer's layout (keepsInitializerLayout): the checker types the
* binding by its annotation, but its value has the initializer's layout. */
* binding by its annotation, but its value has the initializer's layout. A
* declaration lowers once per function instance, and only some instances
* keep a layout, so the recorded type applies only while it maps to the
* layout of the binding the current lowering resolves. */
function preservedLayoutOf(lowerer: Lowerer, arg: ts.Expression): ts.Type | undefined {
let e = arg;
while (ts.isParenthesizedExpression(e) || ts.isNonNullExpression(e)) e = e.expression;
if (!ts.isIdentifier(e)) return undefined;
const symbol = lowerer.resolveValueSymbol(e);
return symbol ? lowerer.preservedLayoutTypes.get(symbol) : undefined;
const recorded = symbol ? lowerer.preservedLayoutTypes.get(symbol) : undefined;
if (recorded === undefined) return undefined;
const binding = lowerer.peekLocal(e)?.type ?? lowerer.globalOf(e)?.type;
const layout = lowerer.mapTypeOf(recorded);
return binding && layout && typeEquals(binding, layout) ? recorded : undefined;
}
/** Whether a `const` binding typed `declared` keeps the record or array
+13
View File
@@ -53,3 +53,16 @@ const moduleSource: Entry[] = [{ key: "m", hits: 1 }];
const moduleView: Counted[] = moduleSource;
track(moduleView, "n");
console.log(moduleView.length, moduleSource.length, moduleView === moduleSource);
// The same function body lowered for a wider argument and for its own type:
// each lowering passes its own binding on.
function addTwice(list: Counted[], key: string): number {
const alias: Counted[] = list;
track(alias, key);
track(alias, `${key}2`);
return list.length;
}
const wideList: Entry[] = [{ key: "w", hits: 1, note: "n" }];
const plainList: Counted[] = [{ key: "p", hits: 1 }];
console.log(addTwice(wideList, "a"), addTwice(plainList, "b"), addTwice(wideList, "c"));
console.log(wideList.length, plainList.length);