Skip to content

Commit

Permalink
Rollup merge of rust-lang#97023 - cjgillot:uniform-anon, r=estebank
Browse files Browse the repository at this point in the history
Diagnose anonymous lifetimes errors more uniformly between async and regular fns

Async fns and regular fns are desugared differently.  For the former, we create a generic parameter at HIR level.  For the latter, we just create an anonymous region for typeck.

I plan to migrate regular fns to the async fn desugaring.

Before that, this PR attempts to merge the diagnostics for both cases.

r? `@estebank`
  • Loading branch information
Dylan-DPC authored Jun 1, 2022
2 parents 5f5a27f + 0cf79d7 commit 9f84345
Show file tree
Hide file tree
Showing 33 changed files with 620 additions and 194 deletions.
10 changes: 6 additions & 4 deletions compiler/rustc_borrowck/src/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,15 +567,17 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
let lifetime =
self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
match lifetime.name {
hir::LifetimeName::Param(_)
hir::LifetimeName::Param(hir::ParamName::Plain(_) | hir::ParamName::Error)
| hir::LifetimeName::Error
| hir::LifetimeName::Static
| hir::LifetimeName::Underscore => {
| hir::LifetimeName::Static => {
let lifetime_span = lifetime.span;
Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span))
}

hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => {
hir::LifetimeName::Param(hir::ParamName::Fresh(_))
| hir::LifetimeName::ImplicitObjectLifetimeDefault
| hir::LifetimeName::Implicit
| hir::LifetimeName::Underscore => {
// In this case, the user left off the lifetime; so
// they wrote something like:
//
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ impl LifetimeName {
}
}

pub fn is_anonymous(&self) -> bool {
match *self {
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit
| LifetimeName::Underscore
| LifetimeName::Param(ParamName::Fresh(_))
| LifetimeName::Error => true,
LifetimeName::Static | LifetimeName::Param(_) => false,
}
}

pub fn is_elided(&self) -> bool {
match self {
LifetimeName::ImplicitObjectLifetimeDefault
Expand Down
62 changes: 36 additions & 26 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ use rustc_middle::ty::{
subst::{GenericArgKind, Subst, SubstsRef},
Binder, EarlyBinder, List, Region, Ty, TyCtxt, TypeFoldable,
};
use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::ops::ControlFlow;
use std::{cmp, fmt, iter};
Expand Down Expand Up @@ -161,35 +161,45 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>(
{
sp = param.span;
}
(format!("the lifetime `{}` as defined here", br.name), sp)
let text = if br.has_name() {
format!("the lifetime `{}` as defined here", br.name)
} else {
format!("the anonymous lifetime as defined here")
};
(text, sp)
}
ty::ReFree(ty::FreeRegion {
bound_region: ty::BoundRegionKind::BrNamed(_, name), ..
}) => {
let mut sp = sm.guess_head_span(tcx.def_span(scope));
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
sp = param.span;
}
(format!("the lifetime `{}` as defined here", name), sp)
}
ty::ReFree(ref fr) => match fr.bound_region {
ty::BrAnon(idx) => {
if let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) {
("the anonymous lifetime defined here".to_string(), ty.span)
} else {
(
("the anonymous lifetime defined here".to_string(), ty.span)
} else {
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let mut sp = sm.guess_head_span(tcx.def_span(scope));
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
{
sp = param.span;
}
let text = if name == kw::UnderscoreLifetime {
format!("the anonymous lifetime as defined here")
} else {
format!("the lifetime `{}` as defined here", name)
};
(text, sp)
}
ty::BrAnon(idx) => (
format!("the anonymous lifetime #{} defined here", idx + 1),
tcx.def_span(scope),
)
tcx.def_span(scope)
),
_ => (
format!("the lifetime `{}` as defined here", region),
sm.guess_head_span(tcx.def_span(scope)),
),
}
}
_ => (
format!("the lifetime `{}` as defined here", region),
sm.guess_head_span(tcx.def_span(scope)),
),
},
}
_ => bug!(),
}
}
Expand Down Expand Up @@ -2552,7 +2562,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
| ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }),
_,
) => {
) if name != kw::UnderscoreLifetime => {
// Does the required lifetime have a nice name we can print?
let mut err = struct_span_err!(
self.tcx.sess,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::{GenericParamKind, Ty};
use rustc_middle::ty::Region;
use rustc_span::symbol::kw;

impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
Expand Down Expand Up @@ -169,7 +170,7 @@ pub fn suggest_adding_lifetime_params<'tcx>(
return false;
};

if !lifetime_sub.name.is_elided() || !lifetime_sup.name.is_elided() {
if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() {
return false;
};

Expand All @@ -188,32 +189,37 @@ pub fn suggest_adding_lifetime_params<'tcx>(
_ => return false,
};

let (suggestion_param_name, introduce_new) = generics
let suggestion_param_name = generics
.params
.iter()
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
.and_then(|p| tcx.sess.source_map().span_to_snippet(p.span).ok())
.map(|name| (name, false))
.unwrap_or_else(|| ("'a".to_string(), true));

let mut suggestions = vec![
if let hir::LifetimeName::Underscore = lifetime_sub.name {
(lifetime_sub.span, suggestion_param_name.clone())
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
.map(|p| p.name.ident().name)
.find(|i| *i != kw::UnderscoreLifetime);
let introduce_new = suggestion_param_name.is_none();
let suggestion_param_name =
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());

debug!(?lifetime_sup.span);
debug!(?lifetime_sub.span);
let make_suggestion = |span: rustc_span::Span| {
if span.is_empty() {
(span, format!("{}, ", suggestion_param_name))
} else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() {
(span.shrink_to_hi(), format!("{} ", suggestion_param_name))
} else {
(lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
},
if let hir::LifetimeName::Underscore = lifetime_sup.name {
(lifetime_sup.span, suggestion_param_name.clone())
} else {
(lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
},
];
(span, suggestion_param_name.clone())
}
};
let mut suggestions =
vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)];

