diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 909cd2aa1551e..e9c98ad3034bf 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -695,6 +695,65 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { )); } } + + // We also check the well formedness of projections, to prevent things like #27675 + // + // ```rust + // trait Setup { + // type From: Copy; + // } + // + // fn copy(from: &U::From) -> U::From { + // *from + // } + // + // pub fn copy_any(t: &T) -> T { + // copy::>(t) + // } + // ``` + if let Some(data) = data.no_bound_vars() { + let tcx = self.infcx.tcx; + for pred in data { + let projection = match pred { + ty::ExistentialPredicate::Trait(_) | ty::ExistentialPredicate::AutoTrait(_) => { + continue; // Nothing to do here. + } + ty::ExistentialPredicate::Projection(proj) => proj, + }; + + let proj = projection.with_self_ty(tcx, tcx.types.self_param); + let self_ty = tcx.mk_projection(projection.item_def_id, proj.projection_ty.substs); + + let preds = tcx + .predicates_of(projection.item_def_id) + .instantiate(tcx, proj.projection_ty.substs); + for pred in preds.predicates.iter() { + match pred.skip_binders() { + ty::PredicateAtom::Trait(pred, ct) => { + // If we have `::AssocTy: Trait`, + // `projection.ty: Trait` must hold. + if pred.self_ty() == self_ty { + let trait_ref = ty::TraitRef::new( + pred.def_id(), + tcx.mk_substs_trait(projection.ty, &pred.trait_ref.substs[1..]), + ); + // FIXME: Use a better obligation cause here. + let cause = self.cause(traits::MiscObligation); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateAtom::Trait(ty::TraitPredicate { trait_ref }, ct) + .to_predicate(tcx), + )); + } + } + // FIXME: Do we have to do something for other predicates here, + // there is probably still subtle unsoundness here. + _ => (), + } + } + } + } } } diff --git a/src/test/ui/wf/existential-projections-wf-1.rs b/src/test/ui/wf/existential-projections-wf-1.rs new file mode 100644 index 0000000000000..25e70b4da2b19 --- /dev/null +++ b/src/test/ui/wf/existential-projections-wf-1.rs @@ -0,0 +1,18 @@ +// Taken from https://github.com/rust-lang/rust/issues/27675#issuecomment-696956878 +trait Setup { + type From: Copy; +} + +fn copy(from: &U::From) -> U::From { + *from +} + +pub fn copy_any(t: &T) -> T { + copy::>(t) + //~^ ERROR the trait bound `T: Copy` +} + +fn main() { + let st = String::from("Hello"); + copy_any(&st); +} diff --git a/src/test/ui/wf/existential-projections-wf-1.stderr b/src/test/ui/wf/existential-projections-wf-1.stderr new file mode 100644 index 0000000000000..5b3a385177724 --- /dev/null +++ b/src/test/ui/wf/existential-projections-wf-1.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/existential-projections-wf-1.rs:11:31 + | +LL | copy::>(t) + | ^ the trait `Copy` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | pub fn copy_any(t: &T) -> T { + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/wf/existential-projections-wf-2.rs b/src/test/ui/wf/existential-projections-wf-2.rs new file mode 100644 index 0000000000000..fe8022d487059 --- /dev/null +++ b/src/test/ui/wf/existential-projections-wf-2.rs @@ -0,0 +1,22 @@ +// Taken from https://github.com/rust-lang/rust/issues/27675#issuecomment-696956785 +trait Id: Sized { + fn id(self) -> T; +} +impl Id for T { + fn id(self) -> T { self } +} + +trait Setup { + type From: Id; +} + +fn transmute + ?Sized>(from: U::From) -> T { + Id::id(from) +} + +pub fn safe_transmute(t: T) -> U { + transmute::>(t) + //~^ ERROR the trait bound +} + +fn main() {} diff --git a/src/test/ui/wf/existential-projections-wf-2.stderr b/src/test/ui/wf/existential-projections-wf-2.stderr new file mode 100644 index 0000000000000..457eb20ac8b3c --- /dev/null +++ b/src/test/ui/wf/existential-projections-wf-2.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Id` is not satisfied + --> $DIR/existential-projections-wf-2.rs:18:42 + | +LL | transmute::>(t) + | ^ the trait `Id` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | pub fn safe_transmute, U>(t: T) -> U { + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.