Skip to content

Commit

Permalink
Detect stricter constraints on gats where clauses in impls vs trait
Browse files Browse the repository at this point in the history
  • Loading branch information
jackh726 committed Sep 7, 2021
1 parent fdf6505 commit af9de99
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{Subtype, ValuePairs};
use crate::infer::{SubregionOrigin, Subtype, ValuePairs};
use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
Expand All @@ -11,44 +11,52 @@ use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{MultiSpan, Span};
use rustc_span::{MultiSpan, Span, Symbol};

impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> {
if let Some(ref error) = self.error {
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
if let RegionResolutionError::SubSupConflict(
_,
var_origin,
sub_origin,
_sub,
sup_origin,
_sup,
) = error.clone()
{
if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) =
(&sup_origin, &sub_origin)
let error = self.error.as_ref()?;
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
if let RegionResolutionError::SubSupConflict(
_,
var_origin,
sub_origin,
_sub,
sup_origin,
_sup,
) = error.clone()
{
if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) {
if let (
ValuePairs::Types(sub_expected_found),
ValuePairs::Types(sup_expected_found),
CompareImplMethodObligation { trait_item_def_id, .. },
) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
{
if let (
ValuePairs::Types(sub_expected_found),
ValuePairs::Types(sup_expected_found),
CompareImplMethodObligation { trait_item_def_id, .. },
) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
{
if sup_expected_found == sub_expected_found {
self.emit_err(
var_origin.span(),
sub_expected_found.expected,
sub_expected_found.found,
*trait_item_def_id,
);
return Some(ErrorReported);
}
if sup_expected_found == sub_expected_found {
self.emit_err(
var_origin.span(),
sub_expected_found.expected,
sub_expected_found.found,
*trait_item_def_id,
);
return Some(ErrorReported);
}
}
}
}
if let RegionResolutionError::ConcreteFailure(origin, _, _) = error.clone() {
if let SubregionOrigin::CompareImplTypeObligation {
span,
item_name,
impl_item_def_id,
trait_item_def_id,
} = origin
{
self.emit_associated_type_err(span, item_name, impl_item_def_id, trait_item_def_id);
}
}
None
}

Expand Down Expand Up @@ -107,6 +115,25 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}
err.emit();
}

fn emit_associated_type_err(
&self,
span: Span,
item_name: Symbol,
impl_item_def_id: DefId,
trait_item_def_id: DefId,
) {
let impl_sp = self.tcx().def_span(impl_item_def_id);
let trait_sp = self.tcx().def_span(trait_item_def_id);
let mut err = self
.tcx()
.sess
.struct_span_err(span, &format!("`impl` associated type signature for `{}` doesn't match `trait` associated type signature", item_name));
err.span_label(impl_sp, &format!("found"));
err.span_label(trait_sp, &format!("expected"));

err.emit();
}
}

struct TypeParamSpanVisitor<'tcx> {
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_infer/src/infer/error_reporting/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
"...so that the definition in impl matches the definition from the trait",
);
}
infer::CompareImplTypeObligation { span, .. } => {
label_or_note(
span,
"...so that the definition in impl matches the definition from the trait",
);
}
}
}

Expand Down Expand Up @@ -356,6 +362,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
trait_item_def_id,
&format!("`{}: {}`", sup, sub),
),
infer::CompareImplTypeObligation {
span,
item_name,
impl_item_def_id,
trait_item_def_id,
} => self.report_extra_impl_obligation(
span,
item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}: {}`", sup, sub),
),
}
}

Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,15 @@ pub enum SubregionOrigin<'tcx> {
impl_item_def_id: DefId,
trait_item_def_id: DefId,
},

/// Comparing the signature and requirements of an impl associated type
/// against the containing trait
CompareImplTypeObligation {
span: Span,
item_name: Symbol,
impl_item_def_id: DefId,
trait_item_def_id: DefId,
},
}

// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
Expand Down Expand Up @@ -1810,6 +1819,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
ReferenceOutlivesReferent(_, a) => a,
CallReturn(a) => a,
CompareImplMethodObligation { span, .. } => span,
CompareImplTypeObligation { span, .. } => span,
}
}

Expand All @@ -1833,6 +1843,17 @@ impl<'tcx> SubregionOrigin<'tcx> {
trait_item_def_id,
},

traits::ObligationCauseCode::CompareImplTypeObligation {
item_name,
impl_item_def_id,
trait_item_def_id,
} => SubregionOrigin::CompareImplTypeObligation {
span: cause.span,
item_name,
impl_item_def_id,
trait_item_def_id,
},

_ => default(),
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/generic-associated-types/impl_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ impl<T> Foo for Fooy<T> {
type A<'a> where Self: 'static = (&'a ());
//~^ ERROR the parameter type `T` may not live long enough
type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
//~^ ERROR lifetime bound not satisfied
//~^ ERROR `impl` associated type
//~| ERROR impl has stricter
//~| ERROR lifetime bound not satisfied
type C where Self: Copy = String;
//~^ ERROR the trait bound `T: Copy` is not satisfied
Expand Down
35 changes: 18 additions & 17 deletions src/test/ui/generic-associated-types/impl_bounds.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,25 @@ LL | type A<'a> where Self: 'static = (&'a ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `T: 'static`...
= note: ...so that the type `Fooy<T>` will meet its required lifetime bounds
= note: ...so that the definition in impl matches the definition from the trait

