-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Implement RFC 1861: Extern types #44295
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @arielb1 (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
I also have an implementation of DynSized which goes with this at https://github.com/plietar/rust/tree/dynsized |
src/librustc_trans/intrinsic.rs
Outdated
let (llsize, _) = | ||
glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]); | ||
llsize | ||
} else { | ||
let lltp_ty = type_of::type_of(ccx, tp_ty); | ||
C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) | ||
C_null(llret_ty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is that supposed to be? Did you mean C_uint(ccx, 0)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, although I'm not really familiar with LLVM IR and I expected the two to be equivalent. I originally took this from the discriminant_value
intrinsic.
return (bcx.struct_gep( | ||
ptr_val, adt::struct_llfields_index(st, ix)), alignment); | ||
} | ||
bcx.ccx.shared().type_is_sized(fty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to do an alignment fixup if you have a foreign vtable. Actually, you only need to do an alignment fixup if the vtable type is Trait
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foreign tails were handled by the !self.has_extra()
a few lines down. I've added a case to the match just beneath, especially since I'm pretty sure the !self.has_extra()
branch should never be taken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's detritus from old trans, but with your patch it's taken by Foo<Foo<Opaque>>
. I'm fine with it because you're trying to forbid that.
@@ -0,0 +1,28 @@ | |||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also add a test that they are !Sized
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -0,0 +1,30 @@ | |||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to use a pointer with an "interesting" bit pattern rather than 0
, to make sure we are not truncating anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I've had to use cfg(target_pointer_width)
to make sure all bits have something interesting.
|
||
assert_eq!(size_of_val(x), 0); | ||
assert_eq!(align_of_val(x), 1); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More tests I think we need:
- test that pointers to extern types have the same size and align as a thin pointer.
- check that pointers to extern types have the right ABI - maybe add a test to https://github.com/rust-lang/rust/tree/e22a3cf7e12b622b143694942504c66a544dc4c5/src/test/run-make/extern-fn-struct-passing-abi
- check that structs with
extern
tails work - e.g.struct Foo<T: ?Sized>(u8, T);
whereT = <foreign type>/Foo<<foreign type>>
Basically LGTM modulo the 2 nits and the tests. |
ea9220e
to
fe7dc7d
Compare
@arielb1 I've addressed your comments.
I've changed most of the tests to try both extern types and struct with foreign tails. I've however not put any test which tries to get the address of the foreign tail, since the alignment of the field is unknown. While it is currently allowed (with an assumed alignement of 1), it shouldn't be once DynSized is implemented. |
@bors r+ |
📌 Commit 5814c88 has been approved by |
⌛ Testing commit 5814c880fcf31abd4895295d14f6d994b2badf34 with merge 9dbee87171690a1188497154b292f6403992db03... |
src/libsyntax/feature_gate.rs
Outdated
@@ -382,6 +382,9 @@ declare_features! ( | |||
|
|||
// allow '|' at beginning of match arms (RFC 1925) | |||
(active, match_beginning_vert, "1.21.0", Some(44101)), | |||
|
|||
// extern types | |||
(active, extern_types, "1.21.0", Some(43467)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be 1.22.0
?
💔 Test failed - status-appveyor |
|
Opened PR in rustfmt : rust-lang/rustfmt#1946 |
5814c88
to
d60d340
Compare
ping @arielb1, I've updated rustfmt |
@bors r+ |
📌 Commit d60d340 has been approved by |
Hey, |
r? @arielb1 |
At last! |
📌 Commit 1e9e319 has been approved by |
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
☀️ Test successful - status-appveyor, status-travis |
Oh no, this shouldn't have merged before #45225. Oh well, time to rebase now. |
The DynSized trait is implemented by all types which have a size and alignment known at runtime. This includes every type other than extern types, introduced in RFC 1861 and implemented in rust-lang#44295, which are completely opaque. The main motivation for this trait is to prevent the use of !DynSized types as struct tails. Consider for example the following types : ```rust extern { type foo; } struct A<T: ?Sized> { a_x: u8 a_y: T } struct B<T: ?Sized> { b_x: u8 b_y: T } ``` Before this change, the type `A<B<foo>>` is considered well-formed. However, the alignment of `B<foo>` and thus the offset of the `a_y` field depends on the alignment of `foo`, which is unknown. By introducing this new trait, struct tails are now required to implement `DynSized`, such that their alignment is known. The trait is an implicit bound, making `A<B<foo>>` ill-formed. Just like the `Sized` trait, the default bound can be opted-out by using `?DynSized`.
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 :
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 :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 : Tracking issue for RFC 1861: Extern types #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