Skip to content

codegen: fix bitcast of union values with inline_roots#61027

Merged
gbaraldi merged 1 commit intomasterfrom
gb/fix-bitcast-inline-roots
Feb 19, 2026
Merged

codegen: fix bitcast of union values with inline_roots#61027
gbaraldi merged 1 commit intomasterfrom
gb/fix-bitcast-inline-roots

Conversation

@gbaraldi
Copy link
Member

Summary

  • When a union type contains members with mixed GC-pointer and non-pointer fields, codegen uses "split representation" (inline_roots). generic_bitcast called ispointer()/data_pointer() which assert inline_roots.empty(), causing an assertion failure.
  • Handle the inline_roots case by loading directly from v.V before falling through to ispointer(). The dynamic runtime checks (isprimitivetype + size match) already guarantee only primitive types reach the load.
  • Discovered via LinearSolve's KLU extension, which bitcasts a union value containing structs with Vector fields.

Test plan

  • Added test in test/intrinsics.jl that exercises Core.Intrinsics.bitcast on a union containing a struct with GC-pointer fields (split representation).

This PR was written with the assistance of generative AI (Claude).

🤖 Generated with Claude Code

Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

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

Look correct, though not very DRY

@giordano giordano added compiler:codegen Generation of LLVM IR and native code labels Feb 13, 2026
@gbaraldi gbaraldi force-pushed the gb/fix-bitcast-inline-roots branch from a13b356 to 94f38c0 Compare February 18, 2026 20:56
When a union type contains members with mixed GC-pointer and non-pointer
fields (e.g. a struct with both Vector and Int64 fields), codegen uses
"split representation" where non-pointer data lives in v.V and GC roots
are stored separately in v.inline_roots. The generic_bitcast codegen
handler called ispointer()/data_pointer() which assert
inline_roots.empty(), causing an assertion failure.

Handle the inline_roots case before falling through to ispointer() by
loading directly from v.V via maybe_decay_tracked(). This is correct
because the dynamic runtime checks emitted earlier in the function
(isprimitivetype + size match) guarantee that only primitive types with
no GC pointers reach the load.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@gbaraldi gbaraldi force-pushed the gb/fix-bitcast-inline-roots branch from 94f38c0 to 1abe05f Compare February 19, 2026 13:26
@oscardssmith oscardssmith added the bugfix This change fixes an existing bug label Feb 19, 2026
@gbaraldi gbaraldi merged commit 114a015 into master Feb 19, 2026
9 checks passed
@gbaraldi gbaraldi deleted the gb/fix-bitcast-inline-roots branch February 19, 2026 18:25
@oscardssmith
Copy link
Member

does this need backport?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix This change fixes an existing bug compiler:codegen Generation of LLVM IR and native code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments