Skip to content

Commit

Permalink
Rollup merge of #93977 - compiler-errors:sized-generic-metadata, r=we…
Browse files Browse the repository at this point in the history
…sleywiser

Type params and assoc types have unit metadata if they are sized

Extend the logic in `Pointee` projection to ensure that we can satisfy `<T as Pointee>::Metadata = ()` if `T: Sized`.

cc: `@SimonSapin` and #93959
  • Loading branch information
matthiaskrgr authored Mar 14, 2022
2 parents 0e42393 + 210e829 commit 774655d
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 15 deletions.
23 changes: 13 additions & 10 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2252,12 +2252,13 @@ impl<'tcx> Ty<'tcx> {
}
}

/// Returns the type of metadata for (potentially fat) pointers to this type.
/// Returns the type of metadata for (potentially fat) pointers to this type,
/// and a boolean signifying if this is conditional on this type being `Sized`.
pub fn ptr_metadata_ty(
self,
tcx: TyCtxt<'tcx>,
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
) -> Ty<'tcx> {
) -> (Ty<'tcx>, bool) {
let tail = tcx.struct_tail_with_normalize(self, normalize);
match tail.kind() {
// Sized types
Expand All @@ -2277,28 +2278,30 @@ impl<'tcx> Ty<'tcx> {
| ty::Closure(..)
| ty::Never
| ty::Error(_)
// Extern types have metadata = ().
| ty::Foreign(..)
// If returned by `struct_tail_without_normalization` this is a unit struct
// without any fields, or not a struct, and therefore is Sized.
| ty::Adt(..)
// If returned by `struct_tail_without_normalization` this is the empty tuple,
// a.k.a. unit type, which is Sized
| ty::Tuple(..) => tcx.types.unit,
| ty::Tuple(..) => (tcx.types.unit, false),

ty::Str | ty::Slice(_) => tcx.types.usize,
ty::Str | ty::Slice(_) => (tcx.types.usize, false),
ty::Dynamic(..) => {
let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap();
tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()])
(tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()]), false)
},

ty::Projection(_)
| ty::Param(_)
| ty::Opaque(..)
| ty::Infer(ty::TyVar(_))
// type parameters only have unit metadata if they're sized, so return true
// to make sure we double check this during confirmation
ty::Param(_) | ty::Projection(_) | ty::Opaque(..) => (tcx.types.unit, true),

ty::Infer(ty::TyVar(_))
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("`ptr_metadata_ty` applied to unexpected type: {:?}", tail)
bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
}
}
}
Expand Down
35 changes: 30 additions & 5 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());

