From 83a9dc92d58c8145b3fb0cec5ea205ad66c399a7 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 23 Jul 2020 01:34:35 -0700 Subject: [PATCH 1/3] Normalize bounds fully when checking defaulted types --- src/librustc_typeck/check/compare_method.rs | 36 +++++++++++++++++-- .../ui/associated-type-bounds/issue-73818.rs | 25 +++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/associated-type-bounds/issue-73818.rs diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index a90ed455d048b..fac87866b8049 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -1196,8 +1196,6 @@ fn compare_projection_bounds<'tcx>( return Ok(()); } - let param_env = tcx.param_env(impl_ty.def_id); - // Given // // impl Foo for (A, B) { @@ -1212,6 +1210,36 @@ fn compare_projection_bounds<'tcx>( impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs); let impl_ty_value = tcx.type_of(impl_ty.def_id); + let param_env = tcx.param_env(impl_ty.def_id); + + // When checking something like + // + // trait X { type Y: PartialEq<::Y> } + // impl X for T { default type Y = S; } + // + // We will have to prove the bound S: PartialEq<::Y>. In this case + // we want ::Y to normalize to S. This is valid because we are + // checking the default value specifically here. Add this equality to the + // ParamEnv for normalization specifically. + let normalize_param_env = if impl_ty.defaultness.is_final() { + // If the associated type is final then normalization can already + // do this without the explicit predicate. + param_env + } else { + let mut predicates = param_env.caller_bounds().iter().collect::>(); + predicates.push( + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + item_def_id: trait_ty.def_id, + substs: rebased_substs, + }, + ty: impl_ty_value, + }) + .to_predicate(tcx), + ); + ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing, None) + }; + // Map the predicate from the trait to the corresponding one for the impl. // For example: // @@ -1275,9 +1303,11 @@ fn compare_projection_bounds<'tcx>( _ => bug!("unexepected projection predicate kind: `{:?}`", predicate), }; + debug!("compare_projection_bounds: concrete predicate = {:?}", concrete_ty_predicate); + let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( &mut selcx, - param_env, + normalize_param_env, normalize_cause.clone(), &concrete_ty_predicate, ); diff --git a/src/test/ui/associated-type-bounds/issue-73818.rs b/src/test/ui/associated-type-bounds/issue-73818.rs new file mode 100644 index 0000000000000..bb890f72a32cf --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-73818.rs @@ -0,0 +1,25 @@ +// Test that associated type bounds are correctly normalized when checking +// default associated type values. +// check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +#[derive(PartialEq)] +enum Never {} +trait Foo { + type Assoc: PartialEq; // PartialEq<::Assoc> +} +impl Foo for T { + default type Assoc = Never; +} + +trait Trait1 { + type Selection: PartialEq; +} +trait Trait2: PartialEq {} +impl Trait1 for T { + default type Selection = T; +} + +fn main() {} From 31a3bb59ce42ae86e19c2e316d0809e8606c810f Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Sat, 25 Jul 2020 13:48:29 -0700 Subject: [PATCH 2/3] Remove manual normalization in compare_projection_bounds --- src/librustc_typeck/check/compare_method.rs | 53 ++----------------- .../unsatisfied-outlives-bound.rs | 2 +- .../unsatisfied-outlives-bound.stderr | 24 +++++++-- 3 files changed, 25 insertions(+), 54 deletions(-) diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index fac87866b8049..bf415406675bb 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -6,15 +6,14 @@ use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt, WithConstness}; +use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use super::{potentially_plural_count, FnCtxt, Inherited}; -use std::iter; /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -1240,22 +1239,6 @@ fn compare_projection_bounds<'tcx>( ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing, None) }; - // Map the predicate from the trait to the corresponding one for the impl. - // For example: - // - // trait X { type Y<'a>: PartialEq } impl X for T { type Y<'a> = &'a S; } - // impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; } - // - // For the `for<'a> <>::Y<'a>: PartialEq` bound, this - // function would translate and partially normalize - // `[>::Y<'a>, A]` to `[&'a u32, &'x u32]`. - let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| { - tcx.mk_substs( - iter::once(impl_ty_value.into()) - .chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, rebased_substs))), - ) - }; - tcx.infer_ctxt().enter(move |infcx| { let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); let infcx = &inh.infcx; @@ -1270,39 +1253,10 @@ fn compare_projection_bounds<'tcx>( ); let predicates = tcx.projection_predicates(trait_ty.def_id); - debug!("compare_projection_bounds: projection_predicates={:?}", predicates); for predicate in predicates { - let concrete_ty_predicate = match predicate.kind() { - ty::PredicateKind::Trait(poly_tr, c) => poly_tr - .map_bound(|tr| { - let trait_substs = translate_predicate_substs(tr.trait_ref.substs); - ty::TraitRef { def_id: tr.def_id(), substs: trait_substs } - }) - .with_constness(*c) - .to_predicate(tcx), - ty::PredicateKind::Projection(poly_projection) => poly_projection - .map_bound(|projection| { - let projection_substs = - translate_predicate_substs(projection.projection_ty.substs); - ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - substs: projection_substs, - item_def_id: projection.projection_ty.item_def_id, - }, - ty: projection.ty.subst(tcx, rebased_substs), - } - }) - .to_predicate(tcx), - ty::PredicateKind::TypeOutlives(poly_outlives) => poly_outlives - .map_bound(|outlives| { - ty::OutlivesPredicate(impl_ty_value, outlives.1.subst(tcx, rebased_substs)) - }) - .to_predicate(tcx), - _ => bug!("unexepected projection predicate kind: `{:?}`", predicate), - }; - + let concrete_ty_predicate = predicate.subst(tcx, rebased_substs); debug!("compare_projection_bounds: concrete predicate = {:?}", concrete_ty_predicate); let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( @@ -1311,7 +1265,6 @@ fn compare_projection_bounds<'tcx>( normalize_cause.clone(), &concrete_ty_predicate, ); - debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); inh.register_predicates(obligations); diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs index 7510c58d57489..4831a8c63e8d7 100644 --- a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs @@ -7,7 +7,7 @@ trait ATy { impl<'b> ATy for &'b () { type Item<'a> = &'b (); - //~^ ERROR does not fulfill the required lifetime + //~^ ERROR cannot infer an appropriate lifetime } trait StaticTy { diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr index 5d612284a2187..80f69becc2c91 100644 --- a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr @@ -1,14 +1,31 @@ -error[E0477]: the type `&'b ()` does not fulfill the required lifetime +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements --> $DIR/unsatisfied-outlives-bound.rs:9:5 | LL | type Item<'a> = &'b (); | ^^^^^^^^^^^^^^^^^^^^^^^ | -note: type must outlive the lifetime `'a` as defined on the associated item at 9:15 +note: first, the lifetime cannot outlive the lifetime `'b` as defined on the impl at 8:6... + --> $DIR/unsatisfied-outlives-bound.rs:8:6 + | +LL | impl<'b> ATy for &'b () { + | ^^ +note: ...so that the types are compatible + --> $DIR/unsatisfied-outlives-bound.rs:9:5 + | +LL | type Item<'a> = &'b (); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `ATy` + found `ATy` +note: but, the lifetime must be valid for the lifetime `'a` as defined on the associated item at 9:15... --> $DIR/unsatisfied-outlives-bound.rs:9:15 | LL | type Item<'a> = &'b (); | ^^ +note: ...so that the type `&()` will meet its required lifetime bounds + --> $DIR/unsatisfied-outlives-bound.rs:9:5 + | +LL | type Item<'a> = &'b (); + | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0477]: the type `&'a ()` does not fulfill the required lifetime --> $DIR/unsatisfied-outlives-bound.rs:18:5 @@ -20,4 +37,5 @@ LL | type Item<'a> = &'a (); error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0477`. +Some errors have detailed explanations: E0477, E0495. +For more information about an error, try `rustc --explain E0477`. From e35d2867f14a36094bdceadc731fdd1d26882fc9 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Sat, 25 Jul 2020 13:52:47 -0700 Subject: [PATCH 3/3] Fix diagnostic by using predicate in GATs too --- src/librustc_typeck/check/compare_method.rs | 6 +---- .../unsatisfied-outlives-bound.rs | 2 +- .../unsatisfied-outlives-bound.stderr | 24 +++---------------- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index bf415406675bb..b739e2fe1fbc4 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -1220,11 +1220,7 @@ fn compare_projection_bounds<'tcx>( // we want ::Y to normalize to S. This is valid because we are // checking the default value specifically here. Add this equality to the // ParamEnv for normalization specifically. - let normalize_param_env = if impl_ty.defaultness.is_final() { - // If the associated type is final then normalization can already - // do this without the explicit predicate. - param_env - } else { + let normalize_param_env = { let mut predicates = param_env.caller_bounds().iter().collect::>(); predicates.push( ty::Binder::dummy(ty::ProjectionPredicate { diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs index 4831a8c63e8d7..7510c58d57489 100644 --- a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs @@ -7,7 +7,7 @@ trait ATy { impl<'b> ATy for &'b () { type Item<'a> = &'b (); - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR does not fulfill the required lifetime } trait StaticTy { diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr index 80f69becc2c91..5d612284a2187 100644 --- a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr @@ -1,31 +1,14 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements +error[E0477]: the type `&'b ()` does not fulfill the required lifetime --> $DIR/unsatisfied-outlives-bound.rs:9:5 | LL | type Item<'a> = &'b (); | ^^^^^^^^^^^^^^^^^^^^^^^ | -note: first, the lifetime cannot outlive the lifetime `'b` as defined on the impl at 8:6... - --> $DIR/unsatisfied-outlives-bound.rs:8:6 - | -LL | impl<'b> ATy for &'b () { - | ^^ -note: ...so that the types are compatible - --> $DIR/unsatisfied-outlives-bound.rs:9:5 - | -LL | type Item<'a> = &'b (); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `ATy` - found `ATy` -note: but, the lifetime must be valid for the lifetime `'a` as defined on the associated item at 9:15... +note: type must outlive the lifetime `'a` as defined on the associated item at 9:15 --> $DIR/unsatisfied-outlives-bound.rs:9:15 | LL | type Item<'a> = &'b (); | ^^ -note: ...so that the type `&()` will meet its required lifetime bounds - --> $DIR/unsatisfied-outlives-bound.rs:9:5 - | -LL | type Item<'a> = &'b (); - | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0477]: the type `&'a ()` does not fulfill the required lifetime --> $DIR/unsatisfied-outlives-bound.rs:18:5 @@ -37,5 +20,4 @@ LL | type Item<'a> = &'a (); error: aborting due to 2 previous errors -Some errors have detailed explanations: E0477, E0495. -For more information about an error, try `rustc --explain E0477`. +For more information about this error, try `rustc --explain E0477`.