Skip to content

Lower the structural_eq intrinsic wrapper as a standalone procedure#10180

Merged
Anton-4 merged 4 commits into
roc-lang:mainfrom
niclas-ahden:fix-issue-10156
Jul 18, 2026
Merged

Lower the structural_eq intrinsic wrapper as a standalone procedure#10180
Anton-4 merged 4 commits into
roc-lang:mainfrom
niclas-ahden:fix-issue-10156

Conversation

@niclas-ahden

Copy link
Copy Markdown
Contributor

I needed a fix for #10156 to unblock myself so I gave it a shot. This is my first Zig contribution and it took me a long time to try and figure stuff out, so hopefully I actually did understand and came to a reasonable solution 😅 I used existing code as a guideline as much as I could, and added fitting tests. I didn't want to use Claude for this since it's my first contribution, but I did ask it for a review and it was happy, so fingers crossed. Let me know if I messed up!

See the issue in #10156, and the solution as follows:

A generic constrained by a.is_eq requires its caller to supply the element's is_eq as evidence. When the concrete element type derives equality structurally, that evidence is the structural-equality intrinsic wrapper, which must then lower as a standalone procedure.

lowerTemplateBody assumed the wrapper was only reachable from direct == sites and hit an invariant: a panic in ReleaseSafe and undefined behavior (false negatives, segfaults) in ReleaseFast.

Instead, synthesize a |lhs, rhs| lhs == rhs body, mirroring how str_inspect is lowered.

Fixes roc-lang#10156.

A generic constrained by `a.is_eq` requires its caller to supply the
element's `is_eq` as evidence. When the concrete element type derives
equality structurally, that evidence is the structural-equality
intrinsic wrapper, which must then lower as a standalone procedure.

lowerTemplateBody assumed the wrapper was only reachable from direct
`==` sites and hit an invariant: a panic in Debug/ReleaseSafe and
undefined behavior (false negatives, segfaults) in ReleaseFast.

Instead, synthesize a `|lhs, rhs| lhs == rhs` body, mirroring how
str_inspect is lowered.
@Anton-4 Anton-4 self-assigned this Jul 17, 2026
niclas-ahden and others added 2 commits July 17, 2026 20:05
The wrapper body synthesized in lowerStructuralEqIntrinsic went through
lowerEqualityExpr, whose exact-method dispatch resolves is_eq for the
wrapper's own type straight back to the wrapper, emitting a body that
tail-calls itself: Str.from_utf8([255]) == Str.from_utf8([255]) compiled
but looped forever at runtime.

Split lowerDerivation's structural expansion into
lowerDerivationExpansion and lower the wrapper body through it, so only
the top layer skips method dispatch; components still derive through
lowerDerivation and dispatch to their exact methods as before.

Extend the issue 10156 fixture with expects that actually execute the
wrapper (equal and unequal Err(BadUtf8(...)) values) and correct the
fixture comments: the trigger is any comparison over a type containing
Str.Utf8Problem (the only structural_eq intrinsic), not the generic
evidence path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@Anton-4 Anton-4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @niclas-ahden,
The Zig-fu was good :)
I did happen to discover a runtime infinite loop with this code:

Probe := [].{ run = |bytes| Str.from_utf8(bytes) }
expect Probe.run([255]) == Probe.run([255])

So I further altered the solution to fix this and added more tests.

Let me know if you would like me to explain things further.

@Anton-4
Anton-4 enabled auto-merge July 17, 2026 18:21
@niclas-ahden

Copy link
Copy Markdown
Contributor Author

@Anton-4 Thanks for the review, and great that you found that issue! I'm just happy to get this merged and I can continue on my merry way of porting code :)

@niclas-ahden

Copy link
Copy Markdown
Contributor Author

Seems we need someone else's approval to merge since Anton was the last to push.

@Anton-4
Anton-4 merged commit 7cec8f7 into roc-lang:main Jul 18, 2026
7 checks passed
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.

3 participants