diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
commit | 4f9fe856a25ab29345b90e7725509e9ee38a37be (patch) | |
tree | e4ffd8a9374cae7b21f7cbfb352927e0e074aff6 /compiler/rustc_hir_analysis/src/collect | |
parent | Adding upstream version 1.68.2+dfsg1. (diff) | |
download | rustc-4f9fe856a25ab29345b90e7725509e9ee38a37be.tar.xz rustc-4f9fe856a25ab29345b90e7725509e9ee38a37be.zip |
Adding upstream version 1.69.0+dfsg1.upstream/1.69.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_analysis/src/collect')
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/generics_of.rs | 16 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/item_bounds.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 65 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs (renamed from compiler/rustc_hir_analysis/src/collect/lifetimes.rs) | 719 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/type_of.rs | 115 |
5 files changed, 562 insertions, 355 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 014ee9fcc..127d4fa90 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -1,4 +1,4 @@ -use crate::middle::resolve_lifetime as rl; +use crate::middle::resolve_bound_vars as rbv; use hir::{ intravisit::{self, Visitor}, GenericParamKind, HirId, Node, @@ -394,10 +394,16 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S return; } - match self.tcx.named_region(lt.hir_id) { - Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} - Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} - Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { + match self.tcx.named_bound_var(lt.hir_id) { + Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {} + Some(rbv::ResolvedArg::LateBound(debruijn, _, _)) + if debruijn < self.outer_index => {} + Some( + rbv::ResolvedArg::LateBound(..) + | rbv::ResolvedArg::Free(..) + | rbv::ResolvedArg::Error(_), + ) + | None => { self.has_late_bound_regions = Some(lt.ident.span); } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 8d479f1c3..9cf3ff65a 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -103,7 +103,7 @@ pub(super) fn item_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> { - let bounds = tcx.mk_predicates( + let bounds = tcx.mk_predicates_from_iter( util::elaborate_predicates( tcx, tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound), diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 46b277d98..2badd66e3 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -9,8 +9,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{GenericPredicates, ToPredicate}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -151,7 +151,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP trace!(?generics); // Collect the predicates that were written inline by the user on each - // type parameter (e.g., `<T: Foo>`). + // type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates + // for each const parameter. for param in ast_generics.params { match param.kind { // We already dealt with early bound lifetimes above. @@ -175,7 +176,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP trace!(?predicates); } GenericParamKind::Const { .. } => { - // Bounds on const parameters are currently not possible. + let name = param.name.ident().name; + let param_const = ty::ParamConst::new(index, name); + + let ct_ty = tcx.type_of(param.def_id.to_def_id()).subst_identity(); + + let ct = tcx.mk_const(param_const, ct_ty); + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::Clause::ConstArgHasType(ct, ct_ty), + )) + .to_predicate(tcx); + predicates.insert((predicate, param.span)); + index += 1; } } @@ -251,7 +264,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP // in trait checking. See `setup_constraining_predicates` // for details. if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node { - let self_ty = tcx.type_of(def_id); + let self_ty = tcx.type_of(def_id).subst_identity(); let trait_ref = tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::subst_identity); cgp::setup_constraining_predicates( tcx, @@ -280,15 +293,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue }; - let dup_def = tcx.hir().local_def_id(duplicate.hir_id).to_def_id(); + let dup_def = duplicate.def_id.to_def_id(); let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() }; - let dup_region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + let dup_region = tcx.mk_re_early_bound(ty::EarlyBoundRegion { def_id: dup_def, index: dup_index, name: duplicate.name.ident().name, - })); + }); predicates.push(( ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives( ty::OutlivesPredicate(orig_region, dup_region), @@ -439,7 +452,9 @@ pub(super) fn explicit_predicates_of<'tcx>( let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let parent_def_id = tcx.hir().get_parent_item(hir_id); - if tcx.hir().opt_const_param_default_param_def_id(hir_id).is_some() { + if let Some(defaulted_param_def_id) = + tcx.hir().opt_const_param_default_param_def_id(hir_id) + { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) @@ -452,7 +467,39 @@ pub(super) fn explicit_predicates_of<'tcx>( // // In the above code we want the anon const to have predicates in its param env for `T: Trait` // and we would be calling `explicit_predicates_of(Foo)` here - return tcx.explicit_predicates_of(parent_def_id); + let parent_preds = tcx.explicit_predicates_of(parent_def_id); + + // If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter + // will ICE because of #106994. FIXME(generic_const_exprs): remove this when a more general solution + // to #106994 is implemented. + let filtered_predicates = parent_preds + .predicates + .into_iter() + .filter(|(pred, _)| { + if let ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, _)) = + pred.kind().skip_binder() + { + match ct.kind() { + ty::ConstKind::Param(param_const) => { + let defaulted_param_idx = tcx + .generics_of(parent_def_id) + .param_def_id_to_index[&defaulted_param_def_id.to_def_id()]; + param_const.index < defaulted_param_idx + } + _ => bug!( + "`ConstArgHasType` in `predicates_of`\ + that isn't a `Param` const" + ), + } + } else { + true + } + }) + .cloned(); + return GenericPredicates { + parent: parent_preds.parent, + predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, + }; } let parent_def_kind = tcx.def_kind(parent_def_id); diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 359122d4e..65a9052a6 100644 --- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -16,67 +16,72 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::resolve_lifetime::*; +use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::fmt; +use crate::errors; + trait RegionExt { - fn early(param: &GenericParam<'_>) -> (LocalDefId, Region); + fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg); - fn late(index: u32, param: &GenericParam<'_>) -> (LocalDefId, Region); + fn late(index: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg); fn id(&self) -> Option<DefId>; - fn shifted(self, amount: u32) -> Region; + fn shifted(self, amount: u32) -> ResolvedArg; } -impl RegionExt for Region { - fn early(param: &GenericParam<'_>) -> (LocalDefId, Region) { - debug!("Region::early: def_id={:?}", param.def_id); - (param.def_id, Region::EarlyBound(param.def_id.to_def_id())) +impl RegionExt for ResolvedArg { + fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) { + debug!("ResolvedArg::early: def_id={:?}", param.def_id); + (param.def_id, ResolvedArg::EarlyBound(param.def_id.to_def_id())) } - fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, Region) { + fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) { let depth = ty::INNERMOST; debug!( - "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?}", + "ResolvedArg::late: idx={:?}, param={:?} depth={:?} def_id={:?}", idx, param, depth, param.def_id, ); - (param.def_id, Region::LateBound(depth, idx, param.def_id.to_def_id())) + (param.def_id, ResolvedArg::LateBound(depth, idx, param.def_id.to_def_id())) } fn id(&self) -> Option<DefId> { match *self { - Region::Static => None, + ResolvedArg::StaticLifetime | ResolvedArg::Error(_) => None, - Region::EarlyBound(id) | Region::LateBound(_, _, id) | Region::Free(_, id) => Some(id), + ResolvedArg::EarlyBound(id) + | ResolvedArg::LateBound(_, _, id) + | ResolvedArg::Free(_, id) => Some(id), } } - fn shifted(self, amount: u32) -> Region { + fn shifted(self, amount: u32) -> ResolvedArg { match self { - Region::LateBound(debruijn, idx, id) => { - Region::LateBound(debruijn.shifted_in(amount), idx, id) + ResolvedArg::LateBound(debruijn, idx, id) => { + ResolvedArg::LateBound(debruijn.shifted_in(amount), idx, id) } _ => self, } } } -/// Maps the id of each lifetime reference to the lifetime decl +/// Maps the id of each bound variable reference to the variable decl /// that it corresponds to. /// -/// FIXME. This struct gets converted to a `ResolveLifetimes` for +/// FIXME. This struct gets converted to a `ResolveBoundVars` for /// actual use. It has the same data, but indexed by `LocalDefId`. This /// is silly. #[derive(Debug, Default)] -struct NamedRegionMap { - // maps from every use of a named (not anonymous) lifetime to a - // `Region` describing how that region is bound - defs: HirIdMap<Region>, +struct NamedVarMap { + // maps from every use of a named (not anonymous) bound var to a + // `ResolvedArg` describing how that variable is bound + defs: HirIdMap<ResolvedArg>, // Maps relevant hir items to the bound vars on them. These include: // - function defs @@ -87,9 +92,9 @@ struct NamedRegionMap { late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>, } -struct LifetimeContext<'a, 'tcx> { +struct BoundVarContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, - map: &'a mut NamedRegionMap, + map: &'a mut NamedVarMap, scope: ScopeRef<'a>, } @@ -102,7 +107,7 @@ enum Scope<'a> { Binder { /// We use an IndexMap here because we want these lifetimes in order /// for diagnostics. - lifetimes: FxIndexMap<LocalDefId, Region>, + bound_vars: FxIndexMap<LocalDefId, ResolvedArg>, scope_type: BinderScopeType, @@ -141,7 +146,7 @@ enum Scope<'a> { /// inferred in a function body or potentially error outside one), /// for the default choice of lifetime in a trait object type. ObjectLifetimeDefault { - lifetime: Option<Region>, + lifetime: Option<ResolvedArg>, s: ScopeRef<'a>, }, @@ -150,7 +155,7 @@ enum Scope<'a> { /// lifetimes encountered when identifying the trait that an associated type /// is declared on. Supertrait { - lifetimes: Vec<ty::BoundVariableKind>, + bound_vars: Vec<ty::BoundVariableKind>, s: ScopeRef<'a>, }, @@ -158,6 +163,15 @@ enum Scope<'a> { s: ScopeRef<'a>, }, + /// Disallows capturing non-lifetime binders from parent scopes. + /// + /// This is necessary for something like `for<T> [(); { /* references T */ }]:`, + /// since we don't do something more correct like replacing any captured + /// late-bound vars with early-bound params in the const's own generics. + AnonConstBoundary { + s: ScopeRef<'a>, + }, + Root { opt_parent_item: Option<LocalDefId>, }, @@ -185,9 +199,9 @@ struct TruncatedScopeDebug<'a>(&'a Scope<'a>); impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { - Scope::Binder { lifetimes, scope_type, hir_id, where_bound_origin, s: _ } => f + Scope::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f .debug_struct("Binder") - .field("lifetimes", lifetimes) + .field("bound_vars", bound_vars) .field("scope_type", scope_type) .field("hir_id", hir_id) .field("where_bound_origin", where_bound_origin) @@ -202,12 +216,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("lifetime", lifetime) .field("s", &"..") .finish(), - Scope::Supertrait { lifetimes, s: _ } => f + Scope::Supertrait { bound_vars, s: _ } => f .debug_struct("Supertrait") - .field("lifetimes", lifetimes) + .field("bound_vars", bound_vars) .field("s", &"..") .finish(), Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Scope::AnonConstBoundary { s: _ } => f.debug_struct("AnonConstBoundary").finish(), Scope::Root { opt_parent_item } => { f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() } @@ -219,27 +234,27 @@ type ScopeRef<'a> = &'a Scope<'a>; pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { - resolve_lifetimes, + resolve_bound_vars, - named_region_map: |tcx, id| tcx.resolve_lifetimes(id).defs.get(&id), + named_variable_map: |tcx, id| tcx.resolve_bound_vars(id).defs.get(&id), is_late_bound_map, object_lifetime_default, - late_bound_vars_map: |tcx, id| tcx.resolve_lifetimes(id).late_bound_vars.get(&id), + late_bound_vars_map: |tcx, id| tcx.resolve_bound_vars(id).late_bound_vars.get(&id), ..*providers }; } -/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. +/// Computes the `ResolveBoundVars` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use -/// `named_region_map`, `is_late_bound_map`, etc. +/// `named_variable_map`, `is_late_bound_map`, etc. #[instrument(level = "debug", skip(tcx))] -fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveLifetimes { - let mut named_region_map = - NamedRegionMap { defs: Default::default(), late_bound_vars: Default::default() }; - let mut visitor = LifetimeContext { +fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars { + let mut named_variable_map = + NamedVarMap { defs: Default::default(), late_bound_vars: Default::default() }; + let mut visitor = BoundVarContext { tcx, - map: &mut named_region_map, + map: &mut named_variable_map, scope: &Scope::Root { opt_parent_item: None }, }; match tcx.hir().owner(local_def_id) { @@ -260,13 +275,13 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveLife hir::OwnerNode::Crate(_) => {} } - let mut rl = ResolveLifetimes::default(); + let mut rl = ResolveBoundVars::default(); - for (hir_id, v) in named_region_map.defs { + for (hir_id, v) in named_variable_map.defs { let map = rl.defs.entry(hir_id.owner).or_default(); map.insert(hir_id.local_id, v); } - for (hir_id, v) in named_region_map.late_bound_vars { + for (hir_id, v) in named_variable_map.late_bound_vars { let map = rl.late_bound_vars.entry(hir_id.owner).or_default(); map.insert(hir_id.local_id, v); } @@ -276,39 +291,53 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveLife rl } -fn late_region_as_bound_region(tcx: TyCtxt<'_>, region: &Region) -> ty::BoundVariableKind { - match region { - Region::LateBound(_, _, def_id) => { +fn late_arg_as_bound_arg<'tcx>( + tcx: TyCtxt<'tcx>, + arg: &ResolvedArg, + param: &GenericParam<'tcx>, +) -> ty::BoundVariableKind { + match arg { + ResolvedArg::LateBound(_, _, def_id) => { let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); - ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) + match param.kind { + GenericParamKind::Lifetime { .. } => { + ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) + } + GenericParamKind::Type { .. } => { + ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(*def_id, name)) + } + GenericParamKind::Const { .. } => ty::BoundVariableKind::Const, + } } - _ => bug!("{:?} is not a late region", region), + _ => bug!("{:?} is not a late argument", arg), } } -impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { +impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) { let mut scope = self.scope; - let mut supertrait_lifetimes = vec![]; + let mut supertrait_bound_vars = vec![]; loop { match scope { Scope::Body { .. } | Scope::Root { .. } => { break (vec![], BinderScopeType::Normal); } - Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } - Scope::Supertrait { s, lifetimes } => { - supertrait_lifetimes = lifetimes.clone(); + Scope::Supertrait { s, bound_vars } => { + supertrait_bound_vars = bound_vars.clone(); scope = s; } Scope::TraitRefBoundary { .. } => { // We should only see super trait lifetimes if there is a `Binder` above - assert!(supertrait_lifetimes.is_empty()); + assert!(supertrait_bound_vars.is_empty()); break (vec![], BinderScopeType::Normal); } @@ -316,14 +345,64 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // Nested poly trait refs have the binders concatenated let mut full_binders = self.map.late_bound_vars.entry(*hir_id).or_default().clone(); - full_binders.extend(supertrait_lifetimes.into_iter()); + full_binders.extend(supertrait_bound_vars.into_iter()); break (full_binders, BinderScopeType::Concatenating); } } } } + + fn visit_poly_trait_ref_inner( + &mut self, + trait_ref: &'tcx hir::PolyTraitRef<'tcx>, + non_lifetime_binder_allowed: NonLifetimeBinderAllowed, + ) { + debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); + + let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); + + let initial_bound_vars = binders.len() as u32; + let mut bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = FxIndexMap::default(); + let binders_iter = + trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| { + let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param); + let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); + bound_vars.insert(pair.0, pair.1); + r + }); + binders.extend(binders_iter); + + if let NonLifetimeBinderAllowed::Deny(where_) = non_lifetime_binder_allowed { + deny_non_region_late_bound(self.tcx, &mut bound_vars, where_); + } + + debug!(?binders); + self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders); + + // Always introduce a scope here, even if this is in a where clause and + // we introduced the binders around the bounded Ty. In that case, we + // just reuse the concatenation functionality also present in nested trait + // refs. + let scope = Scope::Binder { + hir_id: trait_ref.trait_ref.hir_ref_id, + bound_vars, + s: self.scope, + scope_type, + where_bound_origin: None, + }; + self.with(scope, |this| { + walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); + this.visit_trait_ref(&trait_ref.trait_ref); + }); + } +} + +enum NonLifetimeBinderAllowed { + Deny(&'static str), + Allow, } -impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { + +impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { @@ -386,22 +465,23 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = + let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = bound_generic_params .iter() - .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, param); - let r = late_region_as_bound_region(self.tcx, &pair.1); + let pair = ResolvedArg::late(late_bound_idx as u32, param); + let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); (pair, r) }) .unzip(); + deny_non_region_late_bound(self.tcx, &mut bound_vars, "closures"); + self.record_late_bound_vars(e.hir_id, binders); let scope = Scope::Binder { hir_id: e.hir_id, - lifetimes, + bound_vars, s: self.scope, scope_type: BinderScopeType::Normal, where_bound_origin: None, @@ -461,7 +541,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // conservatively add all resolved lifetimes. Otherwise we run into problems in // cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`. let parent_item = self.tcx.hir().get_parent_item(item.hir_id()); - let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes(parent_item); + let resolved_lifetimes: &ResolveBoundVars = + self.tcx.resolve_bound_vars(parent_item); // We need to add *all* deps, since opaque tys may want them from *us* for (&owner, defs) in resolved_lifetimes.defs.iter() { defs.iter().for_each(|(&local_id, region)| { @@ -478,35 +559,33 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_), + origin: hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent), generics, .. }) => { // We want to start our early-bound indices at the end of the parent scope, // not including any parent `impl Trait`s. - let mut lifetimes = FxIndexMap::default(); + let mut bound_vars = FxIndexMap::default(); debug!(?generics.params); for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => { - let (def_id, reg) = Region::early(¶m); - lifetimes.insert(def_id, reg); - } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} - } + let (def_id, reg) = ResolvedArg::early(¶m); + bound_vars.insert(def_id, reg); } - let scope = Scope::Binder { - hir_id: item.hir_id(), - lifetimes, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; + let scope = Scope::Root { opt_parent_item: Some(parent) }; self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| intravisit::walk_item(this, item)) - }); + let scope = Scope::Binder { + hir_id: item.hir_id(), + bound_vars, + s: this.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + this.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| intravisit::walk_item(this, item)) + }); + }) } hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Enum(_, generics) @@ -516,18 +595,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::TraitAlias(generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. - let lifetimes = generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(Region::early(param)), - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, - }) - .collect(); + let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); self.record_late_bound_vars(item.hir_id(), vec![]); let scope = Scope::Binder { hir_id: item.hir_id(), - lifetimes, + bound_vars, scope_type: BinderScopeType::Normal, s: self.scope, where_bound_origin: None, @@ -562,21 +634,23 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { match ty.kind { hir::TyKind::BareFn(c) => { - let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c + let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c .generic_params .iter() - .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, param); - let r = late_region_as_bound_region(self.tcx, &pair.1); + let pair = ResolvedArg::late(late_bound_idx as u32, param); + let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); (pair, r) }) .unzip(); + + deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types"); + self.record_late_bound_vars(ty.hir_id, binders); let scope = Scope::Binder { hir_id: ty.hir_id, - lifetimes, + bound_vars, s: self.scope, scope_type: BinderScopeType::Normal, where_bound_origin: None, @@ -592,7 +666,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { for bound in bounds { - this.visit_poly_trait_ref(bound); + this.visit_poly_trait_ref_inner( + bound, + NonLifetimeBinderAllowed::Deny("trait object types"), + ); } }); match lifetime.res { @@ -674,7 +751,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // well-supported at the moment, so this doesn't work. // In the future, this should be fixed and this error should be removed. let def = self.map.defs.get(&lifetime.hir_id).cloned(); - let Some(Region::LateBound(_, _, def_id)) = def else { + let Some(ResolvedArg::LateBound(_, _, def_id)) = def else { continue }; let Some(def_id) = def_id.as_local() else { @@ -722,18 +799,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } Type(bounds, ty) => { let generics = &trait_item.generics; - let lifetimes = generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(Region::early(param)), - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, - }) - .collect(); + let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); self.record_late_bound_vars(trait_item.hir_id(), vec![]); let scope = Scope::Binder { hir_id: trait_item.hir_id(), - lifetimes, + bound_vars, s: self.scope, scope_type: BinderScopeType::Normal, where_bound_origin: None, @@ -768,18 +838,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }), Type(ty) => { let generics = &impl_item.generics; - let lifetimes: FxIndexMap<LocalDefId, Region> = generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(Region::early(param)), - GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None, - }) - .collect(); + let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = + generics.params.iter().map(ResolvedArg::early).collect(); self.record_late_bound_vars(impl_item.hir_id(), vec![]); let scope = Scope::Binder { hir_id: impl_item.hir_id(), - lifetimes, + bound_vars, s: self.scope, scope_type: BinderScopeType::Normal, where_bound_origin: None, @@ -803,7 +867,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.res { - hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), + hir::LifetimeName::Static => { + self.insert_lifetime(lifetime_ref, ResolvedArg::StaticLifetime) + } hir::LifetimeName::Param(param_def_id) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } @@ -814,13 +880,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: hir::HirId) { for (i, segment) in path.segments.iter().enumerate() { let depth = path.segments.len() - i - 1; if let Some(args) = segment.args { self.visit_segment_args(path.res, depth, args); } } + if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res { + self.resolve_type_ref(param_def_id.expect_local(), hir_id); + } } fn visit_fn( @@ -829,7 +898,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fd: &'tcx hir::FnDecl<'tcx>, body_id: hir::BodyId, _: Span, - _: hir::HirId, + _: LocalDefId, ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, @@ -869,24 +938,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { origin, .. }) => { - let lifetimes: FxIndexMap<LocalDefId, Region> = + let (bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = bound_generic_params .iter() - .filter(|param| { - matches!(param.kind, GenericParamKind::Lifetime { .. }) - }) .enumerate() .map(|(late_bound_idx, param)| { - Region::late(late_bound_idx as u32, param) - }) - .collect(); - let binders: Vec<_> = - lifetimes - .iter() - .map(|(_, region)| { - late_region_as_bound_region(this.tcx, region) + let pair = ResolvedArg::late(late_bound_idx as u32, param); + let r = late_arg_as_bound_arg(this.tcx, &pair.1, param); + (pair, r) }) - .collect(); + .unzip(); this.record_late_bound_vars(hir_id, binders.clone()); // Even if there are no lifetimes defined here, we still wrap it in a binder // scope. If there happens to be a nested poly trait ref (an error), that @@ -894,7 +955,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // being wrong. let scope = Scope::Binder { hir_id, - lifetimes, + bound_vars, s: this.scope, scope_type: BinderScopeType::Normal, where_bound_origin: Some(origin), @@ -920,21 +981,23 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { if lt.res != hir::LifetimeName::Static { continue; } - this.insert_lifetime(lt, Region::Static); - this.tcx - .sess - .struct_span_warn( - lifetime.ident.span, - &format!( - "unnecessary lifetime parameter `{}`", + this.insert_lifetime(lt, ResolvedArg::StaticLifetime); + this.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_LIFETIMES, + lifetime.hir_id, + lifetime.ident.span, + format!( + "unnecessary lifetime parameter `{}`", + lifetime.ident + ), + |lint| { + let help = &format!( + "you can use the `'static` lifetime directly, in place of `{}`", lifetime.ident, - ), - ) - .help(&format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.ident, - )) - .emit(); + ); + lint.help(help) + }, + ); } } } @@ -964,7 +1027,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.record_late_bound_vars(*hir_id, binders); let scope = Scope::Binder { hir_id: *hir_id, - lifetimes: FxIndexMap::default(), + bound_vars: FxIndexMap::default(), s: self.scope, scope_type, where_bound_origin: None, @@ -978,42 +1041,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) { - debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); - - let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); - - let initial_bound_vars = binders.len() as u32; - let mut lifetimes: FxIndexMap<LocalDefId, Region> = FxIndexMap::default(); - let binders_iter = trait_ref - .bound_generic_params - .iter() - .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) - .enumerate() - .map(|(late_bound_idx, param)| { - let pair = Region::late(initial_bound_vars + late_bound_idx as u32, param); - let r = late_region_as_bound_region(self.tcx, &pair.1); - lifetimes.insert(pair.0, pair.1); - r - }); - binders.extend(binders_iter); - - debug!(?binders); - self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders); + self.visit_poly_trait_ref_inner(trait_ref, NonLifetimeBinderAllowed::Allow); + } - // Always introduce a scope here, even if this is in a where clause and - // we introduced the binders around the bounded Ty. In that case, we - // just reuse the concatenation functionality also present in nested trait - // refs. - let scope = Scope::Binder { - hir_id: trait_ref.trait_ref.hir_ref_id, - lifetimes, - s: self.scope, - scope_type, - where_bound_origin: None, - }; - self.with(scope, |this| { - walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); - this.visit_trait_ref(&trait_ref.trait_ref); + fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { + self.with(Scope::AnonConstBoundary { s: self.scope }, |this| { + intravisit::walk_anon_const(this, c); }); } } @@ -1021,55 +1054,63 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: DefId) -> ObjectLifetimeDefault { debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam); let param_def_id = param_def_id.expect_local(); - let parent_def_id = tcx.local_parent(param_def_id); - let generics = tcx.hir().get_generics(parent_def_id).unwrap(); - let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); - let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); - - // Scan the bounds and where-clauses on parameters to extract bounds - // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` - // for each type parameter. - match param.kind { - GenericParamKind::Type { .. } => { - let mut set = Set1::Empty; - - // Look for `type: ...` where clauses. - for bound in generics.bounds_for_param(param_def_id) { - // Ignore `for<'a> type: ...` as they can change what - // lifetimes mean (although we could "just" handle it). - if !bound.bound_generic_params.is_empty() { - continue; - } + let hir::Node::GenericParam(param) = tcx.hir().get_by_def_id(param_def_id) else { + bug!("expected GenericParam for object_lifetime_default"); + }; + match param.source { + hir::GenericParamSource::Generics => { + let parent_def_id = tcx.local_parent(param_def_id); + let generics = tcx.hir().get_generics(parent_def_id).unwrap(); + let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); + let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); + + // Scan the bounds and where-clauses on parameters to extract bounds + // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` + // for each type parameter. + match param.kind { + GenericParamKind::Type { .. } => { + let mut set = Set1::Empty; + + // Look for `type: ...` where clauses. + for bound in generics.bounds_for_param(param_def_id) { + // Ignore `for<'a> type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if !bound.bound_generic_params.is_empty() { + continue; + } - for bound in bound.bounds { - if let hir::GenericBound::Outlives(lifetime) = bound { - set.insert(lifetime.res); + for bound in bound.bounds { + if let hir::GenericBound::Outlives(lifetime) = bound { + set.insert(lifetime.res); + } + } } - } - } - match set { - Set1::Empty => ObjectLifetimeDefault::Empty, - Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeName::Param(param_def_id)) => { - ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + match set { + Set1::Empty => ObjectLifetimeDefault::Empty, + Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeName::Param(param_def_id)) => { + ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + } + _ => ObjectLifetimeDefault::Ambiguous, + } + } + _ => { + bug!("object_lifetime_default_raw must only be called on a type parameter") } - _ => ObjectLifetimeDefault::Ambiguous, } } - _ => { - bug!("object_lifetime_default_raw must only be called on a type parameter") - } + hir::GenericParamSource::Binder => ObjectLifetimeDefault::Empty, } } -impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { +impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F) where - F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>), + F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>), { - let LifetimeContext { tcx, map, .. } = self; - let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope }; + let BoundVarContext { tcx, map, .. } = self; + let mut this = BoundVarContext { tcx: *tcx, map, scope: &wrap_scope }; let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); @@ -1110,23 +1151,25 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { generics: &'tcx hir::Generics<'tcx>, walk: F, ) where - F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>), + F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), { let mut named_late_bound_vars = 0; - let lifetimes: FxIndexMap<LocalDefId, Region> = generics + let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = generics .params .iter() - .filter_map(|param| match param.kind { + .map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { if self.tcx.is_late_bound(param.hir_id) { let late_bound_idx = named_late_bound_vars; named_late_bound_vars += 1; - Some(Region::late(late_bound_idx, param)) + ResolvedArg::late(late_bound_idx, param) } else { - Some(Region::early(param)) + ResolvedArg::early(param) } } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { + ResolvedArg::early(param) + } }) .collect(); @@ -1139,14 +1182,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, param); - late_region_as_bound_region(self.tcx, &pair.1) + let pair = ResolvedArg::late(late_bound_idx as u32, param); + late_arg_as_bound_arg(self.tcx, &pair.1, param) }) .collect(); self.record_late_bound_vars(hir_id, binders); let scope = Scope::Binder { hir_id, - lifetimes, + bound_vars, s: self.scope, scope_type: BinderScopeType::Normal, where_bound_origin: None, @@ -1177,15 +1220,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Root { opt_parent_item } => { if let Some(parent_item) = opt_parent_item && let parent_generics = self.tcx.generics_of(parent_item) - && parent_generics.param_def_id_to_index.contains_key(®ion_def_id.to_def_id()) + && parent_generics.param_def_id_to_index(self.tcx, region_def_id.to_def_id()).is_some() { - break Some(Region::EarlyBound(region_def_id.to_def_id())); + break Some(ResolvedArg::EarlyBound(region_def_id.to_def_id())); } break None; } - Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => { - if let Some(&def) = lifetimes.get(®ion_def_id) { + Scope::Binder { ref bound_vars, scope_type, s, where_bound_origin, .. } => { + if let Some(&def) = bound_vars.get(®ion_def_id) { break Some(def.shifted(late_depth)); } match scope_type { @@ -1252,26 +1295,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } }; if let Some(mut def) = result { - if let Region::EarlyBound(..) = def { + if let ResolvedArg::EarlyBound(..) = def { // Do not free early-bound regions, only late-bound ones. } else if let Some(body_id) = outermost_body { let fn_id = self.tcx.hir().body_owner(body_id); match self.tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + Node::Item(hir::Item { owner_id, kind: hir::ItemKind::Fn(..), .. }) | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(..), .. + owner_id, + kind: hir::TraitItemKind::Fn(..), + .. }) - | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) - | Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { - let scope = self.tcx.hir().local_def_id(fn_id); - def = Region::Free(scope.to_def_id(), def.id().unwrap()); + | Node::ImplItem(hir::ImplItem { + owner_id, + kind: hir::ImplItemKind::Fn(..), + .. + }) => { + def = ResolvedArg::Free(owner_id.to_def_id(), def.id().unwrap()); + } + Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) => { + def = ResolvedArg::Free(closure.def_id.to_def_id(), def.id().unwrap()); } _ => {} } @@ -1310,7 +1361,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1322,6 +1374,87 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); } + fn resolve_type_ref(&mut self, param_def_id: LocalDefId, hir_id: hir::HirId) { + // Walk up the scope chain, tracking the number of fn scopes + // that we pass through, until we find a lifetime with the + // given name or we run out of scopes. + // search. + let mut late_depth = 0; + let mut scope = self.scope; + let mut crossed_anon_const = false; + let result = loop { + match *scope { + Scope::Body { s, .. } => { + scope = s; + } + + Scope::Root { opt_parent_item } => { + if let Some(parent_item) = opt_parent_item + && let parent_generics = self.tcx.generics_of(parent_item) + && parent_generics.param_def_id_to_index(self.tcx, param_def_id.to_def_id()).is_some() + { + break Some(ResolvedArg::EarlyBound(param_def_id.to_def_id())); + } + break None; + } + + Scope::Binder { ref bound_vars, scope_type, s, .. } => { + if let Some(&def) = bound_vars.get(¶m_def_id) { + break Some(def.shifted(late_depth)); + } + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } + scope = s; + } + + Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + + Scope::AnonConstBoundary { s } => { + crossed_anon_const = true; + scope = s; + } + } + }; + + if let Some(def) = result { + if let ResolvedArg::LateBound(..) = def && crossed_anon_const { + let use_span = self.tcx.hir().span(hir_id); + let def_span = self.tcx.def_span(param_def_id); + match self.tcx.def_kind(param_def_id) { + DefKind::ConstParam => { + self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Const { + use_span, + def_span, + }); + } + DefKind::TyParam => { + self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Type { + use_span, + def_span, + }); + } + _ => unreachable!(), + } + return; + } + + self.map.defs.insert(hir_id, def); + return; + } + + self.tcx.sess.delay_span_bug( + self.tcx.hir().span(hir_id), + format!("could not resolve {param_def_id:?}"), + ); + } + #[instrument(level = "debug", skip(self))] fn visit_segment_args( &mut self, @@ -1390,7 +1523,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1408,10 +1542,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if in_body { None } else { - Some(Region::Static) + Some(ResolvedArg::StaticLifetime) } } - ObjectLifetimeDefault::Static => Some(Region::Static), + ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), ObjectLifetimeDefault::Param(param_def_id) => { // This index can be used with `generic_args` since `parent_count == 0`. let index = generics.param_def_id_to_index[¶m_def_id] as usize; @@ -1500,18 +1634,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // in the trait ref `YY<...>` in `Item: YY<...>`. for binding in generic_args.bindings { let scope = Scope::ObjectLifetimeDefault { - lifetime: if has_lifetime_parameter { None } else { Some(Region::Static) }, + lifetime: if has_lifetime_parameter { + None + } else { + Some(ResolvedArg::StaticLifetime) + }, s: self.scope, }; if let Some(type_def_id) = type_def_id { - let lifetimes = LifetimeContext::supertrait_hrtb_lifetimes( - self.tcx, - type_def_id, - binding.ident, - ); + let bound_vars = + BoundVarContext::supertrait_hrtb_vars(self.tcx, type_def_id, binding.ident); self.with(scope, |this| { let scope = Scope::Supertrait { - lifetimes: lifetimes.unwrap_or_default(), + bound_vars: bound_vars.unwrap_or_default(), s: this.scope, }; this.with(scope, |this| this.visit_assoc_type_binding(binding)); @@ -1534,7 +1669,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// ``` /// In this case, if we wanted to the supertrait HRTB lifetimes for `As` on /// the starting trait `Bar`, we would return `Some(['b, 'a])`. - fn supertrait_hrtb_lifetimes( + fn supertrait_hrtb_vars( tcx: TyCtxt<'tcx>, def_id: DefId, assoc_name: Ident, @@ -1556,7 +1691,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // See issue #83753. If someone writes an associated type on a non-trait, just treat it as // there being no supertrait HRTBs. match tcx.def_kind(def_id) { - DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {} + DefKind::Trait | DefKind::TraitAlias | DefKind::Impl { .. } => {} _ => break None, } @@ -1619,13 +1754,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Root { .. } | Scope::Elision { .. } => break Region::Static, + Scope::Root { .. } | Scope::Elision { .. } => break ResolvedArg::StaticLifetime, Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l, - Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { + Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1634,7 +1771,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { + fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: ResolvedArg) { debug!(span = ?lifetime_ref.ident.span); self.map.defs.insert(lifetime_ref.hir_id, def); } @@ -1642,7 +1779,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// Sometimes we resolve a lifetime, but later find that it is an /// error (esp. around impl trait). In that case, we remove the /// entry into `map.defs` so as not to confuse later code. - fn uninsert_lifetime_on_error(&mut self, lifetime_ref: &'tcx hir::Lifetime, bad_def: Region) { + fn uninsert_lifetime_on_error( + &mut self, + lifetime_ref: &'tcx hir::Lifetime, + bad_def: ResolvedArg, + ) { let old_value = self.map.defs.remove(&lifetime_ref.hir_id); assert_eq!(old_value, Some(bad_def)); } @@ -1658,10 +1799,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// "Constrained" basically means that it appears in any type but /// not amongst the inputs to a projection. In other words, `<&'a /// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`. -fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?; - let generics = tcx.hir().get_generics(def_id)?; +fn is_late_bound_map( + tcx: TyCtxt<'_>, + owner_id: hir::OwnerId, +) -> Option<&FxIndexSet<hir::ItemLocalId>> { + let decl = tcx.hir().fn_decl_by_hir_id(owner_id.into())?; + let generics = tcx.hir().get_generics(owner_id.def_id)?; let mut late_bound = FxIndexSet::default(); @@ -1695,24 +1838,22 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => continue, } - let param_def_id = tcx.hir().local_def_id(param.hir_id); - // appears in the where clauses? early-bound. - if appears_in_where_clause.regions.contains(¶m_def_id) { + if appears_in_where_clause.regions.contains(¶m.def_id) { continue; } // does not appear in the inputs, but appears in the return type? early-bound. - if !constrained_by_input.regions.contains(¶m_def_id) - && appears_in_output.regions.contains(¶m_def_id) + if !constrained_by_input.regions.contains(¶m.def_id) + && appears_in_output.regions.contains(¶m.def_id) { continue; } - debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id); + debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.def_id); - let inserted = late_bound.insert(param_def_id); - assert!(inserted, "visited lifetime {:?} twice", param.hir_id); + let inserted = late_bound.insert(param.hir_id.local_id); + assert!(inserted, "visited lifetime {:?} twice", param.def_id); } debug!(?late_bound); @@ -1745,7 +1886,7 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< use std::ops::ControlFlow; use ty::Ty; - impl<'tcx> TypeVisitor<'tcx> for ConstrainedCollectorPostAstConv { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstrainedCollectorPostAstConv { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> { match t.kind() { ty::Param(param_ty) => { @@ -1797,7 +1938,7 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< let mut walker = ConstrainedCollectorPostAstConv { arg_is_constrained: vec![false; generics.params.len()].into_boxed_slice(), }; - walker.visit_ty(self.tcx.type_of(alias_def)); + walker.visit_ty(self.tcx.type_of(alias_def).subst_identity()); match segments.last() { Some(hir::PathSegment { args: Some(args), .. }) => { @@ -1865,3 +2006,37 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< } } } + +pub fn deny_non_region_late_bound( + tcx: TyCtxt<'_>, + bound_vars: &mut FxIndexMap<LocalDefId, ResolvedArg>, + where_: &str, +) { + let mut first = true; + + for (var, arg) in bound_vars { + let Node::GenericParam(param) = tcx.hir().get_by_def_id(*var) else { + bug!(); + }; + + let what = match param.kind { + hir::GenericParamKind::Type { .. } => "type", + hir::GenericParamKind::Const { .. } => "const", + hir::GenericParamKind::Lifetime { .. } => continue, + }; + + let mut diag = tcx.sess.struct_span_err( + param.span, + format!("late-bound {what} parameter not allowed on {where_}"), + ); + + let guar = if tcx.features().non_lifetime_binders && first { + diag.emit() + } else { + diag.delay_as_bug() + }; + + first = false; + *arg = ResolvedArg::Error(guar); + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 5e388a2f2..50073d94e 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -8,7 +8,9 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; +use rustc_middle::ty::{ + self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; @@ -54,15 +56,14 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< // ty which is a fully resolved projection. // For the code example above, this would mean converting Self::Assoc<3> // into a ty::Alias(ty::Projection, <Self as Foo>::Assoc<3>) - let item_hir_id = tcx + let item_def_id = tcx .hir() - .parent_iter(hir_id) - .filter(|(_, node)| matches!(node, Node::Item(_))) - .map(|(id, _)| id) - .next() - .unwrap(); - let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); - let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; + .parent_owner_iter(hir_id) + .find(|(_, node)| matches!(node, OwnerNode::Item(_))) + .unwrap() + .0 + .to_def_id(); + let item_ctxt = &ItemCtxt::new(tcx, item_def_id) as &dyn crate::astconv::AstConv<'_>; let ty = item_ctxt.ast_ty_to_ty(hir_ty); // Iterate through the generics of the projection to find the one that corresponds to @@ -242,7 +243,7 @@ fn get_path_containing_arg_in_pat<'hir>( arg_path } -pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { +pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>> { let def_id = def_id.expect_local(); use rustc_hir::*; @@ -250,7 +251,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let icx = ItemCtxt::new(tcx, def_id.to_def_id()); - match tcx.hir().get(hir_id) { + let output = match tcx.hir().get(hir_id) { Node::TraitItem(item) => match item.kind { TraitItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); @@ -258,13 +259,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } TraitItemKind::Const(ty, body_id) => body_id .and_then(|body_id| { - if is_suggestable_infer_ty(ty) { - Some(infer_placeholder_type( - tcx, def_id, body_id, ty.span, item.ident, "constant", - )) - } else { - None - } + is_suggestable_infer_ty(ty) + .then(|| infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant",)) }) .unwrap_or_else(|| icx.to_ty(ty)), TraitItemKind::Type(_, Some(ty)) => icx.to_ty(ty), @@ -323,8 +319,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { ItemKind::Impl(hir::Impl { self_ty, .. }) => { match self_ty.find_self_aliases() { spans if spans.len() > 0 => { - tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: (), }); - tcx.ty_error() + let guar = tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () }); + tcx.ty_error(guar) }, _ => icx.to_ty(*self_ty), } @@ -381,7 +377,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def { VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_item(hir_id)) + tcx.type_of(tcx.hir().get_parent_item(hir_id)).subst_identity() } VariantData::Tuple(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); @@ -398,7 +394,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => { // We defer to `type_of` of the corresponding parameter // for generic arguments. - tcx.type_of(param) + tcx.type_of(param).subst_identity() } Node::AnonConst(_) => { @@ -450,7 +446,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { && e.hir_id == hir_id => { let Some(trait_def_id) = trait_ref.trait_def_id() else { - return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + return ty::EarlyBinder(tcx.ty_error_with_message(DUMMY_SP, "Could not find trait")); }; let assoc_items = tcx.associated_items(trait_def_id); let assoc_item = assoc_items.find_by_name_and_kind( @@ -460,7 +456,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { def_id.to_def_id(), ); if let Some(assoc_item) = assoc_item { - tcx.type_of(assoc_item.def_id) + tcx.type_of(assoc_item.def_id).subst_identity() } else { // FIXME(associated_const_equality): add a useful error message here. tcx.ty_error_with_message( @@ -484,7 +480,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { }) => { let Some(trait_def_id) = trait_ref.trait_def_id() else { - return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + return ty::EarlyBinder(tcx.ty_error_with_message(DUMMY_SP, "Could not find trait")); }; let assoc_items = tcx.associated_items(trait_def_id); let assoc_item = assoc_items.find_by_name_and_kind( @@ -505,7 +501,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { if let Some(param) = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const()) { - tcx.type_of(param.def_id) + tcx.type_of(param.def_id).subst_identity() } else { // FIXME(associated_const_equality): add a useful error message here. tcx.ty_error_with_message( @@ -519,7 +515,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { def_id: param_def_id, kind: GenericParamKind::Const { default: Some(ct), .. }, .. - }) if ct.hir_id == hir_id => tcx.type_of(param_def_id), + }) if ct.hir_id == hir_id => tcx.type_of(param_def_id).subst_identity(), x => tcx.ty_error_with_message( DUMMY_SP, @@ -537,7 +533,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { x => { bug!("unexpected sort of node in type_of(): {:?}", x); } - } + }; + ty::EarlyBinder(output) } #[instrument(skip(tcx), level = "debug")] @@ -602,8 +599,9 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T // // constant does not contain interior mutability. // ``` let tables = self.tcx.typeck(item_def_id); - if let Some(_) = tables.tainted_by_errors { - self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); + if let Some(guar) = tables.tainted_by_errors { + self.found = + Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) }); return; } let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { @@ -621,8 +619,8 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T debug!(?concrete_type, "found constraint"); if let Some(prev) = &mut self.found { if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { - prev.report_mismatch(&concrete_type, self.tcx); - prev.ty = self.tcx.ty_error(); + let guar = prev.report_mismatch(&concrete_type, self.tcx); + prev.ty = self.tcx.ty_error(guar); } } else { self.found = Some(concrete_type); @@ -709,7 +707,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T _ => "item", }, }); - return tcx.ty_error_with_guaranteed(reported); + return tcx.ty_error(reported); }; // Only check against typeck if we didn't already error @@ -817,11 +815,11 @@ fn find_opaque_ty_constraints_for_rpit( concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { let table = tcx.typeck(owner_def_id); - if let Some(_) = table.tainted_by_errors { + if let Some(guar) = table.tainted_by_errors { // Some error in the // owner fn prevented us from populating // the `concrete_opaque_types` table. - tcx.ty_error() + tcx.ty_error(guar) } else { table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { // We failed to resolve the opaque type or it @@ -846,35 +844,23 @@ fn infer_placeholder_type<'a>( ) -> Ty<'a> { // Attempts to make the type nameable by turning FnDefs into FnPtrs. struct MakeNameable<'tcx> { - success: bool, tcx: TyCtxt<'tcx>, } - impl<'tcx> MakeNameable<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - MakeNameable { success: true, tcx } - } - } - - impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { + impl<'tcx> TypeFolder<TyCtxt<'tcx>> for MakeNameable<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !self.success { - return ty; - } - - match ty.kind() { - ty::FnDef(def_id, _) => self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id)), - // FIXME: non-capturing closures should also suggest a function pointer - ty::Closure(..) | ty::Generator(..) => { - self.success = false; - ty + let ty = match *ty.kind() { + ty::FnDef(def_id, substs) => { + self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs)) } - _ => ty.super_fold_with(self), - } + _ => ty, + }; + + ty.super_fold_with(self) } } @@ -897,15 +883,11 @@ fn infer_placeholder_type<'a>( suggestions.clear(); } - // Suggesting unnameable types won't help. - let mut mk_nameable = MakeNameable::new(tcx); - let ty = mk_nameable.fold_ty(ty); - let sugg_ty = if mk_nameable.success { Some(ty) } else { None }; - if let Some(sugg_ty) = sugg_ty { + if let Some(ty) = ty.make_suggestable(tcx, false) { err.span_suggestion( span, &format!("provide a type for the {item}", item = kind), - format!("{colon} {sugg_ty}"), + format!("{colon} {ty}"), Applicability::MachineApplicable, ); } else { @@ -922,15 +904,12 @@ fn infer_placeholder_type<'a>( let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { - let mut mk_nameable = MakeNameable::new(tcx); - let ty = mk_nameable.fold_ty(ty); - let sugg_ty = if mk_nameable.success { Some(ty) } else { None }; - if let Some(sugg_ty) = sugg_ty { + if let Some(ty) = ty.make_suggestable(tcx, false) { diag.span_suggestion( span, "replace with the correct type", - sugg_ty, - Applicability::MaybeIncorrect, + ty, + Applicability::MachineApplicable, ); } else { with_forced_trimmed_paths!(diag.span_note( |