Skip to content
Open
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
26 changes: 19 additions & 7 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph};

use crate::dep_graph::{DepKind, DepNodeIndex};
Expand Down Expand Up @@ -39,6 +39,15 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtId = DefId;
type ImplId = DefId;
type UnevaluatedConstId = DefId;
type DefinitionAssocTyId = DefId;
type DefinitionAssocConstId = DefId;
type DefinitionAssocTermId = DefId;
type OpaqueTyId = DefId;
type FreeTyAliasId = DefId;
type FreeConstAliasId = DefId;
type ImplAssocTyId = DefId;
type ImplAssocConstId = DefId;
type ImplAssocTermId = DefId;
type Span = Span;

type GenericArgs = ty::GenericArgsRef<'tcx>;
Expand Down Expand Up @@ -285,7 +294,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.mk_type_list_from_iter(args)
}

fn parent(self, def_id: DefId) -> DefId {
fn projection_parent(self, def_id: Self::DefinitionAssocTermId) -> Self::TraitId {
self.parent(def_id)
}

fn impl_assoc_term_parent(self, def_id: Self::ImplAssocTyId) -> Self::ImplId {
self.parent(def_id)
}

Expand Down Expand Up @@ -443,7 +456,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
!self.codegen_fn_attrs(def_id).target_features.is_empty()
}

fn require_lang_item(self, lang_item: SolverLangItem) -> DefId {
fn require_projection_lang_item(self, lang_item: SolverProjectionLangItem) -> DefId {
self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP)
}

Expand All @@ -455,7 +468,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP)
}

fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool {
fn is_projection_lang_item(self, def_id: DefId, lang_item: SolverProjectionLangItem) -> bool {
self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item))
}

Expand All @@ -475,7 +488,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.is_sizedness_trait(def_id)
}

fn as_lang_item(self, def_id: DefId) -> Option<SolverLangItem> {
fn as_projection_lang_item(self, def_id: DefId) -> Option<SolverProjectionLangItem> {
lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?)
}

Expand Down Expand Up @@ -754,7 +767,7 @@ macro_rules! bidirectional_lang_item_map {
}

bidirectional_lang_item_map! {
SolverLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item;
SolverProjectionLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item;

// tidy-alphabetical-start
AsyncFnKindUpvars,
Expand Down Expand Up @@ -788,7 +801,6 @@ bidirectional_lang_item_map! {
AsyncFnKindHelper,
AsyncFnMut,
AsyncFnOnce,
AsyncFnOnceOutput,
AsyncIterator,
BikeshedGuaranteedNoDrop,
Clone,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
fn fetch_eligible_assoc_item(
&self,
goal_trait_ref: ty::TraitRef<Self::Interner>,
trait_assoc_def_id: <Self::Interner as Interner>::DefId,
trait_assoc_def_id: <Self::Interner as Interner>::DefinitionAssocTermId,
impl_def_id: <Self::Interner as Interner>::ImplId,
) -> Result<
Option<<Self::Interner as Interner>::DefId>,
Option<<Self::Interner as Interner>::ImplAssocTermId>,
Copy link
Copy Markdown
Contributor

@lcnr lcnr Apr 30, 2026

Choose a reason for hiding this comment

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

this is surprising to me in the face of associated type/const defaults

trait Trait {
    type Assoc = u32;
}
impl Trait for () {}

this should fail in r-a because fetch_eligible_assoc_item would ry to return a DefinitionAssocTermId?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

r-a does not really have a distinction between trait assoc types and impl assoc types, all type aliases are the same. I considered reflecting that in the solver but decided to put more type safety at almost no cost.

Although... now that you bring it up, we expect the parent of an ImplAssocTermId to be an impl (impl_assoc_term_parent()), which could cause problems. How does rustc handle that?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think it's mainly that we very rarely refer to impl associated types and just never care about whether its parent is an impl or trait 🤔

if that distinction doesn't exist in r-a either, i'd prefer to keep em as the same for now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removing the separation completely will be a bit unfortunate since while we don't know whether the parent of an ImplAssocTermId is an impl or trait, we do know that the parent of DefinitionAssocTermId is a trait, and we rely on this in Interner::projection_parent() to get a TraitId.

But maybe, we can call it with a more generic name (e.g. AssocTermId instead of ImplAssocTermId) and return a generic DefId as its parent, while not touching DefinitionAssocTermId?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Or we can call it something like ConcreteAssocTermId, since we don't want trait assoc types without default to end there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ImplOrTraitAssocTermId 🤔 😅 I think ConcreteAssocTermId is also not quite what you want as fetch_eligible_assoc_item does just look at assoc terms regardless of whether they have a value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't have a strong objection to ImplOrTraitAssocTermId, except that it's long 😅

<Self::Interner as Interner>::ErrorGuaranteed,
>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use derive_where::derive_where;
use rustc_type_ir::data_structures::HashMap;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem};
use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::solve::inspect::ProbeKind;
use rustc_type_ir::{
Expand Down Expand Up @@ -106,7 +106,9 @@ where
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args).skip_norm_wip()]))
Ok(ty::Binder::dummy(vec![
cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(),
]))
}
}
}
Expand Down Expand Up @@ -541,7 +543,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
);
}

let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput);
let future_output_def_id =
cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput);
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind(AsyncCallableRelevantTypes {
Expand Down Expand Up @@ -596,7 +599,8 @@ fn fn_item_to_async_callable<I: Interner>(
let nested = vec![
bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx),
];
let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput);
let future_output_def_id =
cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput);
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind(AsyncCallableRelevantTypes {
Expand Down Expand Up @@ -642,7 +646,8 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
args: ty::CoroutineClosureArgs<I>,
sig: ty::CoroutineClosureSignature<I>,
) -> I::Ty {
let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars);
let upvars_projection_def_id =
cx.require_projection_lang_item(SolverProjectionLangItem::AsyncFnKindUpvars);
let tupled_upvars_ty = Ty::new_projection(
cx,
upvars_projection_def_id,
Expand Down Expand Up @@ -920,7 +925,10 @@ where
// show up in the bounds, but just ones that come from substituting
// `Self` with the dyn type.
let proj = proj.with_self_ty(cx, trait_ref.self_ty());
replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj));
replace_projection_with
.entry(proj.def_id().into())
.or_default()
.push(bound.rebind(proj));
}
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1128,9 +1128,9 @@ where
pub(super) fn fetch_eligible_assoc_item(
&self,
goal_trait_ref: ty::TraitRef<I>,
trait_assoc_def_id: I::DefId,
trait_assoc_def_id: I::DefinitionAssocTermId,
impl_def_id: I::ImplId,
) -> Result<Option<I::DefId>, I::ErrorGuaranteed> {
) -> Result<Option<I::ImplAssocTermId>, I::ErrorGuaranteed> {
self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ where
}
}

fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
fn opaque_type_is_rigid(&self, def_id: I::OpaqueTyId) -> bool {
match self.typing_mode() {
// Opaques are never rigid outside of analysis mode.
TypingMode::Coherence | TypingMode::PostAnalysis => false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ where
let cx = self.cx();
let inherent = goal.predicate.alias;

let impl_def_id = cx.parent(inherent.def_id());
let impl_args = self.fresh_args_for_item(impl_def_id);
let impl_def_id = cx.impl_assoc_term_parent(inherent.def_id().try_into().unwrap());
Copy link
Copy Markdown
Contributor

@lcnr lcnr Apr 30, 2026

Choose a reason for hiding this comment

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

see #155392 (comment), we should open an issue for this unless you're up to update this in this PR

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's what I meant by:

It does require a few try_into().unwrap()s since in the solver's consider_X_candidate() only get an untyped DefId. It's possible to reduce that considerably if we'd pass them the typed def id as a parameter, but I don't know what will be the impact on perf. Should I try to pursue that?

Do you want me to try that and check perf?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd like that, we match on ht eAliasTermKind anyways, so passing in some (SpecificDefId, Args) as the goal to the different functions seems better to me?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's a bit difficult to do on the consider_X_candidate() method (not the method in the submodules but in mod.rs), since they are called from the generic assemble_and_merge_candidates(). Should I do that for them too?

let impl_args = self.fresh_args_for_item(impl_def_id.into());

// Equate impl header and add impl where clauses
self.eq(
goal.param_env,
inherent.self_ty(),
cx.type_of(impl_def_id).instantiate(cx, impl_args).skip_norm_wip(),
cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(),
)?;

// Equate IAT with the RHS of the project goal
Expand Down
108 changes: 57 additions & 51 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod opaque_types;

use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::{
self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _,
};
Expand Down Expand Up @@ -277,7 +277,7 @@ where

let target_item_def_id = match ecx.fetch_eligible_assoc_item(
goal_trait_ref,
goal.predicate.def_id(),
goal.predicate.def_id().try_into().unwrap(),
impl_def_id,
) {
Ok(Some(target_item_def_id)) => target_item_def_id,
Expand Down Expand Up @@ -354,7 +354,7 @@ where
}
}

let target_container_def_id = cx.parent(target_item_def_id);
let target_container_def_id = cx.impl_assoc_term_parent(target_item_def_id);

// Getting the right args here is complex, e.g. given:
// - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
Expand All @@ -371,10 +371,10 @@ where
impl_def_id,
impl_args,
impl_trait_ref,
target_container_def_id,
target_container_def_id.into(),
)?;

if !cx.check_args_compatible(target_item_def_id, target_args) {
if !cx.check_args_compatible(target_item_def_id.into(), target_args) {
return error_response(
ecx,
cx.delay_bug("associated item has mismatched arguments"),
Expand All @@ -384,10 +384,10 @@ where
// Finally we construct the actual value of the associated type.
let term = match goal.predicate.alias.kind(cx) {
ty::AliasTermKind::ProjectionTy { .. } => {
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
cx.type_of(target_item_def_id.into()).map_bound(|ty| ty.into())
}
ty::AliasTermKind::ProjectionConst { .. } => {
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
cx.const_of_item(target_item_def_id.into()).map_bound(|ct| ct.into())
}
kind => panic!("expected projection, found {kind:?}"),
};
Expand Down Expand Up @@ -493,6 +493,7 @@ where
goal_kind: ty::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
let cx = ecx.cx();
let def_id = goal.predicate.def_id().try_into().unwrap();

let env_region = match goal_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
Expand Down Expand Up @@ -520,41 +521,42 @@ where
[output_coroutine_ty],
);

let (projection_term, term) =
if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) {
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
output_coroutine_ty.into(),
)
} else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) {
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[
I::GenericArg::from(goal.predicate.self_ty()),
tupled_inputs_ty.into(),
env_region.into(),
],
),
output_coroutine_ty.into(),
)
} else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) {
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
coroutine_return_ty.into(),
)
} else {
panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id())
};
let (projection_term, term) = if cx
.is_projection_lang_item(def_id, SolverProjectionLangItem::CallOnceFuture)
{
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
Comment thread
ChayimFriedman2 marked this conversation as resolved.
output_coroutine_ty.into(),
)
} else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CallRefFuture) {
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[
I::GenericArg::from(goal.predicate.self_ty()),
tupled_inputs_ty.into(),
env_region.into(),
],
),
output_coroutine_ty.into(),
)
} else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::AsyncFnOnceOutput) {
(
ty::AliasTerm::new(
cx,
cx.alias_term_kind_from_def_id(goal.predicate.def_id()),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
coroutine_return_ty.into(),
)
} else {
panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id())
};
let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx);

Self::probe_and_consider_implied_clause(
Expand Down Expand Up @@ -628,8 +630,8 @@ where
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
let cx = ecx.cx();
let metadata_def_id = cx.require_lang_item(SolverLangItem::Metadata);
assert_eq!(metadata_def_id, goal.predicate.def_id());
let metadata_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::Metadata);
assert_eq!(Into::<I::DefId>::into(metadata_def_id), goal.predicate.def_id());
Comment thread
ChayimFriedman2 marked this conversation as resolved.
let metadata_ty = match goal.predicate.self_ty().kind() {
ty::Bool
| ty::Char
Expand All @@ -655,8 +657,9 @@ where
ty::Str | ty::Slice(_) => Ty::new_usize(cx),

ty::Dynamic(_, _) => {
let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata);
cx.type_of(dyn_metadata)
let dyn_metadata =
cx.require_projection_lang_item(SolverProjectionLangItem::DynMetadata);
cx.type_of(dyn_metadata.into())
.instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())])
.skip_norm_wip()
}
Expand Down Expand Up @@ -854,10 +857,12 @@ where
}

let coroutine = args.as_coroutine();
let def_id = goal.predicate.def_id().try_into().unwrap();

let term = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineReturn) {
let term = if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineReturn)
{
coroutine.return_ty().into()
} else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineYield) {
} else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineYield) {
coroutine.yield_ty().into()
} else {
panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id())
Expand Down Expand Up @@ -981,9 +986,10 @@ where
else {
return Err(NoSolution);
};
let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) {
Some(SolverLangItem::FieldBase) => base,
Some(SolverLangItem::FieldType) => ty,
let ty = match ecx.cx().as_projection_lang_item(goal.predicate.def_id().try_into().unwrap())
{
Some(SolverProjectionLangItem::FieldBase) => base,
Some(SolverProjectionLangItem::FieldType) => ty,
_ => panic!("unexpected associated type {:?} in `Field`", goal.predicate),
};
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ where
goal.predicate.self_ty().kind()
{
debug_assert!(ecx.opaque_type_is_rigid(def_id));
for item_bound in cx.item_self_bounds(def_id).skip_binder() {
for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() {
if item_bound
.as_trait_clause()
.is_some_and(|b| b.def_id() == goal.predicate.def_id())
Expand Down
Loading
Loading