From ed8def2f2ddb8b3b33e60d9784a8749bfced6cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 3 Jul 2024 15:50:47 +0200 Subject: [PATCH] [PoC] Track type-dependent defs in ItemCtxts (minimally) --- compiler/rustc_hir_analysis/src/collect.rs | 37 +++++++++++- .../rustc_hir_analysis/src/collect/type_of.rs | 5 +- .../src/hir_ty_lowering/mod.rs | 49 +++++++++++----- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 4 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 6 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 19 ++++++- compiler/rustc_middle/src/ty/mod.rs | 2 +- .../rustc_middle/src/ty/typeck_results.rs | 57 +++++++++++++------ ...ecutive-shorthand-projections.eager.stderr | 14 +++++ ...secutive-shorthand-projections.lazy.stderr | 2 +- .../non-consecutive-shorthand-projections.rs | 4 +- 11 files changed, 148 insertions(+), 51 deletions(-) create mode 100644 tests/ui/associated-types/non-consecutive-shorthand-projections.eager.stderr diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 41fbef489405..a2a93eba1d71 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -19,15 +19,19 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228}; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, walk_generics, Visitor}; -use rustc_hir::{self as hir}; use rustc_hir::{GenericParamKind, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; +use rustc_middle::ty::typeck_results::{ + HasTypeDependentDefs, LocalTableInContext, LocalTableInContextMut, TypeDependentDef, + TypeDependentDefs, +}; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; @@ -38,7 +42,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use rustc_trait_selection::traits::ObligationCtxt; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::iter; use std::ops::Bound; @@ -120,6 +124,7 @@ pub fn provide(providers: &mut Providers) { pub struct ItemCtxt<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, + type_dependent_defs: RefCell, tainted_by_errors: Cell>, } @@ -342,7 +347,12 @@ fn bad_placeholder<'tcx>( impl<'tcx> ItemCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> { - ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } + ItemCtxt { + tcx, + item_def_id, + type_dependent_defs: Default::default(), + tainted_by_errors: Cell::new(None), + } } pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { @@ -505,6 +515,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { // There's no place to record types from signatures? } + fn record_res(&self, hir_id: hir::HirId, result: TypeDependentDef) { + LocalTableInContextMut::new( + self.hir_id().owner, + &mut self.type_dependent_defs.borrow_mut(), + ) + .insert(hir_id, result); + } + fn infcx(&self) -> Option<&InferCtxt<'tcx>> { None } @@ -595,6 +613,19 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { (input_tys, output_ty) } + + fn upcast(&self) -> &dyn HasTypeDependentDefs { + self + } +} + +impl HasTypeDependentDefs for ItemCtxt<'_> { + fn type_dependent_def(&self, id: hir::HirId) -> Option<(DefKind, DefId)> { + LocalTableInContext::new(self.hir_id().owner, &self.type_dependent_defs.borrow()) + .get(id) + .copied() + .and_then(|result| result.ok()) + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 1e2b0c432338..1765b121406c 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -145,11 +145,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .. }) => { let body_owner = tcx.hir().enclosing_body_owner(hir_id); - let tables = tcx.typeck(body_owner); + let typeck_results = tcx.typeck(body_owner); // This may fail in case the method/path does not actually exist. // As there is no relevant param for `def_id`, we simply return // `None` here. - let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else { + let Some(type_dependent_def) = typeck_results.type_dependent_def_id(parent_node_id) + else { return Ty::new_error_with_message( tcx, span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a74667de70e3..2bf34b696f90 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -39,6 +39,7 @@ use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; +use rustc_middle::ty::typeck_results::{HasTypeDependentDefs, TypeDependentDef}; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, TypeVisitableExt, @@ -98,7 +99,7 @@ pub enum RegionInferReason<'a> { /// the [`rustc_middle::ty`] representation. /// /// This trait used to be called `AstConv`. -pub trait HirTyLowerer<'tcx> { +pub trait HirTyLowerer<'tcx>: HasTypeDependentDefs { fn tcx(&self) -> TyCtxt<'tcx>; /// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered. @@ -173,6 +174,8 @@ pub trait HirTyLowerer<'tcx> { /// Record the lowered type of a HIR node in this context. fn record_ty(&self, hir_id: HirId, ty: Ty<'tcx>, span: Span); + fn record_res(&self, hir_id: hir::HirId, result: TypeDependentDef); + /// The inference context of the lowering context if applicable. fn infcx(&self) -> Option<&InferCtxt<'tcx>>; @@ -192,6 +195,8 @@ pub trait HirTyLowerer<'tcx> { { self } + + fn upcast(&self) -> &dyn HasTypeDependentDefs; } /// New-typed boolean indicating whether explicit late-bound lifetimes @@ -992,6 +997,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// [type-relative]: hir::QPath::TypeRelative /// [#22519]: https://github.com/rust-lang/rust/issues/22519 /// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403 + // FIXME(fmease): Update docs // // NOTE: When this function starts resolving `Trait::AssocTy` successfully // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. @@ -1006,13 +1012,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { debug!(%qself_ty, ?assoc_segment.ident); + let result = self.lower_assoc_path_inner( + hir_ref_id, + span, + qself_ty, + qself, + assoc_segment, + permit_variants, + ); + self.record_res(hir_ref_id, result.map(|(_, def_kind, def_id)| (def_kind, def_id))); + result + } + + fn lower_assoc_path_inner( + &self, + hir_ref_id: HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + permit_variants: bool, + ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { let tcx = self.tcx(); let assoc_ident = assoc_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err + let qself_res = match &qself.kind { + hir::TyKind::Path(qpath) => self.upcast().qpath_res(qpath, qself.hir_id), + _ => Res::Err, }; // Check if we have an enum variant or an inherent associated type. @@ -1038,7 +1064,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(inherent_associated_types, #106719): Support self types other than ADTs. - if let Some((ty, did)) = self.probe_inherent_assoc_ty( + if let Some((ty, def_id)) = self.probe_inherent_assoc_ty( assoc_ident, assoc_segment, adt_def.did(), @@ -1046,7 +1072,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_ref_id, span, )? { - return Ok((ty, DefKind::AssocTy, did)); + return Ok((ty, DefKind::AssocTy, def_id)); } } @@ -1085,14 +1111,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_ident, span, )?, - // FIXME(fmease): - // Require the pre-lowered projectee (the HIR QSelf) to have `DefKind::AssocTy`. Rephrased, - // `T::Assoc::Assoc` typeck'ing shouldn't imply `Identity::Assoc` typeck'ing where - // `Identity` is an eager (i.e., non-lazy) type alias. We should do this - // * for consistency with lazy type aliases (`ty::Weak`) - // * for consistency with the fact that `T::Assoc` typeck'ing doesn't imply `Identity::Assoc` - // typeck'ing - (ty::Alias(ty::Projection, alias_ty), _ /* Res::Def(DefKind::AssocTy, _) */) => { + (ty::Alias(ty::Projection, alias_ty), Res::Def(DefKind::AssocTy, _)) => { // FIXME: Utilizing `item_bounds` for this is cycle-prone. let predicates = tcx.item_bounds(alias_ty.def_id).instantiate(tcx, alias_ty.args); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 5450243417f7..fae4614bb70b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -170,9 +170,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn write_resolution( &self, hir_id: HirId, - r: Result<(DefKind, DefId), ErrorGuaranteed>, + result: Result<(DefKind, DefId), ErrorGuaranteed>, ) { - self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); + self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, result); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 430e12ff7b87..4921065b9108 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1984,12 +1984,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); let ty = LoweredTy::from_raw(self, path_span, ty); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) + (result.map_or(Res::Err, |(_, kind, def_id)| Res::Def(kind, def_id)), ty) } QPath::LangItem(lang_item, span) => { let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index da68da029b41..187095b7e131 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -11,13 +11,14 @@ use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; -use hir::def_id::CRATE_DEF_ID; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; use rustc_infer::infer; use rustc_infer::infer::error_reporting::sub_relations::SubRelations; use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_middle::ty::typeck_results::{HasTypeDependentDefs, TypeDependentDef}; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; @@ -334,6 +335,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { self.write_ty(hir_id, ty) } + fn record_res(&self, hir_id: hir::HirId, result: TypeDependentDef) { + self.write_resolution(hir_id, result); + } + fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { Some(&self.infcx) } @@ -357,6 +362,16 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { }; (input_tys, output_ty) } + + fn upcast(&self) -> &dyn HasTypeDependentDefs { + self + } +} + +impl HasTypeDependentDefs for FnCtxt<'_, '_> { + fn type_dependent_def(&self, id: hir::HirId) -> Option<(DefKind, DefId)> { + self.typeck_results.borrow().type_dependent_def(id) + } } /// The `ty` representation of a user-provided type. Depending on the use-site diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7d57d88f40f4..188fa3019940 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -137,6 +137,7 @@ pub mod pattern; pub mod print; pub mod relate; pub mod trait_def; +pub mod typeck_results; pub mod util; pub mod visit; pub mod vtable; @@ -163,7 +164,6 @@ mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] mod sty; -mod typeck_results; // Data types diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 24e3e623ff27..d9732842708b 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -32,9 +32,7 @@ pub struct TypeckResults<'tcx> { /// The `HirId::owner` all `ItemLocalId`s in this table are relative to. pub hir_owner: OwnerId, - /// Resolved definitions for `::X` associated paths and - /// method calls, including those of overloaded operators. - type_dependent_defs: ItemLocalMap>, + type_dependent_defs: TypeDependentDefs, /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more @@ -254,32 +252,22 @@ impl<'tcx> TypeckResults<'tcx> { /// Returns the final resolution of a `QPath` in an `Expr` or `Pat` node. pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: HirId) -> Res { - match *qpath { - hir::QPath::Resolved(_, path) => path.res, - hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self - .type_dependent_def(id) - .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), - } + HasTypeDependentDefs::qpath_res(self, qpath, id) } - pub fn type_dependent_defs( - &self, - ) -> LocalTableInContext<'_, Result<(DefKind, DefId), ErrorGuaranteed>> { + pub fn type_dependent_defs(&self) -> LocalTableInContext<'_, TypeDependentDef> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.type_dependent_defs } } pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + self.type_dependent_defs().get(id).copied().and_then(|result| result.ok()) } pub fn type_dependent_def_id(&self, id: HirId) -> Option { self.type_dependent_def(id).map(|(_, def_id)| def_id) } - pub fn type_dependent_defs_mut( - &mut self, - ) -> LocalTableInContextMut<'_, Result<(DefKind, DefId), ErrorGuaranteed>> { + pub fn type_dependent_defs_mut(&mut self) -> LocalTableInContextMut<'_, TypeDependentDef> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } @@ -549,6 +537,33 @@ impl<'tcx> TypeckResults<'tcx> { } } +/// Resolved definitions for `::X` associated paths and +/// method calls, including those of overloaded operators. +pub type TypeDependentDefs = ItemLocalMap; + +pub type TypeDependentDef = Result<(DefKind, DefId), ErrorGuaranteed>; + +// FIXME(fmease): Yuck! +pub trait HasTypeDependentDefs { + fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)>; + + /// Returns the final resolution of a `QPath`. + fn qpath_res(&self, qpath: &hir::QPath<'_>, id: HirId) -> Res { + match qpath { + hir::QPath::Resolved(_, path) => path.res, + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self + .type_dependent_def(id) + .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), + } + } +} + +impl HasTypeDependentDefs for TypeckResults<'_> { + fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { + self.type_dependent_def(id) + } +} + /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the maps of a TypeckResults. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in @@ -581,6 +596,10 @@ pub struct LocalTableInContext<'a, V> { } impl<'a, V> LocalTableInContext<'a, V> { + pub fn new(hir_owner: OwnerId, data: &'a ItemLocalMap) -> Self { + Self { hir_owner, data } + } + pub fn contains_key(&self, id: HirId) -> bool { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.contains_key(&id.local_id) @@ -617,6 +636,10 @@ pub struct LocalTableInContextMut<'a, V> { } impl<'a, V> LocalTableInContextMut<'a, V> { + pub fn new(hir_owner: OwnerId, data: &'a mut ItemLocalMap) -> Self { + Self { hir_owner, data } + } + pub fn get_mut(&mut self, id: HirId) -> Option<&mut V> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.get_mut(&id.local_id) diff --git a/tests/ui/associated-types/non-consecutive-shorthand-projections.eager.stderr b/tests/ui/associated-types/non-consecutive-shorthand-projections.eager.stderr new file mode 100644 index 000000000000..7feef6ff22bf --- /dev/null +++ b/tests/ui/associated-types/non-consecutive-shorthand-projections.eager.stderr @@ -0,0 +1,14 @@ +error[E0223]: ambiguous associated type + --> $DIR/non-consecutive-shorthand-projections.rs:14:12 + | +LL | let _: Identity::Project; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `Project` implemented for `::Project`, you could use the fully-qualified path + | +LL | let _: <::Project as Example>::Project; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/associated-types/non-consecutive-shorthand-projections.lazy.stderr b/tests/ui/associated-types/non-consecutive-shorthand-projections.lazy.stderr index 47d8c6e3559a..84d3a425fc05 100644 --- a/tests/ui/associated-types/non-consecutive-shorthand-projections.lazy.stderr +++ b/tests/ui/associated-types/non-consecutive-shorthand-projections.lazy.stderr @@ -1,5 +1,5 @@ error[E0223]: ambiguous associated type - --> $DIR/non-consecutive-shorthand-projections.rs:16:12 + --> $DIR/non-consecutive-shorthand-projections.rs:14:12 | LL | let _: Identity::Project; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/associated-types/non-consecutive-shorthand-projections.rs b/tests/ui/associated-types/non-consecutive-shorthand-projections.rs index 95cbad75b689..34f46d777496 100644 --- a/tests/ui/associated-types/non-consecutive-shorthand-projections.rs +++ b/tests/ui/associated-types/non-consecutive-shorthand-projections.rs @@ -2,7 +2,6 @@ // Of course, under #22519 (arbitrary shorthand projections), this should obviously typeck. // For reference, `T::Alias` typeck'ing does *not* imply `Identity::Alias` typeck'ing. //@ revisions: eager lazy -//@[eager] check-pass #![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] type Identity = T; @@ -12,8 +11,7 @@ trait Trait { } fn scope() { - // FIXME(fmease): Reject this under [eager], too! - let _: Identity::Project; //[lazy]~ ERROR ambiguous associated type + let _: Identity::Project; //~ ERROR ambiguous associated type } fn main() {}