Skip to content

Commit 65f492b

Browse files
committed
Account for returned dyn Trait evaluating to 'static lifetime
Provide a suggestion for `dyn Trait + '_` when possible.
1 parent a724d9a commit 65f492b

19 files changed

+239
-142
lines changed

src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
5555
diag.emit();
5656
ErrorReported
5757
})
58+
.or_else(|| self.try_report_impl_not_conforming_to_trait())
5859
.or_else(|| self.try_report_anon_anon_conflict())
5960
.or_else(|| self.try_report_static_impl_trait())
60-
.or_else(|| self.try_report_impl_not_conforming_to_trait())
6161
}
6262

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

src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
2121
// where the anonymous region appears (there must always be one; we
2222
// only introduced anonymous regions in parameters) as well as a
2323
// version new_ty of its type where the anonymous region is replaced
24-
// with the named one.//scope_def_id
25-
let (named, anon, anon_param_info, region_info) = if self.is_named_region(sub)
24+
// with the named one.
25+
let (named, anon, anon_param_info, region_info) = if sub.has_name()
2626
&& self.tcx().is_suitable_region(sup).is_some()
2727
&& self.find_param_with_region(sup, sub).is_some()
2828
{
@@ -32,7 +32,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
3232
self.find_param_with_region(sup, sub).unwrap(),
3333
self.tcx().is_suitable_region(sup).unwrap(),
3434
)
35-
} else if self.is_named_region(sup)
35+
} else if sup.has_name()
3636
&& self.tcx().is_suitable_region(sub).is_some()
3737
&& self.find_param_with_region(sub, sup).is_some()
3838
{
@@ -74,24 +74,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
7474
}
7575

7676
if let Some((_, fndecl)) = self.find_anon_type(anon, &br) {
77-
let return_type_anon = self.is_return_type_anon(scope_def_id, br, fndecl);
7877
let is_self_anon = self.is_self_anon(is_first, scope_def_id);
79-
debug!(
80-
"try_report_named_anon_conflict: fndecl {:?} {:?} {}",
81-
fndecl, return_type_anon, is_self_anon
82-
);
8378
if is_self_anon {
84-
// We used to check for `return_type_anon.is_some()` here. Removing that improves
85-
// some diagnostics, but we might have to readd the check if there are regressions
86-
// in the wild.
8779
return None;
8880
}
81+
8982
if let FnRetTy::Return(ty) = &fndecl.output {
83+
let mut v = ty::TraitObjectVisitor(vec![]);
84+
rustc_hir::intravisit::walk_ty(&mut v, ty);
85+
9086
debug!("try_report_named_anon_conflict: ret ty {:?}", ty);
91-
if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.kind, sub) {
87+
if sub == &ty::ReStatic && (matches!(ty.kind, TyKind::Def(_, _)) || v.0.len() == 1)
88+
{
9289
debug!("try_report_named_anon_conflict: impl Trait + 'static");
93-
// This is an impl Trait return that evaluates de need of 'static.
94-
// We handle this case better in `static_impl_trait`.
90+
// This is an `impl Trait` or `dyn Trait` return that evaluates de need of
91+
// `'static`. We handle this case better in `static_impl_trait`.
9592
return None;
9693
}
9794
}
@@ -123,17 +120,4 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
123120

124121
Some(diag)
125122
}
126-
127-
// This method returns whether the given Region is Named
128-
pub(super) fn is_named_region(&self, region: ty::Region<'tcx>) -> bool {
129-
match *region {
130-
ty::ReStatic => true,
131-
ty::ReFree(ref free_region) => match free_region.bound_region {
132-
ty::BrNamed(..) => true,
133-
_ => false,
134-
},
135-
ty::ReEarlyBound(ebr) => ebr.has_name(),
136-
_ => false,
137-
}
138-
}
139123
}

src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
2020
) = error.clone()
2121
{
2222
let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?;
23-
let return_ty = self.tcx().return_type_impl_trait(anon_reg_sup.def_id);
24-
if sub_r == &RegionKind::ReStatic && return_ty.is_some() {
23+
let (fn_return_span, is_dyn) =
24+
self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?;
25+
if sub_r == &RegionKind::ReStatic {
2526
let sp = var_origin.span();
2627
let return_sp = sub_origin.span();
2728
let mut err =
2829
self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime");
29-
err.span_label(
30-
return_sp,
31-
"this return type evaluates to the `'static` lifetime...",
32-
);
30+
err.span_label(return_sp, "this evaluates to the `'static` lifetime...");
3331
err.span_label(sup_origin.span(), "...but this borrow...");
3432

3533
let (lifetime, lt_sp_opt) = msg_span_from_free_region(self.tcx(), sup_r);
@@ -39,24 +37,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
3937

4038
let lifetime_name =
4139
if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
42-
let fn_return_span = return_ty.unwrap().1;
43-
if let Ok(snippet) =
44-
self.tcx().sess.source_map().span_to_snippet(fn_return_span)
45-
{
46-
// only apply this suggestion onto functions with
47-
// explicit non-desugar'able return.
48-
if fn_return_span.desugaring_kind().is_none() {
49-
err.span_suggestion(
50-
fn_return_span,
51-
&format!(
52-
"you can add a bound to the return type to make it last less \
53-
than `'static` and match {}",
54-
lifetime
55-
),
56-
format!("{} + {}", snippet, lifetime_name),
57-
Applicability::Unspecified,
58-
);
59-
}
40+
// only apply this suggestion onto functions with
41+
// explicit non-desugar'able return.
42+
if fn_return_span.desugaring_kind().is_none() {
43+
let msg = format!(
44+
"you can add a bound to the returned `{} Trait` to make it last less \
45+
than `'static` and match {}",
46+
if is_dyn { "dyn" } else { "impl" },
47+
lifetime
48+
);
49+
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
50+
err.span_suggestion_verbose(
51+
fn_return_span.shrink_to_hi(),
52+
&msg,
53+
format!(" + {}", lifetime_name),
54+
Applicability::MaybeIncorrect,
55+
);
6056
}
6157
err.emit();
6258
return Some(ErrorReported);

src/librustc_middle/ty/context.rs

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,27 @@
11
//! Type context book-keeping.
22
33
use crate::arena::Arena;
4-
use crate::dep_graph::DepGraph;
5-
use crate::dep_graph::{self, DepConstructor};
4+
use crate::dep_graph::{self, DepConstructor, DepGraph};
65
use crate::hir::exports::Export;
76
use crate::ich::{NodeIdHashingMode, StableHashingContext};
87
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
9-
use crate::lint::LintDiagnosticBuilder;
10-
use crate::lint::{struct_lint_level, LintSource};
8+
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintSource};
119
use crate::middle;
12-
use crate::middle::cstore::CrateStoreDyn;
13-
use crate::middle::cstore::EncodedMetadata;
10+
use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata};
1411
use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault};
1512
use crate::middle::stability;
16-
use crate::mir::interpret::{Allocation, ConstValue, Scalar};
17-
use crate::mir::{interpret, Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
13+
use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
14+
use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
1815
use crate::traits;
19-
use crate::ty::query;
2016
use crate::ty::steal::Steal;
21-
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
22-
use crate::ty::subst::{GenericArgKind, UserSubsts};
23-
use crate::ty::CanonicalPolyFnSig;
24-
use crate::ty::GenericParamDefKind;
25-
use crate::ty::RegionKind;
26-
use crate::ty::ReprOptions;
17+
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts};
2718
use crate::ty::TyKind::*;
28-
use crate::ty::{self, DefIdTree, Ty, TypeAndMut};
29-
use crate::ty::{AdtDef, AdtKind, Const, Region};
30-
use crate::ty::{BindingMode, BoundVar};
31-
use crate::ty::{ConstVid, FloatVar, FloatVid, IntVar, IntVid, TyVar, TyVid};
32-
use crate::ty::{ExistentialPredicate, Predicate, PredicateKind};
33-
use crate::ty::{InferConst, ParamConst};
34-
use crate::ty::{InferTy, ParamTy, PolyFnSig, ProjectionTy};
35-
use crate::ty::{List, TyKind, TyS};
19+
use crate::ty::{
20+
self, query, AdtDef, AdtKind, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid,
21+
DefIdTree, ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy,
22+
IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateKind, ProjectionTy,
23+
Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut,
24+
};
3625
use rustc_ast::ast;
3726
use rustc_ast::expand::allocator::AllocatorKind;
3827
use rustc_attr as attr;
@@ -48,10 +37,8 @@ use rustc_hir as hir;
4837
use rustc_hir::def::{DefKind, Res};
4938
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE};
5039
use rustc_hir::definitions::{DefPathHash, Definitions};
51-
use rustc_hir::lang_items;
52-
use rustc_hir::lang_items::PanicLocationLangItem;
53-
use rustc_hir::{HirId, Node, TraitCandidate};
54-
use rustc_hir::{ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet};
40+
use rustc_hir::lang_items::{self, PanicLocationLangItem};
41+
use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate};
5542
use rustc_index::vec::{Idx, IndexVec};
5643
use rustc_macros::HashStable;
5744
use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
@@ -1396,6 +1383,66 @@ impl<'tcx> TyCtxt<'tcx> {
13961383
})
13971384
}
13981385

