From 58c6591cde1b8a54dbc1ad1b7ac64133835ec154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 10 Oct 2019 16:20:42 -0700 Subject: [PATCH 1/7] Point at associated type for some obligations --- src/librustc/traits/error_reporting.rs | 6 ++ src/librustc/traits/mod.rs | 2 + src/librustc/traits/structural_impls.rs | 1 + src/librustc/ty/mod.rs | 1 + src/librustc/ty/wf.rs | 64 +++++++++++++------ src/librustc_typeck/check/wfcheck.rs | 23 ++++--- .../ui/issues/issue-43784-associated-type.rs | 4 +- .../issues/issue-43784-associated-type.stderr | 17 +++-- src/test/ui/traits/cycle-cache-err-60010.rs | 4 +- .../ui/traits/cycle-cache-err-60010.stderr | 12 +++- 10 files changed, 93 insertions(+), 41 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index daa4a215a238a..7ab907701bf04 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -2199,6 +2199,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } } + ObligationCauseCode::AssocTypeBound(impl_span, orig) => { + err.span_label(orig, "associated type defined here"); + if let Some(sp) = impl_span { + err.span_label(sp, "in this `impl` item"); + } + } } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index d96330bf0a9b4..9640a8a29d1e4 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -268,6 +268,8 @@ pub enum ObligationCauseCode<'tcx> { /// #[feature(trivial_bounds)] is not enabled TrivialBound, + + AssocTypeBound(/*impl*/ Option, /*original*/ Span), } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index dab62a6bcb5b1..f81d4b3ca3049 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -544,6 +544,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { super::MethodReceiver => Some(super::MethodReceiver), super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), super::TrivialBound => Some(super::TrivialBound), + super::AssocTypeBound(impl_sp, sp) => Some(super::AssocTypeBound(impl_sp, sp)), } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index d377b7328e80b..d81f5e4070114 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -3139,6 +3139,7 @@ impl<'tcx> TyCtxt<'tcx> { } } +#[derive(Clone)] pub struct AssocItemsIterator<'tcx> { tcx: TyCtxt<'tcx>, def_ids: &'tcx [DefId], diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index ecb075e30b14d..ce92fd82761bb 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -22,11 +22,14 @@ pub fn obligations<'a, 'tcx>( ty: Ty<'tcx>, span: Span, ) -> Option>> { - let mut wf = WfPredicates { infcx, - param_env, - body_id, - span, - out: vec![] }; + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + item: None, + }; if wf.compute(ty) { debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); let result = wf.normalize(); @@ -47,8 +50,9 @@ pub fn trait_obligations<'a, 'tcx>( body_id: hir::HirId, trait_ref: &ty::TraitRef<'tcx>, span: Span, + item: Option<&'tcx hir::Item>, ) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] }; + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; wf.compute_trait_ref(trait_ref, Elaborate::All); wf.normalize() } @@ -60,7 +64,7 @@ pub fn predicate_obligations<'a, 'tcx>( predicate: &ty::Predicate<'tcx>, span: Span, ) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] }; + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; // (*) ok to skip binders, because wf code is prepared for it match *predicate { @@ -107,6 +111,7 @@ struct WfPredicates<'a, 'tcx> { body_id: hir::HirId, span: Span, out: Vec>, + item: Option<&'tcx hir::Item>, } /// Controls whether we "elaborate" supertraits and so forth on the WF @@ -157,33 +162,54 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - /// Pushes the obligations required for `trait_ref` to be WF into - /// `self.out`. + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); - + let assoc_items = self.infcx.tcx.associated_items(trait_ref.def_id); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; if let Elaborate::All = elaborate { let predicates = obligations.iter() - .map(|obligation| obligation.predicate.clone()) - .collect(); + .map(|obligation| obligation.predicate.clone()) + .collect(); let implied_obligations = traits::elaborate_predicates(self.infcx.tcx, predicates); + let item_span: Option = self.item.map(|i| i.span); + let item = &self.item; let implied_obligations = implied_obligations.map(|pred| { - traits::Obligation::new(cause.clone(), param_env, pred) + let mut cause = cause.clone(); + if let ty::Predicate::Trait(proj) = &pred { + if let ( + ty::Projection(ty::ProjectionTy { item_def_id, .. }), + Some(hir::ItemKind::Impl(.., bounds)), + ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) { + if let Some((bound, assoc_item)) = assoc_items.clone() + .filter(|i| i.def_id == *item_def_id) + .next() + .and_then(|assoc_item| bounds.iter() + .filter(|b| b.ident == assoc_item.ident) + .next() + .map(|bound| (bound, assoc_item))) + { + cause.span = bound.span; + cause.code = traits::AssocTypeBound(item_span, assoc_item.ident.span); + } + } + } + traits::Obligation::new(cause, param_env, pred) }); self.out.extend(implied_obligations); } self.out.extend(obligations); - self.out.extend( - trait_ref.substs.types() - .filter(|ty| !ty.has_escaping_bound_vars()) - .map(|ty| traits::Obligation::new(cause.clone(), - param_env, - ty::Predicate::WellFormed(ty)))); + self.out.extend(trait_ref.substs.types() + .filter(|ty| !ty.has_escaping_bound_vars()) + .map(|ty| traits::Obligation::new( + cause.clone(), + param_env, + ty::Predicate::WellFormed(ty), + ))); } /// Pushes the obligations required for `trait_ref::Item` to be WF diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 18b103960c745..b4b8d4566d577 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -430,7 +430,7 @@ fn check_item_type( fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, - item: &hir::Item, + item: &'tcx hir::Item, ast_self_ty: &hir::Ty, ast_trait_ref: &Option, ) { @@ -445,15 +445,18 @@ fn check_impl<'tcx>( // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). let trait_ref = fcx.tcx.impl_trait_ref(item_def_id).unwrap(); - let trait_ref = - fcx.normalize_associated_types_in( - ast_trait_ref.path.span, &trait_ref); - let obligations = - ty::wf::trait_obligations(fcx, - fcx.param_env, - fcx.body_id, - &trait_ref, - ast_trait_ref.path.span); + let trait_ref = fcx.normalize_associated_types_in( + ast_trait_ref.path.span, + &trait_ref, + ); + let obligations = ty::wf::trait_obligations( + fcx, + fcx.param_env, + fcx.body_id, + &trait_ref, + ast_trait_ref.path.span, + Some(item), + ); for obligation in obligations { fcx.register_predicate(obligation); } diff --git a/src/test/ui/issues/issue-43784-associated-type.rs b/src/test/ui/issues/issue-43784-associated-type.rs index fb58ad6600f7e..92083d88f1b82 100644 --- a/src/test/ui/issues/issue-43784-associated-type.rs +++ b/src/test/ui/issues/issue-43784-associated-type.rs @@ -10,8 +10,8 @@ impl Partial for T::Assoc where { } -impl Complete for T { //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied - type Assoc = T; +impl Complete for T { + type Assoc = T; //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied } fn main() {} diff --git a/src/test/ui/issues/issue-43784-associated-type.stderr b/src/test/ui/issues/issue-43784-associated-type.stderr index e91e53499ce6c..cfab66302cdca 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -1,10 +1,17 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied - --> $DIR/issue-43784-associated-type.rs:13:9 + --> $DIR/issue-43784-associated-type.rs:14:5 | -LL | impl Complete for T { - | - ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` - | | - | help: consider restricting this bound: `T: std::marker::Copy` +LL | type Assoc: Partial; + | ----- associated type defined here +... +LL | / impl Complete for T { + | | - help: consider restricting this bound: `T: std::marker::Copy` +LL | | type Assoc = T; + | | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` +LL | | } + | |_- in this `impl` item + | + = help: consider adding a `where T: std::marker::Copy` bound error: aborting due to previous error diff --git a/src/test/ui/traits/cycle-cache-err-60010.rs b/src/test/ui/traits/cycle-cache-err-60010.rs index 45aa1b3c52239..cbddef082be67 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.rs +++ b/src/test/ui/traits/cycle-cache-err-60010.rs @@ -27,8 +27,8 @@ struct SalsaStorage { _parse: >::Data, //~ ERROR overflow } -impl Database for RootDatabase { //~ ERROR overflow - type Storage = SalsaStorage; +impl Database for RootDatabase { + type Storage = SalsaStorage; //~ ERROR overflow } impl HasQueryGroup for RootDatabase {} impl Query for ParseQuery diff --git a/src/test/ui/traits/cycle-cache-err-60010.stderr b/src/test/ui/traits/cycle-cache-err-60010.stderr index 9192f7ba2e3b0..a7b8dd05527a5 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -7,10 +7,16 @@ LL | _parse: >::Data, = note: required because of the requirements on the impl of `Query` for `ParseQuery` error[E0275]: overflow evaluating the requirement `RootDatabase: SourceDatabase` - --> $DIR/cycle-cache-err-60010.rs:30:6 + --> $DIR/cycle-cache-err-60010.rs:31:5 | -LL | impl Database for RootDatabase { - | ^^^^^^^^ +LL | type Storage; + | ------- associated type defined here +... +LL | / impl Database for RootDatabase { +LL | | type Storage = SalsaStorage; + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | | } + | |_- in this `impl` item | = note: required because of the requirements on the impl of `Query` for `ParseQuery` = note: required because it appears within the type `SalsaStorage` From 88e4e2a2089992ec6e33e29d66986f7574412dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 10 Oct 2019 19:20:51 -0700 Subject: [PATCH 2/7] fix compile-fail test --- src/test/compile-fail/chalkify/impl_wf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/compile-fail/chalkify/impl_wf.rs b/src/test/compile-fail/chalkify/impl_wf.rs index 96b1b2533712b..6bb4cf86e7986 100644 --- a/src/test/compile-fail/chalkify/impl_wf.rs +++ b/src/test/compile-fail/chalkify/impl_wf.rs @@ -25,6 +25,7 @@ impl Bar for Option { impl Bar for f32 { //~^ ERROR the trait bound `f32: Foo` is not satisfied type Item = f32; + //~^ ERROR the trait bound `f32: Foo` is not satisfied } trait Baz where U: Foo { } From 5b58095f68e971b952310b5a402a287f252936d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 10 Oct 2019 19:25:34 -0700 Subject: [PATCH 3/7] Handle projection obligation errors --- src/librustc/ty/wf.rs | 43 ++++++++++++++----- .../point-at-type-on-obligation-failure-2.rs | 11 +++++ ...int-at-type-on-obligation-failure-2.stderr | 15 +++++++ .../point-at-type-on-obligation-failure.rs | 20 +++++++++ ...point-at-type-on-obligation-failure.stderr | 19 ++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs create mode 100644 src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr create mode 100644 src/test/ui/associated-types/point-at-type-on-obligation-failure.rs create mode 100644 src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index ce92fd82761bb..f9abd31850527 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -164,37 +164,60 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { + let tcx = self.infcx.tcx; let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); - let assoc_items = self.infcx.tcx.associated_items(trait_ref.def_id); + let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; if let Elaborate::All = elaborate { + let trait_assoc_items = tcx.associated_items(trait_ref.def_id); + let predicates = obligations.iter() .map(|obligation| obligation.predicate.clone()) .collect(); - let implied_obligations = traits::elaborate_predicates(self.infcx.tcx, predicates); + let implied_obligations = traits::elaborate_predicates(tcx, predicates); let item_span: Option = self.item.map(|i| i.span); let item = &self.item; let implied_obligations = implied_obligations.map(|pred| { let mut cause = cause.clone(); - if let ty::Predicate::Trait(proj) = &pred { + match &pred { + ty::Predicate::Projection(proj) => { + if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) { + let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); + if let Some(impl_item) = impl_items.iter().filter(|item| { + item.ident == trait_assoc_item.ident + }).next() { + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); + } + } + } + ty::Predicate::Trait(proj) => { if let ( ty::Projection(ty::ProjectionTy { item_def_id, .. }), - Some(hir::ItemKind::Impl(.., bounds)), + Some(hir::ItemKind::Impl(.., impl_items)), ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) { - if let Some((bound, assoc_item)) = assoc_items.clone() + if let Some((impl_item, trait_assoc_item)) = trait_assoc_items.clone() .filter(|i| i.def_id == *item_def_id) .next() - .and_then(|assoc_item| bounds.iter() - .filter(|b| b.ident == assoc_item.ident) + .and_then(|trait_assoc_item| impl_items.iter() + .filter(|i| i.ident == trait_assoc_item.ident) .next() - .map(|bound| (bound, assoc_item))) + .map(|impl_item| (impl_item, trait_assoc_item))) { - cause.span = bound.span; - cause.code = traits::AssocTypeBound(item_span, assoc_item.ident.span); + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); + } } } + _ => {} } traits::Obligation::new(cause, param_env, pred) }); diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs new file mode 100644 index 0000000000000..9360d96f05e17 --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs @@ -0,0 +1,11 @@ +trait Bar {} + +trait Foo { + type Assoc: Bar; +} + +impl Foo for () { + type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied +} + +fn main() {} diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr new file mode 100644 index 0000000000000..62bb9388633b0 --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `bool: Bar` is not satisfied + --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + | +LL | type Assoc: Bar; + | ----- associated type defined here +... +LL | / impl Foo for () { +LL | | type Assoc = bool; + | | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` +LL | | } + | |_- in this `impl` item + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure.rs b/src/test/ui/associated-types/point-at-type-on-obligation-failure.rs new file mode 100644 index 0000000000000..dc43dbaf54b99 --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure.rs @@ -0,0 +1,20 @@ +trait Bar { + type Ok; + type Sibling: Bar2; +} +trait Bar2 { + type Ok; +} + +struct Foo; +struct Foo2; + +impl Bar for Foo { + type Ok = (); //~ ERROR type mismatch resolving `::Ok == ()` + type Sibling = Foo2; +} +impl Bar2 for Foo2 { + type Ok = u32; +} + +fn main() {} diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr new file mode 100644 index 0000000000000..95e8ebdd180b0 --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr @@ -0,0 +1,19 @@ +error[E0271]: type mismatch resolving `::Ok == ()` + --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + | +LL | type Ok; + | -- associated type defined here +... +LL | / impl Bar for Foo { +LL | | type Ok = (); + | | ^^^^^^^^^^^^^ expected u32, found () +LL | | type Sibling = Foo2; +LL | | } + | |_- in this `impl` item + | + = note: expected type `u32` + found type `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. From 0118278cdea7f07c2a9f7d75506a48cf554979e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 10 Oct 2019 19:33:05 -0700 Subject: [PATCH 4/7] Drive-by formatting --- src/librustc/traits/error_reporting.rs | 46 +++++++++++++++++--------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 7ab907701bf04..c6bb45172a973 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -195,8 +195,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, error: &MismatchedProjectionTypes<'tcx>, ) { - let predicate = - self.resolve_vars_if_possible(&obligation.predicate); + let predicate = self.resolve_vars_if_possible(&obligation.predicate); if predicate.references_error() { return @@ -228,7 +227,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &mut obligations ); if let Err(error) = self.at(&obligation.cause, obligation.param_env) - .eq(normalized_ty, data.ty) { + .eq(normalized_ty, data.ty) + { values = Some(infer::ValuePairs::Types(ExpectedFound { expected: normalized_ty, found: data.ty, @@ -239,13 +239,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } let msg = format!("type mismatch resolving `{}`", predicate); - let error_id = (DiagnosticMessageId::ErrorId(271), - Some(obligation.cause.span), msg); + let error_id = ( + DiagnosticMessageId::ErrorId(271), + Some(obligation.cause.span), + msg, + ); let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); if fresh { let mut diag = struct_span_err!( - self.tcx.sess, obligation.cause.span, E0271, - "type mismatch resolving `{}`", predicate + self.tcx.sess, + obligation.cause.span, + E0271, + "type mismatch resolving `{}`", + predicate ); self.note_type_err(&mut diag, &obligation.cause, None, values, err); self.note_obligation_cause(&mut diag, obligation); @@ -532,23 +538,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - pub fn report_overflow_error(&self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool) -> ! + pub fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! where T: fmt::Display + TypeFoldable<'tcx> { let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let mut err = struct_span_err!(self.tcx.sess, obligation.cause.span, E0275, - "overflow evaluating the requirement `{}`", - predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + predicate + ); if suggest_increasing_limit { self.suggest_new_overflow_limit(&mut err); } - self.note_obligation_cause_code(&mut err, &obligation.predicate, &obligation.cause.code, - &mut vec![]); + self.note_obligation_cause_code( + &mut err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); err.emit(); self.tcx.sess.abort_if_errors(); From 669a4035eff7e3cc6b295a68ce43cc342ec29ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 22 Oct 2019 12:40:46 -0700 Subject: [PATCH 5/7] review comments: move code, fix indentation and change span --- src/librustc/ty/wf.rs | 90 +++++++++++-------- ...int-at-type-on-obligation-failure-2.stderr | 13 ++- ...point-at-type-on-obligation-failure.stderr | 14 ++- .../issues/issue-43784-associated-type.stderr | 16 ++-- .../ui/traits/cycle-cache-err-60010.stderr | 13 ++- 5 files changed, 77 insertions(+), 69 deletions(-) diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index f9abd31850527..84bcec6bd199f 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -170,55 +170,67 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; - if let Elaborate::All = elaborate { - let trait_assoc_items = tcx.associated_items(trait_ref.def_id); - - let predicates = obligations.iter() - .map(|obligation| obligation.predicate.clone()) - .collect(); - let implied_obligations = traits::elaborate_predicates(tcx, predicates); - let item_span: Option = self.item.map(|i| i.span); - let item = &self.item; - let implied_obligations = implied_obligations.map(|pred| { - let mut cause = cause.clone(); - match &pred { - ty::Predicate::Projection(proj) => { - if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) { - let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); - if let Some(impl_item) = impl_items.iter().filter(|item| { - item.ident == trait_assoc_item.ident - }).next() { - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound( - item_span, - trait_assoc_item.ident.span, - ); - } + let item = &self.item; + let extend_cause_with_original_assoc_item_obligation = | + cause: &mut traits::ObligationCause<'_>, + pred: &ty::Predicate<'_>, + trait_assoc_items: ty::AssocItemsIterator<'_>, + | { + let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); + match pred { + ty::Predicate::Projection(proj) => { + if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) { + let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); + if let Some(impl_item) = impl_items.iter().filter(|item| { + item.ident == trait_assoc_item.ident + }).next() { + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); } } - ty::Predicate::Trait(proj) => { + } + ty::Predicate::Trait(proj) => { if let ( ty::Projection(ty::ProjectionTy { item_def_id, .. }), - Some(hir::ItemKind::Impl(.., impl_items)), - ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) { - if let Some((impl_item, trait_assoc_item)) = trait_assoc_items.clone() + Some(hir::ItemKind::Impl(.., impl_items)), + ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) { + if let Some((impl_item, trait_assoc_item)) = trait_assoc_items .filter(|i| i.def_id == *item_def_id) .next() - .and_then(|trait_assoc_item| impl_items.iter() - .filter(|i| i.ident == trait_assoc_item.ident) - .next() - .map(|impl_item| (impl_item, trait_assoc_item))) + .and_then(|trait_assoc_item| impl_items.iter() + .filter(|i| i.ident == trait_assoc_item.ident) + .next() + .map(|impl_item| (impl_item, trait_assoc_item))) { - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound( - item_span, - trait_assoc_item.ident.span, - ); - } + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); } } - _ => {} } + _ => {} + } + }; + + if let Elaborate::All = elaborate { + let trait_assoc_items = tcx.associated_items(trait_ref.def_id); + + let predicates = obligations.iter() + .map(|obligation| obligation.predicate.clone()) + .collect(); + let implied_obligations = traits::elaborate_predicates(tcx, predicates); + let implied_obligations = implied_obligations.map(|pred| { + let mut cause = cause.clone(); + extend_cause_with_original_assoc_item_obligation( + &mut cause, + &pred, + trait_assoc_items.clone(), + ); traits::Obligation::new(cause, param_env, pred) }); self.out.extend(implied_obligations); diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr index 62bb9388633b0..f1a2e343a7ec3 100644 --- a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr @@ -1,14 +1,13 @@ error[E0277]: the trait bound `bool: Bar` is not satisfied --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 | -LL | type Assoc: Bar; - | ----- associated type defined here +LL | type Assoc: Bar; + | ----- associated type defined here ... -LL | / impl Foo for () { -LL | | type Assoc = bool; - | | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` -LL | | } - | |_- in this `impl` item +LL | impl Foo for () { + | --------------- in this `impl` item +LL | type Assoc = bool; + | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` error: aborting due to previous error diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr index 95e8ebdd180b0..e4bd39c8ba62c 100644 --- a/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr @@ -1,15 +1,13 @@ error[E0271]: type mismatch resolving `::Ok == ()` --> $DIR/point-at-type-on-obligation-failure.rs:13:5 | -LL | type Ok; - | -- associated type defined here +LL | type Ok; + | -- associated type defined here ... -LL | / impl Bar for Foo { -LL | | type Ok = (); - | | ^^^^^^^^^^^^^ expected u32, found () -LL | | type Sibling = Foo2; -LL | | } - | |_- in this `impl` item +LL | impl Bar for Foo { + | ---------------- in this `impl` item +LL | type Ok = (); + | ^^^^^^^^^^^^^ expected u32, found () | = note: expected type `u32` found type `()` diff --git a/src/test/ui/issues/issue-43784-associated-type.stderr b/src/test/ui/issues/issue-43784-associated-type.stderr index cfab66302cdca..33692bd254e76 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -1,15 +1,15 @@ error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied --> $DIR/issue-43784-associated-type.rs:14:5 | -LL | type Assoc: Partial; - | ----- associated type defined here +LL | type Assoc: Partial; + | ----- associated type defined here ... -LL | / impl Complete for T { - | | - help: consider restricting this bound: `T: std::marker::Copy` -LL | | type Assoc = T; - | | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` -LL | | } - | |_- in this `impl` item +LL | impl Complete for T { + | ---------------------- in this `impl` item + | | + | help: consider restricting this bound: `T: std::marker::Copy` +LL | type Assoc = T; + | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | = help: consider adding a `where T: std::marker::Copy` bound diff --git a/src/test/ui/traits/cycle-cache-err-60010.stderr b/src/test/ui/traits/cycle-cache-err-60010.stderr index a7b8dd05527a5..f439de8826117 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -9,14 +9,13 @@ LL | _parse: >::Data, error[E0275]: overflow evaluating the requirement `RootDatabase: SourceDatabase` --> $DIR/cycle-cache-err-60010.rs:31:5 | -LL | type Storage; - | ------- associated type defined here +LL | type Storage; + | ------- associated type defined here ... -LL | / impl Database for RootDatabase { -LL | | type Storage = SalsaStorage; - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | | } - | |_- in this `impl` item +LL | impl Database for RootDatabase { + | ------------------------------ in this `impl` item +LL | type Storage = SalsaStorage; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: required because of the requirements on the impl of `Query` for `ParseQuery` = note: required because it appears within the type `SalsaStorage` From 580a93e2b6bd1817c81a248b469b01476f0ee328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 22 Oct 2019 13:23:55 -0700 Subject: [PATCH 6/7] Fix rebase --- .../issues/issue-43784-associated-type.stderr | 9 ++++----- .../missing-assoc-type-bound-restriction.rs | 2 +- ...missing-assoc-type-bound-restriction.stderr | 18 ++++++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/test/ui/issues/issue-43784-associated-type.stderr b/src/test/ui/issues/issue-43784-associated-type.stderr index 33692bd254e76..393b012f5f8a7 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -5,13 +5,12 @@ LL | type Assoc: Partial; | ----- associated type defined here ... LL | impl Complete for T { - | ---------------------- in this `impl` item - | | - | help: consider restricting this bound: `T: std::marker::Copy` + | ---------------------- + | | | + | | help: consider restricting this bound: `T: std::marker::Copy` + | in this `impl` item LL | type Assoc = T; | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` - | - = help: consider adding a `where T: std::marker::Copy` bound error: aborting due to previous error diff --git a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.rs b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.rs index 265ccb3125ca3..394512c579474 100644 --- a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.rs +++ b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.rs @@ -16,10 +16,10 @@ struct ParentWrapper(T); impl> Parent for ParentWrapper { //~^ ERROR the trait bound `::Assoc: Child` is not satisfied - //~| ERROR the trait bound `::Assoc: Child` is not satisfied type Ty = A; type Assoc = ChildWrapper; //~^ ERROR the trait bound `::Assoc: Child` is not satisfied + //~| ERROR the trait bound `::Assoc: Child` is not satisfied } fn main() {} diff --git a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr index bdea8ab97e5b5..c6f2e5cda66af 100644 --- a/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr +++ b/src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr @@ -9,25 +9,31 @@ LL | impl> Parent for ParentWrapper { | _| | | LL | | -LL | | LL | | type Ty = A; LL | | type Assoc = ChildWrapper; LL | | +LL | | LL | | } | |_^ the trait `Child` is not implemented for `::Assoc` error[E0277]: the trait bound `::Assoc: Child` is not satisfied - --> $DIR/missing-assoc-type-bound-restriction.rs:17:28 + --> $DIR/missing-assoc-type-bound-restriction.rs:20:5 | +LL | type Assoc: Child; + | ----- associated type defined here +... LL | impl> Parent for ParentWrapper { - | ^^^^^^ - help: consider further restricting the associated type: `where ::Assoc: Child` - | | - | the trait `Child` is not implemented for `::Assoc` + | ------------------------------------------------------- help: consider further restricting the associated type: `where ::Assoc: Child` + | | + | in this `impl` item +... +LL | type Assoc = ChildWrapper; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Child` is not implemented for `::Assoc` | = note: required because of the requirements on the impl of `Child` for `ChildWrapper<::Assoc>` error[E0277]: the trait bound `::Assoc: Child` is not satisfied - --> $DIR/missing-assoc-type-bound-restriction.rs:21:5 + --> $DIR/missing-assoc-type-bound-restriction.rs:20:5 | LL | trait Parent { | ------------ required by `Parent` From 3a4cacd45891db4a898606fc7b26d4b73009e5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 22 Oct 2019 14:02:15 -0700 Subject: [PATCH 7/7] Add comments --- src/librustc/ty/wf.rs | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 84bcec6bd199f..9f8e62a061422 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -179,6 +179,47 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); match pred { ty::Predicate::Projection(proj) => { + // The obligation comes not from the current `impl` nor the `trait` being + // implemented, but rather from a "second order" obligation, like in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`: + // + // error[E0271]: type mismatch resolving `::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected u32, found () + // | + // = note: expected type `u32` + // found type `()` + // + // FIXME: we would want to point a span to all places that contributed to this + // obligation. In the case above, it should be closer to: + // + // error[E0271]: type mismatch resolving `::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // LL | type Sibling: Bar2; + // | -------------------------------- obligation set here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected u32, found () + // ... + // LL | impl Bar2 for Foo2 { + // | ---------------- in this `impl` item + // LL | type Ok = u32; + // | -------------- obligation set here + // | + // = note: expected type `u32` + // found type `()` if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) { let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); if let Some(impl_item) = impl_items.iter().filter(|item| { @@ -193,6 +234,38 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } ty::Predicate::Trait(proj) => { + // An associated item obligation born out of the `trait` failed to be met. + // Point at the `impl` that failed the obligation, the associated item that + // needed to meet the obligation, and the definition of that associated item, + // which should hold the obligation in most cases. An example can be seen in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // LL | type Assoc: Bar; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + // + // FIXME: if the obligation comes from the where clause in the `trait`, we + // should point at it: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // | trait Foo where >::Assoc: Bar { + // | -------------------------- obligation set here + // LL | type Assoc; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` if let ( ty::Projection(ty::ProjectionTy { item_def_id, .. }), Some(hir::ItemKind::Impl(.., impl_items)),