Skip to content

Commit 4c71e00

Browse files
committed
pointers: scope define variables to their in
1 parent 67c4538 commit 4c71e00

5 files changed

Lines changed: 104 additions & 1 deletion

File tree

‎packages/pointers/CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ itself are tracked in the root [`CHANGELOG.md`](../../CHANGELOG.md).
66

77
## Unreleased
88

9+
### Fixed
10+
11+
- `dereference` scopes the variables of a `define` to its `in`. They used to
12+
stay defined after the `in` finished, so a `define` inside one member of a
13+
`group` changed the variables seen by the later members. This is how solc's
14+
template for a struct with a string member read the members after it one slot
15+
too far ([#PR]).
16+
917
## 0.1.0-preview.1 — 2026-10-03
1018

1119
### Fixed

‎packages/pointers/src/dereference/generate.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ export async function* generateRegions(
8989
Object.assign(variables, memo.variables);
9090
break;
9191
}
92+
case "restore-variables": {
93+
for (const name of Object.keys(variables)) {
94+
delete variables[name];
95+
}
96+
Object.assign(variables, memo.variables);
97+
break;
98+
}
9299
case "push-region-renames": {
93100
renameStack.push(memo.mapping);
94101
break;

‎packages/pointers/src/dereference/index.test.ts‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,67 @@ describe("dereference", () => {
306306
expect(regions[0].length).toEqual(Data.fromNumber(32));
307307
});
308308

309+
it("scopes defined variables to their `in`", async () => {
310+
const pointer: Pointer = {
311+
define: { slot: "0x00" },
312+
in: {
313+
group: [
314+
{
315+
define: { slot: { $sum: ["slot", 1] } },
316+
in: { location: "storage", slot: "slot", name: "a" },
317+
},
318+
{
319+
location: "storage",
320+
slot: { $sum: ["slot", 2] },
321+
name: "b",
322+
},
323+
],
324+
},
325+
};
326+
327+
const cursor = await dereference(pointer);
328+
329+
const { regions } = await cursor.view(state);
330+
331+
expect(regions.map(({ name }) => name)).toEqual(["a", "b"]);
332+
expect(regions[0]).toMatchObject({ slot: Data.fromNumber(1) });
333+
expect(regions[1]).toMatchObject({ slot: Data.fromNumber(2) });
334+
});
335+
336+
it("scopes variables defined inside referenced templates", async () => {
337+
const pointer: Pointer = {
338+
define: { slot: "0x00" },
339+
in: {
340+
templates: {
341+
shifted: {
342+
expect: ["slot"],
343+
for: {
344+
define: { slot: { $sum: ["slot", 1] } },
345+
in: { location: "storage", slot: "slot", name: "a" },
346+
},
347+
},
348+
},
349+
in: {
350+
group: [
351+
{ template: "shifted" },
352+
{
353+
location: "storage",
354+
slot: { $sum: ["slot", 2] },
355+
name: "b",
356+
},
357+
],
358+
},
359+
},
360+
};
361+
362+
const cursor = await dereference(pointer);
363+
364+
const { regions } = await cursor.view(state);
365+
366+
expect(regions[0]).toMatchObject({ slot: Data.fromNumber(1) });
367+
expect(regions[1]).toMatchObject({ slot: Data.fromNumber(2) });
368+
});
369+
309370
it("works for templates", async () => {
310371
const templates: Pointer.Templates = {
311372
"memory-range": {

‎packages/pointers/src/dereference/memo.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type Memo =
99
| Memo.DereferencePointer
1010
| Memo.SaveRegions
1111
| Memo.SaveVariables
12+
| Memo.RestoreVariables
1213
| Memo.PushRegionRenames
1314
| Memo.PopRegionRenames
1415
| Memo.PushTemplates
@@ -75,6 +76,25 @@ export namespace Memo {
7576
variables,
7677
});
7778

79+
/**
80+
* A request to replace the stateful map of variable values with a
81+
* snapshot taken earlier, discarding every variable saved since.
82+
*/
83+
export interface RestoreVariables {
84+
kind: "restore-variables";
85+
variables: Record<string, Value>;
86+
}
87+
88+
/**
89+
* Initialize a RestoreVariables memo
90+
*/
91+
export const restoreVariables = (
92+
variables: Record<string, Value>,
93+
): RestoreVariables => ({
94+
kind: "restore-variables",
95+
variables,
96+
});
97+
7898
/**
7999
* A request to push a region rename mapping onto the context stack.
80100
* While active, regions with names in the mapping will be saved under

‎packages/pointers/src/dereference/process.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,14 @@ async function* processScope(
153153
newVariables[identifier] = value;
154154
}
155155

156-
return [Memo.saveVariables(newVariables), Memo.dereferencePointer(in_)];
156+
// variables defined here are visible only within `in`
157+
return [
158+
Memo.saveVariables(newVariables),
159+
Memo.dereferencePointer(in_),
160+
Memo.restoreVariables(
161+
Object.assign(Object.create(null), options.variables),
162+
),
163+
];
157164
}
158165

159166
async function* processReference(

0 commit comments

Comments
 (0)