rust-lang / rust-lang/rust-bindgen

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

Open
#3,397 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

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 https://github.com/llvm/llvm-project/pull/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 93726885850c20847ac445664bc7bb2b827d9642 .

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the supplied test_union.h reproducer with bindgen, then inspect bindgen/ir/context.rs around with_loaned_item and the related passes in bindgen/ir/comp.rs. Trace how the recursive RUnion reference is resolved while its ItemId is loaned; done means the command no longer panics and produces bindings or treats the recursive union as opaque.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.