diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a49189ae92a..e89f68fd0ea 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2252,12 +2252,13 @@ pub fn discriminant_ty(self, tcx: TyCtxt<'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 @@ -2277,28 +2278,30 @@ pub fn ptr_metadata_ty( | 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) } } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index ea48fab1ceb..11f0507d6fd 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -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, @@ -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(_) @@ -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(..) @@ -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(..) @@ -1517,7 +1529,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( candidate_set.mark_ambiguous(); } false - }, + } } } super::ImplSource::Param(..) => { @@ -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, @@ -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); diff --git a/src/test/ui/traits/pointee-tail-is-generic-errors.rs b/src/test/ui/traits/pointee-tail-is-generic-errors.rs new file mode 100644 index 00000000000..d081721aca2 --- /dev/null +++ b/src/test/ui/traits/pointee-tail-is-generic-errors.rs @@ -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() { + is_thin::(); + //~^ ERROR type mismatch resolving `::Metadata == ()` + + is_thin::(); + //~^ ERROR type mismatch resolving `::Metadata == ()` +} + +fn is_thin + ?Sized>() {} + +fn main() {} diff --git a/src/test/ui/traits/pointee-tail-is-generic-errors.stderr b/src/test/ui/traits/pointee-tail-is-generic-errors.stderr new file mode 100644 index 00000000000..fa5fe67e53c --- /dev/null +++ b/src/test/ui/traits/pointee-tail-is-generic-errors.stderr @@ -0,0 +1,40 @@ +error[E0271]: type mismatch resolving `::Metadata == ()` + --> $DIR/pointee-tail-is-generic-errors.rs:13:5 + | +LL | is_thin::(); + | ^^^^^^^^^^^^ expected `()`, found associated type + | + = note: expected unit type `()` + found associated type `::Metadata` + = help: consider constraining the associated type `::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 + ?Sized>() {} + | ^^^^^^^^^^^^^ required by this bound in `is_thin` + +error[E0271]: type mismatch resolving `::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::(); + | ^^^^^^^^^^^^^^^^^ expected `()`, found associated type + | + = note: expected unit type `()` + found associated type `::Metadata` +note: required by a bound in `is_thin` + --> $DIR/pointee-tail-is-generic-errors.rs:20:33 + | +LL | fn is_thin + ?Sized>() {} + | ^^^^^^^^^^^^^ required by this bound in `is_thin` +help: consider constraining the associated type `::Metadata` to `()` + | +LL | type Opaque = impl std::fmt::Debug + ?Sized; + | +++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/traits/pointee-tail-is-generic.rs b/src/test/ui/traits/pointee-tail-is-generic.rs new file mode 100644 index 00000000000..e0da0fc3861 --- /dev/null +++ b/src/test/ui/traits/pointee-tail-is-generic.rs @@ -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() { + // type parameter T is known to be sized + is_thin::(); + // tail of ADT (which is a type param) is known to be sized + is_thin::>(); + // opaque type is known to be sized + is_thin::(); +} + +fn a2() { + // associated type is known to be sized + is_thin::(); +} + +fn is_thin>() {} + +fn main() {}