Skip to content

Point at where clauses where the associated item was restricted #65994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2225,11 +2225,14 @@ 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 {
ObligationCauseCode::AssocTypeBound(ref data) => {
err.span_label(data.original, "associated type defined here");
if let Some(sp) = data.impl_span {
err.span_label(sp, "in this `impl` item");
}
for sp in &data.bounds {
err.span_label(*sp, "restricted in this bound");
}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,14 @@ pub enum ObligationCauseCode<'tcx> {
/// #[feature(trivial_bounds)] is not enabled
TrivialBound,

AssocTypeBound(/*impl*/ Option<Span>, /*original*/ Span),
AssocTypeBound(Box<AssocTypeBoundData>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AssocTypeBoundData {
pub impl_span: Option<Span>,
pub original: Span,
pub bounds: Vec<Span>,
}

// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,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)),
super::AssocTypeBound(ref data) => Some(super::AssocTypeBound(data.clone())),
}
}
}
Expand Down
101 changes: 89 additions & 12 deletions src/librustc/ty/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use crate::hir;
use crate::hir::def_id::DefId;
use crate::infer::InferCtxt;
use crate::ty::subst::SubstsRef;
use crate::traits;
use crate::traits::{self, AssocTypeBoundData};
use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
use std::iter::once;
use syntax::symbol::{kw, Ident};
use syntax_pos::Span;
use crate::middle::lang_items;
use crate::mir::interpret::ConstValue;
Expand Down Expand Up @@ -176,6 +177,23 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
pred: &ty::Predicate<'_>,
trait_assoc_items: ty::AssocItemsIterator<'_>,
| {
let trait_item = tcx.hir().as_local_hir_id(trait_ref.def_id).and_then(|trait_id| {
tcx.hir().find(trait_id)
});
let (trait_name, trait_generics) = match trait_item {
Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::Trait(.., generics, _, _),
..
})) |
Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::TraitAlias(generics, _),
..
})) => (Some(ident), Some(generics)),
_ => (None, None),
};

let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
match pred {
ty::Predicate::Projection(proj) => {
Expand Down Expand Up @@ -226,10 +244,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
item.ident == trait_assoc_item.ident
}).next() {
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(
item_span,
trait_assoc_item.ident.span,
);
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds: vec![],
}));
}
}
}
Expand All @@ -251,14 +270,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// 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:
// If the obligation comes from the where clause in the `trait`, we 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 <Self as Foo>>::Assoc: Bar {
// | -------------------------- obligation set here
// | -------------------------- restricted in this bound
// LL | type Assoc;
// | ----- associated type defined here
// ...
Expand All @@ -278,11 +296,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
.next()
.map(|impl_item| (impl_item, trait_assoc_item)))
{
let bounds = trait_generics.map(|generics| get_generic_bound_spans(
&generics,
trait_name,
trait_assoc_item.ident,
)).unwrap_or_else(Vec::new);
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(
item_span,
trait_assoc_item.ident.span,
);
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds,
}));
}
}
}
Expand Down Expand Up @@ -666,3 +690,56 @@ pub fn object_region_bounds<'tcx>(

tcx.required_region_bounds(open_ty, predicates)
}

/// Find the span of a generic bound affecting an associated type.
fn get_generic_bound_spans(
generics: &hir::Generics,
trait_name: Option<&Ident>,
assoc_item_name: Ident,
) -> Vec<Span> {
let mut bounds = vec![];
for clause in generics.where_clause.predicates.iter() {
if let hir::WherePredicate::BoundPredicate(pred) = clause {
match &pred.bounded_ty.kind {
hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => {
let mut s = path.segments.iter();
if let (a, Some(b), None) = (s.next(), s.next(), s.next()) {
if a.map(|s| &s.ident) == trait_name
&& b.ident == assoc_item_name
&& is_self_path(&ty.kind)
{
// `<Self as Foo>::Bar`
bounds.push(pred.span);
}
}
}
hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => {
if segment.ident == assoc_item_name {
if is_self_path(&ty.kind) {
// `Self::Bar`
bounds.push(pred.span);
}
}
}
_ => {}
}
}
}
bounds
}

fn is_self_path(kind: &hir::TyKind) -> bool {
match kind {
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
let mut s = path.segments.iter();
if let (Some(segment), None) = (s.next(), s.next()) {
if segment.ident.name == kw::SelfUpper {
// `type(Self)`
return true;
}
}
}
_ => {}
}
false
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,20 @@ impl Foo for () {
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
}

trait Baz where Self::Assoc: Bar {
type Assoc;
}

impl Baz for () {
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
}

trait Bat where <Self as Bat>::Assoc: Bar {
type Assoc;
}

impl Bat for () {
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ LL | impl Foo for () {
LL | type Assoc = bool;
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`

error: aborting due to previous error
error[E0277]: the trait bound `bool: Bar` is not satisfied
--> $DIR/point-at-type-on-obligation-failure-2.rs:16:5
|
LL | trait Baz where Self::Assoc: Bar {
| ---------------- restricted in this bound
LL | type Assoc;
| ----- associated type defined here
...
LL | impl Baz for () {
| --------------- in this `impl` item
LL | type Assoc = bool;
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`

error[E0277]: the trait bound `bool: Bar` is not satisfied
--> $DIR/point-at-type-on-obligation-failure-2.rs:24:5
|
LL | trait Bat where <Self as Bat>::Assoc: Bar {
| ------------------------- restricted in this bound
LL | type Assoc;
| ----- associated type defined here
...
LL | impl Bat for () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the error would be clearer if this part came first, followed by the explanation of the origin of the requirement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is borne of a limitation we have today, we can't have subdiagnostics with multiple span labels, so it's either this or the more verbose "one subdiagnostic per span" output, which I try to avoid.

| --------------- in this `impl` item
LL | type Assoc = bool;
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.