Skip to content

Commit 99f16e6

Browse files
authored
Rollup merge of rust-lang#76468 - SNCPlay42:lifetime-names, r=Mark-Simulacrum
Improve lifetime name annotations for closures & async functions * Don't refer to async functions as "generators" in error output * Where possible, emit annotations pointing exactly at the `&` in the return type of closures (when they have explicit return types) and async functions, like we do for arguments. Addresses rust-lang#74072, but I wouldn't call that *closed* until annotations are identical for async and non-async functions. * Emit a better annotation when the lifetime doesn't appear in the full name type, which currently happens for opaque types like `impl Future`. Addresses rust-lang#74497, but further improves could probably be made (why *doesn't* it appear in the type as `impl Future + '1`?) This is included in the same PR because the changes to `give_name_if_anonymous_region_appears_in_output` would introduce ICE otherwise (it would return `None` in cases where it didn't previously, which then gets `unwrap`ped)
2 parents 46bce9f + 61b52a3 commit 99f16e6

7 files changed

+282
-46
lines changed

compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs

+161-43
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use rustc_hir::def::{DefKind, Res};
66
use rustc_middle::ty::print::RegionHighlightMode;
77
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
88
use rustc_middle::ty::{self, RegionVid, Ty};
9-
use rustc_span::symbol::kw;
10-
use rustc_span::{symbol::Symbol, Span, DUMMY_SP};
9+
use rustc_span::symbol::{kw, sym, Ident, Symbol};
10+
use rustc_span::{Span, DUMMY_SP};
1111

1212
use crate::borrow_check::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt};
1313

@@ -39,7 +39,7 @@ crate enum RegionNameSource {
3939
/// The region corresponding to a closure upvar.
4040
AnonRegionFromUpvar(Span, String),
4141
/// The region corresponding to the return type of a closure.
42-
AnonRegionFromOutput(Span, String, String),
42+
AnonRegionFromOutput(RegionNameHighlight, String),
4343
/// The region from a type yielded by a generator.
4444
AnonRegionFromYieldTy(Span, String),
4545
/// An anonymous region from an async fn.
@@ -57,6 +57,10 @@ crate enum RegionNameHighlight {
5757
/// The anonymous region corresponds to a region where the type annotation is completely missing
5858
/// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference.
5959
CannotMatchHirTy(Span, String),
60+
/// The anonymous region corresponds to a region where the type annotation is completely missing
61+
/// from the code, and *even if* we print out the full name of the type, the region name won't
62+
/// be included. This currently occurs for opaque types like `impl Future`.
63+
Occluded(Span, String),
6064
}
6165

6266
impl RegionName {
@@ -81,13 +85,14 @@ impl RegionName {
8185
| RegionNameSource::NamedFreeRegion(span)
8286
| RegionNameSource::SynthesizedFreeEnvRegion(span, _)
8387
| RegionNameSource::AnonRegionFromUpvar(span, _)
84-
| RegionNameSource::AnonRegionFromOutput(span, _, _)
8588
| RegionNameSource::AnonRegionFromYieldTy(span, _)
8689
| RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span),
87-
RegionNameSource::AnonRegionFromArgument(ref highlight) => match *highlight {
90+
RegionNameSource::AnonRegionFromArgument(ref highlight)
91+
| RegionNameSource::AnonRegionFromOutput(ref highlight, _) => match *highlight {
8892
RegionNameHighlight::MatchedHirTy(span)
8993
| RegionNameHighlight::MatchedAdtAndSegment(span)
90-
| RegionNameHighlight::CannotMatchHirTy(span, _) => Some(span),
94+
| RegionNameHighlight::CannotMatchHirTy(span, _)
95+
| RegionNameHighlight::Occluded(span, _) => Some(span),
9196
},
9297
}
9398
}
@@ -112,6 +117,7 @@ impl RegionName {
112117
diag.span_label(*span, format!("has type `{}`", type_name));
113118
}
114119
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span))
120+
| RegionNameSource::AnonRegionFromOutput(RegionNameHighlight::MatchedHirTy(span), _)
115121
| RegionNameSource::AnonRegionFromAsyncFn(span) => {
116122
diag.span_label(
117123
*span,
@@ -120,16 +126,44 @@ impl RegionName {
120126
}
121127
RegionNameSource::AnonRegionFromArgument(
122128
RegionNameHighlight::MatchedAdtAndSegment(span),
129+
)
130+
| RegionNameSource::AnonRegionFromOutput(
131+
RegionNameHighlight::MatchedAdtAndSegment(span),
132+
_,
123133
) => {
124134
diag.span_label(*span, format!("let's call this `{}`", self));
125135
}
136+
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::Occluded(
137+
span,
138+
type_name,
139+
)) => {
140+
diag.span_label(
141+
*span,
142+
format!("lifetime `{}` appears in the type {}", self, type_name),
143+
);
144+
}
145+
RegionNameSource::AnonRegionFromOutput(
146+
RegionNameHighlight::Occluded(span, type_name),
147+
mir_description,
148+
) => {
149+
diag.span_label(
150+
*span,
151+
format!(
152+
"return type{} `{}` contains a lifetime `{}`",
153+
mir_description, type_name, self
154+
),
155+
);
156+
}
126157
RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
127158
diag.span_label(
128159
*span,
129160
format!("lifetime `{}` appears in the type of `{}`", self, upvar_name),
130161
);
131162
}
132-
RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
163+
RegionNameSource::AnonRegionFromOutput(
164+
RegionNameHighlight::CannotMatchHirTy(span, type_name),
165+
mir_description,
166+
) => {
133167
diag.span_label(*span, format!("return type{} is {}", mir_description, type_name));
134168
}
135169
RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
@@ -349,19 +383,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
349383
argument_index,
350384
);
351385

