Skip to content

Commit

Permalink
Add a DynSized trait.
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
plietar authored and mikeyhew committed Nov 20, 2017
1 parent 33374fa commit a18bd5a
Show file tree
Hide file tree
Showing 25 changed files with 470 additions and 112 deletions.
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 {}
2 changes: 1 addition & 1 deletion src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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
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
3 changes: 3 additions & 0 deletions src/librustc/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
super::SizedReturnType => Some(super::SizedReturnType),
super::RepeatVec => Some(super::RepeatVec),
super::FieldSized(item) => Some(super::FieldSized(item)),
super::FieldDynSized => Some(super::FieldDynSized),
super::ConstSized => Some(super::ConstSized),
super::SharedStatic => Some(super::SharedStatic),
super::BuiltinDerivedObligation(ref cause) => {
Expand Down Expand Up @@ -522,6 +523,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized(_) |
super::FieldDynSized |
super::ConstSized |
super::SharedStatic |
super::BlockTailExpression(_) |
Expand Down Expand Up @@ -570,6 +572,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized(_) |
super::FieldDynSized |
super::ConstSized |
super::SharedStatic |
super::BlockTailExpression(_) |
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/maps/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_sized_raw<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::is_dynsized_raw<'tcx> {
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
format!("computing whether `{}` is `DynSized`", env.value)
}
}

impl<'tcx> QueryDescription<'tcx> for queries::is_freeze_raw<'tcx> {
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
format!("computing whether `{}` is freeze", env.value)
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ define_maps! { <'tcx>
[] fn adt_def: AdtDefOfItem(DefId) -> &'tcx ty::AdtDef,
[] fn adt_destructor: AdtDestructor(DefId) -> Option<ty::Destructor>,
[] fn adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>],
[] fn adt_dynsized_constraint: DynSizedConstraint(DefId) -> &'tcx [Ty<'tcx>],
[] fn adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>,

/// True if this is a const fn
Expand Down Expand Up @@ -261,6 +262,7 @@ define_maps! { <'tcx>
// `ty.is_copy()`, etc, since that will prune the environment where possible.
[] fn is_copy_raw: is_copy_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_dynsized_raw: is_dynsized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
Expand Down Expand Up @@ -438,6 +440,10 @@ fn is_freeze_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepCo
DepConstructor::IsFreeze { param_env }
}

fn is_dynsized_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
DepConstructor::IsDynSized { param_env }
}

fn needs_drop_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
DepConstructor::NeedsDrop { param_env }
}
Expand Down
Loading

0 comments on commit a18bd5a

Please sign in to comment.