Skip to content
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

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

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 @@ -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 @@ -35,6 +35,10 @@ use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter;
use rustc_middle::query::Providers;
use rustc_middle::ty::fold::fold_regions;
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};
use rustc_middle::{bug, span_bug};
Expand Down Expand Up @@ -126,6 +130,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 @@ -375,7 +380,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 @@ -542,6 +552,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 @@ -630,6 +648,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_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
Expand Down
79 changes: 64 additions & 15 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::ty::fold::fold_regions;
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, TypingMode,
Expand Down Expand Up @@ -107,7 +108,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 @@ -184,6 +185,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 All @@ -202,18 +205,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 @@ -925,7 +930,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn probe_single_bound_for_assoc_item<I>(
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
qself: AssocItemQSelf<'tcx>,
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
Expand Down Expand Up @@ -1062,6 +1067,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 @@ -1076,8 +1082,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 @@ -1103,22 +1129,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 @@ -1147,15 +1172,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 +1176 to +1177
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,
ty::AssocKind::Type,
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),
ty::AssocKind::Type,
assoc_ident,
span,
None,
)?
}
_ => {
let reported = if variant_resolution.is_some() {
// Variant in type position
Expand Down Expand Up @@ -1303,6 +1351,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
});
}

Ok((ty, DefKind::AssocTy, assoc_ty.def_id))
}

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)]
#![warn(unreachable_pub)]
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 @@ -173,9 +173,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 @@ -2110,12 +2110,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::{CRATE_DEF_ID, DefId, LocalDefId};
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 @@ -337,6 +338,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 @@ -358,6 +363,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 @@ -123,6 +123,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 @@ -150,7 +151,6 @@ mod rvalue_scopes;
mod structural_impls;
#[allow(hidden_glob_reexports)]
mod sty;
mod typeck_results;

// Data types

Expand Down
Loading
Loading