Skip to content

Commit

Permalink
[PoC] Track type-dependent defs in ItemCtxts (minimally)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Aug 15, 2024
1 parent cc8c507 commit 0664285
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 51 deletions.
31 changes: 29 additions & 2 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//! At present, however, we do run collection across all items in the
//! crate as a kind of pass. This should eventually be factored away.

use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::iter;
use std::ops::Bound;

Expand All @@ -33,6 +33,10 @@ 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};
Expand Down Expand Up @@ -122,6 +126,7 @@ pub fn provide(providers: &mut Providers) {
pub struct ItemCtxt<'tcx> {
tcx: TyCtxt<'tcx>,
item_def_id: LocalDefId,
type_dependent_defs: RefCell<TypeDependentDefs>,
tainted_by_errors: Cell<Option<ErrorGuaranteed>>,
}

Expand Down Expand Up @@ -355,7 +360,12 @@ fn bad_placeholder<'cx, '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> {
Expand Down Expand Up @@ -521,6 +531,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
}
Expand Down Expand Up @@ -609,6 +627,15 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
}
}

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.
fn get_new_lifetime_name<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,12 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span
..
}) => {
let body_owner = tcx.hir().enclosing_body_owner(arg_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,
Expand Down
48 changes: 32 additions & 16 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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,
Expand Down Expand Up @@ -101,7 +102,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>;

fn dcx(&self) -> DiagCtxtHandle<'_>;
Expand Down Expand Up @@ -178,6 +179,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>>;

Expand Down Expand Up @@ -995,6 +998,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.
Expand All @@ -1009,8 +1013,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
permit_variants: bool,
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
debug!(%qself_ty, ?assoc_segment.ident);
let tcx = self.tcx();
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;

// Check if we have an enum variant or an inherent associated type.
Expand All @@ -1036,22 +1060,21 @@ 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(),
qself_ty,
hir_ref_id,
span,
)? {
return Ok((ty, DefKind::AssocTy, did));
return Ok((ty, DefKind::AssocTy, def_id));
}
}

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.qpath_res(qpath, qself.hir_id),
_ => Res::Err,
};

// Find the type of the associated item, and the trait where the associated
Expand Down Expand Up @@ -1089,14 +1112,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<T::Assoc>::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<T>::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);

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ This API is completely unstable and subject to change.
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(slice_partition_dedup)]
#![feature(trait_upcasting)]
#![feature(try_blocks)]
#![feature(unwrap_infallible)]
// tidy-alphabetical-end
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,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))]
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2051,12 +2051,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);
Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ mod suggestions;
use std::cell::{Cell, RefCell};
use std::ops::Deref;

use hir::def_id::CRATE_DEF_ID;
use rustc_errors::DiagCtxtHandle;
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_middle::ty::typeck_results::{HasTypeDependentDefs, TypeDependentDef};
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
Expand Down Expand Up @@ -338,6 +339,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)
}
Expand All @@ -359,6 +364,12 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
}
}

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
/// we want to either use the unnormalized or the normalized form of this type.
///
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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;
Expand Down Expand Up @@ -159,7 +160,6 @@ mod rvalue_scopes;
mod structural_impls;
#[allow(hidden_glob_reexports)]
mod sty;
mod typeck_results;

// Data types

Expand Down
57 changes: 40 additions & 17 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<T>::X` associated paths and
/// method calls, including those of overloaded operators.
type_dependent_defs: ItemLocalMap<Result<(DefKind, DefId), ErrorGuaranteed>>,
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
Expand Down Expand Up @@ -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<DefId> {
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 }
}

Expand Down Expand Up @@ -549,6 +537,33 @@ impl<'tcx> TypeckResults<'tcx> {
}
}

/// Resolved definitions for `<T>::X` associated paths and
/// method calls, including those of overloaded operators.
pub type TypeDependentDefs = ItemLocalMap<TypeDependentDef>;

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
Expand Down Expand Up @@ -581,6 +596,10 @@ pub struct LocalTableInContext<'a, V> {
}

impl<'a, V> LocalTableInContext<'a, V> {
pub fn new(hir_owner: OwnerId, data: &'a ItemLocalMap<V>) -> 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)
Expand Down Expand Up @@ -619,6 +638,10 @@ pub struct LocalTableInContextMut<'a, V> {
}

impl<'a, V> LocalTableInContextMut<'a, V> {
pub fn new(hir_owner: OwnerId, data: &'a mut ItemLocalMap<V>) -> 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)
Expand Down
Loading

0 comments on commit 0664285

Please sign in to comment.