error[E0478]: lifetime bound not satisfied
error: `impl` associated type signature for `B` doesn't match `trait` associated type signature
--> $DIR/impl_bounds.rs:17:5
|
LL | type B<'a, 'b> where 'a: 'b;
| ---------------------------- expected
...
LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the lifetime `'b` as defined on the associated item at 17:16
--> $DIR/impl_bounds.rs:17:16
|
LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
| ^^
note: but lifetime parameter must outlive the lifetime `'a` as defined on the associated item at 17:12
--> $DIR/impl_bounds.rs:17:12
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found

error[E0276]: impl has stricter requirements than trait
--> $DIR/impl_bounds.rs:17:5
|
LL | type B<'a, 'b> where 'a: 'b;
| ---------------------------- definition of `B` from trait
...
LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
| ^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'b: 'a`

error[E0478]: lifetime bound not satisfied
--> $DIR/impl_bounds.rs:17:5
Expand All @@ -42,7 +43,7 @@ LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
| ^^

error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/impl_bounds.rs:20:5
--> $DIR/impl_bounds.rs:21:5
|
LL | type C where Self: Copy = String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T`
Expand All @@ -67,7 +68,7 @@ LL | impl<T: std::marker::Copy> Foo for Fooy<T> {
| +++++++++++++++++++

error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/impl_bounds.rs:22:5
--> $DIR/impl_bounds.rs:23:5
|
LL | fn d() where Self: Copy {}
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T`
Expand All @@ -91,7 +92,7 @@ help: consider restricting type parameter `T`
LL | impl<T: std::marker::Copy> Foo for Fooy<T> {
| +++++++++++++++++++

error: aborting due to 5 previous errors
error: aborting due to 6 previous errors

Some errors have detailed explanations: E0277, E0310, E0478.
For more information about an error, try `rustc --explain E0277`.
Some errors have detailed explanations: E0276, E0277, E0310, E0478.
For more information about an error, try `rustc --explain E0276`.
4 changes: 2 additions & 2 deletions src/test/ui/generic-associated-types/issue-86787.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | | <Left as HasChildrenOf>::T: 'a,
LL | | <Right as HasChildrenOf>::T: 'a
| | - help: consider adding a where clause: `, <Left as HasChildrenOf>::T: 'a`
LL | | = Either<&'a Left::T, &'a Right::T>;
| |________________________________________^ ...so that the type `<Left as HasChildrenOf>::T` will meet its required lifetime bounds
| |________________________________________^ ...so that the definition in impl matches the definition from the trait

error[E0309]: the associated type `<Right as HasChildrenOf>::T` may not live long enough
--> $DIR/issue-86787.rs:23:5
Expand All @@ -22,7 +22,7 @@ LL | | <Left as HasChildrenOf>::T: 'a,
LL | | <Right as HasChildrenOf>::T: 'a
| | - help: consider adding a where clause: `, <Right as HasChildrenOf>::T: 'a`
LL | | = Either<&'a Left::T, &'a Right::T>;
| |________________________________________^ ...so that the type `<Right as HasChildrenOf>::T` will meet its required lifetime bounds
| |________________________________________^ ...so that the definition in impl matches the definition from the trait

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// check-fail

#![feature(generic_associated_types)]

trait Foo {
type Assoc<'a, 'b>;
}
impl Foo for () {
type Assoc<'a, 'b> where 'a: 'b = ();
//~^ `impl` associated type
//~| impl has stricter
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error: `impl` associated type signature for `Assoc` doesn't match `trait` associated type signature
--> $DIR/missing-where-clause-on-trait.rs:9:5
|
LL | type Assoc<'a, 'b>;
| ------------------- expected
...
LL | type Assoc<'a, 'b> where 'a: 'b = ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found

error[E0276]: impl has stricter requirements than trait
--> $DIR/missing-where-clause-on-trait.rs:9:5
|
LL | type Assoc<'a, 'b>;
| ------------------- definition of `Assoc` from trait
...
LL | type Assoc<'a, 'b> where 'a: 'b = ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'a: 'b`

error: aborting due to 2 previous errors

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

0 comments on commit af9de99

Please sign in to comment.