1386+
pub fn return_type_impl_or_dyn_trait(&self, scope_def_id: DefId) -> Option<(Span, bool)> {
1387+
let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local());
1388+
let hir_output = match self.hir().get(hir_id) {
1389+
Node::Item(hir::Item {
1390+
kind:
1391+
ItemKind::Fn(
1392+
hir::FnSig {
1393+
decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. },
1394+
..
1395+
},
1396+
..,
1397+
),
1398+
..
1399+
})
1400+
| Node::ImplItem(hir::ImplItem {
1401+
kind:
1402+
hir::ImplItemKind::Fn(
1403+
hir::FnSig {
1404+
decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. },
1405+
..
1406+
},
1407+
_,
1408+
),
1409+
..
1410+
})
1411+
| Node::TraitItem(hir::TraitItem {
1412+
kind:
1413+
hir::TraitItemKind::Fn(
1414+
hir::FnSig {
1415+
decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. },
1416+
..
1417+
},
1418+
_,
1419+
),
1420+
..
1421+
}) => ty,
1422+
_ => return None,
1423+
};
1424+
1425+
let ret_ty = self.type_of(scope_def_id);
1426+
match ret_ty.kind {
1427+
ty::FnDef(_, _) => {
1428+
let sig = ret_ty.fn_sig(*self);
1429+
let output = self.erase_late_bound_regions(&sig.output());
1430+
if output.is_impl_trait() {
1431+
let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap();
1432+
Some((fn_decl.output.span(), false))
1433+
} else {
1434+
let mut v = TraitObjectVisitor(vec![]);
1435+
rustc_hir::intravisit::walk_ty(&mut v, hir_output);
1436+
if v.0.len() == 1 {
1437+
return Some((v.0[0], true));
1438+
}
1439+
None
1440+
}
1441+
}
1442+
_ => None,
1443+
}
1444+
}
1445+
13991446
pub fn return_type_impl_trait(&self, scope_def_id: DefId) -> Option<(Ty<'tcx>, Span)> {
14001447
// HACK: `type_of_def_id()` will fail on these (#55796), so return `None`.
14011448
let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local());

src/librustc_middle/ty/diagnostics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,22 @@ pub fn suggest_constraining_type_param(
249249
true
250250
}
251251
}
252+
253+
pub struct TraitObjectVisitor(pub Vec<rustc_span::Span>);
254+
impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor {
255+
type Map = rustc_hir::intravisit::ErasedMap<'v>;
256+
257+
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
258+
hir::intravisit::NestedVisitorMap::None
259+
}
260+
261+
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
262+
if let hir::TyKind::TraitObject(
263+
_,
264+
hir::Lifetime { name: hir::LifetimeName::ImplicitObjectLifetimeDefault, .. },
265+
) = ty.kind
266+
{
267+
self.0.push(ty.span);
268+
}
269+
}
270+
}

src/test/ui/async-await/issues/issue-62097.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: cannot infer an appropriate lifetime
44
LL | pub async fn run_dummy_fn(&self) {
55
| ^^^^^ ...but this borrow...
66
LL | foo(|| self.bar()).await;
7-
| --- this return type evaluates to the `'static` lifetime...
7+
| --- this evaluates to the `'static` lifetime...
88
|
99
note: ...can't outlive the lifetime `'_` as defined on the method body at 12:31
1010
--> $DIR/issue-62097.rs:12:31

src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,53 @@ error: cannot infer an appropriate lifetime
44
LL | fn elided(x: &i32) -> impl Copy { x }
55
| --------- ^ ...but this borrow...
66
| |
7-
| this return type evaluates to the `'static` lifetime...
7+
| this evaluates to the `'static` lifetime...
88
|
99
note: ...can't outlive the anonymous lifetime #1 defined on the function body at 3:1
1010
--> $DIR/must_outlive_least_region_or_bound.rs:3:1
1111
|
1212
LL | fn elided(x: &i32) -> impl Copy { x }
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14-
help: you can add a bound to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 3:1
14+
help: you can add a bound to the returned `impl Trait` to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 3:1
1515
|
1616
LL | fn elided(x: &i32) -> impl Copy + '_ { x }
17-
| ^^^^^^^^^^^^^^
17+
| ^^^^
1818

1919
error: cannot infer an appropriate lifetime
2020
--> $DIR/must_outlive_least_region_or_bound.rs:6:44
2121
|
2222
LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x }
2323
| --------- ^ ...but this borrow...
2424
| |
25-
| this return type evaluates to the `'static` lifetime...
25+
| this evaluates to the `'static` lifetime...
2626
|
2727
note: ...can't outlive the lifetime `'a` as defined on the function body at 6:13
2828
--> $DIR/must_outlive_least_region_or_bound.rs:6:13
2929
|
3030
LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x }
3131
| ^^
32-
help: you can add a bound to the return type to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 6:13
32+
help: you can add a bound to the returned `impl Trait` to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 6:13
3333
|
3434
LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x }
35-
| ^^^^^^^^^^^^^^
35+
| ^^^^
3636

3737
error: cannot infer an appropriate lifetime
3838
--> $DIR/must_outlive_least_region_or_bound.rs:12:69
3939
|
4040
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
4141
| -------------------------------- ^ ...but this borrow...
4242
| |
43-
| this return type evaluates to the `'static` lifetime...
43+
| this evaluates to the `'static` lifetime...
4444
|
4545
note: ...can't outlive the lifetime `'a` as defined on the function body at 12:15
4646
--> $DIR/must_outlive_least_region_or_bound.rs:12:15
4747
|
4848
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
4949
| ^^
50-
help: you can add a bound to the return type to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 12:15
50+
help: you can add a bound to the returned `impl Trait` to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 12:15
5151
|
5252
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x }
53-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
53+
| ^^^^
5454

5555
error[E0623]: lifetime mismatch
5656
--> $DIR/must_outlive_least_region_or_bound.rs:17:61

0 commit comments

Comments
 (0)