if introduce_new {
let new_param_suggestion = match &generics.params {
[] => (generics.span, format!("<{}>", suggestion_param_name)),
[first, ..] => (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)),
};
let new_param_suggestion =
if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) {
(first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
} else {
(generics.span, format!("<{}>", suggestion_param_name))
};

suggestions.push(new_param_suggestion);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_middle::ty;
use rustc_span::symbol::kw;

impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
Expand Down Expand Up @@ -67,7 +68,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
let is_impl_item = region_info.is_impl_item;

match br {
ty::BrAnon(_) => {}
ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) => {}
_ => {
/* not an anonymous region */
debug!("try_report_named_anon_conflict: not an anonymous region");
Expand Down
71 changes: 28 additions & 43 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2177,61 +2177,47 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
define_scoped_cx!(self);

let mut region_index = self.region_index;
let mut next_name = |this: &Self| loop {
let name = name_by_region_index(region_index);
region_index += 1;
if !this.used_region_names.contains(&name) {
break name;
}
};

// If we want to print verbosely, then print *all* binders, even if they
// aren't named. Eventually, we might just want this as the default, but
// this is not *quite* right and changes the ordering of some output
// anyways.
let (new_value, map) = if self.tcx().sess.verbose() {
// anon index + 1 (BrEnv takes 0) -> name
let mut region_map: BTreeMap<u32, Symbol> = BTreeMap::default();
let mut region_map: FxHashMap<_, _> = Default::default();
let bound_vars = value.bound_vars();
for var in bound_vars {
let ty::BoundVariableKind::Region(var) = var else { continue };
match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name)) => {
ty::BrAnon(_) | ty::BrEnv => {
start_or_continue(&mut self, "for<", ", ");
let name = next_name(&self);
do_continue(&mut self, name);
region_map.insert(var, ty::BrNamed(CRATE_DEF_ID.to_def_id(), name));
}
ty::BoundVariableKind::Region(ty::BrAnon(i)) => {
ty::BrNamed(def_id, kw::UnderscoreLifetime) => {
start_or_continue(&mut self, "for<", ", ");
let name = loop {
let name = name_by_region_index(region_index);
region_index += 1;
if !self.used_region_names.contains(&name) {
break name;
}
};
let name = next_name(&self);
do_continue(&mut self, name);
region_map.insert(i + 1, name);
region_map.insert(var, ty::BrNamed(def_id, name));
}
ty::BoundVariableKind::Region(ty::BrEnv) => {
ty::BrNamed(_, name) => {
start_or_continue(&mut self, "for<", ", ");
let name = loop {
let name = name_by_region_index(region_index);
region_index += 1;
if !self.used_region_names.contains(&name) {
break name;
}
};
do_continue(&mut self, name);
region_map.insert(0, name);
}
_ => continue,
}
}
start_or_continue(&mut self, "", "> ");

self.tcx.replace_late_bound_regions(value.clone(), |br| {
let kind = match br.kind {
ty::BrNamed(_, _) => br.kind,
ty::BrAnon(i) => {
let name = region_map[&(i + 1)];
ty::BrNamed(CRATE_DEF_ID.to_def_id(), name)
}
ty::BrEnv => {
let name = region_map[&0];
ty::BrNamed(CRATE_DEF_ID.to_def_id(), name)
}
};
let kind = region_map[&br.kind];
self.tcx.mk_region(ty::ReLateBound(
ty::INNERMOST,
ty::BoundRegion { var: br.var, kind },
Expand All @@ -2242,21 +2228,20 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
let mut name = |br: ty::BoundRegion| {
start_or_continue(&mut self, "for<", ", ");
let kind = match br.kind {
ty::BrNamed(_, name) => {
do_continue(&mut self, name);
br.kind
}
ty::BrAnon(_) | ty::BrEnv => {
let name = loop {
let name = name_by_region_index(region_index);
region_index += 1;
if !self.used_region_names.contains(&name) {
break name;
}
};
let name = next_name(&self);
do_continue(&mut self, name);
ty::BrNamed(CRATE_DEF_ID.to_def_id(), name)
}
ty::BrNamed(def_id, kw::UnderscoreLifetime) => {
let name = next_name(&self);
do_continue(&mut self, name);
ty::BrNamed(def_id, name)
}
ty::BrNamed(_, name) => {
do_continue(&mut self, name);
br.kind
}
};
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var: br.var, kind }))
};
Expand Down
Loading

0 comments on commit 9f84345

Please sign in to comment.