Skip to content

Implement consecutive shorthand projections (associated type paths) like T::AssocA::AssocB #126651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -15,7 +15,7 @@
//! crate as a kind of pass. This should eventually be factored away.

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

Expand All @@ -34,6 +34,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, TypingMode, fold_regions};
use rustc_middle::{bug, span_bug};
Expand Down Expand Up @@ -125,6 +129,7 @@ pub(crate) fn provide(providers: &mut Providers) {
pub(crate) 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 @@ -365,7 +370,12 @@ fn bad_placeholder<'cx, 'tcx>(

impl<'tcx> ItemCtxt<'tcx> {
pub(crate) 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(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
Expand Down Expand Up @@ -539,6 +549,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 @@ -628,6 +646,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
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
pub(super) fn complain_about_assoc_item_not_found<I>(
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
qself: AssocItemQSelf<'tcx>,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
span: Span,
Expand Down
78 changes: 64 additions & 14 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::LitToConstInput;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
use rustc_middle::ty::typeck_results::{HasTypeDependentDefs, TypeDependentDef};
use rustc_middle::ty::{
self, AssocTag, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty,
TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions,
Expand Down Expand Up @@ -105,7 +106,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 @@ -190,6 +191,9 @@ 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);

/// Record the resolution of a HIR node corresponding to a type-dependent definition in this context.
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 All @@ -208,18 +212,20 @@ pub trait HirTyLowerer<'tcx> {
/// The "qualified self" of an associated item path.
///
/// For diagnostic purposes only.
enum AssocItemQSelf {
enum AssocItemQSelf<'tcx> {
Trait(DefId),
TyParam(LocalDefId, Span),
SelfTyAlias,
AssocTy(Ty<'tcx>),
}

impl AssocItemQSelf {
fn to_string(&self, tcx: TyCtxt<'_>) -> String {
impl<'tcx> AssocItemQSelf<'tcx> {
fn to_string(&self, tcx: TyCtxt<'tcx>) -> String {
match *self {
Self::Trait(def_id) => tcx.def_path_str(def_id),
Self::TyParam(def_id, _) => tcx.hir_ty_param_name(def_id).to_string(),
Self::SelfTyAlias => kw::SelfUpper.to_string(),
Self::AssocTy(ty) => ty.to_string(),
}
}
}
Expand Down Expand Up @@ -1010,8 +1016,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn probe_single_bound_for_assoc_item<I>(
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
assoc_tag: AssocTag,
qself: AssocItemQSelf<'tcx>,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
span: Span,
constraint: Option<&hir::AssocItemConstraint<'tcx>>,
Expand Down Expand Up @@ -1151,6 +1157,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 Down Expand Up @@ -1231,8 +1238,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
mode: LowerAssocMode,
) -> Result<LoweredAssoc<'tcx>, 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, mode);
self.record_res(
hir_ref_id,
result.map(|assoc| match assoc {
LoweredAssoc::Term(def_id, _) => (self.tcx().def_kind(def_id), def_id),
LoweredAssoc::Variant { .. } => todo!(), // FIXME: Properly handle
}),
);
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>,
mode: LowerAssocMode,
) -> Result<LoweredAssoc<'tcx>, ErrorGuaranteed> {
let tcx = self.tcx();
let assoc_ident = assoc_segment.ident;

// Check if we have an enum variant or an inherent associated type.
Expand Down Expand Up @@ -1273,10 +1300,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

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 @@ -1305,15 +1331,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
)?
}
(
&ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
ty::Param(_),
Res::SelfTyParam { trait_: param_def_id }
| Res::Def(DefKind::TyParam, param_def_id),
Comment on lines +1335 to +1336
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated: are there ever cases where we have a ty::Param with a different res? and if so, do we want to support them? we can go from the index of the param to its def_id after all

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self params dont resolve to DefKind::TyParam, they resolve to the trait defid and DefKind::Trait iirc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually wait im blind thats SelfTyParam already

) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(),
param_def_id.expect_local(),
qself.span,
mode.assoc_tag(),
assoc_ident,
span,
)?,
(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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this cycle for

trait Foo {
    type FooAssoc;
}

trait Bar<T> {}

trait Trait {
    type Assoc: Foo + Bar<Self::Assoc::FooAssoc>;
} 

Copy link
Member Author

@fmease fmease Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it does. I guess there's no way around introducing a specialized variant of item_bounds if at all possible (well, HIR ty lowering is notorious for following such an approach).

Copy link
Contributor

@lcnr lcnr Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that may work 🤔 an easier alternative may be to forbid the consecutive shorthand in item bounds and super traits. It's not great, but it does avoid the issue


self.probe_single_bound_for_assoc_item(
|| {
let trait_refs = predicates.iter().filter_map(|pred| {
pred.as_trait_clause().map(|t| t.map_bound(|t| t.trait_ref))
});
traits::transitive_bounds_that_define_assoc_item(
tcx,
trait_refs,
assoc_ident,
)
},
AssocItemQSelf::AssocTy(qself_ty),
mode.assoc_tag(),
assoc_ident,
span,
None,
)?
}
_ => {
let kind_str = assoc_tag_str(mode.assoc_tag());
let reported = if variant_resolution.is_some() {
Expand Down Expand Up @@ -1466,6 +1515,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
});
}

Ok(result)
}

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 @@ -176,9 +176,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 @@ -2178,12 +2178,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
19 changes: 15 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ 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::def_id::{DefId, LocalDefId};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::{self as hir, HirId, ItemLocalMap};
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
use rustc_infer::infer;
use rustc_infer::traits::Obligation;
use rustc_middle::ty::typeck_results::{HasTypeDependentDefs, TypeDependentDef};
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
Expand Down Expand Up @@ -350,7 +351,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
}
}

fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
fn record_ty(&self, hir_id: HirId, ty: Ty<'tcx>, span: Span) {
// FIXME: normalization and escaping regions
let ty = if !ty.has_escaping_bound_vars() {
// NOTE: These obligations are 100% redundant and are implied by
Expand All @@ -370,6 +371,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
self.write_ty(hir_id, ty)
}

fn record_res(&self, hir_id: HirId, result: TypeDependentDef) {
self.write_resolution(hir_id, result);
}

fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
Some(&self.infcx)
}
Expand All @@ -378,7 +383,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
&self,
decl: &rustc_hir::FnDecl<'tcx>,
_generics: Option<&rustc_hir::Generics<'_>>,
_hir_id: rustc_hir::HirId,
_hir_id: HirId,
_hir_ty: Option<&hir::Ty<'_>>,
) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect();
Expand All @@ -391,6 +396,12 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
}
}

impl HasTypeDependentDefs for FnCtxt<'_, '_> {
fn type_dependent_def(&self, id: 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 @@ -125,6 +125,7 @@ pub mod print;
pub mod relate;
pub mod significant_drop_order;
pub mod trait_def;
pub mod typeck_results;
pub mod util;
pub mod vtable;

Expand All @@ -151,7 +152,6 @@ mod rvalue_scopes;
mod structural_impls;
#[allow(hidden_glob_reexports)]
mod sty;
mod typeck_results;
mod visit;

// Data types
Expand Down
Loading
Loading