Skip to content

Commit

Permalink
Auto merge of rust-lang#51444 - estebank:impl-static, r=nikomatsakis
Browse files Browse the repository at this point in the history
Suggestion for 'static impl Trait return

When encountering a named or anonymous sup requirement (for example,
`&'a self`) and a `'static` impl Trait return type, suggest adding the
`'_` lifetime constraing to the return type.

Fix rust-lang#43719, rust-lang#51282.

```
error: cannot infer an appropriate lifetime
  --> $DIR/static-return-lifetime-infered.rs:17:16
   |
LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
   |                                   ----------------------- this return type evaluates to the `'static` lifetime...
LL |         self.x.iter().map(|a| a.0)
   |         ------ ^^^^
   |         |
   |         ...but this borrow...
   |
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 16:5
  --> $DIR/static-return-lifetime-infered.rs:16:5
   |
LL | /     fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
LL | |         self.x.iter().map(|a| a.0)
LL | |     }
   | |_____^
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 method body at 16:5
   |
LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ {
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
  • Loading branch information
bors committed Jun 28, 2018
2 parents 99a9d68 + 612657d commit ba196bd
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/librustc/infer/error_reporting/nice_region_error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod different_lifetimes;
mod find_anon_type;
mod named_anon_conflict;
mod outlives_closure;
mod static_impl_trait;
mod util;

impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
Expand Down Expand Up @@ -67,6 +68,7 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> {
self.try_report_named_anon_conflict()
.or_else(|| self.try_report_anon_anon_conflict())
.or_else(|| self.try_report_outlives_closure())
.or_else(|| self.try_report_static_impl_trait())
}

pub fn get_regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Error Reporting for static impl Traits.
use infer::error_reporting::nice_region_error::NiceRegionError;
use infer::lexical_region_resolve::RegionResolutionError;
use ty::{BoundRegion, FreeRegion, RegionKind};
use util::common::ErrorReported;

impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
/// Print the error message for lifetime errors when the return type is a static impl Trait.
pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
if let Some(ref error) = self.error {
match error.clone() {
RegionResolutionError::SubSupConflict(
var_origin,
sub_origin,
sub_r,
sup_origin,
sup_r,
) => {
let anon_reg_sup = self.is_suitable_region(sup_r)?;
if sub_r == &RegionKind::ReStatic &&
self.is_return_type_impl_trait(anon_reg_sup.def_id)
{
let sp = var_origin.span();
let return_sp = sub_origin.span();
let mut err = self.tcx.sess.struct_span_err(
sp,
"cannot infer an appropriate lifetime",
);
err.span_label(
return_sp,
"this return type evaluates to the `'static` lifetime...",
);
err.span_label(
sup_origin.span(),
"...but this borrow...",
);

let (lifetime, lt_sp_opt) = self.tcx.msg_span_from_free_region(sup_r);
if let Some(lifetime_sp) = lt_sp_opt {
err.span_note(
lifetime_sp,
&format!("...can't outlive {}", lifetime),
);
}

let lifetime_name = match sup_r {
RegionKind::ReFree(FreeRegion {
bound_region: BoundRegion::BrNamed(_, ref name), ..
}) => format!("{}", name),
_ => "'_".to_owned(),
};
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(return_sp) {
err.span_suggestion(
return_sp,
&format!(
"you can add a constraint to the return type to make it last \
less than `'static` and match {}",
lifetime,
),
format!("{} + {}", snippet, lifetime_name),
);
}
err.emit();
return Some(ErrorReported);
}
}
_ => {}
}
}
None
}
}
17 changes: 17 additions & 0 deletions src/librustc/infer/error_reporting/nice_region_error/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,23 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
}
None
}

pub(super) fn is_return_type_impl_trait(
&self,
scope_def_id: DefId,
) -> bool {
let ret_ty = self.tcx.type_of(scope_def_id);
match ret_ty.sty {
ty::TyFnDef(_, _) => {
let sig = ret_ty.fn_sig(self.tcx);
let output = self.tcx.erase_late_bound_regions(&sig.output());
return output.is_impl_trait();
}
_ => {}
}
false
}

// Here we check for the case where anonymous region
// corresponds to self and if yes, we display E0312.
// FIXME(#42700) - Need to format self properly to
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub enum Note {
// and how it is located, as well as the mutability of the memory in
// which the value is stored.
//
// *WARNING* The field `cmt.type` is NOT necessarily the same as the
// *WARNING* The field `cmt.ty` is NOT necessarily the same as the
// result of `node_id_to_type(cmt.id)`. This is because the `id` is
// always the `id` of the node producing the type; in an expression
// like `*x`, the type of this deref node is the deref'd type (`T`),
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

pub fn is_impl_trait(&self) -> bool {
match self.sty {
TyAnon(..) => true,
_ => false,
}
}

pub fn ty_to_def_id(&self) -> Option<DefId> {
match self.sty {
TyDynamic(ref tt, ..) => tt.principal().map(|p| p.def_id()),
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
warning: not reporting region error due to nll
--> $DIR/static-return-lifetime-infered.rs:17:16
|
LL | self.x.iter().map(|a| a.0)
| ^^^^

warning: not reporting region error due to nll
--> $DIR/static-return-lifetime-infered.rs:21:16
|
LL | self.x.iter().map(|a| a.0)
| ^^^^

error: free region `` does not outlive free region `'static`
--> $DIR/static-return-lifetime-infered.rs:17:9
|
LL | self.x.iter().map(|a| a.0)
| ^^^^^^^^^^^^^

error: free region `'a` does not outlive free region `'static`
--> $DIR/static-return-lifetime-infered.rs:21:9
|
LL | self.x.iter().map(|a| a.0)
| ^^^^^^^^^^^^^

error: aborting due to 2 previous errors

26 changes: 26 additions & 0 deletions src/test/ui/impl-trait/static-return-lifetime-infered.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct A {
x: [(u32, u32); 10]
}

impl A {
fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
self.x.iter().map(|a| a.0)
}
//~^^ ERROR cannot infer an appropriate lifetime
fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
self.x.iter().map(|a| a.0)
}
//~^^ ERROR cannot infer an appropriate lifetime
}

fn main() {}
44 changes: 44 additions & 0 deletions src/test/ui/impl-trait/static-return-lifetime-infered.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
error: cannot infer an appropriate lifetime
--> $DIR/static-return-lifetime-infered.rs:17:16
|
LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
| ----------------------- this return type evaluates to the `'static` lifetime...
LL | self.x.iter().map(|a| a.0)
| ------ ^^^^
| |
| ...but this borrow...
|
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 16:5
--> $DIR/static-return-lifetime-infered.rs:16:5
|
LL | / fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
LL | | self.x.iter().map(|a| a.0)
LL | | }
| |_____^
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 method body at 16:5
|
LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer an appropriate lifetime
--> $DIR/static-return-lifetime-infered.rs:21:16
|
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
| ----------------------- this return type evaluates to the `'static` lifetime...
LL | self.x.iter().map(|a| a.0)
| ------ ^^^^
| |
| ...but this borrow...
|
note: ...can't outlive the lifetime 'a as defined on the method body at 20:5
--> $DIR/static-return-lifetime-infered.rs:20:5
|
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can add a constraint to the return type to make it last less than `'static` and match the lifetime 'a as defined on the method body at 20:5
|
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

0 comments on commit ba196bd

Please sign in to comment.