diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index 59b36a50a2b09..f50c23b0aa752 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -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> { @@ -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>) { diff --git a/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs new file mode 100644 index 0000000000000..f9ec5fa13c960 --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -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 or the MIT license +// , 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 { + 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 + } +} diff --git a/src/librustc/infer/error_reporting/nice_region_error/util.rs b/src/librustc/infer/error_reporting/nice_region_error/util.rs index 8aadec6455414..1cc2b9d50b99b 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/util.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/util.rs @@ -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 diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index e3b59d25ab91f..6d4e9a1b767a9 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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`), diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index d8ca817ff2bd0..412dc7dc7e715 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -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 { match self.sty { TyDynamic(ref tt, ..) => tt.principal().map(|p| p.def_id()), diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr new file mode 100644 index 0000000000000..7099316d694a2 --- /dev/null +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr @@ -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 + diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.rs b/src/test/ui/impl-trait/static-return-lifetime-infered.rs new file mode 100644 index 0000000000000..a05c889528280 --- /dev/null +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.rs @@ -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 or the MIT license +// , 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 { + self.x.iter().map(|a| a.0) + } + //~^^ ERROR cannot infer an appropriate lifetime + fn iter_values<'a>(&'a self) -> impl Iterator { + self.x.iter().map(|a| a.0) + } + //~^^ ERROR cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr new file mode 100644 index 0000000000000..2795bb92ed56f --- /dev/null +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -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 { + | ----------------------- 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 { +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 + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: cannot infer an appropriate lifetime + --> $DIR/static-return-lifetime-infered.rs:21:16 + | +LL | fn iter_values<'a>(&'a self) -> impl Iterator { + | ----------------------- 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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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 + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +