Skip to content

Commit 53a4455

Browse files
Introduce a new kind of associated types, RPITIT associated types
They don't exist in hir-def, only hir-ty. The idea is that the trait/impl datum query will compute and intern them, then other queries can treat them as (partially) normal associated types. (Lowering RPITIT to synthesized associated types, like rustc does, is required to properly support Return Type Notation).
1 parent f8e7843 commit 53a4455

File tree

13 files changed

+424
-164
lines changed

13 files changed

+424
-164
lines changed

crates/base-db/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use syntax::{Parse, SyntaxError, ast};
2828
use triomphe::Arc;
2929
pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
3030

31+
/// Prefer to use `impl_intern_key_ref!()`, which will not clone the value.
3132
#[macro_export]
3233
macro_rules! impl_intern_key {
3334
($id:ident, $loc:ident) => {
@@ -47,6 +48,26 @@ macro_rules! impl_intern_key {
4748
};
4849
}
4950

51+
#[macro_export]
52+
macro_rules! impl_intern_key_ref {
53+
($id:ident, $loc:ident) => {
54+
#[salsa_macros::interned(no_lifetime)]
55+
pub struct $id {
56+
#[return_ref]
57+
pub loc: $loc,
58+
}
59+
60+
// If we derive this salsa prints the values recursively, and this causes us to blow.
61+
impl ::std::fmt::Debug for $id {
62+
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
63+
f.debug_tuple(stringify!($id))
64+
.field(&format_args!("{:04x}", self.0.as_u32()))
65+
.finish()
66+
}
67+
}
68+
};
69+
}
70+
5071
pub const DEFAULT_FILE_TEXT_LRU_CAP: u16 = 16;
5172
pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
5273
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;

crates/hir-ty/src/chalk_db.rs

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use span::Edition;
99
use tracing::debug;
1010

1111
use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift};
12-
use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
12+
use chalk_solve::rust_ir::{self, AssociatedTyDatumBound, OpaqueTyDatumBound, WellKnownTrait};
1313

