Skip to content

Commit 3cd5ad5

Browse files
committed
Better diagnostics when mismatched types due to implict static lifetime
1 parent 77d1559 commit 3cd5ad5

File tree

11 files changed

+168
-24
lines changed

11 files changed

+168
-24
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -1590,17 +1590,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15901590
}
15911591
};
15921592
if let Some((expected, found)) = expected_found {
1593-
let expected_label = match exp_found {
1594-
Mismatch::Variable(ef) => ef.expected.prefix_string(self.tcx),
1595-
Mismatch::Fixed(s) => s.into(),
1596-
};
1597-
let found_label = match exp_found {
1598-
Mismatch::Variable(ef) => ef.found.prefix_string(self.tcx),
1599-
Mismatch::Fixed(s) => s.into(),
1600-
};
1601-
let exp_found = match exp_found {
1602-
Mismatch::Variable(exp_found) => Some(exp_found),
1603-
Mismatch::Fixed(_) => None,
1593+
let (expected_label, found_label, exp_found) = match exp_found {
1594+
Mismatch::Variable(ef) => (
1595+
ef.expected.prefix_string(self.tcx),
1596+
ef.found.prefix_string(self.tcx),
1597+
Some(ef),
1598+
),
1599+
Mismatch::Fixed(s) => (s.into(), s.into(), None),
16041600
};
16051601
match (&terr, expected == found) {
16061602
(TypeError::Sorts(values), extra) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
2+
//! to hold.
3+
4+
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
5+
use crate::infer::error_reporting::note_and_explain_region;
6+
use crate::infer::lexical_region_resolve::RegionResolutionError;
7+
use crate::infer::{SubregionOrigin, TypeTrace};
8+
use crate::traits::ObligationCauseCode;
9+
use rustc_errors::{Applicability, ErrorReported};
10+
use rustc_hir as hir;
11+
use rustc_hir::intravisit::Visitor;
12+
use rustc_middle::ty::{self, TypeVisitor};
13+
use rustc_span::MultiSpan;
14+
15+
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
16+
pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorReported> {
17+
let error = self.error.as_ref()?;
18+
debug!("try_report_mismatched_static_lifetime {:?}", error);
19+
20+
let (origin, sub, sup) = match error.clone() {
21+
RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup),
22+
_ => return None,
23+
};
24+
if *sub != ty::RegionKind::ReStatic {
25+
return None;
26+
}
27+
let cause = match origin {
28+
SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause,
29+
_ => return None,
30+
};
31+
let (parent, impl_def_id) = match &cause.code {
32+
ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id),
33+
_ => return None,
34+
};
35+
let binding_span = match **parent {
36+
ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span,
37+
_ => return None,
38+
};
39+
let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
40+
// FIXME: we should point at the lifetime
41+
let mut multi_span: MultiSpan = vec![binding_span].into();
42+
multi_span
43+
.push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
44+
err.span_note(multi_span, "because this has an unmet lifetime requirement");
45+
note_and_explain_region(self.tcx(), &mut err, "...", sup, "...");
46+
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
47+
let ty = self.tcx().type_of(*impl_def_id);
48+
let mut v = super::static_impl_trait::TraitObjectVisitor(vec![]);
49+
v.visit_ty(ty);
50+
let matching_def_ids = v.0;
51+
52+
let impl_self_ty = match impl_node {
53+
hir::Node::Item(hir::Item {
54+
kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }),
55+
..
56+
}) => self_ty,
57+
_ => bug!("Node not an impl."),
58+
};
59+
60+
for matching_def_id in matching_def_ids {
61+
let mut hir_v =
62+
super::static_impl_trait::HirTraitObjectVisitor(vec![], matching_def_id);
63+
hir_v.visit_ty(&impl_self_ty);
64+
65+
let mut multi_span: MultiSpan = hir_v.0.clone().into();
66+
for span in &hir_v.0 {
67+
multi_span.push_span_label(
68+
*span,
69+
"this has an implicit `'static` lifetime requirement".to_string(),
70+
);
71+
err.span_suggestion_verbose(
72+
span.shrink_to_hi(),
73+
"consider relaxing the implicit `'static` requirement",
74+
" + '_".to_string(),
75+
Applicability::MaybeIncorrect,
76+
);
77+
}
78+
err.span_note(multi_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
79+
}
80+
}
81+
err.emit();
82+
Some(ErrorReported)
83+
}
84+
}

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_span::source_map::Span;
77

