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).

Activity

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

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