Skip to content

bugc: compare bytes and strings by contents - #391

Merged
gnidan merged 4 commits into
mainfrom
bugc-bytes-eq
Oct 15, 2026
Merged

gnidan merged 4 commits into
mainfrom
bugc-bytes-eq

Conversation

@gnidan

@gnidan gnidan commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

== and != on dynamic bytes or string compiled to a comparison of the two memory addresses, so equal contents in two places compared unequal. In examples/advanced/token-registry.bug, every selector check compares bytes copied from calldata against bytes sliced from a hash, so no selector ever matched.

The type checker already accepts == between two bytes or two string values in memory (and rejects them in calldata), and the token registry example is written to compare contents, as is a mapping<bytes, …> key. So this makes the operator do what the language already says it does, instead of rejecting it: irgen now emits a hash of each operand's data and compares the two hashes. The hash instruction already hashes the data of a single memory reference, so evmgen needs no change. Different lengths give different hashes, so there is no separate length check. bytesN are values and still compare as words.

Comparing against literals exposed a second bug, fixed here too. Code generation and constant folding each turned a bytes or string constant into bytes from its text alone: folding hashed a hex literal longer than 32 bytes as ASCII text (so b == 0x<33 bytes> and keccak256(0x<33 bytes>) were wrong at O1–O3), and code generation decoded a string literal that starts with "0x", such as "0x12", as hex (wrong at O0). Both now use one function, Ir.Utils.constantBytes, which encodes by the constant's type: a string as UTF-8, bytes from their hex.

Tests run == and != at O0–O3 on bytes and string copies from calldata (equal and unequal contents, different lengths, empty, and lengths around 32 bytes), against a string literal, and against a string in storage. Others compare and hash a 33-byte hex literal and the string "0x12" (also cast to bytes), with the same results at every level. The type checker tests cover the accepted forms and the errors for bytes == string and bytes == bytes4. The case study gains a short "Equality" section, and says to cast or copy bytes in calldata to memory before comparing them.

The only program whose code changes is token-registry.bug, at every level (the activation-pairing digest snapshot is updated for it). With this change and #388, its selectors match at O0–O3, so the example can lose its @wip marker in a follow-up.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-15 02:32 UTC

gnidan added a commit that referenced this pull request Oct 15, 2026
`==` and `!=` on dynamic bytes or strings compared the two memory
addresses, so equal contents in two places compared unequal. Compare
the keccak256 of each operand's data instead.
Code generation and constant folding each encoded a string constant
from its text: folding hashed a long hex literal as ASCII, and code
generation decoded a string starting with "0x" as hex. Both now use
one function that encodes by the constant's type.
@gnidan
gnidan merged commit b6c5cdc into main Oct 15, 2026
8 checks passed
@gnidan
gnidan deleted the bugc-bytes-eq branch October 15, 2026 02:28
gnidan added a commit that referenced this pull request Oct 15, 2026
Now that #388 and #391 are in, the example compiles and runs the same at O0-O3, so drop its @wip marker. Also note in constantBytes and foldCast that a string constant's bytes follow its type.
gnidan added a commit that referenced this pull request Oct 15, 2026
Now that #388 and #391 are in, the example compiles and runs the same at O0-O3, so drop its @wip marker. Also note in constantBytes and foldCast that a string constant's bytes follow its type.
gnidan added a commit that referenced this pull request Oct 15, 2026
Now that #388 and #391 are in, the example compiles and runs the same at O0-O3, so drop its @wip marker. Also note in constantBytes and foldCast that a string constant's bytes follow its type.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant