Skip to content

Commit e4d9bc6

Browse files
committed
improve diagnosts for GATs
1 parent fe62c6e commit e4d9bc6

File tree

113 files changed

+1585
-850
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1585
-850
lines changed

compiler/rustc_ast_lowering/src/path.rs

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2424
param_mode: ParamMode,
2525
mut itctx: ImplTraitContext<'_, 'hir>,
2626
) -> hir::QPath<'hir> {
27+
debug!("lower_qpath(id: {:?}, qself: {:?}, p: {:?})", id, qself, p);
2728
let qself_position = qself.as_ref().map(|q| q.position);
2829
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx.reborrow()));
2930

@@ -222,6 +223,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
222223
itctx: ImplTraitContext<'_, 'hir>,
223224
explicit_owner: Option<NodeId>,
224225
) -> hir::PathSegment<'hir> {
226+
debug!(
227+
"path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})",
228+
path_span, segment, expected_lifetimes
229+
);
225230
let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
226231
let msg = "parenthesized type parameters may only be used with a `Fn` trait";
227232
match **generic_args {

compiler/rustc_middle/src/middle/resolve_lifetime.rs

+14
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ pub enum Region {
4949
Free(DefId, /* lifetime decl */ DefId),
5050
}
5151

52+
/// This is used in diagnostics to improve suggestions for missing generic arguments.
53+
/// It gives information on the type of lifetimes that are in scope for a particular `PathSegment`,
54+
/// so that we can e.g. suggest elided-lifetimes-in-paths of the form <'_, '_> e.g.
55+
#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)]
56+
pub enum LifetimeScopeForPath {
57+
// Contains all lifetime names that are in scope and could possibly be used in generics
58+
// arguments of path.
59+
NonElided(Vec<String>),
60+
61+
// Information that allows us to suggest args of the form `<'_>` in case
62+
// no generic arguments were provided for a path.
63+
Elided,
64+
}
65+
5266
/// A set containing, at most, one known element.
5367
/// If two distinct values are inserted into a set, then it
5468
/// becomes `Many`, which can be used to detect ambiguities.

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,10 @@ rustc_queries! {
13011301
desc { "looking up late bound vars" }
13021302
}
13031303

1304+
query lifetime_scope_map(_: LocalDefId) -> Option<FxHashMap<ItemLocalId, LifetimeScopeForPath>> {
1305+
desc { "finds the lifetime scope for an HirId of a PathSegment" }
1306+
}
1307+
13041308
query visibility(def_id: DefId) -> ty::Visibility {
13051309
eval_always
13061310
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }

compiler/rustc_middle/src/ty/context.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
99
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
1010
use crate::middle;
1111
use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata};
12-
use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault};
12+
use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault};
1313
use crate::middle::stability;
1414
use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
1515
use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
@@ -2686,6 +2686,10 @@ impl<'tcx> TyCtxt<'tcx> {
26862686
.iter(),
26872687
)
26882688
}
2689+
2690+
pub fn lifetime_scope(self, id: HirId) -> Option<LifetimeScopeForPath> {
2691+
self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id))
2692+
}
26892693
}
26902694

26912695
impl TyCtxtAt<'tcx> {

compiler/rustc_middle/src/ty/query/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
99
use crate::middle::lib_features::LibFeatures;
1010
use crate::middle::privacy::AccessLevels;
1111
use crate::middle::region;
12-
use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes};
12+
use crate::middle::resolve_lifetime::{
13+
LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes,
14+
};
1315
use crate::middle::stability::{self, DeprecationEntry};
1416
use crate::mir;
1517
use crate::mir::interpret::GlobalId;

compiler/rustc_resolve/src/late/lifetimes.rs

+114-18
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
99
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
1010
use rustc_ast::walk_list;
11-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
11+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
1212
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
1313
use rustc_hir as hir;
1414
use rustc_hir::def::{DefKind, Res};
15-
use rustc_hir::def_id::DefIdMap;
15+
use rustc_hir::def_id::{DefIdMap, LocalDefId};
1616
use rustc_hir::hir_id::ItemLocalId;
1717
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1818
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath};
@@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime::*;
2222
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
2323
use rustc_middle::{bug, span_bug};
2424
use rustc_session::lint;
25-
use rustc_span::def_id::{DefId, LocalDefId};
25+
use rustc_span::def_id::DefId;
2626
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2727
use rustc_span::Span;
2828
use std::borrow::Cow;
@@ -158,6 +158,9 @@ struct NamedRegionMap {
158158
// - trait refs
159159
// - bound types (like `T` in `for<'a> T<'a>: Foo`)
160160
late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>,
161+
162+
// maps `PathSegment` `HirId`s to lifetime scopes.
163+
scope_for_path: Option<FxHashMap<LocalDefId, FxHashMap<ItemLocalId, LifetimeScopeForPath>>>,
161164
}
162165

163166
crate struct LifetimeContext<'a, 'tcx> {
@@ -195,7 +198,9 @@ enum Scope<'a> {
195198
/// it should be shifted by the number of `Binder`s in between the
196199
/// declaration `Binder` and the location it's referenced from.
197200
Binder {
198-
lifetimes: FxHashMap<hir::ParamName, Region>,
201+
/// We use an IndexMap here because we want these lifetimes in order
202+
/// for diagnostics.
203+
lifetimes: FxIndexMap<hir::ParamName, Region>,
199204

200205
/// if we extend this scope with another scope, what is the next index
201206
/// we should use for an early-bound region?
@@ -379,6 +384,10 @@ pub fn provide(providers: &mut ty::query::Providers) {
379384
}
380385
},
381386
late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id),
387+
lifetime_scope_map: |tcx, id| {
388+
let item_id = item_for(tcx, id);
389+
do_resolve(tcx, item_id, false, true).scope_for_path.unwrap().remove(&id)
390+
},
382391

383392
..*providers
384393
};
@@ -419,27 +428,29 @@ fn resolve_lifetimes_trait_definition(
419428
tcx: TyCtxt<'_>,
420429
local_def_id: LocalDefId,
421430
) -> ResolveLifetimes {
422-
do_resolve(tcx, local_def_id, true)
431+
convert_named_region_map(do_resolve(tcx, local_def_id, true, false))
423432
}
424433

425434
/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
426435
/// You should not read the result of this query directly, but rather use
427436
/// `named_region_map`, `is_late_bound_map`, etc.
428437
#[tracing::instrument(level = "debug", skip(tcx))]
429438
fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
430-
do_resolve(tcx, local_def_id, false)
439+
convert_named_region_map(do_resolve(tcx, local_def_id, false, false))
431440
}
432441

433442
fn do_resolve(
434443
tcx: TyCtxt<'_>,
435444
local_def_id: LocalDefId,
436445
trait_definition_only: bool,
437-
) -> ResolveLifetimes {
446+
with_scope_for_path: bool,
447+
) -> NamedRegionMap {
438448
let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(local_def_id));
439449
let mut named_region_map = NamedRegionMap {
440450
defs: Default::default(),
441451
late_bound: Default::default(),
442452
late_bound_vars: Default::default(),
453+
scope_for_path: with_scope_for_path.then(|| Default::default()),
443454
};
444455
let mut visitor = LifetimeContext {
445456
tcx,
@@ -455,6 +466,10 @@ fn do_resolve(
455466
};
456467
visitor.visit_item(item);
457468

469+
named_region_map
470+
}
471+
472+
fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes {
458473
let mut rl = ResolveLifetimes::default();
459474

460475
for (hir_id, v) in named_region_map.defs {
@@ -567,6 +582,41 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
567582
}
568583
}
569584

585+
#[tracing::instrument(level = "debug")]
586+
fn get_lifetime_scopes_for_path(mut scope: &Scope<'_>) -> LifetimeScopeForPath {
587+
let mut available_lifetimes = vec![];
588+
loop {
589+
match scope {
590+
Scope::Binder { lifetimes, s, .. } => {
591+
available_lifetimes.extend(lifetimes.keys().filter_map(|p| match p {
592+
hir::ParamName::Plain(ident) => Some(ident.name.to_string()),
593+
_ => None,
594+
}));
595+
scope = s;
596+
}
597+
Scope::Body { s, .. } => {
598+
scope = s;
599+
}
600+
Scope::Elision { elide, s } => {
601+
if let Elide::Exact(_) = elide {
602+
return LifetimeScopeForPath::Elided;
603+
} else {
604+
scope = s;
605+
}
606+
}
607+
Scope::ObjectLifetimeDefault { s, .. } => {
608+
scope = s;
609+
}
610+
Scope::Root => {
611+
return LifetimeScopeForPath::NonElided(available_lifetimes);
612+
}
613+
Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
614+
scope = s;
615+
}
616+
}
617+
}
618+
}
619+
570620
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
571621
/// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
572622
fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
@@ -656,7 +706,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
656706
self.map.late_bound_vars.insert(hir_id, vec![]);
657707
let scope = Scope::Binder {
658708
hir_id,
659-
lifetimes: FxHashMap::default(),
709+
lifetimes: FxIndexMap::default(),
660710
next_early_index: self.next_early_index(),
661711
s: self.scope,
662712
track_lifetime_uses: true,
@@ -720,9 +770,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
720770
// We need to add *all* deps, since opaque tys may want them from *us*
721771
for (&owner, defs) in resolved_lifetimes.defs.iter() {
722772
defs.iter().for_each(|(&local_id, region)| {
723-
self.map
724-
.defs
725-
.insert(hir::HirId { owner, local_id }, region.clone());
773+
self.map.defs.insert(hir::HirId { owner, local_id }, *region);
726774
});
727775
}
728776
for (&owner, late_bound) in resolved_lifetimes.late_bound.iter() {
@@ -836,7 +884,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
836884
};
837885
self.missing_named_lifetime_spots
838886
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
839-
let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) = c
887+
let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) = c
840888
.generic_params
841889
.iter()
842890
.filter_map(|param| match param.kind {
@@ -1010,7 +1058,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
10101058
debug!(?index);
10111059

10121060
let mut elision = None;
1013-
let mut lifetimes = FxHashMap::default();
1061+
let mut lifetimes = FxIndexMap::default();
10141062
let mut non_lifetime_count = 0;
10151063
for param in generics.params {
10161064
match param.kind {
@@ -1181,7 +1229,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
11811229
let mut index = self.next_early_index();
11821230
let mut non_lifetime_count = 0;
11831231
debug!("visit_ty: index = {}", index);
1184-
let lifetimes: FxHashMap<hir::ParamName, Region> = generics
1232+
let lifetimes: FxIndexMap<hir::ParamName, Region> = generics
11851233
.params
11861234
.iter()
11871235
.filter_map(|param| match param.kind {
@@ -1241,15 +1289,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
12411289
self.resolve_lifetime_ref(lifetime_ref);
12421290
}
12431291

1292+
fn visit_assoc_type_binding(&mut self, type_binding: &'tcx hir::TypeBinding<'_>) {
1293+
let scope = self.scope;
1294+
if let Some(scope_for_path) = self.map.scope_for_path.as_mut() {
1295+
// We add lifetime scope information for `Ident`s in associated type bindings and use
1296+
// the `HirId` of the type binding as the key in `LifetimeMap`
1297+
let lifetime_scope = get_lifetime_scopes_for_path(scope);
1298+
let map = scope_for_path.entry(type_binding.hir_id.owner).or_default();
1299+
map.insert(type_binding.hir_id.local_id, lifetime_scope);
1300+
}
1301+
hir::intravisit::walk_assoc_type_binding(self, type_binding);
1302+
}
1303+
12441304
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
12451305
for (i, segment) in path.segments.iter().enumerate() {
12461306
let depth = path.segments.len() - i - 1;
12471307
if let Some(ref args) = segment.args {
12481308
self.visit_segment_args(path.res, depth, args);
12491309
}
1310+
1311+
let scope = self.scope;
1312+
if let Some(scope_for_path) = self.map.scope_for_path.as_mut() {
1313+
// Add lifetime scope information to path segment. Note we cannot call `visit_path_segment`
1314+
// here because that call would yield to resolution problems due to `walk_path_segment`
1315+
// being called, which processes the path segments generic args, which we have already
1316+
// processed using `visit_segment_args`.
1317+
let lifetime_scope = get_lifetime_scopes_for_path(scope);
1318+
if let Some(hir_id) = segment.hir_id {
1319+
let map = scope_for_path.entry(hir_id.owner).or_default();
1320+
map.insert(hir_id.local_id, lifetime_scope);
1321+
}
1322+
}
12501323
}
12511324
}
12521325

1326+
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'tcx hir::PathSegment<'tcx>) {
1327+
let scope = self.scope;
1328+
if let Some(scope_for_path) = self.map.scope_for_path.as_mut() {
1329+
let lifetime_scope = get_lifetime_scopes_for_path(scope);
1330+
if let Some(hir_id) = path_segment.hir_id {
1331+
let map = scope_for_path.entry(hir_id.owner).or_default();
1332+
map.insert(hir_id.local_id, lifetime_scope);
1333+
}
1334+
}
1335+
1336+
intravisit::walk_path_segment(self, path_span, path_segment);
1337+
}
1338+
12531339
fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
12541340
let output = match fd.output {
12551341
hir::FnRetTy::DefaultReturn(_) => None,
@@ -1290,7 +1376,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
12901376
ref bound_generic_params,
12911377
..
12921378
}) => {
1293-
let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) =
1379+
let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) =
12941380
bound_generic_params
12951381
.iter()
12961382
.filter_map(|param| match param.kind {
@@ -1360,7 +1446,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
13601446
self.map.late_bound_vars.insert(*hir_id, binders);
13611447
let scope = Scope::Binder {
13621448
hir_id: *hir_id,
1363-
lifetimes: FxHashMap::default(),
1449+
lifetimes: FxIndexMap::default(),
13641450
s: self.scope,
13651451
next_early_index: self.next_early_index(),
13661452
track_lifetime_uses: true,
@@ -1388,7 +1474,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
13881474
let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
13891475

13901476
let initial_bound_vars = binders.len() as u32;
1391-
let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
1477+
let mut lifetimes: FxIndexMap<hir::ParamName, Region> = FxIndexMap::default();
13921478
let binders_iter = trait_ref
13931479
.bound_generic_params
13941480
.iter()
@@ -2115,7 +2201,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21152201

21162202
let mut non_lifetime_count = 0;
21172203
let mut named_late_bound_vars = 0;
2118-
let lifetimes: FxHashMap<hir::ParamName, Region> = generics
2204+
let lifetimes: FxIndexMap<hir::ParamName, Region> = generics
21192205
.params
21202206
.iter()
21212207
.filter_map(|param| match param.kind {
@@ -3034,6 +3120,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
30343120
}
30353121
};
30363122

3123+
// If we specifically need the `scope_for_path` map, then we're in the
3124+
// diagnostic pass and we don't want to emit more errors.
3125+
if self.map.scope_for_path.is_some() {
3126+
self.tcx.sess.delay_span_bug(
3127+
rustc_span::DUMMY_SP,
3128+
"Encountered unexpected errors during diagnostics related part",
3129+
);
3130+
return;
3131+
}
3132+
30373133
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
30383134
spans.sort();
30393135
let mut spans_dedup = spans.clone();

0 commit comments

Comments
 (0)