Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support ManuallyDrop, MaybeUninit and Box as simplified types for C only #578

Closed
wants to merge 10 commits into from

Conversation

Hywan
Copy link
Contributor

@Hywan Hywan commented Sep 24, 2020

Fixes #406.

This PR declares std::mem::{ManuallyDrop, MaybeUninit} and std::boxed::Box as simplified types, for C only.

I hope this is correct. I'm a long time user of cbindgen, but it's my first contribution.

Copy link
Collaborator

@emilio emilio left a comment

Choose a reason for hiding this comment

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

So... some of this looks fair enough, but I'm not sure about the interaction with C++. Consider:

#[repr(C)]
struct HasDtors {
   x: i32,
}

impl Drop for HasDtors { ... }

Where HasDtors has a non-trivial destructor in C++. A struct with a ManuallyDrop<HasDtors> member will run destructors in C++...

I suspect the correct behavior here is putting the field inside a single-field union in C++, but I'm not sure how to better represent that internally.

@Hywan
Copy link
Contributor Author

Hywan commented Sep 28, 2020

I'm not sure I'm following you.

If the destructor is implemented in Rust, it must be called by Rust, not by C++. The user must take care of that carefully. Thoughts?

@emilio
Copy link
Collaborator

emilio commented Sep 28, 2020

Well, implementing the destructor in both rust and C++ is a common pattern (so that you can effectively use the type from both languages). I wrote a bit on how we do that in here fwiw.

@Hywan
Copy link
Contributor Author

Hywan commented Sep 28, 2020

Can we just say that in case of ManuallyDrop, the user should avoid implementing a destructor the C++ side?

@Hywan
Copy link
Contributor Author

Hywan commented Sep 28, 2020

@emilio I'm reading your article. Thanks for the great writing! I understand that Box<T> cannot be mapped easily to *const T in C. For the moment, cbindgen generates code like typedef struct Box_T Box_T; instead. Is that correct?

@emilio
Copy link
Collaborator

emilio commented Sep 28, 2020

Can we just say that in case of ManuallyDrop, the user should avoid implementing a destructor the C++ side?

Yeah, documenting it is an option. One problem is that it's not only implementing a destructor for that type, but for any transitive types it uses as members as well... Basically you need to document that using ManuallyDrop<T> means that you need std::is_trivially_destructible_v<T> in C++. But it makes me a bit uncomfortable to bake in that assumption in the code without an opt-in...

Maybe we could do ManuallyDrop / MaybeUninit when the language is C only? I think that's totally uncontroversial. For C++ I'd be more comfortable with an opt-in which acknowledges that the user is aware of the risks.

@emilio I'm reading your article. Thanks for the great writing! I understand that Box cannot be mapped easily to *const T in C. For the moment, cbindgen generates code like typedef struct Box_T Box_T; instead. Is that correct?

Yeah, see the discussion in #451. I think you can technically exclude the box and define your own typedefs in C, but that's not great.

Doing that kind of simplification automatically in C mode is probably sensible (that PR was closed by the reporter, who was using C++ and for C++ you can choose the behavior you want quite trivially currently).

For C++ it could be done automatically in "top-level" context (arguments / return types), but not members (at least without an opt-in). Implementing that last bit is a bit more work I suspect, so if "magic in C, opt-in in C++" (or "C-only") makes sense to you, I think that should be pretty easy on top of your current patch :)

@Hywan
Copy link
Contributor Author

Hywan commented Sep 28, 2020

Maybe we could do ManuallyDrop / MaybeUninit when the language is C only?

That was one my idea too. Is it possible to do that with the current cbindgen API? What happens when we want a C++ binding: Do we emit an error?

I think that should be pretty easy on top of your current patch :)

Yes, correct. Box has a similar process than Cell I believe.

@emilio
Copy link
Collaborator

emilio commented Sep 28, 2020

Maybe we could do ManuallyDrop / MaybeUninit when the language is C only?

That was one my idea too. Is it possible to do that with the current cbindgen API? What happens when we want a C++ binding: Do we emit an error?

I think keeping the current behavior to that regard is better. In C++ you can get the same behavior with template <typename T> using MaybeUninit = T;.

So I think you need to plumb a config: &Config argument through simplify_standard_types (the library.rs caller has access to it), and just do something like:

"ManuallyDrop" | "MaybeUninit" if config.language == Language::C => Some(generic),

in simplified_type.

Following the example of `.skip_warning_as_error`, this patch
introduces a `.skip_cpp` suffix to skip the generation of `.cpp`
files.

This patch also simplifies the code. Ideally, we would create and
implement a new trait offering an API like `is_cpp_skipped`,
`is_warning_as_error_skipped`, and `normalize` (to remove all
suffixes), but it's a little bit overkill for our needs right now.
@Hywan Hywan changed the title feat: Support ManuallyDrop and MaybeUninit as simplified types feat: Support ManuallyDrop, MaybeUninit and Box as simplified types for C only Sep 29, 2020
@Hywan
Copy link
Contributor Author

Hywan commented Sep 29, 2020

I had to update the test “framework” to support the .skip_cpp suffix, following the example of the .skip_warning_as_error suffix. The code has been refactored a little bit, see 7571296 for more explanations and details.

ManuallyDrop and MaybeUninit are simplified only when C binding is targeted, see 8e42a71, where we inject &Config everywhere it is required. The immediate consequence of that is that we can drop the .cpp test artifact for those types, see ece9627.

Finally, Box<T> is simplified to *T for C only (excluding Swift!), see d925991.

Copy link
Collaborator

@emilio emilio left a comment

Choose a reason for hiding this comment

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

This looks good, thanks! Instead of skip_cpp, you can just say language = "C" in the relevant toml file. Or even better, you can just make it work with C++.

I think the swift_name_macro check can also go... I'll do both of those before merging. Thanks!

@@ -433,6 +433,16 @@ impl Type {
is_nullable: false,
is_ref: false,
}),
"Box"
if config.language == Language::C && config.function.swift_name_macro.is_none() =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the swift_name_macro check is not correct... Can you elaborate on why you added it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't sure it was correct. I had no idea what swift_name_macro is used for!

@emilio
Copy link
Collaborator

emilio commented Sep 29, 2020

I merged this in 1e6234f...1fc4cb0. My two tweaks are 5d1217c and 0b9f90b.

Thanks for the patches, and for bearing with me! :)

@emilio emilio closed this Sep 29, 2020
@Hywan
Copy link
Contributor Author

Hywan commented Sep 29, 2020

Thanks for the fast merge and reviews!

Hywan added a commit to Hywan/wasmer that referenced this pull request Sep 29, 2020
Our PR to support `ManuallyDrop`, `MaybeUninit` and `Box` has been
merged, mozilla/cbindgen#578.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support MaybeUninit and ManuallyDrop
2 participants