From b3057f4d5fe7156aa85e1844d3638fa14ba48b79 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 28 Jun 2020 12:41:46 +0100 Subject: [PATCH] Check projections are well-formed when using projection candidates --- .../src/traits/select/candidate_assembly.rs | 6 +- .../src/traits/select/confirmation.rs | 34 ++++++++-- .../src/traits/select/mod.rs | 29 ++++----- .../rustc_typeck/src/check/compare_method.rs | 6 +- compiler/rustc_typeck/src/check/wfcheck.rs | 2 +- .../projection-bound-cycle-generic.rs | 62 ++++++++++++++++++ .../projection-bound-cycle-generic.stderr | 21 ++++++ .../projection-bound-cycle.rs | 64 +++++++++++++++++++ .../projection-bound-cycle.stderr | 21 ++++++ src/test/ui/specialization/issue-38091-2.rs | 23 +++++++ .../ui/specialization/issue-38091-2.stderr | 23 +++++++ .../{issues => specialization}/issue-38091.rs | 1 + .../issue-38091.stderr | 16 ++++- 13 files changed, 278 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle.rs create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle.stderr create mode 100644 src/test/ui/specialization/issue-38091-2.rs create mode 100644 src/test/ui/specialization/issue-38091-2.stderr rename src/test/ui/{issues => specialization}/issue-38091.rs (86%) rename src/test/ui/{issues => specialization}/issue-38091.stderr (55%) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 9cb5c232646..6c7732f28e4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -323,9 +323,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => return, } - let result = self - .infcx - .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); + let result = self.infcx.probe(|_| { + self.match_projection_obligation_against_definition_bounds(obligation).is_some() + }); if result { candidates.vec.push(ProjectionCandidate); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 88b656ce680..625bb5d7a44 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -69,8 +69,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ProjectionCandidate => { - self.confirm_projection_candidate(obligation); - Ok(ImplSource::Param(Vec::new())) + let obligations = self.confirm_projection_candidate(obligation); + Ok(ImplSource::Param(obligations)) } ClosureCandidate => { @@ -116,10 +116,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { + fn confirm_projection_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Vec> { self.infcx.commit_unconditionally(|_| { - let result = self.match_projection_obligation_against_definition_bounds(obligation); - assert!(result); + let candidate = self + .match_projection_obligation_against_definition_bounds(obligation) + .unwrap_or_else(|| bug!("Can't find selected projection candidate")); + let mut obligations = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation.predicate.to_poly_trait_ref(), candidate) + .map(|InferOk { obligations, .. }| obligations) + .unwrap_or_else(|_| { + bug!( + "Projection bound `{:?}` was applicable to `{:?}` but now is not", + candidate, + obligation + ); + }); + // Require that the projection is well-formed. + let self_ty = obligation.predicate.skip_binder().self_ty(); + obligations.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env, + ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()), + )); + obligations }) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9bebf8d90c2..2d6ccfe031e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1159,7 +1159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, - ) -> bool { + ) -> Option> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); let (placeholder_trait_predicate, _) = self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); @@ -1170,9 +1170,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let tcx = self.infcx.tcx; - let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { - ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs), - ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs), + let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { + ty::Projection(ref data) => (data.item_def_id, data.substs), + ty::Opaque(def_id, substs) => (def_id, substs), _ => { span_bug!( obligation.cause.span, @@ -1182,12 +1182,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } }; + let bounds = tcx.item_bounds(def_id).subst(tcx, substs); - let matching_bound = predicates.iter().find_map(|bound| { + let matching_bound = bounds.iter().find_map(|bound| { if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { let bound = ty::Binder::bind(pred.trait_ref); if self.infcx.probe(|_| { self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) + .is_ok() }) { return Some(bound); } @@ -1200,17 +1202,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { matching_bound={:?}", matching_bound ); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref); - - assert!(result); - true - } - } + matching_bound } fn match_projection( @@ -1218,12 +1210,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> bool { + ) -> Result>, ()> { debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); self.infcx .at(&obligation.cause, obligation.param_env) .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) } fn evaluate_where_clause<'o>( diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 3515fb12e4c..b059b03bc05 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1269,7 +1269,11 @@ pub fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); - fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]); + let implied_bounds = match impl_ty.container { + ty::TraitContainer(_) => vec![], + ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span), + }; + fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds); Ok(()) }) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index f31ba886d52..b4e950ab6e9 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1425,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec> { + pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec> { match self.tcx.impl_trait_ref(impl_def_id) { Some(ref trait_ref) => { // Trait impl: take implied bounds from all types that diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs new file mode 100644 index 00000000000..c88fb8ad96e --- /dev/null +++ b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs @@ -0,0 +1,62 @@ +// Like `projection-bound-cycle.rs` but this avoids using +// `feature(trivial_bounds)`. + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete + +trait Print { + fn print(); +} + +trait Foo { + type Item: Sized where ::Item: Sized; +} + +struct Number { t: T } + +impl Foo for Number { + // Well-formedness checks require that the following + // goal is true: + // ``` + // if ([T]: Sized) { # if the where clauses hold + // [T]: Sized # then the bound on the associated type hold + // } + // ``` + // which it is :) + type Item where [T]: Sized = [T]; +} + +struct OnlySized where T: Sized { f: T } +impl Print for OnlySized { + fn print() { + println!("{}", std::mem::size_of::()); + } +} + +trait Bar { + type Assoc: Print; +} + +impl Bar for T where T: Foo { + // This is not ok, we need to prove `wf(::Item)`, which requires + // knowing that `::Item: Sized` to satisfy the where clause. We + // can use the bound on `Foo::Item` for this, but that requires + // `wf(::Item)`, which is an invalid cycle. + type Assoc = OnlySized<::Item>; + //~^ ERROR overflow evaluating the requirement `::Item: std::marker::Sized` +} + +fn foo() { + T::print() // oops, in fact `T = OnlySized` which is ill-formed +} + +fn bar() { + // we have `FromEnv(T: Bar)` hence + // `::Assoc` is well-formed and + // `Implemented(::Assoc: Print)` hold + foo::<::Assoc>() +} + +fn main() { + bar::>() +} diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr new file mode 100644 index 00000000000..5967752ef79 --- /dev/null +++ b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr @@ -0,0 +1,21 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/projection-bound-cycle-generic.rs:4:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0275]: overflow evaluating the requirement `::Item: std::marker::Sized` + --> $DIR/projection-bound-cycle-generic.rs:45:5 + | +LL | struct OnlySized where T: Sized { f: T } + | - required by this bound in `OnlySized` +... +LL | type Assoc = OnlySized<::Item>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle.rs b/src/test/ui/generic-associated-types/projection-bound-cycle.rs new file mode 100644 index 00000000000..0728dc1bcbc --- /dev/null +++ b/src/test/ui/generic-associated-types/projection-bound-cycle.rs @@ -0,0 +1,64 @@ +// Test case from Chalk. +// Make sure that we make sure that we don't allow arbitrary bounds to be +// proven when a bound and a where clause of an associated type are the same. + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete +#![feature(trivial_bounds)] + +trait Print { + fn print(); +} + +trait Foo { + type Item: Sized where ::Item: Sized; +} + +struct Number { } + +impl Foo for Number { + // Well-formedness checks require that the following + // goal is true: + // ``` + // if (str: Sized) { # if the where clauses hold + // str: Sized # then the bound on the associated type hold + // } + // ``` + // which it is :) + type Item where str: Sized = str; +} + +struct OnlySized where T: Sized { f: T } +impl Print for OnlySized { + fn print() { + println!("{}", std::mem::size_of::()); + } +} + +trait Bar { + type Assoc: Print; +} + +impl Bar for T where T: Foo { + // This is not ok, we need to prove `wf(::Item)`, which requires + // knowing that `::Item: Sized` to satisfy the where clause. We + // can use the bound on `Foo::Item` for this, but that requires + // `wf(::Item)`, which is an invalid cycle. + type Assoc = OnlySized<::Item>; + //~^ ERROR overflow evaluating the requirement `::Item: std::marker::Sized` +} + +fn foo() { + T::print() // oops, in fact `T = OnlySized` which is ill-formed +} + +fn bar() { + // we have `FromEnv(T: Bar)` hence + // `::Assoc` is well-formed and + // `Implemented(::Assoc: Print)` hold + foo::<::Assoc>() +} + +fn main() { + bar::() +} diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle.stderr b/src/test/ui/generic-associated-types/projection-bound-cycle.stderr new file mode 100644 index 00000000000..80d102013a7 --- /dev/null +++ b/src/test/ui/generic-associated-types/projection-bound-cycle.stderr @@ -0,0 +1,21 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/projection-bound-cycle.rs:5:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0275]: overflow evaluating the requirement `::Item: std::marker::Sized` + --> $DIR/projection-bound-cycle.rs:47:5 + | +LL | struct OnlySized where T: Sized { f: T } + | - required by this bound in `OnlySized` +... +LL | type Assoc = OnlySized<::Item>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/specialization/issue-38091-2.rs b/src/test/ui/specialization/issue-38091-2.rs new file mode 100644 index 00000000000..da7f6737733 --- /dev/null +++ b/src/test/ui/specialization/issue-38091-2.rs @@ -0,0 +1,23 @@ +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait Iterate<'a> { + type Ty: Valid; + fn iterate(self); +} +impl<'a, T> Iterate<'a> for T where T: Check { + default type Ty = (); + default fn iterate(self) {} +} + +trait Check {} +impl<'a, T> Check for T where >::Ty: Valid {} + +trait Valid {} + +impl Valid for () {} + +fn main() { + Iterate::iterate(0); + //~^ ERROR overflow evaluating the requirement `{integer}: Check` +} diff --git a/src/test/ui/specialization/issue-38091-2.stderr b/src/test/ui/specialization/issue-38091-2.stderr new file mode 100644 index 00000000000..e776ff0fafb --- /dev/null +++ b/src/test/ui/specialization/issue-38091-2.stderr @@ -0,0 +1,23 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-38091-2.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error[E0275]: overflow evaluating the requirement `{integer}: Check` + --> $DIR/issue-38091-2.rs:21:5 + | +LL | fn iterate(self); + | ----------------- required by `Iterate::iterate` +... +LL | Iterate::iterate(0); + | ^^^^^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/issues/issue-38091.rs b/src/test/ui/specialization/issue-38091.rs similarity index 86% rename from src/test/ui/issues/issue-38091.rs rename to src/test/ui/specialization/issue-38091.rs index a84391b94d1..cc0eeb53687 100644 --- a/src/test/ui/issues/issue-38091.rs +++ b/src/test/ui/specialization/issue-38091.rs @@ -18,4 +18,5 @@ trait Valid {} fn main() { Iterate::iterate(0); + //~^ ERROR overflow evaluating the requirement `{integer}: Check` } diff --git a/src/test/ui/issues/issue-38091.stderr b/src/test/ui/specialization/issue-38091.stderr similarity index 55% rename from src/test/ui/issues/issue-38091.stderr rename to src/test/ui/specialization/issue-38091.stderr index f47fd8d1b0d..beec702b89e 100644 --- a/src/test/ui/issues/issue-38091.stderr +++ b/src/test/ui/specialization/issue-38091.stderr @@ -16,6 +16,18 @@ LL | type Ty: Valid; LL | default type Ty = (); | ^^^^^^^^^^^^^^^^^^^^^ the trait `Valid` is not implemented for `()` -error: aborting due to previous error; 1 warning emitted +error[E0275]: overflow evaluating the requirement `{integer}: Check` + --> $DIR/issue-38091.rs:20:5 + | +LL | fn iterate(self); + | ----------------- required by `Iterate::iterate` +... +LL | Iterate::iterate(0); + | ^^^^^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}` -For more information about this error, try `rustc --explain E0277`. +error: aborting due to 2 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0275, E0277. +For more information about an error, try `rustc --explain E0275`.