Skip to content

Attempt to implement support for self-referential union types - #3419

Open
erickt wants to merge 1 commit into
rust-lang:mainfrom
erickt:union2
Open

Attempt to implement support for self-referential union types#3419
erickt wants to merge 1 commit into
rust-lang:mainfrom
erickt:union2

Conversation

@erickt

@erickt erickt commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

In llvm/llvm-project#185449, LLVM's libc++ changed a header to use a recursive self-referential type, which looks approximately like:

template <class A0, class... As> union RUnion { A0 arg; RUnion<As...> u; };
template <class A> union RUnion<A> { A arg; };
struct Wrap { RUnion<int, float> u; };

This code caused bindgen to panic. The problem seems to be that bindgen can't handle the recursive self-referential union. Digging into the code, it seems that when this type is being parsed, with_loaned_item removes the type from the context. Later on, when parsing the CompKind::Union, CompInfo::layout would call resolve_type on an item that was loaned out, so it panics. This bug was filed in #3397.

I've attempted to fix this bug with the help of Gemini agent, and it seems that we might be able to swap out resolve_type with safe_resolve_type in a few locations to get it to stop erroring out. This seems to make sense as best as I understand this situation, although there may be a chance that returning None in these callsites might be incorrect. But I couldn't find a counter example that shows incorrect code. As a safeguard, this also tracks which items we have loaned out, and makes sure that safe_resolve_type will panic if these types weren't loaned out.

Fixes #3397

@erickt

erickt commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

r? @emilio

@erickt
erickt force-pushed the union2 branch 8 times, most recently from cff08ee to d21afc5 Compare August 4, 2026 22:22
In llvm/llvm-project#185449, LLVM's libc++ changed a header to use a
recursive self-referential type, which looks approximately like:

```
template <class A0, class... As> union RUnion { A0 arg; RUnion<As...> u; };
template <class A> union RUnion<A> { A arg; };
struct Wrap { RUnion<int, float> u; };
```

This code caused bindgen to panic. The problem seems to be that bindgen
can't handle the recursive self-referential union. Digging into the
code, it seems that when this type is being parsed, `with_loaned_item`
removes the type from the context. Later on, when parsing the
CompKind::Union, CompInfo::layout would call resolve_type on an item
that was loaned out, so it panics. This bug was filed in rust-lang#3397.

This patch avoids that by first keeping track of which items we have
loaed out with `with_loaned_item`, then changing `safe_resolve_type` to
panic if we don't have an entry for that type, or it has been loaned
out. Then we've updated the call sites to use it.

This also adds LLVM-21 tests, since clang generates different code than
LLVM-20.

Test: cargo test -p bindgen-tests

TAG=agy
CONV=619bdf72-4d2d-494f-8cb0-f61a6db9674c

@emilio emilio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you split the "test LLVM 21" into its own PR? Or is it hitting some of these issues?

View changes since this review

Comment thread bindgen/ir/comp.rs
if !ty.can_derive_copy(ctx) {
return false;
}
if ctx.in_codegen_phase() &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It feels like a huge smell to change the answer here depending on which phase are we on...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah I agree, but I'm still new in the code so I wasn't sure if there's a better way to do this. Do you recommend an approach? I could see how invasive it'd be for us to split this code into a parsing context, and a type checking context, but that might be a bit invasive. I'll dig around in the meantime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So for the background, as best as I can tell that we're doing two passes through the types. First, when we're parsing the types, we need to accommodate forward declared types, so when we see them, we end up pushing in a None into BindgenContext::items, which gets populated later on during the parse. Second, during code generation, we walk through the items, and temporarily pop them from the items list when we recurse into types.

In the first version of this patch, I just changed a bunch of the callsites from using resolve_type to safe_resolve_type, where we just exit the function if the type is loaned out. But I'm a little nervous that this could lead towards us hiding bugs where we actually are referencing undefined types, so I added in tracking which types we're loaning out so at least we won't hide that issue.

Comment thread bindgen/ir/comp.rs
let has_generic_params = self.fields().iter().any(|f| match *f {
Field::DataMember(ref field_data) => {
field_data.ty().can_derive_copy(ctx)
ctx.in_codegen_phase() &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here, I don't understand why this is needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll reply in the other question, but I'll keep this thread open in case there's other issues we want to discuss.

@erickt

erickt commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Could you split the "test LLVM 21" into its own PR? Or is it hitting some of these issues?

View changes since this review

Sure, I'll add it in a separate PR. Also, LLVM 22 also recently came out, but it looks like the helper action doesn't support it yet KyleMayes/install-llvm-action#103.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bindgen panicked with message "Not an item: ItemId(...)" when parsing recursive union templates (breaks recent LLVM libc++ std::aligned_union)

3 participants