88
mod different_lifetimes;
99
pub mod find_anon_type;
10+
mod mismatched_static_lifetime;
1011
mod named_anon_conflict;
1112
mod placeholder_error;
1213
mod static_impl_trait;
@@ -58,6 +59,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
5859
.or_else(|| self.try_report_impl_not_conforming_to_trait())
5960
.or_else(|| self.try_report_anon_anon_conflict())
6061
.or_else(|| self.try_report_static_impl_trait())
62+
.or_else(|| self.try_report_mismatched_static_lifetime())
6163
}
6264

6365
pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
185185
}
186186
}
187187
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin {
188-
if let ObligationCauseCode::ItemObligation(item_def_id) = cause.code {
188+
let code = match &cause.code {
189+
ObligationCauseCode::MatchImpl(parent, ..) => &**parent,
190+
_ => &cause.code,
191+
};
192+
if let ObligationCauseCode::ItemObligation(item_def_id) = *code {
189193
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
190194
// lifetime as above, but called using a fully-qualified path to the method:
191195
// `Foo::qux(bar)`.
@@ -468,7 +472,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
468472
}
469473

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

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

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

490494
impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
491495
type Map = ErasedMap<'tcx>;

compiler/rustc_middle/src/traits/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ pub enum ObligationCauseCode<'tcx> {
333333
/// This is purely for diagnostic purposes - it is always
334334
/// correct to use `MiscObligation` instead
335335
WellFormed(Option<hir::HirId>),
336+
337+
/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
338+
MatchImpl(Lrc<ObligationCauseCode<'tcx>>, DefId),
336339
}
337340

338341
impl ObligationCauseCode<'_> {

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1903,7 +1903,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19031903
| ObligationCauseCode::UnifyReceiver(..)
19041904
| ObligationCauseCode::OpaqueType
19051905
| ObligationCauseCode::MiscObligation
1906-
| ObligationCauseCode::WellFormed(..) => {}
1906+
| ObligationCauseCode::WellFormed(..)
1907+
| ObligationCauseCode::MatchImpl(..) => {}
19071908
ObligationCauseCode::SliceOrArrayElem => {
19081909
err.note("slice and array elements must have `Sized` type");
19091910
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1903,9 +1903,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
19031903

19041904
debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref);
19051905

1906+
let cause = ObligationCause::new(
1907+
obligation.cause.span,
1908+
obligation.cause.body_id,
1909+
ObligationCauseCode::MatchImpl(Lrc::new(obligation.cause.code.clone()), impl_def_id),
1910+
);
1911+
19061912
let InferOk { obligations, .. } = self
19071913
.infcx
1908-
.at(&obligation.cause, obligation.param_env)
1914+
.at(&cause, obligation.param_env)
19091915
.eq(placeholder_obligation_trait_ref, impl_trait_ref)
19101916
.map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
19111917
nested_obligations.extend(obligations);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs
2+
3+
// check-fail
4+
5+
#![feature(generic_associated_types)]
6+
7+
pub trait A {}
8+
impl A for &dyn A {}
9+
impl A for Box<dyn A> {}
10+
11+
pub trait B {
12+
type T<'a>: A;
13+
}
14+
15+
impl B for () {
16+
type T<'a> = Box<dyn A + 'a>; //~ incompatible lifetime on type
17+
}
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: incompatible lifetime on type
2+
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:5
3+
|
4+
LL | type T<'a> = Box<dyn A + 'a>;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: because this has an unmet lifetime requirement
8+
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:12:17
9+
|
10+
LL | type T<'a>: A;
11+
| ^ introduces a `'static` lifetime requirement
12+
note: ...the lifetime `'a` as defined on the associated item at 16:12...
13+
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:12
14+
|
15+
LL | type T<'a> = Box<dyn A + 'a>;
16+
| ^^
17+
note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
18+
--> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:9:20
19+
|
20+
LL | impl A for Box<dyn A> {}
21+
| ^ this has an implicit `'static` lifetime requirement
22+
help: consider relaxing the implicit `'static` requirement
23+
|
24+
LL | impl A for Box<dyn A + '_> {}
25+
| ^^^^
26+
27+
error: aborting due to previous error
28+

src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ pub struct Ref<'a>(&'a u8);
1313
impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here
1414

1515
extern "C" {
16-
pub fn repro(_: Wrapper<Ref>); //~ ERROR: mismatched types
16+
pub fn repro(_: Wrapper<Ref>); //~ ERROR: incompatible lifetime on type
1717
}

src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@ error[E0726]: implicit elided lifetime not allowed here
44
LL | impl Trait for Ref {}
55
| ^^^- help: indicate the anonymous lifetime: `<'_>`
66

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

2224
error: aborting due to 2 previous errors
2325

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

0 commit comments

Comments
 (0)