Skip to content
Permalink
Browse files

Suggest using anonymous lifetime in `impl Trait` return without hacks

Fallback to `static_impl_trait` for nice error message by peeking at the
return type and the lifetime type. Point at the return type instead of
the return expr/stmt in NLL mode.
  • Loading branch information...
estebank committed Mar 31, 2019
1 parent ee2a9d9 commit 30c247f881a87e40aed30983d4c923efb6148f9b
@@ -1,6 +1,7 @@
//! Error Reporting for Anonymous Region Lifetime Errors
//! where one region is named and the other is anonymous.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::hir::{FunctionRetTy, TyKind};
use crate::ty;
use errors::{Applicability, DiagnosticBuilder};

@@ -11,9 +12,10 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
let (span, sub, sup) = self.get_regions();

debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?})",
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
sub,
sup
sup,
self.error,
);

// Determine whether the sub and sup consist of one named region ('a)
@@ -84,6 +86,13 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
{
return None;
}
if let FunctionRetTy::Return(ty) = &fndecl.output {
if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.node, sub) {
// This is an impl Trait return that evaluates de need of 'static.
// We handle this case better in `static_impl_trait`.
return None;
}
}
}

let (error_var, span_label_var) = if let Some(simple_ident) = arg.pat.simple_ident() {
@@ -103,40 +112,10 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
error_var
);

let many = if let Ok(snippet) = self.tcx().sess.source_map().span_to_snippet(span) {
if "'static" == &named.to_string() && snippet.starts_with("impl ") {
diag.span_suggestion(
span,
"add explicit unnamed lifetime `'_` to the return type to constrain it",
format!("{} + '_", snippet),
Applicability::Unspecified,
);
true
} else {
false
}
} else {
false
};
if many {
diag.span_label(
span,
"`impl Trait` types can only capture lifetimes that they reference"
);
} else {
diag.span_label(span, format!("lifetime `{}` required", named));
}
diag.span_label(span, format!("lifetime `{}` required", named));
diag.span_suggestion(
new_ty_span,
&format!("{}add explicit lifetime `{}` to {}",
if many {
"otherwise, "
} else {
""
},
named,
span_label_var,
),
&format!("add explicit lifetime `{}` to {}", named, span_label_var),
new_ty.to_string(),
Applicability::Unspecified,
);
@@ -132,6 +132,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
});
if let Some(i) = best_choice {
if let Some(next) = categorized_path.get(i + 1) {
if categorized_path[i].0 == ConstraintCategory::Return
&& next.0 == ConstraintCategory::OpaqueType
{
// The return expression is being influenced by the return type being
// impl Trait, point at the return type and not the return expr.
return *next;
}
}
return categorized_path[i];
}

@@ -240,6 +249,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.provides_universal_region(r, fr, outlived_fr)
});

debug!("report_error: category={:?} {:?}", category, span);
// Check if we can use one of the "nice region errors".
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
@@ -1,7 +1,7 @@
use std::fmt::Debug;

fn elided(x: &i32) -> impl Copy { x }
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
//~^ ERROR cannot infer an appropriate lifetime

fn explicit<'a>(x: &'a i32) -> impl Copy { x }
//~^ ERROR cannot infer an appropriate lifetime
@@ -1,16 +1,20 @@
error[E0621]: explicit lifetime required in the type of `x`
--> $DIR/must_outlive_least_region_or_bound.rs:3:23
error: cannot infer an appropriate lifetime
--> $DIR/must_outlive_least_region_or_bound.rs:3:35
|
LL | fn elided(x: &i32) -> impl Copy { x }
| ^^^^^^^^^ `impl Trait` types can only capture lifetimes that they reference
help: add explicit unnamed lifetime `'_` to the return type to constrain it
| --------- ^ ...but this borrow...
| |
| this return type evaluates to the `'static` lifetime...
|
note: ...can't outlive the anonymous lifetime #1 defined on the function body at 3:1
--> $DIR/must_outlive_least_region_or_bound.rs:3:1
|
LL | fn elided(x: &i32) -> impl Copy { x }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can add a constraint 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
|
LL | fn elided(x: &i32) -> impl Copy + '_ { x }
| ^^^^^^^^^^^^^^
help: otherwise, add explicit lifetime `'static` to the type of `x`
|
LL | fn elided(x: &'static i32) -> impl Copy { x }
| ^^^^^^^^^^^^

error: cannot infer an appropriate lifetime
--> $DIR/must_outlive_least_region_or_bound.rs:6:44
@@ -73,5 +77,5 @@ LL | fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {

error: aborting due to 5 previous errors

Some errors occurred: E0310, E0621, E0623.
Some errors occurred: E0310, E0623.
For more information about an error, try `rustc --explain E0310`.
@@ -8,8 +8,8 @@ trait Foo<'a> {
impl<'a, T> Foo<'a> for T { }

fn foo<'a, T>(x: &T) -> impl Foo<'a> {
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
x
//~^ ERROR explicit lifetime required in the type of `x` [E0621]
}

fn main() {}
@@ -1,8 +1,8 @@
error[E0621]: explicit lifetime required in the type of `x`
--> $DIR/impl-trait-captures.rs:11:5
--> $DIR/impl-trait-captures.rs:10:25
|
LL | x
| ^ lifetime `ReEarlyBound(0, 'a)` required
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> {
| ^^^^^^^^^^^^ lifetime `ReEarlyBound(0, 'a)` required
help: add explicit lifetime `ReEarlyBound(0, 'a)` to the type of `x`
|
LL | fn foo<'a, T>(x: &ReEarlyBound(0, 'a) T) -> impl Foo<'a> {

0 comments on commit 30c247f

Please sign in to comment.
You can’t perform that action at this time.