Skip to content

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

Description

@zeroomega

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:

  • Create a header file:
// 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):

  1. 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.
  2. 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...
    }
  1. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions