Skip to content

Handle C++23 explicit object member functions - #3457

Open
fly1d wants to merge 1 commit into
rust-lang:mainfrom
fly1d:codex/fix-explicit-object-parameter
Open

Handle C++23 explicit object member functions#3457
fly1d wants to merge 1 commit into
rust-lang:mainfrom
fly1d:codex/fix-explicit-object-parameter

Conversation

@fly1d

@fly1d fly1d commented Sep 7, 2026

Copy link
Copy Markdown

Fixes #3441.

What changed

libclang represents a C++23 explicit object parameter as the first ParmDecl, but exposes no dedicated C API for identifying it. Bindgen previously treated the enclosing CXXMethod as an ordinary member and inserted an additional implicit this pointer.

This change detects the leading this keyword on the first parameter and treats the method as receiver-free during IR construction. The declared object parameter remains in the generated FFI signature, and the Rust convenience wrapper becomes an associated function instead of replacing that parameter with &self.

The regression test covers a by-value explicit object parameter, an attributed lvalue-reference parameter, and an ordinary member method to ensure existing receiver generation is unchanged.

Verification

  • cargo +nightly-2025-10-01 fmt --all -- --check
  • cargo clippy --all-targets --workspace --exclude bindgen-integration --exclude tests_expectations -- -D warnings
  • cargo +1.71.0 check -p bindgen --no-default-features --features=runtime
  • cargo test -p bindgen: 31 unit tests and 3 doc tests passed
  • bindgen-integration cargo test: 24 tests passed
  • tests/expectations cargo test: passed
  • bindgen-tests generation suite: 629 passed, including the new regression; 3 existing expectation differences under Apple libclang 21 also reproduce on unchanged upstream/main (issue-544-stylo-creduce-2, nsBaseHashtable, and objc_template)

Implementation and tests were developed with assistance from OpenAI Codex.

@fly1d

fly1d commented Sep 7, 2026

Copy link
Copy Markdown
Author

r? @emilio

Comment thread bindgen-tests/tests/tests.rs Outdated
let actual = builder()
.disable_header_comment()
.header_contents(
"test.hpp",

@emilio emilio Sep 7, 2026

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.

Why can't it use our regular tests infra? We could skip that test in older clang versions if needed.

View changes since the review

@fly1d fly1d Sep 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The regression now uses the regular header/expectation infrastructure. The bindgen-min-clang-version: 18 marker skips it on older libclang versions, while libclang 18 and newer follow the normal generation and comparison path.

Comment thread bindgen/ir/comp.rs Outdated
let is_static = cur.method_is_static();
let is_static = cur.kind() == CXCursor_CXXMethod &&
(cur.method_is_static() ||
cur.method_is_explicit_object());

@emilio emilio Sep 7, 2026

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.

Doesn't look like a static method to me.

View changes since the review

@fly1d fly1d Sep 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Explicit-object members now use MethodKind::ExplicitObject rather than Static. This preserves the declared object parameter without injecting an implicit receiver while keeping the method classified as non-static.

Comment thread bindgen/ir/comp.rs
let is_static = cur.kind() == CXCursor_CXXMethod &&
(cur.method_is_static() ||
cur.method_is_explicit_object());
debug_assert!(!(is_static && is_virtual), "How?");

@emilio emilio Sep 7, 2026

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.

Can't you use virtual with explicit this? If so this assert would break?

View changes since the review

@fly1d fly1d Sep 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

C++23 dcl.fct prohibits a member function with an explicit object parameter from being declared virtual. Clang confirms this rule, so well-formed input cannot violate the assertion. Explicit-object members remain separate from both static and virtual methods.

Detect explicit object parameters from the first parameter token because libclang does not expose this distinction directly. Represent those members separately from static methods so their declared object parameter is preserved without injecting an implicit receiver.

Move the regression into the standard header expectation infrastructure with a minimum libclang version marker, covering value and reference receivers while leaving ordinary member functions unchanged.

Fixes rust-lang#3441

Developed with assistance from OpenAI Codex.
@fly1d
fly1d force-pushed the codex/fix-explicit-object-parameter branch from a9cc3c2 to 3b8da28 Compare September 8, 2026 01:41
@rustbot rustbot added the A-C++ label Sep 8, 2026
@fly1d

fly1d commented Sep 8, 2026

Copy link
Copy Markdown
Author

Thanks for the review. I initially classified explicit-object members as static because neither form has an implicit this argument. That fixed the immediate ABI mismatch, but the review clarified that this shared property does not make them the same language construct. Under C++23 dcl.fct, an explicit-object function remains a non-static member, carries its object in the first declared parameter, and cannot be virtual.

In 3b8da281, I added MethodKind::ExplicitObject, preserved the declared object parameter without injecting a receiver, and left ordinary, static, and virtual method behavior unchanged. I also moved the regression to the regular header/expectation infrastructure. The test is skipped on libclang versions earlier than 18.

The generated bindings now retain S for a by-value object parameter and *mut S for a reference parameter, while ordinary methods still use &self or &mut self. All GitHub Actions checks pass.

@rustbot

rustbot commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Requested reviewer is already assigned to this pull request.

Please choose another assignee.

@emilio
emilio added this pull request to the merge queue Sep 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 8, 2026
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.

C++23 explicit object member functions get an extra implicit this pointer

3 participants