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

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

Merged
merged 3 commits into from
Mar 15, 2022
Merged
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
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 @@ -2244,12 +2244,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 @@ -2269,28 +2270,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 @@ -1399,6 +1399,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 @@ -1415,7 +1417,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Foreign(_)
| ty::Str
| ty::Array(..)
| ty::Slice(_)
Expand All @@ -1428,6 +1429,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 @@ -1436,9 +1439,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 @@ -1447,7 +1459,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
candidate_set.mark_ambiguous();
}
false
},
}
}
}
super::ImplSource::Param(..) => {
Expand Down Expand Up @@ -1657,7 +1669,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 @@ -1667,6 +1679,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>() {
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
// 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() {}