From 9061420e282d96fb6e9827c35c31b287436d11e3 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Mon, 9 Nov 2026 18:27:23 -0500 Subject: [PATCH 1/3] format: state the sort of pointer variables --- schemas/pointer/collection/list.schema.yaml | 2 +- schemas/pointer/collection/scope.schema.yaml | 20 ++++++++++++++++++++ schemas/pointer/expression.schema.yaml | 16 ++++++++++------ schemas/pointer/template.schema.yaml | 4 ++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/schemas/pointer/collection/list.schema.yaml b/schemas/pointer/collection/list.schema.yaml index dbbcef1380..1e42d289e7 100644 --- a/schemas/pointer/collection/list.schema.yaml +++ b/schemas/pointer/collection/list.schema.yaml @@ -16,7 +16,7 @@ properties: each: description: | An identifier name whose value as an expression resolves to the index - in the list + in the list. Its value is an integer. $ref: "schema:ethdebug/format/pointer/identifier" is: description: | diff --git a/schemas/pointer/collection/scope.schema.yaml b/schemas/pointer/collection/scope.schema.yaml index 8217653c3b..4fc1486d5f 100644 --- a/schemas/pointer/collection/scope.schema.yaml +++ b/schemas/pointer/collection/scope.schema.yaml @@ -10,6 +10,12 @@ description: | expression by identifier. Variables are specified **in order**, so that later appearing variables may reference earlier ones in the same object. + Each variable has the sort of the expression that defines it (see + **ethdebug/format/pointer/expression**). A variable defined by an integer + expression is an integer. A variable defined by a bytes expression is + bytes and keeps its width, so it may be used where bytes are required, + such as an operand of `~concat` or `~keccak256`. + The variables are visible only within `in`. Pointers outside this scope do not see them: later members of an enclosing group, for example, see only the variables of their own enclosing scopes. A variable defined here @@ -45,3 +51,17 @@ examples: location: memory offset: example-offset length: example-length + + - # example: a variable with a bytes value keeps its width, so it can be + # hashed directly. this is the storage slot of `m["abc"]` for a Solidity + # `mapping(string => uint256) m` at slot 0: the key's raw bytes followed + # by the word-sized slot. + define: + key: "0x616263" + in: + name: entry + location: storage + slot: + ~keccak256: + - key + - ~wordsized: 0 diff --git a/schemas/pointer/expression.schema.yaml b/schemas/pointer/expression.schema.yaml index d7b8318dca..35ab08bf9d 100644 --- a/schemas/pointer/expression.schema.yaml +++ b/schemas/pointer/expression.schema.yaml @@ -17,16 +17,18 @@ description: | The two sorts are produced by different forms: - **Integers** are produced by a JSON-number literal, the `~wordsize` - constant, a variable or lookup (`.offset` / `.length` / `.slot`) that - denotes an index or count, an arithmetic operation (`~sum`, - `~difference`, `~product`, `~quotient`, `~remainder`), and a - hexadecimal literal that has an **odd** number of digits (which has no - whole-byte width — see `Literal`). + constant, a lookup (`.offset` / `.length` / `.slot`), an arithmetic + operation (`~sum`, `~difference`, `~product`, `~quotient`, + `~remainder`), and a hexadecimal literal that has an **odd** number of + digits (which has no whole-byte width — see `Literal`). - **Bytes** are produced by a hexadecimal literal with an **even** number of digits (its width is the number of bytes written), `~read` (its width is the length of the region read), the resize forms `~sizedN` / `~wordsized` (whose width is `N` / the word size), `~keccak256` (width 32), and `~concat` (width the sum of its operands'). + - A **variable** has the sort of its value. A list's `each` variable is + an **integer** (the index). A variable from a scope's `define` has the + sort of the expression that defines it, and bytes keep their width. ## Coercion and the width-bearing requirement @@ -87,7 +89,9 @@ $defs: description: | A string that matches an identifier used in an earlier declaration of a scalar variable. This expression evaluates to the value of that - variable. + variable, with that value's sort: an integer for a list's `each` + index, and the sort of the defining expression for a variable from a + scope's `define`. $ref: "schema:ethdebug/format/pointer/identifier" Arithmetic: diff --git a/schemas/pointer/template.schema.yaml b/schemas/pointer/template.schema.yaml index 262adf74a4..d2bb1cad90 100644 --- a/schemas/pointer/template.schema.yaml +++ b/schemas/pointer/template.schema.yaml @@ -13,6 +13,10 @@ properties: description: | An array of variable identifiers used in the definition of the pointer template. + + A template does not give these variables values. Each one takes the + value, and so the sort, of the variable with that identifier in scope + where the template is referenced. type: array items: $ref: "schema:ethdebug/format/pointer/identifier" From 41e2e11ed3178134d7eb7d35269292da6ceee6ef Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Mon, 9 Nov 2026 18:27:36 -0500 Subject: [PATCH 2/3] format: add changelog entry for variable sorts --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0621ab07..fddb0b1972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,24 @@ The sections do not signal obligations; the prefixes do. ### Changed +- The pointer expression schema did not say what sort a variable has. It + listed variables under integers only when they "denote an index or count". + The schemas now say that a list's `each` variable is an integer, that a + variable from a scope's `define` has the sort of the expression that + defines it (so a bytes variable keeps its width), and that a template's + `expect`ed variable takes the sort of the variable in scope where the + template is referenced ([#404]). + - Schemas: **ethdebug/format/pointer/expression**, + **ethdebug/format/pointer/collection/scope**, + **ethdebug/format/pointer/collection/list**, + **ethdebug/format/pointer/template** + - Producers: optional: a variable defined by a bytes expression may be used + where bytes are required, such as an operand of `~concat` or + `~keccak256`, without a resize. + - Consumers: required: a variable has the sort of the expression that + defines it ("Each variable has the sort of the expression that defines + it"). A consumer that evaluated every variable as an integer must keep a + bytes variable's width. - The description of the resize forms (`~sized`, `~wordsized`) said that their left-side padding and truncation match how Solidity resizes both `bytesN` and `uintN`. That holds only for `uintN`. Solidity pads and @@ -732,3 +750,4 @@ First published version of the specification. [#323]: https://github.com/ethdebug/format/pull/323 [#325]: https://github.com/ethdebug/format/pull/325 [#402]: https://github.com/ethdebug/format/pull/402 +[#404]: https://github.com/ethdebug/format/pull/404 From b014dcef7391f938ca5706b8a4e7be31683d1b59 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Mon, 9 Nov 2026 18:32:12 -0500 Subject: [PATCH 3/3] format: tighten the variable sorts changelog entry --- CHANGELOG.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fddb0b1972..2f809e6b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,24 +55,21 @@ The sections do not signal obligations; the prefixes do. ### Changed -- The pointer expression schema did not say what sort a variable has. It - listed variables under integers only when they "denote an index or count". - The schemas now say that a list's `each` variable is an integer, that a - variable from a scope's `define` has the sort of the expression that - defines it (so a bytes variable keeps its width), and that a template's - `expect`ed variable takes the sort of the variable in scope where the - template is referenced ([#404]). +- Variables now have a stated sort. A list's `each` variable is an integer. + A `define` variable has the sort of its expression, so a bytes variable + keeps its width. A template's `expect`ed variable takes the sort of the + variable in scope at the reference ([#404]). - Schemas: **ethdebug/format/pointer/expression**, **ethdebug/format/pointer/collection/scope**, **ethdebug/format/pointer/collection/list**, **ethdebug/format/pointer/template** - - Producers: optional: a variable defined by a bytes expression may be used - where bytes are required, such as an operand of `~concat` or - `~keccak256`, without a resize. - - Consumers: required: a variable has the sort of the expression that - defines it ("Each variable has the sort of the expression that defines - it"). A consumer that evaluated every variable as an integer must keep a - bytes variable's width. + - Producers: optional: a bytes variable may be an operand of `~concat` or + `~keccak256` without a resize. + - Consumers: required: a variable has the sort of its defining expression. + The previous text did not say so. A consumer that read every variable as + an integer must keep a bytes variable's width. One that already kept the + sort (as `@ethdebug/pointers` does) needs no change. + - The description of the resize forms (`~sized`, `~wordsized`) said that their left-side padding and truncation match how Solidity resizes both `bytesN` and `uintN`. That holds only for `uintN`. Solidity pads and