352-
self.get_argument_hir_ty_for_highlighting(argument_index)
386+
let highlight = self
387+
.get_argument_hir_ty_for_highlighting(argument_index)
353388
.and_then(|arg_hir_ty| self.highlight_if_we_can_match_hir_ty(fr, arg_ty, arg_hir_ty))
354-
.or_else(|| {
389+
.unwrap_or_else(|| {
355390
// `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to
356391
// the anonymous region. If it succeeds, the `synthesize_region_name` call below
357392
// will increment the counter, "reserving" the number we just used.
358393
let counter = *self.next_region_name.try_borrow().unwrap();
359394
self.highlight_if_we_cannot_match_hir_ty(fr, arg_ty, span, counter)
360-
})
361-
.map(|highlight| RegionName {
362-
name: self.synthesize_region_name(),
363-
source: RegionNameSource::AnonRegionFromArgument(highlight),
364-
})
395+
});
396+
397+
Some(RegionName {
398+
name: self.synthesize_region_name(),
399+
source: RegionNameSource::AnonRegionFromArgument(highlight),
400+
})
365401
}
366402

367403
fn get_argument_hir_ty_for_highlighting(
@@ -399,7 +435,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
399435
ty: Ty<'tcx>,
400436
span: Span,
401437
counter: usize,
402-
) -> Option<RegionNameHighlight> {
438+
) -> RegionNameHighlight {
403439
let mut highlight = RegionHighlightMode::default();
404440
highlight.highlighting_region_vid(needle_fr, counter);
405441
let type_name =
@@ -411,9 +447,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
411447
);
412448
if type_name.find(&format!("'{}", counter)).is_some() {
413449
// Only add a label if we can confirm that a region was labelled.
414-
Some(RegionNameHighlight::CannotMatchHirTy(span, type_name))
450+
RegionNameHighlight::CannotMatchHirTy(span, type_name)
415451
} else {
416-
None
452+
RegionNameHighlight::Occluded(span, type_name)
417453
}
418454
}
419455

@@ -643,49 +679,131 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
643679
/// or be early bound (named, not in argument).
644680
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
645681
let tcx = self.infcx.tcx;
682+
let hir = tcx.hir();
646683

647684
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
648685
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
649686
if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
650687
return None;
651688
}
652689

653-
let mut highlight = RegionHighlightMode::default();
654-
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
655-
let type_name =
656-
self.infcx.extract_inference_diagnostics_data(return_ty.into(), Some(highlight)).name;
690+
let mir_hir_id = self.mir_hir_id();
657691

658-
let (return_span, mir_description) = match tcx.hir().get(self.mir_hir_id()) {
692+
let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) {
659693
hir::Node::Expr(hir::Expr {
660-
kind: hir::ExprKind::Closure(_, return_ty, _, span, gen_move),
661-
..
662-
}) => (
663-
match return_ty.output {
664-
hir::FnRetTy::DefaultReturn(_) => tcx.sess.source_map().end_point(*span),
665-
hir::FnRetTy::Return(_) => return_ty.output.span(),
666-
},
667-
if gen_move.is_some() { " of generator" } else { " of closure" },
668-
),
669-
hir::Node::ImplItem(hir::ImplItem {
670-
kind: hir::ImplItemKind::Fn(method_sig, _),
694+
kind: hir::ExprKind::Closure(_, return_ty, body_id, span, _),
671695
..
672-
}) => (method_sig.decl.output.span(), ""),
673-
_ => (self.body.span, ""),
696+
}) => {
697+
let (mut span, mut hir_ty) = match return_ty.output {
698+
hir::FnRetTy::DefaultReturn(_) => {
699+
(tcx.sess.source_map().end_point(*span), None)
700+
}
701+
hir::FnRetTy::Return(hir_ty) => (return_ty.output.span(), Some(hir_ty)),
702+
};
703+
let mir_description = match hir.body(*body_id).generator_kind {
704+
Some(hir::GeneratorKind::Async(gen)) => match gen {
705+
hir::AsyncGeneratorKind::Block => " of async block",
706+
hir::AsyncGeneratorKind::Closure => " of async closure",
707+
hir::AsyncGeneratorKind::Fn => {
708+
let parent_item = hir.get(hir.get_parent_item(mir_hir_id));
709+
let output = &parent_item
710+
.fn_decl()
711+
.expect("generator lowered from async fn should be in fn")
712+
.output;
713+
span = output.span();
714+
if let hir::FnRetTy::Return(ret) = output {
715+
hir_ty = Some(self.get_future_inner_return_ty(*ret));
716+
}
717+
" of async function"
718+
}
719+
},
720+
Some(hir::GeneratorKind::Gen) => " of generator",
721+
None => " of closure",
722+
};
723+
(span, mir_description, hir_ty)
724+
}
725+
node => match node.fn_decl() {
726+
Some(fn_decl) => {
727+
let hir_ty = match fn_decl.output {
728+
hir::FnRetTy::DefaultReturn(_) => None,
729+
hir::FnRetTy::Return(ty) => Some(ty),
730+
};
731+
(fn_decl.output.span(), "", hir_ty)
732+
}
733+
None => (self.body.span, "", None),
734+
},
674735
};
675736

737+
let highlight = hir_ty
738+
.and_then(|hir_ty| self.highlight_if_we_can_match_hir_ty(fr, return_ty, hir_ty))
739+
.unwrap_or_else(|| {
740+
// `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to
741+
// the anonymous region. If it succeeds, the `synthesize_region_name` call below
742+
// will increment the counter, "reserving" the number we just used.
743+
let counter = *self.next_region_name.try_borrow().unwrap();
744+
self.highlight_if_we_cannot_match_hir_ty(fr, return_ty, return_span, counter)
745+
});
746+
676747
Some(RegionName {
677-
// This counter value will already have been used, so this function will increment it
678-
// so the next value will be used next and return the region name that would have been
679-
// used.
680748
name: self.synthesize_region_name(),
681-
source: RegionNameSource::AnonRegionFromOutput(
682-
return_span,
683-
mir_description.to_string(),
684-
type_name,
685-
),
749+
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()),
686750
})
687751
}
688752

753+
/// From the [`hir::Ty`] of an async function's lowered return type,
754+
/// retrieve the `hir::Ty` representing the type the user originally wrote.
755+
///
756+
/// e.g. given the function:
757+
///
758+
/// ```
759+
/// async fn foo() -> i32 {}
760+
/// ```
761+
///
762+
/// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`,
763+
/// returns the `i32`.
764+
///
765+
/// [`OpaqueDef`]: hir::TyKind::OpaqueDef
766+
fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
767+
let hir = self.infcx.tcx.hir();
768+
769+
if let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind {
770+
let opaque_ty = hir.item(id.id);
771+
if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
772+
bounds:
773+
[hir::GenericBound::LangItemTrait(
774+
hir::LangItem::Future,
775+
_,
776+
_,
777+
hir::GenericArgs {
778+
bindings:
779+
[hir::TypeBinding {
780+
ident: Ident { name: sym::Output, .. },
781+
kind: hir::TypeBindingKind::Equality { ty },
782+
..
783+
}],
784+
..
785+
},
786+
)],
787+
..
788+
}) = opaque_ty.kind
789+
{
790+
ty
791+
} else {
792+
span_bug!(
793+
hir_ty.span,
794+
"bounds from lowered return type of async fn did not match expected format: {:?}",
795+
opaque_ty
796+
);
797+
}
798+
} else {
799+
span_bug!(
800+
hir_ty.span,
801+
"lowered return type of async fn is not OpaqueDef: {:?}",
802+
hir_ty
803+
);
804+
}
805+
}
806+
689807
fn give_name_if_anonymous_region_appears_in_yield_ty(
690808
&self,
691809
fr: RegionVid,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// edition:2018
2+
#![feature(async_closure)]
3+
use std::future::Future;
4+
5+
// test the quality of annotations giving lifetimes names (`'1`) when async constructs are involved
6+
7+
pub async fn async_fn(x: &mut i32) -> &i32 {
8+
let y = &*x;
9+
*x += 1; //~ ERROR cannot assign to `*x` because it is borrowed
10+
y
11+
}
12+
13+
pub fn async_closure(x: &mut i32) -> impl Future<Output=&i32> {
14+
(async move || {
15+
let y = &*x;
16+
*x += 1; //~ ERROR cannot assign to `*x` because it is borrowed
17+
y
18+
})()
19+
}
20+
21+
pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future<Output=&i32> {
22+
(async move || -> &i32 {
23+
let y = &*x;
24+
*x += 1; //~ ERROR cannot assign to `*x` because it is borrowed
25+
y
26+
})()
27+
}
28+
29+
pub fn async_block(x: &mut i32) -> impl Future<Output=&i32> {
30+
async move {
31+
let y = &*x;
32+
*x += 1; //~ ERROR cannot assign to `*x` because it is borrowed
33+
y
34+
}
35+
}
36+
37+
fn main() {}

0 commit comments

Comments
 (0)