diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index daa4a215a238a..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(); @@ -2199,6 +2215,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..9f8e62a061422 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,162 @@ 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 tcx = self.infcx.tcx; let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; + 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) => { + // 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| { + 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) => { + // 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)), + ) = (&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))) + { + 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(self.infcx.tcx, predicates); + .map(|obligation| obligation.predicate.clone()) + .collect(); + let implied_obligations = traits::elaborate_predicates(tcx, predicates); let implied_obligations = implied_obligations.map(|pred| { - traits::Obligation::new(cause.clone(), param_env, 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); } 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/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 { } 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..f1a2e343a7ec3 --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr @@ -0,0 +1,14 @@ +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` + +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..e4bd39c8ba62c --- /dev/null +++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure.stderr @@ -0,0 +1,17 @@ +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 `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. 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..393b012f5f8a7 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -1,10 +1,16 @@ 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 | type Assoc: Partial; + | ----- associated type defined here +... LL | impl Complete for T { - | - ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` - | | - | 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` 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` 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..f439de8826117 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -7,10 +7,15 @@ 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 | type Storage; + | ------- associated type defined here +... 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`