Skip to content

Commit

Permalink
Better diagnostics when mismatched types due to implict static lifetime
Browse files Browse the repository at this point in the history
  • Loading branch information
jackh726 committed Jul 19, 2021
1 parent 77d1559 commit 3cd5ad5
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 24 deletions.
18 changes: 7 additions & 11 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1590,17 +1590,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
};
if let Some((expected, found)) = expected_found {
let expected_label = match exp_found {
Mismatch::Variable(ef) => ef.expected.prefix_string(self.tcx),
Mismatch::Fixed(s) => s.into(),
};
let found_label = match exp_found {
Mismatch::Variable(ef) => ef.found.prefix_string(self.tcx),
Mismatch::Fixed(s) => s.into(),
};
let exp_found = match exp_found {
Mismatch::Variable(exp_found) => Some(exp_found),
Mismatch::Fixed(_) => None,
let (expected_label, found_label, exp_found) = match exp_found {
Mismatch::Variable(ef) => (
ef.expected.prefix_string(self.tcx),
ef.found.prefix_string(self.tcx),
Some(ef),
),
Mismatch::Fixed(s) => (s.into(), s.into(), None),
};
match (&terr, expected == found) {
(TypeError::Sorts(values), extra) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
//! to hold.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::error_reporting::note_and_explain_region;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::ObligationCauseCode;
use rustc_errors::{Applicability, ErrorReported};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_middle::ty::{self, TypeVisitor};
use rustc_span::MultiSpan;

impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorReported> {
let error = self.error.as_ref()?;
debug!("try_report_mismatched_static_lifetime {:?}", error);

let (origin, sub, sup) = match error.clone() {
RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup),
_ => return None,
};
if *sub != ty::RegionKind::ReStatic {
return None;
}
let cause = match origin {
SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause,
_ => return None,
};
let (parent, impl_def_id) = match &cause.code {
ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id),
_ => return None,
};
let binding_span = match **parent {
ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span,
_ => return None,
};
let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
// FIXME: we should point at the lifetime
let mut multi_span: MultiSpan = vec![binding_span].into();
multi_span
.push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
err.span_note(multi_span, "because this has an unmet lifetime requirement");
note_and_explain_region(self.tcx(), &mut err, "...", sup, "...");
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
let ty = self.tcx().type_of(*impl_def_id);
let mut v = super::static_impl_trait::TraitObjectVisitor(vec![]);
v.visit_ty(ty);
let matching_def_ids = v.0;

let impl_self_ty = match impl_node {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }),
..
}) => self_ty,
_ => bug!("Node not an impl."),
};

for matching_def_id in matching_def_ids {
let mut hir_v =
super::static_impl_trait::HirTraitObjectVisitor(vec![], matching_def_id);
hir_v.visit_ty(&impl_self_ty);

let mut multi_span: MultiSpan = hir_v.0.clone().into();
for span in &hir_v.0 {
multi_span.push_span_label(
*span,
"this has an implicit `'static` lifetime requirement".to_string(),
);
err.span_suggestion_verbose(
span.shrink_to_hi(),
"consider relaxing the implicit `'static` requirement",
" + '_".to_string(),
Applicability::MaybeIncorrect,
);
}
err.span_note(multi_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
}
}
err.emit();
Some(ErrorReported)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_span::source_map::Span;

mod different_lifetimes;
pub mod find_anon_type;
mod mismatched_static_lifetime;
mod named_anon_conflict;
mod placeholder_error;
mod static_impl_trait;
Expand Down Expand Up @@ -58,6 +59,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
.or_else(|| self.try_report_impl_not_conforming_to_trait())
.or_else(|| self.try_report_anon_anon_conflict())
.or_else(|| self.try_report_static_impl_trait())
.or_else(|| self.try_report_mismatched_static_lifetime())
}

pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}
}
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin {
if let ObligationCauseCode::ItemObligation(item_def_id) = cause.code {
let code = match &cause.code {
ObligationCauseCode::MatchImpl(parent, ..) => &**parent,
_ => &cause.code,
};
if let ObligationCauseCode::ItemObligation(item_def_id) = *code {
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
// lifetime as above, but called using a fully-qualified path to the method:
// `Foo::qux(bar)`.
Expand Down Expand Up @@ -468,7 +472,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}

/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
struct TraitObjectVisitor(Vec<DefId>);
pub(super) struct TraitObjectVisitor(pub(super) Vec<DefId>);

impl TypeVisitor<'_> for TraitObjectVisitor {
fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<Self::BreakTy> {
Expand All @@ -485,7 +489,7 @@ impl TypeVisitor<'_> for TraitObjectVisitor {
}

/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
struct HirTraitObjectVisitor(Vec<Span>, DefId);
pub(super) struct HirTraitObjectVisitor(pub(super) Vec<Span>, pub(super) DefId);

impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
type Map = ErasedMap<'tcx>;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ pub enum ObligationCauseCode<'tcx> {
/// This is purely for diagnostic purposes - it is always
/// correct to use `MiscObligation` instead
WellFormed(Option<hir::HirId>),

/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
MatchImpl(Lrc<ObligationCauseCode<'tcx>>, DefId),
}

impl ObligationCauseCode<'_> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1903,7 +1903,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| ObligationCauseCode::UnifyReceiver(..)
| ObligationCauseCode::OpaqueType
| ObligationCauseCode::MiscObligation
| ObligationCauseCode::WellFormed(..) => {}
| ObligationCauseCode::WellFormed(..)
| ObligationCauseCode::MatchImpl(..) => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1903,9 +1903,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref);

let cause = ObligationCause::new(
obligation.cause.span,
obligation.cause.body_id,
ObligationCauseCode::MatchImpl(Lrc::new(obligation.cause.code.clone()), impl_def_id),
);

let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.at(&cause, obligation.param_env)
.eq(placeholder_obligation_trait_ref, impl_trait_ref)
.map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
nested_obligations.extend(obligations);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs

// check-fail

#![feature(generic_associated_types)]

pub trait A {}
impl A for &dyn A {}
impl A for Box<dyn A> {}

pub trait B {
type T<'a>: A;
}

impl B for () {
type T<'a> = Box<dyn A + 'a>; //~ incompatible lifetime on type
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error: incompatible lifetime on type
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:5
|
LL | type T<'a> = Box<dyn A + 'a>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: because this has an unmet lifetime requirement
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:12:17
|
LL | type T<'a>: A;
| ^ introduces a `'static` lifetime requirement
note: ...the lifetime `'a` as defined on the associated item at 16:12...
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:12
|
LL | type T<'a> = Box<dyn A + 'a>;
| ^^
note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:9:20
|
LL | impl A for Box<dyn A> {}
| ^ this has an implicit `'static` lifetime requirement
help: consider relaxing the implicit `'static` requirement
|
LL | impl A for Box<dyn A + '_> {}
| ^^^^

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ pub struct Ref<'a>(&'a u8);
impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here

extern "C" {
pub fn repro(_: Wrapper<Ref>); //~ ERROR: mismatched types
pub fn repro(_: Wrapper<Ref>); //~ ERROR: incompatible lifetime on type
}
15 changes: 8 additions & 7 deletions src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ error[E0726]: implicit elided lifetime not allowed here
LL | impl Trait for Ref {}
| ^^^- help: indicate the anonymous lifetime: `<'_>`

error[E0308]: mismatched types
error: incompatible lifetime on type
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21
|
LL | pub fn repro(_: Wrapper<Ref>);
| ^^^^^^^^^^^^ lifetime mismatch
| ^^^^^^^^^^^^
|
= note: expected trait `Trait`
found trait `Trait`
note: the anonymous lifetime #1 defined on the method body at 16:5...
note: because this has an unmet lifetime requirement
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:8:23
|
LL | pub struct Wrapper<T: Trait>(T);
| ^^^^^ introduces a `'static` lifetime requirement
note: ...the anonymous lifetime #1 defined on the method body at 16:5...
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5
|
LL | pub fn repro(_: Wrapper<Ref>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...does not necessarily outlive the static lifetime

error: aborting due to 2 previous errors

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

0 comments on commit 3cd5ad5

Please sign in to comment.