autocxx icon indicating copy to clipboard operation
autocxx copied to clipboard

Apple LLVM 16 has headers with unused template parameters in strings

Open adetaylor opened this issue 1 year ago • 5 comments

I can't build HEAD 48c74b6530db3dddda7fe8c20971ed664379b041 on one of my Macs, for unknown reasons. I get:

            impl Goat {
                ///autocxx bindings couldn't be generated: Found an attempt at using a forward declaration (std::__1::string) inside a templated cxx type such as UniquePtr or CxxVector. If the forward declaration is a typedef, perhaps autocxx wasn't sure whether or not it involved a forward declaration. If you're sure it didn't, then you may be able to solve this by using instantiable!.
                fn describe(_uhoh: autocxx::BindingGenerationFailure) {}

adetaylor avatar Sep 23 '24 12:09 adetaylor

Note to self: a repro file is at ~/Downloads/repro-1396.json.zip

adetaylor avatar Sep 23 '24 14:09 adetaylor

升级Xcode后,默认clang --version变成了 Apple clang version 16.0.0 (clang-1600.0.26.3), 再安装一下 15版本就可以了

brew install llvm@15

添加环境 ~/.zshrc

export PATH="/usr/local/opt/llvm@15/bin:$PATH" # Intel Mac

export PATH="/opt/homebrew/opt/llvm@15/bin:$PATH" # Apple Silicon

刷新环境

source ~/.zshrc

FaceWaller avatar Sep 24 '24 08:09 FaceWaller

Thank you! Yes - however I need to make it ideally work with Apple's LLVM 16 headers. Somehow :)

adetaylor avatar Sep 24 '24 14:09 adetaylor

A bit of diagnosis. TL;DR: this is going to be hard to fix, but if I can pull it off it might make autocxx work in quite a few more circumstances.

Diagnosis

autocxx-bindgen is giving us this for std::basic_string:

                #[repr(C)]
                #[cpp_semantics(unused_template_param)]
                #[cpp_semantics(source_location(
                    "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/string",
                    709,
                    7,
                    42586
                ))]
                pub struct basic_string {
                    __r_: root::std::__1::__compressed_pair,
                }

The cpp_semantics attributes are the bits added by autocxx_bindgen on top of the standard bindgen output.

This is generated from C++ thus:

template <class _CharT, class _Traits, class _Allocator>
class basic_string {
// ...
}

The problem here is #[cpp_semantics(unused_template_param)].

bindgen does an analysis of which of a type's template parameters are used, by following edges on the graph from the type to each of its fields. Rust requires template arguments to be used, so when generating a templated type A<B,C,D> bindgen will generate A<B> if only B is required by its fields. (This is drastically simplifying the complexity of course).

That's fine for normal users of bindgen, but for us, we need to generate C++ code containing this type. Providing just A<B> may not work in C++ code.

So, in autocxx, we refuse to generate A<B> (or in this real case, std::basic_string) because we think it's always necessary to generate A<B,C,D> or std::basic_string<_CharT,_Traits,_Allocator>.

Hence we are currently completely refusing to generate std::basic_string, and all sorts of things fail.

I don't know what specifically changed with this version of Apple's string headers to cause this to occur now.

Solution option 1

  • Instead of just generating #[cpp_semantics(unused_template_param)], autocxx-bindgen tells us which template parameters are unused.
  • We then always provide such parameters when specifying the type in C++, but they remain omitted from Rust.

Solution option 2

  • Instead of just generating #[cpp_semantics(unused_template_param)], autocxx-bindgen tells us which template parameters are unused.
  • We then always provide such parameters when specifying the type in C++
  • If we need to specify them in Rust for some reason (?) then we add a PhantomData<B> field. I can't see any reason why this would occur, but we'll see

Solution option 3

  • Instead of just generating #[cpp_semantics(unused_template_param)], autocxx-bindgen tells us which template parameters are unused.
  • We generate a typedef A_concrete<B> A<B,C,D> along the lines of the instantiate! directive. It's not clear what we'd put into C or D here but perhaps there's template magic to look up their default values, or something.

If I can get any of these to work, it should greatly increase autocxx's versatility to work with a wider range of templated types.

adetaylor avatar Sep 27 '24 16:09 adetaylor

Hm, in fact - although there's a core of an idea there which might help autocxx in general - I don't think it solves everything (or even anything?) in this case. bindgen gives us this:

#[link_name = "\u{1}__Z11give_str_upv"]
        pub fn give_str_up() -> root::std::__1::unique_ptr;

for the test_give_string_up test. So the returned type isn't complete enough, irrespective of any attributes autocxx adds.

adetaylor avatar Sep 27 '24 17:09 adetaylor

Fixed by https://github.com/rust-lang/rust-bindgen/commit/7fd78ad70c0c4329206421109dc5259b7b923f7e which I've now rolled into autocxx-bindgen.

adetaylor avatar Jan 08 '25 22:01 adetaylor