Tracking issue for RFC 1861: Extern types #43467
Comments
This is not explicitly mentioned in the RFC, but I'm assuming different instances of extern {
type A;
type B;
}
fn convert_ref(r: &A) -> &B { r } |
@jethrogb That's certainly the intention, yes. |
Relatedly, is deciding whether we want to call it EDIT: rust-lang/rfcs#2071 is also relevant here w.r.t. the connotations of |
Can we get a bullet for the panic vs DynSized debate? |
I've started working on this, and I have a working simple initial version (no generics, no DynSized). I've however noticed a slight usability issue. In FFI code, it's frequent for raw pointers to be initialized to null using Despite being unsized, extern types are used through thin pointers, so it should be possible to use It is still possible to cast an integer to an extern type pointer, but this is not as nice as just using the function designed for this. Also this can never be done in a generic context. extern {
type foo;
}
fn null_foo() -> *const foo {
0usize as *const foo
} Really we'd want is a new trait to distinguish types which use thin pointers. It would be implemented automatically for all sized types and extern types. Then the cast above would succeed whenever the type is bounded by this trait. Eg, the function fn null<T: ?Sized + Thin>() -> *const T {
0usize as *const T
} However there's a risk of more and more such traits creeping up, such as |
I think we can add extern types now and live with |
@SimonSapin yeah, it's definitely a minor concern for now. I do think this problem of not having a trait bound to express "this type may be unsized be must have a thin pointer" might crop up in other places though. |
Oh yeah, I agree we should solve that eventually too. I’m only saying we might not need to solve all of it before we ship any of it. |
I've pushed an initial implementation in #44295 |
Implement RFC 1861: Extern types A few notes : - Type parameters are not supported. This was an unresolved question from the RFC. It is not clear how useful this feature is, and how variance should be treated. This can be added in a future PR. - `size_of_val` / `align_of_val` can be called with extern types, and respectively return 0 and 1. This differs from the RFC, which specified that they should panic, but after discussion with @eddyb on IRC this seems like a better solution. If/when a `DynSized` trait is added, this will be disallowed statically. - Auto traits are not implemented by default, since the contents of extern types is unknown. This means extern types are `!Sync`, `!Send` and `!Freeze`. This seems like the correct behaviour to me. Manual `unsafe impl Sync for Foo` is still possible. - This PR allows extern type to be used as the tail of a struct, as described by the RFC : ```rust extern { type OpaqueTail; } #[repr(C)] struct FfiStruct { data: u8, more_data: u32, tail: OpaqueTail, } ``` However this is undesirable, as the alignment of `tail` is unknown (the current PR assumes an alignment of 1). Unfortunately we can't prevent it in the general case as the tail could be a type parameter : ```rust #[repr(C)] struct FfiStruct<T: ?Sized> { data: u8, more_data: u32, tail: T, } ``` Adding a `DynSized` trait would solve this as well, by requiring tail fields to be bound by it. - Despite being unsized, pointers to extern types are thin and can be casted from/to integers. However it is not possible to write a `null<T>() -> *const T` function which works with extern types, as I've explained here : #43467 (comment) - Trait objects cannot be built from extern types. I intend to support it eventually, although how this interacts with `DynSized`/`size_of_val` is still unclear. - The definition of `c_void` is unmodified
Implement RFC 1861: Extern types A few notes : - Type parameters are not supported. This was an unresolved question from the RFC. It is not clear how useful this feature is, and how variance should be treated. This can be added in a future PR. - `size_of_val` / `align_of_val` can be called with extern types, and respectively return 0 and 1. This differs from the RFC, which specified that they should panic, but after discussion with @eddyb on IRC this seems like a better solution. If/when a `DynSized` trait is added, this will be disallowed statically. - Auto traits are not implemented by default, since the contents of extern types is unknown. This means extern types are `!Sync`, `!Send` and `!Freeze`. This seems like the correct behaviour to me. Manual `unsafe impl Sync for Foo` is still possible. - This PR allows extern type to be used as the tail of a struct, as described by the RFC : ```rust extern { type OpaqueTail; } #[repr(C)] struct FfiStruct { data: u8, more_data: u32, tail: OpaqueTail, } ``` However this is undesirable, as the alignment of `tail` is unknown (the current PR assumes an alignment of 1). Unfortunately we can't prevent it in the general case as the tail could be a type parameter : ```rust #[repr(C)] struct FfiStruct<T: ?Sized> { data: u8, more_data: u32, tail: T, } ``` Adding a `DynSized` trait would solve this as well, by requiring tail fields to be bound by it. - Despite being unsized, pointers to extern types are thin and can be casted from/to integers. However it is not possible to write a `null<T>() -> *const T` function which works with extern types, as I've explained here : #43467 (comment) - Trait objects cannot be built from extern types. I intend to support it eventually, although how this interacts with `DynSized`/`size_of_val` is still unclear. - The definition of `c_void` is unmodified
Implement RFC 1861: Extern types A few notes : - Type parameters are not supported. This was an unresolved question from the RFC. It is not clear how useful this feature is, and how variance should be treated. This can be added in a future PR. - `size_of_val` / `align_of_val` can be called with extern types, and respectively return 0 and 1. This differs from the RFC, which specified that they should panic, but after discussion with @eddyb on IRC this seems like a better solution. If/when a `DynSized` trait is added, this will be disallowed statically. - Auto traits are not implemented by default, since the contents of extern types is unknown. This means extern types are `!Sync`, `!Send` and `!Freeze`. This seems like the correct behaviour to me. Manual `unsafe impl Sync for Foo` is still possible. - This PR allows extern type to be used as the tail of a struct, as described by the RFC : ```rust extern { type OpaqueTail; } #[repr(C)] struct FfiStruct { data: u8, more_data: u32, tail: OpaqueTail, } ``` However this is undesirable, as the alignment of `tail` is unknown (the current PR assumes an alignment of 1). Unfortunately we can't prevent it in the general case as the tail could be a type parameter : ```rust #[repr(C)] struct FfiStruct<T: ?Sized> { data: u8, more_data: u32, tail: T, } ``` Adding a `DynSized` trait would solve this as well, by requiring tail fields to be bound by it. - Despite being unsized, pointers to extern types are thin and can be casted from/to integers. However it is not possible to write a `null<T>() -> *const T` function which works with extern types, as I've explained here : #43467 (comment) - Trait objects cannot be built from extern types. I intend to support it eventually, although how this interacts with `DynSized`/`size_of_val` is still unclear. - The definition of `c_void` is unmodified
Implement RFC 1861: Extern types A few notes : - Type parameters are not supported. This was an unresolved question from the RFC. It is not clear how useful this feature is, and how variance should be treated. This can be added in a future PR. - `size_of_val` / `align_of_val` can be called with extern types, and respectively return 0 and 1. This differs from the RFC, which specified that they should panic, but after discussion with @eddyb on IRC this seems like a better solution. If/when a `DynSized` trait is added, this will be disallowed statically. - Auto traits are not implemented by default, since the contents of extern types is unknown. This means extern types are `!Sync`, `!Send` and `!Freeze`. This seems like the correct behaviour to me. Manual `unsafe impl Sync for Foo` is still possible. - This PR allows extern type to be used as the tail of a struct, as described by the RFC : ```rust extern { type OpaqueTail; } #[repr(C)] struct FfiStruct { data: u8, more_data: u32, tail: OpaqueTail, } ``` However this is undesirable, as the alignment of `tail` is unknown (the current PR assumes an alignment of 1). Unfortunately we can't prevent it in the general case as the tail could be a type parameter : ```rust #[repr(C)] struct FfiStruct<T: ?Sized> { data: u8, more_data: u32, tail: T, } ``` Adding a `DynSized` trait would solve this as well, by requiring tail fields to be bound by it. - Despite being unsized, pointers to extern types are thin and can be casted from/to integers. However it is not possible to write a `null<T>() -> *const T` function which works with extern types, as I've explained here : #43467 (comment) - Trait objects cannot be built from extern types. I intend to support it eventually, although how this interacts with `DynSized`/`size_of_val` is still unclear. - The definition of `c_void` is unmodified
Implement RFC 1861: Extern types A few notes : - Type parameters are not supported. This was an unresolved question from the RFC. It is not clear how useful this feature is, and how variance should be treated. This can be added in a future PR. - `size_of_val` / `align_of_val` can be called with extern types, and respectively return 0 and 1. This differs from the RFC, which specified that they should panic, but after discussion with @eddyb on IRC this seems like a better solution. If/when a `DynSized` trait is added, this will be disallowed statically. - Auto traits are not implemented by default, since the contents of extern types is unknown. This means extern types are `!Sync`, `!Send` and `!Freeze`. This seems like the correct behaviour to me. Manual `unsafe impl Sync for Foo` is still possible. - This PR allows extern type to be used as the tail of a struct, as described by the RFC : ```rust extern { type OpaqueTail; } #[repr(C)] struct FfiStruct { data: u8, more_data: u32, tail: OpaqueTail, } ``` However this is undesirable, as the alignment of `tail` is unknown (the current PR assumes an alignment of 1). Unfortunately we can't prevent it in the general case as the tail could be a type parameter : ```rust #[repr(C)] struct FfiStruct<T: ?Sized> { data: u8, more_data: u32, tail: T, } ``` Adding a `DynSized` trait would solve this as well, by requiring tail fields to be bound by it. - Despite being unsized, pointers to extern types are thin and can be casted from/to integers. However it is not possible to write a `null<T>() -> *const T` function which works with extern types, as I've explained here : #43467 (comment) - Trait objects cannot be built from extern types. I intend to support it eventually, although how this interacts with `DynSized`/`size_of_val` is still unclear. - The definition of `c_void` is unmodified
While it is possible for Sync, Send, UnwindSafe and RefUnwindSafe, doing Should Or is it possible to declare an extern type is safe-by-default, which opt-out instead of opt-in? extern {
#[unsafe_impl_all_auto_traits_by_default]
type Foo;
}
impl !Send for Foo {} |
@kennytm What's the usecase? The semantics of |
@eddyb Use case: Trying to see if it's possible to make CStr a thin DST. I don't see anything related to a cell in #44295? It is reported to LLVM as an |
@kennytm So with |
Are the "Unresolved questions" in the OP still blocking this from stabilization? Are there any other unresolved questions or issues that are also blocking this? |
If this doesn't support generics, I'd expect people to hack it like this: extern {
type Helper;
}
pub struct MyExternType<'a, T> {
_phantom: core::marker::PhantomData<&'a T>,
_helper: Helper,
} Since the suggested syntax doesn't support it directly, I think that the idea with marker is superior. Maybe worth revisiting. |
@Kixunil Btw you probably want But yeah, that's a good way to describe some FFI APIs, although without rust-lang/rfcs#2770, it's more painful than it has to be. |
@eddyb oh, I was unclear, I didn't mean |
One advantage |
Would it be possible to override it if one knows what he's doing though? |
No, you'd need to do it like in your example if you wanted something weaker. |
10: Allow type aliases in extern blocks r=jonas-schievink a=jonas-schievink This is for the unstable feature rust-lang/rust#43467, which rustc uses internally Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
5861: Support extern types r=matklad a=jonas-schievink This is a currently unstable feature tracked at rust-lang/rust#43467 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
I want to add a note to this discussion: the current implementation seems to be |
Update on |
@SimonSapin in #42847 (comment) rightly points out that resisting math is futile, accept |
You’re putting words in my mouth. That the trait resolver should be able to deduce |
rust-lang/rfcs#2984 (comment) what I wrote that I don't want to twist your words, I just see that chipping away at the argument against effectively. |
I would like to raise an issue about this. In ISO 9899 N1124, §6.2.5 Clause 26 defines the classes of pointer types which are required to be compatible. Notably, pointers in the following classes MUST have the same representation and alignment-requirements with others in the same class, and no further requirements are imposed:
This proposal seems to only allow one, presumably unified ABI (if not, there is no indication of which of the listed classes extern types are guaranteed to be included in). This is not generally compatible with the C abi. Unless rust further restricts compatible C platforms, there needs to be a way to select at least between the classes of struct and union. In a discussion on the Rust Programming Language Community [Discord] Server, a conversion made mention that the class of character types would also be a good idea (though this can be suplemented by a newtype arround |
@chorman0773 Are there any existing ABIs that differentiate between pointer types? Strictly speaking C doesn't have or define an ABI, only implementations of C's "abstract machine" have an ABI (i.e. OS and ISA). If there are ABIs that really do differentiate between pointer types then I suppose something like |
@mjbshaw An option for void/character-like extern types would also be highly appreciated, at least on my part. It's not strictly necessary to correctly interface with C due to These parameters/return values could then be written as extern type references (which creates appropriate speed bumps when using the API), instead of adding the lifetime constraints to the (I'm mostly talking about myself here, though, since I'm a bit prone to these types of mistakes if I don't add the constraints from the get-go.) |
I am not aware of any, though I am only familiar with the System V x86_64 abi. I also raised a similar issue in https://github.com/rust-lang/unsafe-coding-guidelines/issues/266, and one of the comments pointed out control-flow integrety schemes. I don't know that this is mandated in any ABI, though as it mentions, clang supports it with a software implementation (and it seems one of the santizers does check indirect function calls, though I don't see anything for direct calls to a free function). |
This is a tracking issue for RFC 1861 "Extern types".
Steps:
Unresolved questions:
Should we allow generic lifetime and type parameters on extern types?
If so, how do they effect the type in terms of variance?
In std's source, it is mentioned that LLVM expects
i8*
for C'svoid*
.We'd need to continue to hack this for the two
c_void
s in std and libc.But perhaps this should be done across-the-board for all extern types?
Somebody should check what Clang does. Also see #59095.
The text was updated successfully, but these errors were encountered: