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

Add DynSized trait (rebase of #44469) #46108

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ has explictly opted out via a negative impl.
impl !Type for Trait
```

[#46108] added the `DynSized` trait, which is an implicit bound for all traits. Auto traits may not
Copy link
Contributor

Choose a reason for hiding this comment

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

the new move to auto trait syntax would make this easier to do automatically, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikomatsakis by "do this automatically" do you mean automatically relaxing the default DynSized bound for auto traits? I guess so, because with the old way (impl Trait for .. {}) there is no way to tell from the trait definition whether it is an auto trait or not, whereas with auto trait it is obvious.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, that is what I mean.

have bounds, so you must explicitly remove this bound with `?DynSized`. [#44917] adds a `Move` trait
which is also implicit, so when that lands, you will have to add `?Move` as well.

[#46108]: https://github.com/rust-lang/rust/pull/46108
[#44917]: https://github.com/rust-lang/rust/pull/44917


Example:

```rust
#![feature(optin_builtin_traits)]
#![feature(optin_builtin_traits, dynsized)]

use std::marker::DynSized;

auto trait Valid {}
auto trait Valid: ?DynSized {}

struct True;
struct False;
Expand All @@ -38,7 +48,6 @@ fn must_be_valid<T: Valid>(_t: T) { }
fn main() {
// works
must_be_valid( MaybeValid(True) );

// compiler error - trait bound not satisfied
// must_be_valid( MaybeValid(False) );
}
Expand Down
97 changes: 72 additions & 25 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ use hash::Hasher;
/// [ub]: ../../reference/behavior-considered-undefined.html
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(stage0, lang = "send")]
#[cfg(not(stage0))]
#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
pub unsafe trait Send: ?DynSized {
// empty.
}

/// docs
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(stage0, lang = "send")]
#[cfg(stage0)]
#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
pub unsafe trait Send {
// empty.
Expand All @@ -51,9 +61,9 @@ pub unsafe trait Send {
unsafe impl Send for .. { }

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for *const T { }
impl<T: ?DynSized> !Send for *const T { }
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for *mut T { }
impl<T: ?DynSized> !Send for *mut T { }

/// Types with a constant size known at compile time.
///
Expand Down Expand Up @@ -96,6 +106,29 @@ pub trait Sized {
// Empty.
}

/// Types with a size known at run time.
///
/// This trait is implemented both by `Sized` types, and by dynamically sized
/// types such as slices and [trait objects]. [Extern types], whose size is not
/// known, even at runtime, do not implement this trait.
///
/// All traits and type parameters have an implicit bound of `DynSized`. The
/// special syntax `?DynSized` can be used to remove this bound if it's not
/// appropriate.
///
/// [trait object]: ../../book/first-edition/trait-objects.html
#[cfg(not(stage0))]
#[unstable(feature = "dynsized", issue = "0")]
#[lang = "dynsized"]
#[rustc_on_unimplemented = "`{Self}` does not have a size known at run-time"]
#[fundamental]
pub trait DynSized: ?DynSized {
// Empty.
}

#[cfg(stage0)]
use self::Sized as DynSized; // This is just so we don't have to stage too much stuff

/// Types that can be "unsized" to a dynamically-sized type.
///
/// For example, the sized array type `[i8; 2]` implements `Unsize<[i8]>` and
Expand Down Expand Up @@ -345,6 +378,16 @@ pub trait Copy : Clone {
/// [transmute]: ../../std/mem/fn.transmute.html
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "sync"]
#[cfg(not(stage0))]
#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"]
pub unsafe trait Sync: ?DynSized {
// Empty
}

/// docs
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "sync"]
#[cfg(stage0)]
#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"]
pub unsafe trait Sync {
// Empty
Expand All @@ -356,56 +399,56 @@ pub unsafe trait Sync {
unsafe impl Sync for .. { }

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *const T { }
impl<T: ?DynSized> !Sync for *const T { }
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *mut T { }
impl<T: ?DynSized> !Sync for *mut T { }

macro_rules! impls{
($t: ident) => (
#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> Hash for $t<T> {
impl<T:?DynSized> Hash for $t<T> {
#[inline]
fn hash<H: Hasher>(&self, _: &mut H) {
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> cmp::PartialEq for $t<T> {
impl<T:?DynSized> cmp::PartialEq for $t<T> {
fn eq(&self, _other: &$t<T>) -> bool {
true
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> cmp::Eq for $t<T> {
impl<T:?DynSized> cmp::Eq for $t<T> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> cmp::PartialOrd for $t<T> {
impl<T:?DynSized> cmp::PartialOrd for $t<T> {
fn partial_cmp(&self, _other: &$t<T>) -> Option<cmp::Ordering> {
Option::Some(cmp::Ordering::Equal)
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> cmp::Ord for $t<T> {
impl<T:?DynSized> cmp::Ord for $t<T> {
fn cmp(&self, _other: &$t<T>) -> cmp::Ordering {
cmp::Ordering::Equal
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> Copy for $t<T> { }
impl<T:?DynSized> Copy for $t<T> { }

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> Clone for $t<T> {
impl<T:?DynSized> Clone for $t<T> {
fn clone(&self) -> $t<T> {
$t
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T:?Sized> Default for $t<T> {
impl<T:?DynSized> Default for $t<T> {
fn default() -> $t<T> {
$t
}
Expand Down Expand Up @@ -548,31 +591,35 @@ macro_rules! impls{
/// [drop check]: ../../nomicon/dropck.html
#[lang = "phantom_data"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct PhantomData<T:?Sized>;
pub struct PhantomData<T:?DynSized>;

impls! { PhantomData }

mod impls {
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<'a, T: Sync + ?Sized> Send for &'a T {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<'a, T: Send + ?Sized> Send for &'a mut T {}
}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<'a, T: Sync + ?DynSized> Send for &'a T {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<'a, T: Send + ?DynSized> Send for &'a mut T {}

/// Compiler-internal trait used to determine whether a type contains
/// any `UnsafeCell` internally, but not through an indirection.
/// This affects, for example, whether a `static` of that type is
/// placed in read-only static memory or writable static memory.
#[lang = "freeze"]
#[cfg(not(stage0))]
unsafe trait Freeze: ?DynSized {}

/// docs
#[lang = "freeze"]
#[cfg(stage0)]
unsafe trait Freeze {}

#[allow(unknown_lints)]
#[allow(auto_impl)]
unsafe impl Freeze for .. {}

impl<T: ?Sized> !Freeze for UnsafeCell<T> {}
unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
unsafe impl<T: ?Sized> Freeze for *const T {}
unsafe impl<T: ?Sized> Freeze for *mut T {}
unsafe impl<'a, T: ?Sized> Freeze for &'a T {}
unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {}
impl<T: ?DynSized> !Freeze for UnsafeCell<T> {}
unsafe impl<T: ?DynSized> Freeze for PhantomData<T> {}
unsafe impl<T: ?DynSized> Freeze for *const T {}
unsafe impl<T: ?DynSized> Freeze for *mut T {}
unsafe impl<'a, T: ?DynSized> Freeze for &'a T {}
unsafe impl<'a, T: ?DynSized> Freeze for &'a mut T {}
13 changes: 10 additions & 3 deletions src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ use nonzero::NonZero;

use cmp::Ordering::{self, Less, Equal, Greater};

#[cfg(stage0)]
use marker::Sized as DynSized;
#[cfg(not(stage0))]
use marker::DynSized;

// FIXME #19649: intrinsic docs don't render, so these have no docs :(

#[stable(feature = "rust1", since = "1.0.0")]
pub use intrinsics::copy_nonoverlapping;

Expand Down Expand Up @@ -56,7 +63,7 @@ pub use intrinsics::write_bytes;
#[stable(feature = "drop_in_place", since = "1.8.0")]
#[lang = "drop_in_place"]
#[allow(unconditional_recursion)]
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
pub unsafe fn drop_in_place<T: ?DynSized>(to_drop: *mut T) {
// Code here does not matter - this is replaced by the
// real drop glue by the compiler.
drop_in_place(to_drop);
Expand Down Expand Up @@ -471,7 +478,7 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
}

#[lang = "const_ptr"]
impl<T: ?Sized> *const T {
impl<T: ?DynSized> *const T {
/// Returns `true` if the pointer is null.
///
/// # Examples
Expand Down Expand Up @@ -1104,7 +1111,7 @@ impl<T: ?Sized> *const T {
}

#[lang = "mut_ptr"]
impl<T: ?Sized> *mut T {
impl<T: ?DynSized> *mut T {
/// Returns `true` if the pointer is null.
///
/// # Examples
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ define_dep_nodes!( <'tcx>
[] IsForeignItem(DefId),
[] TypeParamPredicates { item_id: DefId, param_id: DefId },
[] SizedConstraint(DefId),
[] DynSizedConstraint(DefId),
[] DtorckConstraint(DefId),
[] AdtDestructor(DefId),
[] AssociatedItemDefIds(DefId),
Expand All @@ -527,6 +528,7 @@ define_dep_nodes!( <'tcx>

[] IsCopy { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsDynSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsFreeze { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] NeedsDrop { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] Layout { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ language_item_table! {
F64ImplItem, "f64", f64_impl;

SizedTraitLangItem, "sized", sized_trait;
DynSizedTraitLangItem, "dynsized", dynsized_trait;
UnsizeTraitLangItem, "unsize", unsize_trait;
CopyTraitLangItem, "copy", copy_trait;
CloneTraitLangItem, "clone", clone_trait;
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ObligationCauseCode::StructInitializerSized => {
err.note("structs must have a statically known size to be initialized");
}
ObligationCauseCode::FieldDynSized => {
err.note("the last field of a struct or tuple must have a dynamically sized type");
}
ObligationCauseCode::FieldSized(ref item) => {
match *item {
AdtKind::Struct => {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ pub enum ObligationCauseCode<'tcx> {
/// Types of fields (other than the last) in a struct must be sized.
FieldSized(AdtKind),

/// Last field of a struct must be DynSized.
FieldDynSized,

/// Constant expressions must be sized.
ConstSized,

Expand Down
56 changes: 56 additions & 0 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions,
&mut candidates)?;
} else if lang_items.dynsized_trait() == Some(def_id) {
// DynSized is never implementable by end-users, it is
// always automatically computed.
let dynsized_conditions = self.dynsized_conditions(obligation);
self.assemble_builtin_bound_candidates(dynsized_conditions,
&mut candidates)?;
} else if lang_items.unsize_trait() == Some(def_id) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else {
Expand Down Expand Up @@ -2054,6 +2060,53 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
}

fn dynsized_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-> BuiltinImplConditions<'tcx>
{
use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};

// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(
obligation.predicate.skip_binder().self_ty());

match self_ty.sty {
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
ty::TyChar | ty::TyRef(..) | ty::TyGenerator(..) |
ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) |
ty::TyError => {
// safe for everything
Where(ty::Binder(Vec::new()))
}

ty::TyTuple(tys, _) => {
Where(ty::Binder(tys.last().into_iter().cloned().collect()))
}

ty::TyAdt(def, substs) => {
let dynsized_crit = def.dynsized_constraint(self.tcx());
// (*) binder moved here
Where(ty::Binder(
dynsized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect()
))
}

ty::TyForeign(..) => Never,

ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
ty::TyInfer(ty::TyVar(_)) => Ambiguous,

ty::TyInfer(ty::FreshTy(_))
| ty::TyInfer(ty::FreshIntTy(_))
| ty::TyInfer(ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}",
self_ty);
}
}
}

fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-> BuiltinImplConditions<'tcx>
{
Expand Down Expand Up @@ -2382,6 +2435,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
_ if Some(trait_def) == lang_items.sized_trait() => {
self.sized_conditions(obligation)
}
_ if Some(trait_def) == lang_items.dynsized_trait() => {
self.dynsized_conditions(obligation)
}
_ if Some(trait_def) == lang_items.copy_trait() => {
self.copy_clone_conditions(obligation)
}
Expand Down
Loading