use RegionNameHighlight for async fn and closure returns
This commit is contained in:
parent
5042dbdb2e
commit
61b52a33b3
7 changed files with 173 additions and 39 deletions
|
@ -6,8 +6,8 @@ use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_middle::ty::print::RegionHighlightMode;
|
use rustc_middle::ty::print::RegionHighlightMode;
|
||||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||||
use rustc_middle::ty::{self, RegionVid, Ty};
|
use rustc_middle::ty::{self, RegionVid, Ty};
|
||||||
use rustc_span::symbol::kw;
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{symbol::Symbol, Span, DUMMY_SP};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
|
|
||||||
use crate::borrow_check::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt};
|
use crate::borrow_check::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt};
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ crate enum RegionNameSource {
|
||||||
/// The region corresponding to a closure upvar.
|
/// The region corresponding to a closure upvar.
|
||||||
AnonRegionFromUpvar(Span, String),
|
AnonRegionFromUpvar(Span, String),
|
||||||
/// The region corresponding to the return type of a closure.
|
/// The region corresponding to the return type of a closure.
|
||||||
AnonRegionFromOutput(Span, String, String),
|
AnonRegionFromOutput(RegionNameHighlight, String),
|
||||||
/// The region from a type yielded by a generator.
|
/// The region from a type yielded by a generator.
|
||||||
AnonRegionFromYieldTy(Span, String),
|
AnonRegionFromYieldTy(Span, String),
|
||||||
/// An anonymous region from an async fn.
|
/// An anonymous region from an async fn.
|
||||||
|
@ -85,10 +85,10 @@ impl RegionName {
|
||||||
| RegionNameSource::NamedFreeRegion(span)
|
| RegionNameSource::NamedFreeRegion(span)
|
||||||
| RegionNameSource::SynthesizedFreeEnvRegion(span, _)
|
| RegionNameSource::SynthesizedFreeEnvRegion(span, _)
|
||||||
| RegionNameSource::AnonRegionFromUpvar(span, _)
|
| RegionNameSource::AnonRegionFromUpvar(span, _)
|
||||||
| RegionNameSource::AnonRegionFromOutput(span, _, _)
|
|
||||||
| RegionNameSource::AnonRegionFromYieldTy(span, _)
|
| RegionNameSource::AnonRegionFromYieldTy(span, _)
|
||||||
| RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span),
|
| RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span),
|
||||||
RegionNameSource::AnonRegionFromArgument(ref highlight) => match *highlight {
|
RegionNameSource::AnonRegionFromArgument(ref highlight)
|
||||||
|
| RegionNameSource::AnonRegionFromOutput(ref highlight, _) => match *highlight {
|
||||||
RegionNameHighlight::MatchedHirTy(span)
|
RegionNameHighlight::MatchedHirTy(span)
|
||||||
| RegionNameHighlight::MatchedAdtAndSegment(span)
|
| RegionNameHighlight::MatchedAdtAndSegment(span)
|
||||||
| RegionNameHighlight::CannotMatchHirTy(span, _)
|
| RegionNameHighlight::CannotMatchHirTy(span, _)
|
||||||
|
@ -117,6 +117,7 @@ impl RegionName {
|
||||||
diag.span_label(*span, format!("has type `{}`", type_name));
|
diag.span_label(*span, format!("has type `{}`", type_name));
|
||||||
}
|
}
|
||||||
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span))
|
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span))
|
||||||
|
| RegionNameSource::AnonRegionFromOutput(RegionNameHighlight::MatchedHirTy(span), _)
|
||||||
| RegionNameSource::AnonRegionFromAsyncFn(span) => {
|
| RegionNameSource::AnonRegionFromAsyncFn(span) => {
|
||||||
diag.span_label(
|
diag.span_label(
|
||||||
*span,
|
*span,
|
||||||
|
@ -125,6 +126,10 @@ impl RegionName {
|
||||||
}
|
}
|
||||||
RegionNameSource::AnonRegionFromArgument(
|
RegionNameSource::AnonRegionFromArgument(
|
||||||
RegionNameHighlight::MatchedAdtAndSegment(span),
|
RegionNameHighlight::MatchedAdtAndSegment(span),
|
||||||
|
)
|
||||||
|
| RegionNameSource::AnonRegionFromOutput(
|
||||||
|
RegionNameHighlight::MatchedAdtAndSegment(span),
|
||||||
|
_,
|
||||||
) => {
|
) => {
|
||||||
diag.span_label(*span, format!("let's call this `{}`", self));
|
diag.span_label(*span, format!("let's call this `{}`", self));
|
||||||
}
|
}
|
||||||
|
@ -137,13 +142,28 @@ impl RegionName {
|
||||||
format!("lifetime `{}` appears in the type {}", self, type_name),
|
format!("lifetime `{}` appears in the type {}", self, type_name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
RegionNameSource::AnonRegionFromOutput(
|
||||||
|
RegionNameHighlight::Occluded(span, type_name),
|
||||||
|
mir_description,
|
||||||
|
) => {
|
||||||
|
diag.span_label(
|
||||||
|
*span,
|
||||||
|
format!(
|
||||||
|
"return type{} `{}` contains a lifetime `{}`",
|
||||||
|
mir_description, type_name, self
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
|
RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
|
||||||
diag.span_label(
|
diag.span_label(
|
||||||
*span,
|
*span,
|
||||||
format!("lifetime `{}` appears in the type of `{}`", self, upvar_name),
|
format!("lifetime `{}` appears in the type of `{}`", self, upvar_name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
|
RegionNameSource::AnonRegionFromOutput(
|
||||||
|
RegionNameHighlight::CannotMatchHirTy(span, type_name),
|
||||||
|
mir_description,
|
||||||
|
) => {
|
||||||
diag.span_label(*span, format!("return type{} is {}", mir_description, type_name));
|
diag.span_label(*span, format!("return type{} is {}", mir_description, type_name));
|
||||||
}
|
}
|
||||||
RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
|
RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
|
||||||
|
@ -659,6 +679,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||||
/// or be early bound (named, not in argument).
|
/// or be early bound (named, not in argument).
|
||||||
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
|
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
|
let hir = tcx.hir();
|
||||||
|
|
||||||
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
|
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
|
||||||
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
|
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
|
||||||
|
@ -666,60 +687,123 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut highlight = RegionHighlightMode::default();
|
let mir_hir_id = self.mir_hir_id();
|
||||||
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
|
|
||||||
let type_name =
|
|
||||||
self.infcx.extract_inference_diagnostics_data(return_ty.into(), Some(highlight)).name;
|
|
||||||
|
|
||||||
let (return_span, mir_description) = match tcx.hir().get(self.mir_hir_id()) {
|
let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) {
|
||||||
hir::Node::Expr(hir::Expr {
|
hir::Node::Expr(hir::Expr {
|
||||||
kind: hir::ExprKind::Closure(_, return_ty, body_id, span, _),
|
kind: hir::ExprKind::Closure(_, return_ty, body_id, span, _),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let mut span = match return_ty.output {
|
let (mut span, mut hir_ty) = match return_ty.output {
|
||||||
hir::FnRetTy::DefaultReturn(_) => tcx.sess.source_map().end_point(*span),
|
hir::FnRetTy::DefaultReturn(_) => {
|
||||||
hir::FnRetTy::Return(_) => return_ty.output.span(),
|
(tcx.sess.source_map().end_point(*span), None)
|
||||||
|
}
|
||||||
|
hir::FnRetTy::Return(hir_ty) => (return_ty.output.span(), Some(hir_ty)),
|
||||||
};
|
};
|
||||||
let mir_description = match tcx.hir().body(*body_id).generator_kind {
|
let mir_description = match hir.body(*body_id).generator_kind {
|
||||||
Some(hir::GeneratorKind::Async(gen)) => match gen {
|
Some(hir::GeneratorKind::Async(gen)) => match gen {
|
||||||
hir::AsyncGeneratorKind::Block => " of async block",
|
hir::AsyncGeneratorKind::Block => " of async block",
|
||||||
hir::AsyncGeneratorKind::Closure => " of async closure",
|
hir::AsyncGeneratorKind::Closure => " of async closure",
|
||||||
hir::AsyncGeneratorKind::Fn => {
|
hir::AsyncGeneratorKind::Fn => {
|
||||||
span = tcx
|
let parent_item = hir.get(hir.get_parent_item(mir_hir_id));
|
||||||
.hir()
|
let output = &parent_item
|
||||||
.get(tcx.hir().get_parent_item(mir_hir_id))
|
|
||||||
.fn_decl()
|
.fn_decl()
|
||||||
.expect("generator lowered from async fn should be in fn")
|
.expect("generator lowered from async fn should be in fn")
|
||||||
.output
|
.output;
|
||||||
.span();
|
span = output.span();
|
||||||
|
if let hir::FnRetTy::Return(ret) = output {
|
||||||
|
hir_ty = Some(self.get_future_inner_return_ty(*ret));
|
||||||
|
}
|
||||||
" of async function"
|
" of async function"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(hir::GeneratorKind::Gen) => " of generator",
|
Some(hir::GeneratorKind::Gen) => " of generator",
|
||||||
None => " of closure",
|
None => " of closure",
|
||||||
};
|
};
|
||||||
(span, mir_description)
|
(span, mir_description, hir_ty)
|
||||||
}
|
}
|
||||||
hir::Node::ImplItem(hir::ImplItem {
|
node => match node.fn_decl() {
|
||||||
kind: hir::ImplItemKind::Fn(method_sig, _),
|
Some(fn_decl) => {
|
||||||
..
|
let hir_ty = match fn_decl.output {
|
||||||
}) => (method_sig.decl.output.span(), ""),
|
hir::FnRetTy::DefaultReturn(_) => None,
|
||||||
_ => (self.body.span, ""),
|
hir::FnRetTy::Return(ty) => Some(ty),
|
||||||
|
};
|
||||||
|
(fn_decl.output.span(), "", hir_ty)
|
||||||
|
}
|
||||||
|
None => (self.body.span, "", None),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let highlight = hir_ty
|
||||||
|
.and_then(|hir_ty| self.highlight_if_we_can_match_hir_ty(fr, return_ty, hir_ty))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to
|
||||||
|
// the anonymous region. If it succeeds, the `synthesize_region_name` call below
|
||||||
|
// will increment the counter, "reserving" the number we just used.
|
||||||
|
let counter = *self.next_region_name.try_borrow().unwrap();
|
||||||
|
self.highlight_if_we_cannot_match_hir_ty(fr, return_ty, return_span, counter)
|
||||||
|
});
|
||||||
|
|
||||||
Some(RegionName {
|
Some(RegionName {
|
||||||
// This counter value will already have been used, so this function will increment it
|
|
||||||
// so the next value will be used next and return the region name that would have been
|
|
||||||
// used.
|
|
||||||
name: self.synthesize_region_name(),
|
name: self.synthesize_region_name(),
|
||||||
source: RegionNameSource::AnonRegionFromOutput(
|
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()),
|
||||||
return_span,
|
|
||||||
mir_description.to_string(),
|
|
||||||
type_name,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// From the [`hir::Ty`] of an async function's lowered return type,
|
||||||
|
/// retrieve the `hir::Ty` representing the type the user originally wrote.
|
||||||
|
///
|
||||||
|
/// e.g. given the function:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// async fn foo() -> i32 {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`,
|
||||||
|
/// returns the `i32`.
|
||||||
|
///
|
||||||
|
/// [`OpaqueDef`]: hir::TyKind::OpaqueDef
|
||||||
|
fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
|
||||||
|
let hir = self.infcx.tcx.hir();
|
||||||
|
|
||||||
|
if let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind {
|
||||||
|
let opaque_ty = hir.item(id.id);
|
||||||
|
if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||||
|
bounds:
|
||||||
|
[hir::GenericBound::LangItemTrait(
|
||||||
|
hir::LangItem::Future,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
hir::GenericArgs {
|
||||||
|
bindings:
|
||||||
|
[hir::TypeBinding {
|
||||||
|
ident: Ident { name: sym::Output, .. },
|
||||||
|
kind: hir::TypeBindingKind::Equality { ty },
|
||||||
|
..
|
||||||
|
}],
|
||||||
|
..
|
||||||
|
},
|
||||||
|
)],
|
||||||
|
..
|
||||||
|
}) = opaque_ty.kind
|
||||||
|
{
|
||||||
|
ty
|
||||||
|
} else {
|
||||||
|
span_bug!(
|
||||||
|
hir_ty.span,
|
||||||
|
"bounds from lowered return type of async fn did not match expected format: {:?}",
|
||||||
|
opaque_ty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
span_bug!(
|
||||||
|
hir_ty.span,
|
||||||
|
"lowered return type of async fn is not OpaqueDef: {:?}",
|
||||||
|
hir_ty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn give_name_if_anonymous_region_appears_in_yield_ty(
|
fn give_name_if_anonymous_region_appears_in_yield_ty(
|
||||||
&self,
|
&self,
|
||||||
fr: RegionVid,
|
fr: RegionVid,
|
||||||
|
|
|
@ -18,6 +18,14 @@ pub fn async_closure(x: &mut i32) -> impl Future<Output=&i32> {
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future<Output=&i32> {
|
||||||
|
(async move || -> &i32 {
|
||||||
|
let y = &*x;
|
||||||
|
*x += 1; //~ ERROR cannot assign to `*x` because it is borrowed
|
||||||
|
y
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn async_block(x: &mut i32) -> impl Future<Output=&i32> {
|
pub fn async_block(x: &mut i32) -> impl Future<Output=&i32> {
|
||||||
async move {
|
async move {
|
||||||
let y = &*x;
|
let y = &*x;
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0506]: cannot assign to `*x` because it is borrowed
|
||||||
--> $DIR/issue-74072-lifetime-name-annotations.rs:9:5
|
--> $DIR/issue-74072-lifetime-name-annotations.rs:9:5
|
||||||
|
|
|
|
||||||
LL | pub async fn async_fn(x: &mut i32) -> &i32 {
|
LL | pub async fn async_fn(x: &mut i32) -> &i32 {
|
||||||
| ---- return type of async function is &'1 i32
|
| - let's call the lifetime of this reference `'1`
|
||||||
LL | let y = &*x;
|
LL | let y = &*x;
|
||||||
| --- borrow of `*x` occurs here
|
| --- borrow of `*x` occurs here
|
||||||
LL | *x += 1;
|
LL | *x += 1;
|
||||||
|
@ -25,6 +25,18 @@ LL | })()
|
||||||
error[E0506]: cannot assign to `*x` because it is borrowed
|
error[E0506]: cannot assign to `*x` because it is borrowed
|
||||||
--> $DIR/issue-74072-lifetime-name-annotations.rs:24:9
|
--> $DIR/issue-74072-lifetime-name-annotations.rs:24:9
|
||||||
|
|
|
|
||||||
|
LL | (async move || -> &i32 {
|
||||||
|
| - let's call the lifetime of this reference `'1`
|
||||||
|
LL | let y = &*x;
|
||||||
|
| --- borrow of `*x` occurs here
|
||||||
|
LL | *x += 1;
|
||||||
|
| ^^^^^^^ assignment to borrowed `*x` occurs here
|
||||||
|
LL | y
|
||||||
|
| - returning this value requires that `*x` is borrowed for `'1`
|
||||||
|
|
||||||
|
error[E0506]: cannot assign to `*x` because it is borrowed
|
||||||
|
--> $DIR/issue-74072-lifetime-name-annotations.rs:32:9
|
||||||
|
|
|
||||||
LL | let y = &*x;
|
LL | let y = &*x;
|
||||||
| --- borrow of `*x` occurs here
|
| --- borrow of `*x` occurs here
|
||||||
LL | *x += 1;
|
LL | *x += 1;
|
||||||
|
@ -34,6 +46,6 @@ LL | y
|
||||||
LL | }
|
LL | }
|
||||||
| - return type of async block is &'1 i32
|
| - return type of async block is &'1 i32
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0506`.
|
For more information about this error, try `rustc --explain E0506`.
|
||||||
|
|
19
src/test/ui/async-await/issue-74497-lifetime-in-opaque.rs
Normal file
19
src/test/ui/async-await/issue-74497-lifetime-in-opaque.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// edition:2018
|
||||||
|
|
||||||
|
// test that names give to anonymous lifetimes in opaque types like `impl Future` are correctly
|
||||||
|
// introduced in error messages
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pub async fn foo<F, T>(_: F)
|
||||||
|
where
|
||||||
|
F: Fn(&u8) -> T,
|
||||||
|
T: Future<Output = ()>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn bar(_: &u8) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = foo(|x| bar(x)); //~ ERROR lifetime may not live long enough
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/issue-74497-lifetime-in-opaque.rs:18:21
|
||||||
|
|
|
||||||
|
LL | let _ = foo(|x| bar(x));
|
||||||
|
| -- ^^^^^^ returning this value requires that `'1` must outlive `'2`
|
||||||
|
| ||
|
||||||
|
| |return type of closure `impl Future` contains a lifetime `'2`
|
||||||
|
| has type `&'1 u8`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -4,7 +4,7 @@ error: lifetime may not live long enough
|
||||||
LL | let _action = move || {
|
LL | let _action = move || {
|
||||||
| -------
|
| -------
|
||||||
| | |
|
| | |
|
||||||
| | return type of closure is [closure@$DIR/issue-53432-nested-closure-outlives-borrowed-value.rs:4:9: 4:15]
|
| | return type of closure `[closure@$DIR/issue-53432-nested-closure-outlives-borrowed-value.rs:4:9: 4:15]` contains a lifetime `'2`
|
||||||
| lifetime `'1` represents this closure's body
|
| lifetime `'1` represents this closure's body
|
||||||
LL | || f() // The `nested` closure
|
LL | || f() // The `nested` closure
|
||||||
| ^^^^^^ returning this value requires that `'1` must outlive `'2`
|
| ^^^^^^ returning this value requires that `'1` must outlive `'2`
|
||||||
|
|
|
@ -2,9 +2,9 @@ error: lifetime may not live long enough
|
||||||
--> $DIR/issue-58053.rs:6:33
|
--> $DIR/issue-58053.rs:6:33
|
||||||
|
|
|
|
||||||
LL | let f = |x: &i32| -> &i32 { x };
|
LL | let f = |x: &i32| -> &i32 { x };
|
||||||
| - ---- ^ returning this value requires that `'1` must outlive `'2`
|
| - - ^ returning this value requires that `'1` must outlive `'2`
|
||||||
| | |
|
| | |
|
||||||
| | return type of closure is &'2 i32
|
| | let's call the lifetime of this reference `'2`
|
||||||
| let's call the lifetime of this reference `'1`
|
| let's call the lifetime of this reference `'1`
|
||||||
|
|
||||||
error: lifetime may not live long enough
|
error: lifetime may not live long enough
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue