We noticed that bindgen is panicing on our code base after we rolled our clang toolchain with libc++. We bisect the LLVM and found out the panic starts to appear after llvm/llvm-project#185449 was landed. This PR revised the aligned_union with a recursive union template.
We created a small reproducer:
// test_union.h
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; };
- Run
bindgen test_union.h -- -x c++ -std=c++20
Expected Behavior
bindgen should successfully generate FFI bindings (or gracefully treat the recursive template union as opaque/unsupported) without crashing or panicking.
Actual Behavior
bindgen immediately crashes with the following panic:
panicked at bindgen/ir/context.rs:1495:21:
Not an item: ItemId(1)
The bindgen we use is 9372688 .
Root Cause Analysis
The panic occurs due to an interaction between self-referential / cyclic type references in the AST and bindgen's temporary item borrowing mechanism (with_loaned_item):
- Self-Referential Type Reference: When bindgen parses union
RUnion with member RUnion<As...> u, field u is represented as an UnresolvedTypeRef whose target type ID resolves back to union RUnion's own ItemId.
- IR Traversal Loaning (
with_loaned_item): During post-parsing IR analysis and modification passes (such as compute_bitfield_units or deanonymize_fields in bindgen/ir/context.rs / bindgen/ir/comp.rs),
bindgen mutably borrows items from BindgenContext using with_loaned_item:
fn with_loaned_item<F, T>(&mut self, id: ItemId, f: F) -> T {
let mut item = self.items[id.0].take().unwrap(); // Temporarily removes Item from self.items
// Executes closure on &mut item...
}
- The Panic: When
with_loaned_item runs on union RUnion (ItemId(1)), ItemId(1) is temporarily removed from self.items (self.items[1] = None). As the pass inspects the members of union RUnion, it traverses
field u and attempts to resolve its target type (union RUnion's ItemId(1)) by calling ctx.resolve_item(ItemId(1)). Because ItemId(1) is currently loaned out, self.items[1] is None, causing resolve_item to panic with Not an item: ItemId(1).
We noticed that bindgen is panicing on our code base after we rolled our clang toolchain with libc++. We bisect the LLVM and found out the panic starts to appear after llvm/llvm-project#185449 was landed. This PR revised the
aligned_unionwith a recursive union template.We created a small reproducer:
bindgen test_union.h -- -x c++ -std=c++20Expected Behavior
bindgen should successfully generate FFI bindings (or gracefully treat the recursive template union as opaque/unsupported) without crashing or panicking.
Actual Behavior
bindgen immediately crashes with the following panic:
The bindgen we use is 9372688 .
Root Cause Analysis
The panic occurs due to an interaction between self-referential / cyclic type references in the AST and bindgen's temporary item borrowing mechanism (with_loaned_item):
RUnionwith memberRUnion<As...> u, fielduis represented as anUnresolvedTypeRefwhose target typeIDresolves back to unionRUnion's ownItemId.with_loaned_item): During post-parsing IR analysis and modification passes (such ascompute_bitfield_unitsordeanonymize_fieldsinbindgen/ir/context.rs/bindgen/ir/comp.rs),bindgen mutably borrows items from
BindgenContextusingwith_loaned_item:with_loaned_itemruns on unionRUnion (ItemId(1)),ItemId(1)is temporarily removed fromself.items(self.items[1] = None). As the pass inspects the members of unionRUnion, it traversesfield
uand attempts to resolve its target type (union RUnion's ItemId(1)) by callingctx.resolve_item(ItemId(1)). BecauseItemId(1)is currently loaned out,self.items[1]is None, causingresolve_itemto panic withNot an item: ItemId(1).