1414
use base_db::Crate;
1515
use hir_def::{
@@ -24,12 +24,15 @@ use crate::{
2424
AliasEq, AliasTy, BoundVar, DebruijnIndex, Interner, ProjectionTy, ProjectionTyExt,
2525
QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
2626
WhereClause,
27-
db::{HirDatabase, InternedCoroutine},
27+
db::{HirDatabase, InternedCoroutine, RpititImplAssocTy, RpititImplAssocTyId},
2828
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
2929
generics::generics,
3030
lower::LifetimeElisionKind,
3131
make_binders, make_single_type_binders,
32-
mapping::{ToChalk, TypeAliasAsValue, from_chalk},
32+
mapping::{
33+
AnyImplAssocType, AnyTraitAssocType, ToChalk, from_assoc_type_value_id, from_chalk,
34+
to_assoc_type_id_rpitit, to_assoc_type_value_id, to_assoc_type_value_id_rpitit,
35+
},
3336
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint},
3437
to_assoc_type_id, to_chalk_trait_id,
3538
traits::ChalkContext,
@@ -54,23 +57,48 @@ pub(crate) type Variances = chalk_ir::Variances<Interner>;
5457

5558
impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
5659
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
57-
self.db.associated_ty_data(from_assoc_type_id(id))
60+
match from_assoc_type_id(self.db, id) {
61+
AnyTraitAssocType::Normal(id) => self.db.associated_ty_data(id),
62+
AnyTraitAssocType::Rpitit(assoc_type_id) => {
63+
let assoc_type = assoc_type_id.loc(self.db);
64+
Arc::new(AssociatedTyDatum {
65+
id,
66+
trait_id: to_chalk_trait_id(assoc_type.trait_id),
67+
name: sym::synthesized_rpitit_assoc,
68+
binders: assoc_type
69+
.bounds
70+
.clone()
71+
.map(|bounds| AssociatedTyDatumBound { bounds, where_clauses: Vec::new() }),
72+
})
73+
}
74+
}
5875
}
5976
fn associated_ty_from_impl(
6077
&self,
6178
impl_id: chalk_ir::ImplId<Interner>,
6279
assoc_type_id: chalk_ir::AssocTypeId<Interner>,
6380
) -> Option<rust_ir::AssociatedTyValueId<Interner>> {
64-
let alias_id = from_assoc_type_id(assoc_type_id);
65-
let trait_sig = self.db.type_alias_signature(alias_id);
66-
self.db.impl_items(hir_def::ImplId::from_chalk(self.db, impl_id)).items.iter().find_map(
67-
|(name, item)| match item {
68-
AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => {
69-
Some(TypeAliasAsValue(*alias).to_chalk(self.db))
70-
}
71-
_ => None,
72-
},
73-
)
81+
match from_assoc_type_id(self.db, assoc_type_id) {
82+
AnyTraitAssocType::Normal(alias_id) => {
83+
let trait_sig = self.db.type_alias_signature(alias_id);
84+
self.db
85+
.impl_items(hir_def::ImplId::from_chalk(self.db, impl_id))
86+
.items
87+
.iter()
88+
.find_map(|(name, item)| match item {
89+
AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => {
90+
Some(to_assoc_type_value_id(*alias))
91+
}
92+
_ => None,
93+
})
94+
}
95+
AnyTraitAssocType::Rpitit(trait_assoc) => {
96+
Some(to_assoc_type_value_id_rpitit(RpititImplAssocTyId::new(
97+
self.db,
98+
RpititImplAssocTy { impl_id: from_chalk(self.db, impl_id), trait_assoc },
99+
)))
100+
}
101+
}
74102
}
75103
fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> {
76104
self.db.trait_datum(self.krate, trait_id)
@@ -456,8 +484,13 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
456484
Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false))
457485
}
458486
fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
459-
let id = self.db.associated_ty_data(from_assoc_type_id(assoc_ty_id)).name;
460-
self.db.type_alias_signature(id).name.display(self.db, self.edition()).to_string()
487+
let name = match from_assoc_type_id(self.db, assoc_ty_id) {
488+
AnyTraitAssocType::Normal(id) => self.db.type_alias_signature(id).name.clone(),
489+
AnyTraitAssocType::Rpitit(id) => {
490+
self.db.function_signature(id.loc(self.db).synthesized_from_method).name.clone()
491+
}
492+
};
493+
name.display(self.db, self.edition()).to_string()
461494
}
462495
fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
463496
format!("Opaque_{:?}", opaque_ty_id.0)
@@ -673,7 +706,7 @@ pub(crate) fn associated_ty_data_query(
673706
let datum = AssociatedTyDatum {
674707
trait_id: to_chalk_trait_id(trait_),
675708
id: to_assoc_type_id(type_alias),
676-
name: type_alias,
709+
name: type_alias_data.name.symbol().clone(),
677710
binders: make_binders(db, &generic_params, bound_data),
678711
};
679712
Arc::new(datum)
@@ -883,7 +916,7 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId)
883916
let name = &db.type_alias_signature(type_alias).name;
884917
trait_data.associated_type_by_name(name).is_some()
885918
})
886-
.map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db))
919+
.map(|type_alias| to_assoc_type_value_id(type_alias))
887920
.collect();
888921
debug!("impl_datum: {:?}", impl_datum_bound);
889922
let impl_datum = ImplDatum {
@@ -900,8 +933,19 @@ pub(crate) fn associated_ty_value_query(
900933
krate: Crate,
901934
id: AssociatedTyValueId,
902935
) -> Arc<AssociatedTyValue> {
903-
let type_alias: TypeAliasAsValue = from_chalk(db, id);
904-
type_alias_associated_ty_value(db, krate, type_alias.0)
936+
match from_assoc_type_value_id(db, id) {
937+
AnyImplAssocType::Normal(type_alias) => {
938+
type_alias_associated_ty_value(db, krate, type_alias)
939+
}
940+
AnyImplAssocType::Rpitit(assoc_type_id) => {
941+
let assoc_type = assoc_type_id.loc(db);
942+
Arc::new(AssociatedTyValue {
943+
impl_id: assoc_type.impl_id.to_chalk(db),
944+
associated_ty_id: to_assoc_type_id_rpitit(assoc_type.trait_assoc),
945+
value: assoc_type.value.clone(),
946+
})
947+
}
948+
}
905949
}
906950

907951
fn type_alias_associated_ty_value(
@@ -1037,7 +1081,16 @@ pub(super) fn generic_predicate_to_inline_bound(
10371081
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
10381082
}
10391083
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
1040-
let generics = generics(db, from_assoc_type_id(projection_ty.associated_ty_id).into());
1084+
let generic_def = match from_assoc_type_id(db, projection_ty.associated_ty_id) {
1085+
AnyTraitAssocType::Normal(type_alias) => type_alias.into(),
1086+
AnyTraitAssocType::Rpitit(_) => {
1087+
unreachable!(
1088+
"there is no way to refer to a RPITIT synthesized \
1089+
associated type on associated type's self bounds (`type Assoc: Bound`)"
1090+
)
1091+
}
1092+
};
1093+
let generics = generics(db, generic_def);
10411094
let parent_len = generics.parent_generics().map_or(0, |g| g.len_self());
10421095
let (trait_args, assoc_args) =
10431096
projection_ty.substitution.as_slice(Interner).split_at(parent_len);

crates/hir-ty/src/chalk_ext.rs

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ use chalk_ir::{
44
FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy, cast::Cast,
55
};
66
use hir_def::{
7-
DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
7+
DefWithBodyId, FunctionId, HasModule, ItemContainerId, Lookup, TraitId,
88
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
99
hir::generics::{TypeOrConstParamData, TypeParamProvenance},
1010
lang_item::LangItem,
1111
type_ref::Rawness,
1212
};
1313

1414
use crate::{
15-
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
16-
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
17-
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
18-
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
19-
from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst,
15+
AdtId, AliasEq, AliasTy, AssocTypeId, Binders, CallableDefId, CallableSig, Canonical,
16+
CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime,
17+
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
18+
WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx,
19+
generics::generics, mapping::AnyTraitAssocType, to_chalk_trait_id, utils::ClosureSubst,
2020
};
2121

2222
pub trait TyExt {
@@ -39,7 +39,6 @@ pub trait TyExt {
3939
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
4040
fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>;
4141
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
42-
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
4342

4443
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
4544
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
@@ -187,19 +186,6 @@ impl TyExt for Ty {
187186
}
188187
}
189188

190-
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
191-
match *self.kind(Interner) {
192-
TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
193-
TyKind::FnDef(callable, ..) => Some(GenericDefId::from_callable(
194-
db,
195-
db.lookup_intern_callable_def(callable.into()),
196-
)),
197-
TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
198-
TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
199-
_ => None,
200-
}
201-
}
202-
203189
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
204190
match self.kind(Interner) {
205191
&TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
@@ -346,15 +332,9 @@ impl TyExt for Ty {
346332

347333
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
348334
match self.kind(Interner) {
349-
TyKind::AssociatedType(id, ..) => match from_assoc_type_id(*id).lookup(db).container {
350-
ItemContainerId::TraitId(trait_id) => Some(trait_id),
351-
_ => None,
352-
},
335+
TyKind::AssociatedType(id, ..) => Some(assoc_type_parent_trait(db, *id)),
353336
TyKind::Alias(AliasTy::Projection(projection_ty)) => {
354-
match from_assoc_type_id(projection_ty.associated_ty_id).lookup(db).container {
355-
ItemContainerId::TraitId(trait_id) => Some(trait_id),
356-
_ => None,
357-
}
337+
Some(assoc_type_parent_trait(db, projection_ty.associated_ty_id))
358338
}
359339
_ => None,
360340
}
@@ -405,6 +385,16 @@ impl TyExt for Ty {
405385
}
406386
}
407387

388+
fn assoc_type_parent_trait(db: &dyn HirDatabase, id: AssocTypeId) -> TraitId {
389+
match from_assoc_type_id(db, id) {
390+
AnyTraitAssocType::Normal(type_alias) => match type_alias.lookup(db).container {
391+
ItemContainerId::TraitId(trait_id) => trait_id,
392+
_ => panic!("`AssocTypeId` without parent trait"),
393+
},
394+
AnyTraitAssocType::Rpitit(assoc_type) => assoc_type.loc(db).trait_id,
395+
}
396+
}
397+
408398
pub trait ProjectionTyExt {
409399
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
410400
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
@@ -414,18 +404,23 @@ pub trait ProjectionTyExt {
414404
impl ProjectionTyExt for ProjectionTy {
415405
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
416406
// FIXME: something like `Split` trait from chalk-solve might be nice.
417-
let generics = generics(db, from_assoc_type_id(self.associated_ty_id).into());
407+
let generic_def = match from_assoc_type_id(db, self.associated_ty_id) {
408+
AnyTraitAssocType::Normal(type_alias) => type_alias.into(),
409+
// FIXME: This isn't entirely correct, the generics of the RPITIT assoc type may differ from its method
410+
// wrt. lifetimes, but we don't handle that currently. See https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html.
411+
AnyTraitAssocType::Rpitit(assoc_type) => {
412+
assoc_type.loc(db).synthesized_from_method.into()
413+
}
414+
};
415+
let generics = generics(db, generic_def);
418416
let parent_len = generics.parent_generics().map_or(0, |g| g.len_self());
419417
let substitution =
420418
Substitution::from_iter(Interner, self.substitution.iter(Interner).take(parent_len));
421419
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
422420
}
423421

424422
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
425-
match from_assoc_type_id(self.associated_ty_id).lookup(db).container {
426-
ItemContainerId::TraitId(it) => it,
427-
_ => panic!("projection ty without parent trait"),
428-
}
423+
assoc_type_parent_trait(db, self.associated_ty_id)
429424
}
430425

431426
fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {

crates/hir-ty/src/db.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use std::sync;
55

6-
use base_db::{Crate, impl_intern_key};
6+
use base_db::{Crate, impl_intern_key, impl_intern_key_ref};
77
use hir_def::{
88
AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
99
GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
@@ -349,3 +349,35 @@ impl_intern_key!(InternedCoroutineId, InternedCoroutine);
349349
// This exists just for Chalk, because Chalk just has a single `FnDefId` where
350350
// we have different IDs for struct and enum variant constructors.
351351
impl_intern_key!(InternedCallableDefId, CallableDefId);
352+
353+
/// An associated type synthesized from a Return Position Impl Trait In Trait
354+
/// of the trait (not the impls).
355+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
356+
pub struct RpititTraitAssocTy {
357+
pub trait_id: TraitId,
358+
/// The method that contains this RPITIT.
359+
pub synthesized_from_method: FunctionId,
360+
/// The bounds of this associated type (coming from the `impl Bounds`).
361+
///
362+
/// The generics are the generics of the method (with some modifications that we
363+
/// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html).
364+
pub bounds: Binders<Vec<chalk_solve::rust_ir::QuantifiedInlineBound<Interner>>>,
365+
}
366+
367+
impl_intern_key_ref!(RpititTraitAssocTyId, RpititTraitAssocTy);
368+
369+
/// An associated type synthesized from a Return Position Impl Trait In Trait
370+
/// of the impl (not the trait).
371+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
372+
pub struct RpititImplAssocTy {
373+
pub impl_id: ImplId,
374+
/// The definition of this associated type in the trait.
375+
pub trait_assoc: RpititTraitAssocTyId,
376+
/// The bounds of this associated type (coming from the `impl Bounds`).
377+
///
378+
/// The generics are the generics of the method (with some modifications that we
379+
/// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html).
380+
pub value: Binders<chalk_solve::rust_ir::AssociatedTyValueBound<Interner>>,
381+
}
382+
383+
impl_intern_key_ref!(RpititImplAssocTyId, RpititImplAssocTy);

0 commit comments

Comments
 (0)