let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| {
// We throw away any obligations we get from this, since we normalize
// and confirm these obligations once again during confirmation
normalize_with_depth(
selcx,
obligation.param_env,
Expand All @@ -1485,7 +1487,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Foreign(_)
| ty::Str
| ty::Array(..)
| ty::Slice(_)
Expand All @@ -1498,6 +1499,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| ty::Generator(..)
| ty::GeneratorWitness(..)
| ty::Never
// Extern types have unit metadata, according to RFC 2850
| ty::Foreign(_)
// If returned by `struct_tail_without_normalization` this is a unit struct
// without any fields, or not a struct, and therefore is Sized.
| ty::Adt(..)
Expand All @@ -1506,9 +1509,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// Integers and floats are always Sized, and so have unit type metadata.
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,

ty::Projection(..)
// type parameters, opaques, and unnormalized projections have pointer
// metadata if they're known (e.g. by the param_env) to be sized
ty::Param(_) | ty::Projection(..) | ty::Opaque(..)
if tail.is_sized(selcx.tcx().at(obligation.cause.span), obligation.param_env) =>
{
true
}

// FIXME(compiler-errors): are Bound and Placeholder types ever known sized?
ty::Param(_)
| ty::Projection(..)
| ty::Opaque(..)
| ty::Param(..)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Infer(..)
Expand All @@ -1517,7 +1529,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
candidate_set.mark_ambiguous();
}
false
},
}
}
}
super::ImplSource::Param(..) => {
Expand Down Expand Up @@ -1727,7 +1739,7 @@ fn confirm_pointee_candidate<'cx, 'tcx>(
let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());

let mut obligations = vec![];
let metadata_ty = self_ty.ptr_metadata_ty(tcx, |ty| {
let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
normalize_with_depth_to(
selcx,
obligation.param_env,
Expand All @@ -1737,6 +1749,19 @@ fn confirm_pointee_candidate<'cx, 'tcx>(
&mut obligations,
)
});
if check_is_sized {
let sized_predicate = ty::Binder::dummy(ty::TraitRef::new(
tcx.require_lang_item(LangItem::Sized, None),
tcx.mk_substs_trait(self_ty, &[]),
))
.without_const()
.to_predicate(tcx);
obligations.push(Obligation::new(
obligation.cause.clone(),
obligation.param_env,
sized_predicate,
));
}

let substs = tcx.mk_substs([self_ty.into()].iter());
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
Expand Down
22 changes: 22 additions & 0 deletions src/test/ui/traits/pointee-tail-is-generic-errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// edition:2018

#![feature(ptr_metadata)]
#![feature(type_alias_impl_trait)]

type Opaque = impl std::fmt::Debug + ?Sized;

fn opaque() -> &'static Opaque {
&[1] as &[i32]
}

fn a<T: ?Sized>() {
is_thin::<T>();
//~^ ERROR type mismatch resolving `<T as Pointee>::Metadata == ()`

is_thin::<Opaque>();
//~^ ERROR type mismatch resolving `<impl Debug + ?Sized as Pointee>::Metadata == ()`
}

fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}

fn main() {}
40 changes: 40 additions & 0 deletions src/test/ui/traits/pointee-tail-is-generic-errors.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
error[E0271]: type mismatch resolving `<T as Pointee>::Metadata == ()`
--> $DIR/pointee-tail-is-generic-errors.rs:13:5
|
LL | is_thin::<T>();
| ^^^^^^^^^^^^ expected `()`, found associated type
|
= note: expected unit type `()`
found associated type `<T as Pointee>::Metadata`
= help: consider constraining the associated type `<T as Pointee>::Metadata` to `()`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: required by a bound in `is_thin`
--> $DIR/pointee-tail-is-generic-errors.rs:20:33
|
LL | fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}
| ^^^^^^^^^^^^^ required by this bound in `is_thin`

error[E0271]: type mismatch resolving `<impl Debug + ?Sized as Pointee>::Metadata == ()`
--> $DIR/pointee-tail-is-generic-errors.rs:16:5
|
LL | type Opaque = impl std::fmt::Debug + ?Sized;
| ----------------------------- the found opaque type
...
LL | is_thin::<Opaque>();
| ^^^^^^^^^^^^^^^^^ expected `()`, found associated type
|
= note: expected unit type `()`
found associated type `<impl Debug + ?Sized as Pointee>::Metadata`
note: required by a bound in `is_thin`
--> $DIR/pointee-tail-is-generic-errors.rs:20:33
|
LL | fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}
| ^^^^^^^^^^^^^ required by this bound in `is_thin`
help: consider constraining the associated type `<impl Debug + ?Sized as Pointee>::Metadata` to `()`
|
LL | type Opaque = impl std::fmt::Debug<Metadata = ()> + ?Sized;
| +++++++++++++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0271`.
29 changes: 29 additions & 0 deletions src/test/ui/traits/pointee-tail-is-generic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// check-pass
// edition:2018

#![feature(ptr_metadata)]
#![feature(type_alias_impl_trait)]

type Opaque = impl std::future::Future;

fn opaque() -> Opaque {
async {}
}

fn a<T>() {
// type parameter T is known to be sized
is_thin::<T>();
// tail of ADT (which is a type param) is known to be sized
is_thin::<std::cell::Cell<T>>();
// opaque type is known to be sized
is_thin::<Opaque>();
}

fn a2<T: Iterator>() {
// associated type is known to be sized
is_thin::<T::Item>();
}

fn is_thin<T: std::ptr::Pointee<Metadata = ()>>() {}

fn main() {}

0 comments on commit 774655d

Please sign in to comment.