diff options
| author | bors <bors@rust-lang.org> | 2022-07-25 20:02:55 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-07-25 20:02:55 +0000 |
| commit | 6dbae3ad19309bb541d9e76638e6aa4b5449f29a (patch) | |
| tree | a35d361e27036633b9422e60fae27c050e298ec8 /compiler/rustc_resolve/src | |
| parent | bdf520fd419cd4dea184332f57206f1cf5ca3e8f (diff) | |
| parent | b9bd65e2ca68342bdb8ab56134677ad330d786ba (diff) | |
| download | rust-6dbae3ad19309bb541d9e76638e6aa4b5449f29a.tar.gz rust-6dbae3ad19309bb541d9e76638e6aa4b5449f29a.zip | |
Auto merge of #97313 - cjgillot:ast-lifetimes-anon, r=petrochenkov
Resolve function lifetime elision on the AST ~Based on https://github.com/rust-lang/rust/pull/97720~ Lifetime elision for functions is purely syntactic in nature, so can be resolved on the AST. This PR replicates the elision logic and diagnostics on the AST, and replaces HIR-based resolution by a `delay_span_bug`. This refactor allows for more consistent diagnostics, which don't have to guess the original code from HIR. r? `@petrochenkov`
Diffstat (limited to 'compiler/rustc_resolve/src')
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 660 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 851 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/lifetimes.rs | 235 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/lib.rs | 1 |
4 files changed, 823 insertions, 924 deletions
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e739ed678d8..9b5fd4ea6d1 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_middle::middle::resolve_lifetime::Set1; use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -43,6 +44,10 @@ type IdentMap<T> = FxHashMap<Ident, T>; /// Map from the name in a pattern to its binding mode. type BindingMap = IdentMap<BindingInfo>; +use diagnostics::{ + ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind, +}; + #[derive(Copy, Clone, Debug)] struct BindingInfo { span: Span, @@ -258,7 +263,13 @@ enum LifetimeRibKind { AnonymousReportError, /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId, /* in_fn_return */ bool), + AnonymousPassThrough(NodeId), + + /// Replace all anonymous lifetimes by provided lifetime. + Elided(LifetimeRes), + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, } #[derive(Copy, Clone, Debug)] @@ -522,6 +533,10 @@ struct DiagnosticMetadata<'ast> { /// When processing impl trait currently_processing_impl_trait: Option<(TraitRef, Ty)>, + + /// Accumulate the errors due to missed lifetime elision, + /// and report them all at once for each function. + current_elision_failures: Vec<MissingLifetime>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -540,6 +555,13 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// The current set of local scopes for lifetimes. lifetime_ribs: Vec<LifetimeRib>, + /// We are looking for lifetimes in an elision context. + /// The set contains all the resolutions that we encountered so far. + /// They will be used to determine the correct lifetime for the fn return type. + /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named + /// lifetimes. + lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>, + /// The trait that the current context can refer to. current_trait_ref: Option<(Module<'a>, TraitRef)>, @@ -580,7 +602,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_anon_const(&mut self, constant: &'ast AnonConst) { // We deal with repeat expressions explicitly in `resolve_expr`. self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(constant, IsRepeatExpr::No); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(constant, IsRepeatExpr::No); + }) }) } fn visit_expr(&mut self, expr: &'ast Expr) { @@ -607,8 +631,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { TyKind::Rptr(None, _) => { // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with // NodeId `ty.id`. + // This span will be used in case of elision failure. let span = self.r.session.source_map().next_point(ty.span.shrink_to_lo()); self.resolve_elided_lifetime(ty.id, span); + visit::walk_ty(self, ty); } TyKind::Path(ref qself, ref path) => { self.diagnostic_metadata.current_type_path = Some(ty); @@ -634,8 +660,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| this.visit_path(&path, ty.id), ); - self.diagnostic_metadata.current_type_path = prev_ty; - return; + } else { + visit::walk_ty(self, ty) } } TyKind::ImplicitSelf => { @@ -649,9 +675,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { ) .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); + visit::walk_ty(self, ty) + } + TyKind::ImplTrait(..) => { + let candidates = self.lifetime_elision_candidates.take(); + visit::walk_ty(self, ty); + self.lifetime_elision_candidates = candidates; } TyKind::TraitObject(ref bounds, ..) => { self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); + visit::walk_ty(self, ty) } TyKind::BareFn(ref bare_fn) => { let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo()); @@ -665,25 +698,20 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| { this.visit_generic_params(&bare_fn.generic_params, false); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: ty.id, - report_in_path: false, - }, - |this| walk_list!(this, visit_param, &bare_fn.decl.inputs), - ); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(ty.id, true), - |this| this.visit_fn_ret_ty(&bare_fn.decl.output), + this.resolve_fn_signature( + ty.id, + None, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &bare_fn.decl.output, ); }, - ); - self.diagnostic_metadata.current_trait_object = prev; - return; + ) } - _ => (), + _ => visit::walk_ty(self, ty), } - visit::walk_ty(self, ty); self.diagnostic_metadata.current_trait_object = prev; self.diagnostic_metadata.current_type_path = prev_ty; } @@ -757,18 +785,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { | FnKind::Fn(_, _, sig, _, generics, None) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: false, - }, - |this| walk_list!(this, visit_param, &sig.decl.inputs), - ); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, true), - |this| this.visit_fn_ret_ty(&sig.decl.output), + self.resolve_fn_signature( + fn_id, + None, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, ); return; } @@ -793,19 +815,15 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let declaration = &sig.decl; let async_node_id = sig.header.asyncness.opt_return_id(); - // Argument-position elided lifetimes must be transformed into fresh - // generic parameters. This is especially useful for `async fn`, where - // these fresh generic parameters can be applied to the opaque `impl Trait` - // return type. - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - // Only emit a hard error for `async fn`, since this kind of - // elision has always been allowed in regular `fn`s. - report_in_path: async_node_id.is_some(), - }, - // Add each argument to the rib. - |this| this.resolve_params(&declaration.inputs), + this.resolve_fn_signature( + fn_id, + async_node_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, ); // Construct the list of in-scope lifetime parameters for async lowering. @@ -844,23 +862,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .insert(async_node_id, extra_lifetime_params); } - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough( - // For async fn, the return type appears inside a custom - // `impl Future` RPIT, so we override the binder's id. - async_node_id.unwrap_or(fn_id), - true, - ), - |this| visit::walk_fn_ret_ty(this, &declaration.output), - ); - if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), + LifetimeRibKind::AnonymousPassThrough(fn_id), |this| this.visit_block(body), ); @@ -888,7 +896,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::AnonymousPassThrough(fn_id, true) + LifetimeRibKind::AnonymousPassThrough(fn_id) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -900,7 +908,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), + LifetimeRibKind::AnonymousPassThrough(fn_id), |this| this.visit_expr(body), ); @@ -1030,16 +1038,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { kind: LifetimeBinderKind::PolyTrait, .. } => { - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder, - report_in_path: false, - }, - |this| walk_list!(this, visit_ty, &p_args.inputs), - ); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(binder, true), - |this| visit::walk_fn_ret_ty(this, &p_args.output), + self.resolve_fn_signature( + binder, + None, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, ); break; } @@ -1052,6 +1056,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { LifetimeRibKind::AnonymousPassThrough(..) | LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure | LifetimeRibKind::AnonConst | LifetimeRibKind::ConstGeneric => {} } @@ -1156,6 +1162,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, label_ribs: Vec::new(), lifetime_ribs: Vec::new(), + lifetime_elision_candidates: None, current_trait_ref: None, diagnostic_metadata: Box::new(DiagnosticMetadata::default()), // errors at module scope should always be reported @@ -1362,7 +1369,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { work: impl FnOnce(&mut Self) -> T, ) -> T { self.lifetime_ribs.push(LifetimeRib::new(kind)); + let outer_elision_candidates = self.lifetime_elision_candidates.take(); let ret = work(self); + self.lifetime_elision_candidates = outer_elision_candidates; self.lifetime_ribs.pop(); ret } @@ -1372,7 +1381,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let ident = lifetime.ident; if ident.name == kw::StaticLifetime { - self.record_lifetime_res(lifetime.id, LifetimeRes::Static); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Static, + LifetimeElisionCandidate::Named, + ); return; } @@ -1385,7 +1398,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let rib = &self.lifetime_ribs[i]; let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); if let LifetimeRes::Param { param, .. } = res { match self.lifetime_uses.entry(param) { @@ -1399,15 +1412,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Do not suggest eliding a lifetime where an anonymous // lifetime would be illegal. LifetimeRibKind::Item - | LifetimeRibKind::AnonymousPassThrough(_, true) - | LifetimeRibKind::AnonymousReportError => { - Some(LifetimeUseSet::Many) - } + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousPassThrough(_, false) + LifetimeRibKind::AnonymousPassThrough(_) | LifetimeRibKind::AnonymousCreateParameter { .. } => { Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) } + // Only report if eliding the lifetime would have the same + // semantics. + LifetimeRibKind::Elided(r) => Some(if res == r { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }), LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => None, @@ -1429,12 +1447,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Item => break, LifetimeRibKind::ConstGeneric => { self.emit_non_static_lt_in_const_generic_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } LifetimeRibKind::AnonConst => { self.maybe_emit_forbidden_non_static_lifetime_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } _ => {} @@ -1452,19 +1478,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } self.emit_undeclared_lifetime_error(lifetime, outer_res); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); + let missing_lifetime = MissingLifetime { + id: lifetime.id, + span: lifetime.ident.span, + kind: if elided { + MissingLifetimeKind::Ampersand + } else { + MissingLifetimeKind::Underscore + }, + count: 1, + }; + let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for i in (0..self.lifetime_ribs.len()).rev() { let rib = &mut self.lifetime_ribs[i]; + debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder); - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } LifetimeRibKind::AnonymousReportError => { @@ -1486,39 +1524,46 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(lifetime.ident.span, note) .emit(); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } - LifetimeRibKind::AnonymousPassThrough(node_id, _) => { + LifetimeRibKind::AnonymousPassThrough(node_id) => { self.record_lifetime_res( lifetime.id, LifetimeRes::Anonymous { binder: node_id, elided }, + elision_candidate, ); return; } + LifetimeRibKind::Elided(res) => { + self.record_lifetime_res(lifetime.id, res, elision_candidate); + return; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + return; + } LifetimeRibKind::Item => break, LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => {} } } - // This resolution is wrong, it passes the work to HIR lifetime resolution. - // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic. - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided }, - ); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); + let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; + self.record_lifetime_res( anchor_id, LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) }, + LifetimeElisionCandidate::Ignore, ); - - let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; self.resolve_anonymous_lifetime(<, true); } @@ -1603,16 +1648,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res( segment_id, LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end }, + LifetimeElisionCandidate::Ignore, ); if !missing { // Do not create a parameter for patterns and expressions. for rib in self.lifetime_ribs.iter().rev() { match rib.kind { - LifetimeRibKind::AnonymousPassThrough(binder, _) => { + LifetimeRibKind::AnonymousPassThrough(binder) => { let res = LifetimeRes::Anonymous { binder, elided: true }; for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); } break; } @@ -1627,11 +1673,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); } break; } LifetimeRibKind::AnonymousCreateParameter { .. } + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure | LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => {} @@ -1640,6 +1688,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { continue; } + let missing_lifetime = MissingLifetime { + id: node_ids.start, + span: elided_lifetime_span, + kind: if segment.has_generic_args { + MissingLifetimeKind::Comma + } else { + MissingLifetimeKind::Brackets + }, + count: expected_lifetimes, + }; let mut should_lint = true; for rib in self.lifetime_ribs.iter().rev() { match rib.kind { @@ -1670,23 +1728,60 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { should_lint = false; for id in node_ids { - self.record_lifetime_res(id, LifetimeRes::Error); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Named, + ); } break; } // Do not create a parameter for patterns and expressions. LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { + // Group all suggestions into the first record. + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { let res = self.create_fresh_lifetime(id, ident, binder); - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Named), + ); } break; } // `PassThrough` is the normal case. - LifetimeRibKind::AnonymousPassThrough(binder, _) => { + LifetimeRibKind::AnonymousPassThrough(binder) => { let res = LifetimeRes::Anonymous { binder, elided: true }; + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Ignore), + ); + } + break; + } + LifetimeRibKind::Elided(res) => { + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); + for id in node_ids { + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Ignore), + ); + } + break; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } break; } @@ -1695,13 +1790,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // we simply resolve to an implicit lifetime, which will be checked later, at // which point a suitable error will be emitted. LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); break; } LifetimeRibKind::Generics { .. } @@ -1728,13 +1824,223 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[tracing::instrument(level = "debug", skip(self))] - fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) { + fn record_lifetime_res( + &mut self, + id: NodeId, + res: LifetimeRes, + candidate: LifetimeElisionCandidate, + ) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!( "lifetime {:?} resolved multiple times ({:?} before, {:?} now)", id, prev_res, res ) } + match res { + LifetimeRes::Param { .. } + | LifetimeRes::Fresh { .. } + | LifetimeRes::Anonymous { .. } + | LifetimeRes::Static => { + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + candidates.insert(res, candidate); + } + } + LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + } + } + + #[tracing::instrument(level = "debug", skip(self))] + fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) { + if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { + panic!( + "lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)", + id, prev_res, res + ) + } + } + + /// Perform resolution of a function signature, accounting for lifetime elision. + #[tracing::instrument(level = "debug", skip(self, inputs))] + fn resolve_fn_signature( + &mut self, + fn_id: NodeId, + async_node_id: Option<NodeId>, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone, + output_ty: &'ast FnRetTy, + ) { + // Add each argument to the rib. + let parameter_rib = LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: async_node_id.is_some(), + }; + let elision_lifetime = + self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs)); + debug!(?elision_lifetime); + + let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); + let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + LifetimeRibKind::Elided(*res) + } else { + LifetimeRibKind::ElisionFailure + }; + self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, &output_ty)); + let elision_failures = + replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures); + if !elision_failures.is_empty() { + let Err(failure_info) = elision_lifetime else { bug!() }; + self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + } + } + + /// Resolve inside function parameters and parameter types. + /// Returns the lifetime for elision in fn return type, + /// or diagnostic information in case of elision failure. + fn resolve_fn_params( + &mut self, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>, + ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> { + let outer_candidates = + replace(&mut self.lifetime_elision_candidates, Some(Default::default())); + + let mut elision_lifetime = None; + let mut lifetime_count = 0; + let mut parameter_info = Vec::new(); + + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + for (index, (pat, ty)) in inputs.enumerate() { + debug!(?pat, ?ty); + if let Some(pat) = pat { + self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + } + self.visit_ty(ty); + + if let Some(ref candidates) = self.lifetime_elision_candidates { + let new_count = candidates.len(); + let local_count = new_count - lifetime_count; + if local_count != 0 { + parameter_info.push(ElisionFnParameter { + index, + ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind { + Some(ident) + } else { + None + }, + lifetime_count: local_count, + span: ty.span, + }); + } + lifetime_count = new_count; + } + + // Handle `self` specially. + if index == 0 && has_self { + let self_lifetime = self.find_lifetime_for_self(ty); + if let Set1::One(lifetime) = self_lifetime { + elision_lifetime = Some(lifetime); + self.lifetime_elision_candidates = None; + } else { + self.lifetime_elision_candidates = Some(Default::default()); + lifetime_count = 0; + } + } + debug!("(resolving function / closure) recorded parameter"); + } + + let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates); + debug!(?all_candidates); + + if let Some(res) = elision_lifetime { + return Ok(res); + } + + // We do not have a `self` candidate, look at the full list. + let all_candidates = all_candidates.unwrap(); + if all_candidates.len() == 1 { + Ok(*all_candidates.first().unwrap().0) + } else { + let all_candidates = all_candidates + .into_iter() + .filter_map(|(_, candidate)| match candidate { + LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None, + LifetimeElisionCandidate::Missing(missing) => Some(missing), + }) + .collect(); + Err((all_candidates, parameter_info)) + } + } + + /// List all the lifetimes that appear in the provided type. + fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> { + struct SelfVisitor<'r, 'a> { + r: &'r Resolver<'a>, + impl_self: Option<Res>, + lifetime: Set1<LifetimeRes>, + } + + impl SelfVisitor<'_, '_> { + // Look for `self: &'a Self` - also desugared from `&'a self`, + // and if that matches, use it for elision and return early. + fn is_self_ty(&self, ty: &Ty) -> bool { + match ty.kind { + TyKind::ImplicitSelf => true, + TyKind::Path(None, _) => { + let path_res = self.r.partial_res_map[&ty.id].base_res(); + if let Res::SelfTy { .. } = path_res { + return true; + } + Some(path_res) == self.impl_self + } + _ => false, + } + } + } + + impl<'a> Visitor<'a> for SelfVisitor<'_, '_> { + fn visit_ty(&mut self, ty: &'a Ty) { + trace!("SelfVisitor considering ty={:?}", ty); + if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) { + let lt_id = if let Some(lt) = lt { + lt.id + } else { + let res = self.r.lifetimes_res_map[&ty.id]; + let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() }; + start + }; + let lt_res = self.r.lifetimes_res_map[<_id]; + trace!("SelfVisitor inserting res={:?}", lt_res); + self.lifetime.insert(lt_res); + } + visit::walk_ty(self, ty) + } + } + + let impl_self = self + .diagnostic_metadata + .current_self_type + .as_ref() + .and_then(|ty| { + if let TyKind::Path(None, _) = ty.kind { + self.r.partial_res_map.get(&ty.id) + } else { + None + } + }) + .map(|res| res.base_res()) + .filter(|res| { + // Permit the types that unambiguously always + // result in the same type constructor being used + // (it can't differ between `Self` and `self`). + matches!( + res, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_) + ) + }); + let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty }; + visitor.visit_ty(ty); + trace!("SelfVisitor found={:?}", visitor.lifetime); + visitor.lifetime } /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved @@ -1959,22 +2265,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { self.with_item_rib(|this| { - this.visit_ty(ty); - if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), - ); - } + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.visit_ty(ty); + }); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(item.id), + |this| { + if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); + } + }, + ); }); } @@ -2045,7 +2358,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { { diagnostics::signal_lifetime_shadowing(self.r.session, original, param.ident); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2056,7 +2369,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.report_error(param.ident.span, err); if let GenericParamKind::Lifetime = param.kind { // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } } @@ -2075,7 +2388,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "`'_` is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2090,7 +2403,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "'static is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2102,7 +2415,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), GenericParamKind::Lifetime => { let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_res(param.id, res); + self.record_lifetime_param(param.id, res); function_lifetime_rib.bindings.insert(ident, (param.id, res)); continue; } @@ -2125,7 +2438,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[TypeNS].pop(); self.ribs[ValueNS].pop(); - self.lifetime_ribs.pop(); + let function_lifetime_rib = self.lifetime_ribs.pop().unwrap(); + + // Do not account for the parameters we just bound for function lifetime elision. + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + for (_, res) in function_lifetime_rib.bindings.values() { + candidates.remove(res); + } + } if let LifetimeBinderKind::BareFnType | LifetimeBinderKind::WhereBound @@ -2223,20 +2543,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - None, - |this| this.visit_expr(expr), + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(item.id), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }, ); } } AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } - AssocItemKind::TyAlias(box TyAlias { generics, .. }) => { - walk_assoc_item(self, generics, LifetimeBinderKind::Item, item); - } + AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self + .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + walk_assoc_item(this, generics, LifetimeBinderKind::Item, item) + }), AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") } @@ -2307,7 +2633,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Generics { span: generics.span, binder: item_id, - kind: LifetimeBinderKind::ImplBlock + kind: LifetimeBinderKind::ImplBlock, }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -2327,7 +2653,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Register the trait definitions from here. if let Some(trait_id) = trait_id { - this.r.trait_impls.entry(trait_id).or_default().push(item_def_id); + this.r + .trait_impls + .entry(trait_id) + .or_default() + .push(item_def_id); } let item_def_id = item_def_id.to_def_id(); @@ -2346,21 +2676,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_generics(generics); // Resolve the items within the impl. - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false), - |this| { - this.with_current_self_type(self_type, |this| { - this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { - debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); - for item in impl_items { - this.resolve_impl_item(&**item); - } - }); - }); - }, - ); + this.with_current_self_type(self_type, |this| { + this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { + debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); + for item in impl_items { + this.resolve_impl_item(&**item); + } + }); + }); }); }, - ); + ) }, ); }); @@ -2391,9 +2717,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| { - this.visit_expr(expr) - }); + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(item.id), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }, + ); } } AssocItemKind::Fn(box Fn { generics, .. }) => { @@ -2435,18 +2769,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { kind: LifetimeBinderKind::Item, }, |this| { - // If this is a trait impl, ensure the type - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - TypeNS, - item.span, - |i, s, c| TypeNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + // If this is a trait impl, ensure the type + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + TypeNS, + item.span, + |i, s, c| TypeNotMemberOfTrait(i, s, c), + ); - visit::walk_assoc_item(this, item, AssocCtxt::Impl) + visit::walk_assoc_item(this, item, AssocCtxt::Impl) + }); }, ); } @@ -3581,7 +3917,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(ct, IsRepeatExpr::Yes) + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(ct, IsRepeatExpr::Yes) + }) }); } ExprKind::ConstBlock(ref ct) => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e428bae479b..f979403e097 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,7 +1,6 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; -use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; +use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; @@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment}; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, - NodeId, Path, Ty, TyKind, + NodeId, Path, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust::path_segment_to_string; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -20,7 +19,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -29,7 +28,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span}; use std::iter; use std::ops::Deref; @@ -59,45 +58,6 @@ impl AssocSuggestion { } } -pub(crate) enum MissingLifetimeSpot<'tcx> { - Generics(&'tcx hir::Generics<'tcx>), - HigherRanked { span: Span, span_type: ForLifetimeSpanType }, - Static, -} - -pub(crate) enum ForLifetimeSpanType { - BoundEmpty, - BoundTail, - TypeEmpty, - TypeTail, - ClosureEmpty, - ClosureTail, -} - -impl ForLifetimeSpanType { - pub(crate) fn descr(&self) -> &'static str { - match self { - Self::BoundEmpty | Self::BoundTail => "bound", - Self::TypeEmpty | Self::TypeTail => "type", - Self::ClosureEmpty | Self::ClosureTail => "closure", - } - } - - pub(crate) fn suggestion(&self, sugg: impl std::fmt::Display) -> String { - match self { - Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), - Self::ClosureEmpty => format!("for<{}>", sugg), - Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg), - } - } -} - -impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> { - fn into(self) -> MissingLifetimeSpot<'tcx> { - MissingLifetimeSpot::Generics(self) - } -} - fn is_self_type(path: &[Segment], namespace: Namespace) -> bool { namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper } @@ -122,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str (variant_path_string, enum_path_string) } +/// Description of an elided lifetime. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) struct MissingLifetime { + /// Used to overwrite the resolution with the suggestion, to avoid cascasing errors. + pub id: NodeId, + /// Where to suggest adding the lifetime. + pub span: Span, + /// How the lifetime was introduced, to have the correct space and comma. + pub kind: MissingLifetimeKind, + /// Number of elided lifetimes, used for elision in path. + pub count: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) enum MissingLifetimeKind { + /// An explicit `'_`. + Underscore, + /// An elided lifetime `&' ty`. + Ampersand, + /// An elided lifetime in brackets with written brackets. + Comma, + /// An elided lifetime with elided brackets. + Brackets, +} + +/// Description of the lifetimes appearing in a function parameter. +/// This is used to provide a literal explanation to the elision failure. +#[derive(Clone, Debug)] +pub(super) struct ElisionFnParameter { + /// The index of the argument in the original definition. + pub index: usize, + /// The name of the argument if it's a simple ident. + pub ident: Option<Ident>, + /// The number of lifetimes in the parameter. + pub lifetime_count: usize, + /// The span of the parameter. + pub span: Span, +} + +/// Description of lifetimes that appear as candidates for elision. +/// This is used to suggest introducing an explicit lifetime. +#[derive(Debug)] +pub(super) enum LifetimeElisionCandidate { + /// This is not a real lifetime. + Ignore, + /// There is a named lifetime, we won't suggest anything. + Named, + Missing(MissingLifetime), +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { @@ -2003,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(lifetime_ref.ident.span, "undeclared lifetime"); err }; - let mut suggest_note = true; + self.suggest_introducing_lifetime( + &mut err, + Some(lifetime_ref.ident.name.as_str()), + |err, _, span, message, suggestion| { + err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + true + }, + ); + err.emit(); + } + fn suggest_introducing_lifetime( + &self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + name: Option<&str>, + suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool, + ) { + let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { + let mut should_continue = true; match rib.kind { LifetimeRibKind::Generics { binder: _, span, kind } => { - if !span.can_be_used_for_suggestions() && suggest_note { + if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( span, &format!( "lifetime `{}` is missing in item created through this procedural macro", - lifetime_ref.ident, + name, ), ); continue; @@ -2030,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - lifetime_ref.ident, + name.unwrap_or("'a"), if higher_ranked { " " } else { "" }, ); (span, sugg) } else { let span = self.r.session.source_map().span_through_char(span, '<').shrink_to_hi(); - let sugg = format!("{}, ", lifetime_ref.ident); + let sugg = format!("{}, ", name.unwrap_or("'a")); (span, sugg) }; if higher_ranked { - err.span_suggestion( - span, - &format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - kind.descr(), - lifetime_ref - ), - sugg, - Applicability::MaybeIncorrect, + let message = format!( + "consider making the {} lifetime-generic with a new `{}` lifetime", + kind.descr(), + name.unwrap_or("'a"), ); + should_continue = suggest(err, true, span, &message, sugg); err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", ); + } else if let Some(name) = name { + let message = format!("consider introducing lifetime `{}` here", name); + should_continue = suggest(err, false, span, &message, sugg); } else { - err.span_suggestion( - span, - &format!("consider introducing lifetime `{}` here", lifetime_ref.ident), - sugg, - Applicability::MaybeIncorrect, - ); + let message = format!("consider introducing a named lifetime parameter"); + should_continue = suggest(err, false, span, &message, sugg); } } LifetimeRibKind::Item => break, _ => {} } + if !should_continue { + break; + } } - - err.emit(); } pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { @@ -2105,552 +2128,209 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .emit(); } } -} -/// Report lifetime/lifetime shadowing as an error. -pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { - let mut err = struct_span_err!( - sess, - shadower.span, - E0496, - "lifetime name `{}` shadows a lifetime name that is already in scope", - orig.name, - ); - err.span_label(orig.span, "first declared here"); - err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); - err.emit(); -} - -/// Shadowing involving a label is only a warning for historical reasons. -//FIXME: make this a proper lint. -pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { - let name = shadower.name; - let shadower = shadower.span; - let mut err = sess.struct_span_warn( - shadower, - &format!("label name `{}` shadows a label name that is already in scope", name), - ); - err.span_label(orig, "first declared here"); - err.span_label(shadower, format!("label `{}` already in scope", name)); - err.emit(); -} - -impl<'tcx> LifetimeContext<'_, 'tcx> { pub(crate) fn report_missing_lifetime_specifiers( - &self, - spans: Vec<Span>, - count: usize, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - self.tcx.sess, + &mut self, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) -> ErrorGuaranteed { + let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum(); + let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + + let mut err = struct_span_err!( + self.r.session, spans, E0106, "missing lifetime specifier{}", - pluralize!(count) - ) + pluralize!(num_lifetimes) + ); + self.add_missing_lifetime_specifiers_label( + &mut err, + lifetime_refs, + function_param_lifetimes, + ); + err.emit() } - /// Returns whether to add `'static` lifetime to the suggested lifetime list. - pub(crate) fn report_elision_failure( - &self, - diag: &mut Diagnostic, - params: &[ElisionFailureInfo], - ) -> bool { - let mut m = String::new(); - let len = params.len(); + pub(crate) fn add_missing_lifetime_specifiers_label( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) { + for < in &lifetime_refs { + err.span_label( + lt.span, + format!( + "expected {} lifetime parameter{}", + if lt.count == 1 { "named".to_string() } else { lt.count.to_string() }, + pluralize!(lt.count), + ), + ); + } - let elided_params: Vec<_> = - params.iter().cloned().filter(|info| info.lifetime_count > 0).collect(); + let mut in_scope_lifetimes: Vec<_> = self + .lifetime_ribs + .iter() + .rev() + .take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item)) + .flat_map(|rib| rib.bindings.iter()) + .map(|(&ident, &res)| (ident, res)) + .filter(|(ident, _)| ident.name != kw::UnderscoreLifetime) + .collect(); + debug!(?in_scope_lifetimes); - let elided_len = elided_params.len(); + debug!(?function_param_lifetimes); + if let Some((param_lifetimes, params)) = &function_param_lifetimes { + let elided_len = param_lifetimes.len(); + let num_params = params.len(); - for (i, info) in elided_params.into_iter().enumerate() { - let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } = - info; + let mut m = String::new(); - diag.span_label(span, ""); - let help_name = if let Some(ident) = - parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident()) - { - format!("`{}`", ident) - } else { - format!("argument {}", index + 1) - }; + for (i, info) in params.iter().enumerate() { + let ElisionFnParameter { ident, index, lifetime_count, span } = *info; + debug_assert_ne!(lifetime_count, 0); - m.push_str( - &(if n == 1 { - help_name + err.span_label(span, ""); + + if i != 0 { + if i + 1 < num_params { + m.push_str(", "); + } else if num_params == 2 { + m.push_str(" or "); + } else { + m.push_str(", or "); + } + } + + let help_name = if let Some(ident) = ident { + format!("`{}`", ident) } else { - format!( - "one of {}'s {} {}lifetimes", - help_name, - n, - if have_bound_regions { "free " } else { "" } - ) - })[..], - ); + format!("argument {}", index + 1) + }; - if elided_len == 2 && i == 0 { - m.push_str(" or "); - } else if i + 2 == elided_len { - m.push_str(", or "); - } else if i != elided_len - 1 { - m.push_str(", "); + if lifetime_count == 1 { + m.push_str(&help_name[..]) + } else { + m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..]) + } } - } - if len == 0 { - diag.help( - "this function's return type contains a borrowed value, \ + if num_params == 0 { + err.help( + "this function's return type contains a borrowed value, \ but there is no value for it to be borrowed from", - ); - true - } else if elided_len == 0 { - diag.help( - "this function's return type contains a borrowed value with \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if elided_len == 0 { + err.help( + "this function's return type contains a borrowed value with \ an elided lifetime, but the lifetime cannot be derived from \ the arguments", - ); - true - } else if elided_len == 1 { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if num_params == 1 { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say which {} it is borrowed from", - m - )); - false - } else { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + m + )); + } else { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say whether it is borrowed from {}", - m - )); - false + m + )); + } } - } - pub(crate) fn is_trait_ref_fn_scope( - &mut self, - trait_ref: &'tcx hir::PolyTraitRef<'tcx>, - ) -> bool { - if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res { - if [ - self.tcx.lang_items().fn_once_trait(), - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - ] - .contains(&Some(did)) - { - let (span, span_type) = if let Some(bound) = - trait_ref.bound_generic_params.iter().rfind(|param| { - matches!( - param.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) { - (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail) - } else { - (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - return true; - } + let existing_name = match &in_scope_lifetimes[..] { + [] => Symbol::intern("'a"), + [(existing, _)] => existing.name, + _ => Symbol::intern("'lifetime"), }; - false - } - - pub(crate) fn add_missing_lifetime_specifiers_label( - &self, - err: &mut Diagnostic, - mut spans_with_counts: Vec<(Span, usize)>, - in_scope_lifetimes: FxIndexSet<LocalDefId>, - params: Option<&[ElisionFailureInfo]>, - ) { - let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes - .iter() - .filter_map(|def_id| { - let name = self.tcx.item_name(def_id.to_def_id()); - let span = self.tcx.def_ident_span(def_id.to_def_id())?; - Some((name, span)) - }) - .filter(|&(n, _)| n != kw::UnderscoreLifetime) - .unzip(); - if let Some(params) = params { - // If there's no lifetime available, suggest `'static`. - if self.report_elision_failure(err, params) && lifetime_names.is_empty() { - lifetime_names.insert(kw::StaticLifetime); + let mut spans_suggs: Vec<_> = Vec::new(); + let build_sugg = |lt: MissingLifetime| match lt.kind { + MissingLifetimeKind::Underscore => { + debug_assert_eq!(lt.count, 1); + (lt.span, existing_name.to_string()) } - } - let params = params.unwrap_or(&[]); - - let snippets: Vec<Option<String>> = spans_with_counts - .iter() - .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok()) - .collect(); - - // Empty generics are marked with a span of "<", but since from now on - // that information is in the snippets it can be removed from the spans. - for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) { - if snippet.as_deref() == Some("<") { - *span = span.shrink_to_hi(); + MissingLifetimeKind::Ampersand => { + debug_assert_eq!(lt.count, 1); + (lt.span.shrink_to_hi(), format!("{} ", existing_name)) } + MissingLifetimeKind::Comma => { + let sugg: String = std::iter::repeat([existing_name.as_str(), ", "]) + .take(lt.count) + .flatten() + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + MissingLifetimeKind::Brackets => { + let sugg: String = std::iter::once("<") + .chain( + std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "), + ) + .chain([">"]) + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + }; + for < in &lifetime_refs { + spans_suggs.push(build_sugg(lt)); } - - for &(span, count) in &spans_with_counts { - err.span_label( - span, - format!( - "expected {} lifetime parameter{}", - if count == 1 { "named".to_string() } else { count.to_string() }, - pluralize!(count), - ), - ); - } - - let suggest_existing = - |err: &mut Diagnostic, - name: Symbol, - formatters: Vec<Option<Box<dyn Fn(Symbol) -> String>>>| { - if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = - self.missing_named_lifetime_spots.iter().rev().next() - { - // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest - // using `'a`, but also introduce the concept of HRLTs by suggesting - // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) - let mut introduce_suggestion = vec![]; - - let a_to_z_repeat_n = |n| { - (b'a'..=b'z').map(move |c| { - let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); - s - }) - }; - - // If all single char lifetime names are present, we wrap around and double the chars. - let lt_name = (1..) - .flat_map(a_to_z_repeat_n) - .map(|lt| Symbol::intern(<)) - .find(|lt| !lifetime_names.contains(lt)) - .unwrap(); - let msg = format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - span_type.descr(), - lt_name, - ); - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - let for_sugg = span_type.suggestion(<_name); - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) - { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); - } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, stripped))); - } - } + debug!(?spans_suggs); + match in_scope_lifetimes.len() { + 0 => { + if let Some((param_lifetimes, _)) = function_param_lifetimes { + for lt in param_lifetimes { + spans_suggs.push(build_sugg(lt)) } - introduce_suggestion.push((*for_span, for_sugg)); - for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) { - if let Some(formatter) = formatter { - introduce_suggestion.push((*span, formatter(lt_name))); - } - } - err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, - Applicability::MaybeIncorrect, - ); } - - let spans_suggs: Vec<_> = formatters - .into_iter() - .zip(spans_with_counts.iter()) - .filter_map(|(formatter, (span, _))| { - if let Some(formatter) = formatter { - Some((*span, formatter(name))) - } else { - None - } - }) - .collect(); - if spans_suggs.is_empty() { - // If all the spans come from macros, we cannot extract snippets and then - // `formatters` only contains None and `spans_suggs` is empty. - return; - } - err.multipart_suggestion_verbose( - &format!( - "consider using the `{}` lifetime", - lifetime_names.iter().next().unwrap() - ), - spans_suggs, - Applicability::MaybeIncorrect, - ); - }; - let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| { - for missing in self.missing_named_lifetime_spots.iter().rev() { - let mut introduce_suggestion = vec![]; - let msg; - let should_break; - introduce_suggestion.push(match missing { - MissingLifetimeSpot::Generics(generics) => { - if generics.span == DUMMY_SP { - // Account for malformed generics in the HIR. This shouldn't happen, - // but if we make a mistake elsewhere, mainly by keeping something in - // `missing_named_lifetime_spots` that we shouldn't, like associated - // `const`s or making a mistake in the AST lowering we would provide - // nonsensical suggestions. Guard against that by skipping these. - // (#74264) - continue; - } - msg = "consider introducing a named lifetime parameter".to_string(); - should_break = true; - if let Some(param) = generics.params.iter().find(|p| { - !matches!( - p.kind, - hir::GenericParamKind::Type { synthetic: true, .. } - | hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided - } - ) - }) { - (param.span.shrink_to_lo(), "'a, ".to_string()) - } else { - (generics.span, "<'a>".to_string()) - } - } - MissingLifetimeSpot::HigherRanked { span, span_type } => { - msg = format!( - "consider making the {} lifetime-generic with a new `'a` lifetime", - span_type.descr(), - ); - should_break = false; - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - (*span, span_type.suggestion("'a")) - } - MissingLifetimeSpot::Static => { - let mut spans_suggs = Vec::new(); - for ((span, count), snippet) in - spans_with_counts.iter().copied().zip(snippets.iter()) - { - let (span, sugg) = match snippet.as_deref() { - Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), - Some("'_") => (span, "'static".to_owned()), - Some(snippet) if !snippet.ends_with('>') => { - if snippet == "" { - ( - span, - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else if snippet == "<" || snippet == "(" { - ( - span.shrink_to_hi(), - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else { - ( - span.shrink_to_hi(), - format!( - "<{}>", - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ), - ) - } - } - _ => continue, - }; - spans_suggs.push((span, sugg.to_string())); - } + self.suggest_introducing_lifetime( + err, + None, + |err, higher_ranked, span, message, intro_sugg| { err.multipart_suggestion_verbose( - "consider using the `'static` lifetime", - spans_suggs, + message, + std::iter::once((span, intro_sugg)) + .chain(spans_suggs.clone()) + .collect(), Applicability::MaybeIncorrect, ); - continue; - } - }); - - struct Lifetime(Span, String); - impl Lifetime { - fn is_unnamed(&self) -> bool { - self.1.starts_with('&') && !self.1.starts_with("&'") - } - fn is_underscore(&self) -> bool { - self.1.starts_with("&'_ ") - } - fn is_named(&self) -> bool { - self.1.starts_with("&'") - } - fn suggestion(&self, sugg: String) -> Option<(Span, String)> { - Some( - match ( - self.is_unnamed(), - self.is_underscore(), - self.is_named(), - sugg.starts_with('&'), - ) { - (true, _, _, false) => (self.span_unnamed_borrow(), sugg), - (true, _, _, true) => { - (self.span_unnamed_borrow(), sugg[1..].to_string()) - } - (_, true, _, false) => { - (self.span_underscore_borrow(), sugg.trim().to_string()) - } - (_, true, _, true) => { - (self.span_underscore_borrow(), sugg[1..].trim().to_string()) - } - (_, _, true, false) => { - (self.span_named_borrow(), sugg.trim().to_string()) - } - (_, _, true, true) => { - (self.span_named_borrow(), sugg[1..].trim().to_string()) - } - _ => return None, - }, - ) - } - fn span_unnamed_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo).with_hi(lo) - } - fn span_named_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo) - } - fn span_underscore_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - let hi = lo + BytePos(2); - self.0.with_lo(lo).with_hi(hi) - } - } - - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { - if let Some((span, sugg)) = - Lifetime(param.span, snippet).suggestion("'a ".to_string()) - { - introduce_suggestion.push((span, sugg)); - } - } - } - for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map( - |((span, _), sugg)| match &sugg { - Some(sugg) => Some((span, sugg.to_string())), - _ => None, + higher_ranked }, - ) { - let (span, sugg) = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .ok() - .and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone())) - .unwrap_or((span, sugg)); - introduce_suggestion.push((span, sugg.to_string())); - } + ); + } + 1 => { err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, + &format!("consider using the `{}` lifetime", existing_name), + spans_suggs, Applicability::MaybeIncorrect, ); - if should_break { - break; - } - } - }; - let lifetime_names: Vec<_> = lifetime_names.iter().collect(); - match &lifetime_names[..] { - [name] => { - let mut suggs: Vec<Option<Box<dyn Fn(Symbol) -> String>>> = Vec::new(); - for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some(Box::new(|name| format!("&{} ", name))), - Some("'_") => Some(Box::new(|n| n.to_string())), - Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))), - Some("<") => Some(Box::new(move |n| { - std::iter::repeat(n) - .take(count) - .map(|n| n.to_string()) - .collect::<Vec<_>>() - .join(", ") - })), - Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::<Vec<_>>() - .join(", ") - ) - })), - _ => None, - }); + // Record as using the suggested resolution. + let (_, (_, res)) = in_scope_lifetimes[0]; + for < in &lifetime_refs { + self.r.lifetimes_res_map.insert(lt.id, res); } - suggest_existing(err, **name, suggs); } - [] => { - let mut suggs = Vec::new(); - for (snippet, (_, count)) in - snippets.iter().cloned().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some("&'a ".to_string()), - Some("'_") => Some("'a".to_string()), - Some("") => { - Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join("")) - } - Some("<") => { - Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")) - } - Some(snippet) => Some(format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "), - )), - None => None, - }); - } - suggest_new(err, suggs); - } - lts if lts.len() > 1 => { + _ => { + let lifetime_spans: Vec<_> = + in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect(); err.span_note(lifetime_spans, "these named lifetimes are available to use"); - let mut spans_suggs: Vec<_> = Vec::new(); - for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) { - match snippet.as_deref() { - Some("") => spans_suggs.push((span, "'lifetime, ".to_string())), - Some("&") => spans_suggs - .push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())), - _ => {} - } - } - if spans_suggs.len() > 0 { // This happens when we have `Foo<T>` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. @@ -2661,7 +2341,34 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { ); } } - _ => unreachable!(), } } } + +/// Report lifetime/lifetime shadowing as an error. +pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { + let mut err = struct_span_err!( + sess, + shadower.span, + E0496, + "lifetime name `{}` shadows a lifetime name that is already in scope", + orig.name, + ); + err.span_label(orig.span, "first declared here"); + err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); + err.emit(); +} + +/// Shadowing involving a label is only a warning for historical reasons. +//FIXME: make this a proper lint. +pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { + let name = shadower.name; + let shadower = shadower.span; + let mut err = sess.struct_span_warn( + shadower, + &format!("label name `{}` shadows a label name that is already in scope", name), + ); + err.span_label(orig, "first declared here"); + err.span_label(shadower, format!("label `{}` already in scope", name)); + err.emit(); +} diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 0eb11cd3e9f..a7fd7c427c7 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,12 +1,12 @@ // ignore-tidy-filelength -//! Name resolution for lifetimes. +//! Resolution of early vs late bound lifetimes. //! -//! Name resolution for lifetimes follows *much* simpler rules than the -//! full resolve. For example, lifetime names are never exported or -//! used between functions, and they operate in a purely top-down -//! way. Therefore, we break lifetime name resolution into a separate pass. +//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this +//! information, typechecking needs to transform the lifetime parameters into bound lifetimes. +//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit +//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file +//! is also responsible for assigning their semantics to implicit lifetimes in trait objects. -use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::struct_span_err; @@ -14,13 +14,12 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node}; -use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind}; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; +use rustc_middle::bug; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -152,10 +151,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>, - - /// When encountering an undefined named lifetime, we will suggest introducing it in these - /// places. - pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, } #[derive(Debug)] @@ -323,23 +318,12 @@ enum Elide { /// Always use this one lifetime. Exact(Region), /// Less or more than one lifetime were found, error on unspecified. - Error(Vec<ElisionFailureInfo>), + Error, /// Forbid lifetime elision inside of a larger scope where it would be /// permitted. For example, in let position impl trait. Forbid, } -#[derive(Clone, Debug)] -pub(crate) struct ElisionFailureInfo { - /// Where we can find the argument pattern. - pub(crate) parent: Option<hir::BodyId>, - /// The index of the argument in the original definition. - pub(crate) index: usize, - pub(crate) lifetime_count: usize, - pub(crate) have_bound_regions: bool, - pub(crate) span: Span, -} - type ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -421,7 +405,6 @@ fn do_resolve( scope: ROOT_SCOPE, trait_definition_only, xcrate_object_lifetime_defaults: Default::default(), - missing_named_lifetime_spots: vec![], }; visitor.visit_item(item); @@ -644,40 +627,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { where_bound_origin: None, }; - if let &hir::ClosureBinder::For { span, .. } = binder { - let last_lt = bound_generic_params - .iter() - .filter(|p| { - matches!( - p, - GenericParam { - kind: GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit - }, - .. - } - ) - }) - .last(); - let (span, span_type) = match last_lt { - Some(GenericParam { span: last_sp, .. }) => { - (last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail) - } - None => (span, ForLifetimeSpanType::ClosureEmpty), - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - } - self.with(scope, |this| { // a closure has no bounds, so everything // contained within is scoped within its binder. intravisit::walk_expr(this, e) }); - - if let hir::ClosureBinder::For { .. } = binder { - self.missing_named_lifetime_spots.pop(); - } } else { intravisit::walk_expr(self, e) } @@ -694,11 +648,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } match item.kind { hir::ItemKind::Fn(_, ref generics, _) => { - self.missing_named_lifetime_spots.push(generics.into()); self.visit_early_late(None, item.hir_id(), generics, |this| { intravisit::walk_item(this, item); }); - self.missing_named_lifetime_spots.pop(); } hir::ItemKind::ExternCrate(_) @@ -761,8 +713,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::TraitAlias(ref generics, ..) | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { - self.missing_named_lifetime_spots.push(generics.into()); - // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes @@ -800,7 +750,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_item(this, item); }); }); - self.missing_named_lifetime_spots.pop(); } } } @@ -826,20 +775,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match ty.kind { hir::TyKind::BareFn(ref c) => { let next_early_index = self.next_early_index(); - let lifetime_span: Option<Span> = - c.generic_params.iter().rev().find_map(|param| match param.kind { - GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => { - Some(param.span) - } - _ => None, - }); - let (span, span_type) = if let Some(span) = lifetime_span { - (span.shrink_to_hi(), ForLifetimeSpanType::TypeTail) - } else { - (ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c .generic_params .iter() @@ -867,7 +802,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // contained within is scoped within its binder. intravisit::walk_ty(this, ty); }); - self.missing_named_lifetime_spots.pop(); } hir::TyKind::TraitObject(bounds, ref lifetime, _) => { debug!(?bounds, ?lifetime, "TraitObject"); @@ -878,11 +812,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }); match lifetime.name { - LifetimeName::Implicit => { - // For types like `dyn Foo`, we should - // generate a special form of elided. - span_bug!(ty.span, "object-lifetime-default expected, not implicit",); - } LifetimeName::ImplicitObjectLifetimeDefault => { // If the user does not write *anything*, we // use the object lifetime defaulting @@ -890,7 +819,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box<dyn Debug + 'static>`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Underscore => { + LifetimeName::Implicit | LifetimeName::Underscore => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be // resolved the same as the `'_` in `&'_ Foo`. @@ -1010,10 +939,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut elision = None; let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; + debug!(?generics.params); for param in generics.params { match param.kind { GenericParamKind::Lifetime { .. } => { let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); + lifetimes.insert(def_id, reg); if let hir::ParamName::Plain(Ident { name: kw::UnderscoreLifetime, .. @@ -1022,8 +953,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Pick the elided lifetime "definition" if one exists // and use it to make an elision scope. elision = Some(reg); - } else { - lifetimes.insert(def_id, reg); } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { @@ -1088,7 +1017,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::TraitItemKind::*; match trait_item.kind { Fn(_, _) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(trait_item.hir_id())), @@ -1096,10 +1024,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &trait_item.generics, |this| intravisit::walk_trait_item(this, trait_item), ); - self.missing_named_lifetime_spots.pop(); } Type(bounds, ref ty) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let generics = &trait_item.generics; let mut index = self.next_early_index(); debug!("visit_ty: index = {}", index); @@ -1140,14 +1066,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(trait_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_trait_item(self, trait_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1156,7 +1079,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::ImplItemKind::*; match impl_item.kind { Fn(..) => { - self.missing_named_lifetime_spots.push((&impl_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(impl_item.hir_id())), @@ -1164,11 +1086,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &impl_item.generics, |this| intravisit::walk_impl_item(this, impl_item), ); - self.missing_named_lifetime_spots.pop(); } TyAlias(ref ty) => { let generics = &impl_item.generics; - self.missing_named_lifetime_spots.push(generics.into()); let mut index = self.next_early_index(); let mut non_lifetime_count = 0; debug!("visit_ty: index = {}", index); @@ -1203,14 +1123,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.visit_ty(ty); }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(impl_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_impl_item(self, impl_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1393,8 +1310,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ) { debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); - let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); - let next_early_index = self.next_early_index(); let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); @@ -1435,10 +1350,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); this.visit_trait_ref(&trait_ref.trait_ref); }); - - if should_pop_missing_lt { - self.missing_named_lifetime_spots.pop(); - } } } @@ -1584,14 +1495,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { { let LifetimeContext { tcx, map, .. } = self; let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); - let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots); let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope, trait_definition_only: self.trait_definition_only, xcrate_object_lifetime_defaults, - missing_named_lifetime_spots, }; let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { @@ -1599,7 +1508,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { f(&mut this); } self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; - self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; } /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. @@ -2202,24 +2110,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut assoc_item_kind = None; let mut impl_self = None; let parent = self.tcx.hir().get_parent_node(output.hir_id); - let body = match self.tcx.hir().get(parent) { + match self.tcx.hir().get(parent) { // `fn` definitions and methods. - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {} - Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => { + Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => { if let hir::ItemKind::Trait(.., ref trait_items) = self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind { assoc_item_kind = trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); } - match *m { - hir::TraitFn::Required(_) => None, - hir::TraitFn::Provided(body) => Some(body), - } } - Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => { + Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => { if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind { @@ -2227,13 +2131,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { assoc_item_kind = items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind); } - Some(body) } // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). - Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None, + Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {}, - Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None, + Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {}, // Everything else (only closures?) doesn't // actually enjoy elision in return types. @@ -2320,42 +2223,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // have that lifetime. let mut possible_implied_output_region = None; let mut lifetime_count = 0; - let arg_lifetimes = inputs - .iter() - .enumerate() - .skip(has_self as usize) - .map(|(i, input)| { - let mut gather = GatherLifetimes { - map: self.map, - outer_index: ty::INNERMOST, - have_bound_regions: false, - lifetimes: Default::default(), - }; - gather.visit_ty(input); - - lifetime_count += gather.lifetimes.len(); + for input in inputs.iter().skip(has_self as usize) { + let mut gather = GatherLifetimes { + map: self.map, + outer_index: ty::INNERMOST, + have_bound_regions: false, + lifetimes: Default::default(), + }; + gather.visit_ty(input); - if lifetime_count == 1 && gather.lifetimes.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = gather.lifetimes.iter().cloned().next(); - } + lifetime_count += gather.lifetimes.len(); - ElisionFailureInfo { - parent: body, - index: i, - lifetime_count: gather.lifetimes.len(), - have_bound_regions: gather.have_bound_regions, - span: input.span, - } - }) - .collect(); + if lifetime_count == 1 && gather.lifetimes.len() == 1 { + // there's a chance that the unique lifetime of this + // iteration will be the appropriate lifetime for output + // parameters, so lets store it. + possible_implied_output_region = gather.lifetimes.iter().cloned().next(); + } + } let elide = if lifetime_count == 1 { Elide::Exact(possible_implied_output_region.unwrap()) } else { - Elide::Error(arg_lifetimes) + Elide::Error }; debug!(?elide); @@ -2487,17 +2377,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut late_depth = 0; let mut scope = self.scope; - let mut in_scope_lifetimes = FxIndexSet::default(); - let error = loop { + loop { match *scope { // Do not assign any resolution, it will be inferred. Scope::Body { .. } => return, - Scope::Root => break None, + Scope::Root => break, - Scope::Binder { s, ref lifetimes, scope_type, .. } => { - // collect named lifetimes for suggestions - in_scope_lifetimes.extend(lifetimes.keys().copied()); + Scope::Binder { s, scope_type, .. } => { match scope_type { BinderScopeType::Normal => late_depth += 1, BinderScopeType::Concatenating => {} @@ -2526,27 +2413,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => { - let mut scope = s; - loop { - match scope { - Scope::Binder { ref lifetimes, s, .. } => { - // Collect named lifetimes for suggestions. - in_scope_lifetimes.extend(lifetimes.keys().copied()); - scope = s; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = s; - } - _ => break, - } - } - break Some(&e[..]); - } - - Scope::Elision { elide: Elide::Forbid, .. } => break None, + Scope::Elision { elide: Elide::Error, .. } + | Scope::Elision { elide: Elide::Forbid, .. } => break, Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } @@ -2554,26 +2422,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } } - }; - - let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); - spans.sort(); - let mut spans_dedup = spans.clone(); - spans_dedup.dedup(); - let spans_with_counts: Vec<_> = spans_dedup - .into_iter() - .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count())) - .collect(); - - let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len()); + } - self.add_missing_lifetime_specifiers_label( - &mut err, - spans_with_counts, - in_scope_lifetimes, - error, - ); - err.emit(); + for lt in lifetime_refs { + self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier"); + } } fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2fcbe1d4c14..6945306a691 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -11,6 +11,7 @@ #![feature(drain_filter)] #![feature(if_let_guard)] #![cfg_attr(bootstrap, feature(let_chains))] +#![feature(iter_intersperse)] #![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] |
