diff --git a/Cargo.lock b/Cargo.lock index 54cc9d1c6802..bd6307e0001b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -211,9 +211,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chalk-derive" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2d131019373f0d0d1f2af0abd4f719739f6583c1b33965112455f643a910af" +version = "0.101.0-dev.0" dependencies = [ "proc-macro2", "quote", @@ -223,9 +221,7 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f114996bda14c0213f014a4ef31a7867dcf5f539a3900477fc6b20138e7a17b" +version = "0.101.0-dev.0" dependencies = [ "bitflags 2.9.0", "chalk-derive", @@ -233,9 +229,7 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551e956e031c09057c7b21f17d48d91de99c9b6b6e34bceaf5e7202d71021268" +version = "0.101.0-dev.0" dependencies = [ "chalk-derive", "chalk-ir", @@ -246,9 +240,7 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7ca50181156ce649efe8e5dd00580f573651554e4dcd11afa4e2ac93f53324" +version = "0.101.0-dev.0" dependencies = [ "chalk-derive", "chalk-ir", diff --git a/Cargo.toml b/Cargo.toml index 03ecc8f2741e..8ffb5e69c725 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ debug = 2 [patch.'crates-io'] # rowan = { path = "../rowan" } -# chalk-solve = { path = "../chalk/chalk-solve" } -# chalk-ir = { path = "../chalk/chalk-ir" } -# chalk-recursive = { path = "../chalk/chalk-recursive" } -# chalk-derive = { path = "../chalk/chalk-derive" } +chalk-solve = { path = "../chalk/chalk-solve" } +chalk-ir = { path = "../chalk/chalk-ir" } +chalk-recursive = { path = "../chalk/chalk-recursive" } +chalk-derive = { path = "../chalk/chalk-derive" } # line-index = { path = "lib/line-index" } # la-arena = { path = "lib/la-arena" } # lsp-server = { path = "lib/lsp-server" } @@ -105,10 +105,10 @@ arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" camino = "1.1.6" -chalk-solve = { version = "0.100.0", default-features = false } -chalk-ir = "0.100.0" -chalk-recursive = { version = "0.100.0", default-features = false } -chalk-derive = "0.100.0" +chalk-solve = { version = "0.101.0-dev.0", default-features = false } +chalk-ir = "0.101.0-dev.0" +chalk-recursive = { version = "0.101.0-dev.0", default-features = false } +chalk-derive = "0.101.0-dev.0" crossbeam-channel = "0.5.8" dissimilar = "1.0.7" dot = "0.1.4" diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index e6059e9e7905..40bdd2356b19 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -24,6 +24,7 @@ use syntax::{Parse, SyntaxError, ast}; use triomphe::Arc; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; +/// Prefer to use `impl_intern_key_ref!()`, which will not clone the value. #[macro_export] macro_rules! impl_intern_key { ($id:ident, $loc:ident) => { @@ -43,6 +44,26 @@ macro_rules! impl_intern_key { }; } +#[macro_export] +macro_rules! impl_intern_key_ref { + ($id:ident, $loc:ident) => { + #[salsa::interned(no_debug, no_lifetime)] + pub struct $id { + #[return_ref] + pub loc: $loc, + } + + // If we derive this salsa prints the values recursively, and this causes us to blow. + impl ::std::fmt::Debug for $id { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.debug_tuple(stringify!($id)) + .field(&format_args!("{:04x}", self.0.as_u32())) + .finish() + } + } + }; +} + pub trait Upcast { fn upcast(&self) -> &T; } diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index e0975b5aeb40..7c9ac83852dd 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -8,8 +8,12 @@ use intern::sym; use span::Edition; use tracing::debug; -use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; +use chalk_ir::{ + Binders, CanonicalVarKinds, + cast::{Cast, Caster}, + fold::shift::Shift, +}; +use chalk_solve::rust_ir::{self, AssociatedTyDatumBound, OpaqueTyDatumBound, WellKnownTrait}; use base_db::Crate; use hir_def::{ @@ -23,17 +27,22 @@ use hir_def::{ use crate::{ AliasEq, AliasTy, BoundVar, DebruijnIndex, FnDefId, Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - WhereClause, + VariableKinds, WhereClause, db::{HirDatabase, InternedCoroutine}, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, generics::generics, + lower::trait_fn_signature, make_binders, make_single_type_binders, - mapping::{ToChalk, TypeAliasAsValue, from_chalk}, + mapping::{ + AnyImplAssocType, AnyTraitAssocType, ToChalk, from_assoc_type_value_id, from_chalk, + to_assoc_type_id_rpitit, to_assoc_type_value_id, to_assoc_type_value_id_rpitit, + }, method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, + rpitit::{RpititImplAssocTy, RpititImplAssocTyId, impl_method_rpitit_values}, to_assoc_type_id, to_chalk_trait_id, traits::ChalkContext, utils::ClosureSubst, - wrap_empty_binders, + variable_kinds_from_generics, wrap_empty_binders, }; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; @@ -52,6 +61,25 @@ pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; pub(crate) type Variances = chalk_ir::Variances; impl chalk_solve::RustIrDatabase for ChalkContext<'_> { + fn associated_ty_matches( + &self, + trait_item: AssocTypeId, + impl_item: AssociatedTyValueId, + ) -> bool { + let trait_item = from_assoc_type_id(self.db, trait_item); + let impl_item = from_assoc_type_value_id(self.db, impl_item); + match (trait_item, impl_item) { + (AnyTraitAssocType::Normal(impl_item), AnyImplAssocType::Normal(trait_item)) => { + let trait_assoc = self.db.type_alias_data(trait_item); + let impl_assoc = self.db.type_alias_data(impl_item); + trait_assoc.name == impl_assoc.name + } + (AnyTraitAssocType::Rpitit(trait_item), AnyImplAssocType::Rpitit(impl_item)) => { + impl_item.loc(self.db).trait_assoc == trait_item + } + _ => false, + } + } fn associated_ty_data(&self, id: AssocTypeId) -> Arc { self.db.associated_ty_data(id) } @@ -448,8 +476,7 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false)) } fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { - let id = self.db.associated_ty_data(assoc_ty_id).name; - self.db.type_alias_data(id).name.display(self.db.upcast(), self.edition()).to_string() + self.db.associated_ty_data(assoc_ty_id).name.as_str().to_owned() } fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { format!("Opaque_{:?}", opaque_ty_id.0) @@ -606,7 +633,23 @@ pub(crate) fn associated_ty_data_query( id: AssocTypeId, ) -> Arc { debug!("associated_ty_data {:?}", id); - let type_alias: TypeAliasId = from_assoc_type_id(id); + + let type_alias = match from_assoc_type_id(db, id) { + AnyTraitAssocType::Normal(type_alias) => type_alias, + AnyTraitAssocType::Rpitit(assoc_type_id) => { + let assoc_type = assoc_type_id.loc(db); + return Arc::new(AssociatedTyDatum { + id, + trait_id: to_chalk_trait_id(assoc_type.trait_id), + name: sym::consts::synthesized_rpitit_assoc, + binders: assoc_type + .bounds + .clone() + .map(|bounds| AssociatedTyDatumBound { bounds, where_clauses: Vec::new() }), + }); + } + }; + let trait_ = match type_alias.lookup(db.upcast()).container { ItemContainerId::TraitId(t) => t, _ => panic!("associated type not in trait"), @@ -658,7 +701,7 @@ pub(crate) fn associated_ty_data_query( let datum = AssociatedTyDatum { trait_id: to_chalk_trait_id(trait_), id, - name: type_alias, + name: type_alias_data.name.symbol().clone(), binders: make_binders(db, &generic_params, bound_data), }; Arc::new(datum) @@ -685,8 +728,20 @@ pub(crate) fn trait_datum_query( fundamental: trait_data.flags.contains(TraitFlags::IS_FUNDAMENTAL), }; let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); + let trait_items = db.trait_items(trait_); + + let rpitits = trait_items + .items + .iter() + .filter_map(|&(_, item)| match item { + AssocItemId::FunctionId(it) => Some(it), + _ => None, + }) + .flat_map(|method| &trait_fn_signature(db, method).1) + .map(|assoc_id| to_assoc_type_id_rpitit(*assoc_id)); let associated_ty_ids = - db.trait_items(trait_).associated_types().map(to_assoc_type_id).collect(); + trait_items.associated_types().map(to_assoc_type_id).chain(rpitits).collect(); + let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); let trait_datum = TraitDatum { @@ -836,12 +891,11 @@ pub(crate) fn impl_datum_query( } fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) -> Arc { - let trait_ref = db + let trait_ref_binders = db .impl_trait(impl_id) // ImplIds for impls where the trait ref can't be resolved should never reach Chalk - .expect("invalid impl passed to Chalk") - .into_value_and_skipped_binders() - .0; + .expect("invalid impl passed to Chalk"); + let trait_ref = trait_ref_binders.skip_binders().clone(); let impl_data = db.impl_data(impl_id); let generic_params = generics(db.upcast(), impl_id.into()); @@ -859,8 +913,9 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; let trait_data = db.trait_items(trait_); - let associated_ty_value_ids = db - .impl_items(impl_id) + let impl_items = db.impl_items(impl_id); + let trait_datum = db.trait_datum(krate, to_chalk_trait_id(trait_)); + let associated_ty_value_ids = impl_items .items .iter() .filter_map(|(_, item)| match item { @@ -872,7 +927,15 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) let name = &db.type_alias_data(type_alias).name; trait_data.associated_type_by_name(name).is_some() }) - .map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db)) + .map(to_assoc_type_value_id) + .chain(trait_datum.associated_ty_ids.iter().filter_map(|&trait_assoc| { + match from_assoc_type_id(db, trait_assoc) { + AnyTraitAssocType::Rpitit(trait_assoc) => Some(to_assoc_type_value_id_rpitit( + RpititImplAssocTyId::new(db, RpititImplAssocTy { impl_id, trait_assoc }), + )), + AnyTraitAssocType::Normal(_) => None, + } + })) .collect(); debug!("impl_datum: {:?}", impl_datum_bound); let impl_datum = ImplDatum { @@ -884,13 +947,95 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) Arc::new(impl_datum) } +pub(crate) fn inline_bound_to_generic_predicate( + bound: &Binders>, + self_ty: Ty, +) -> QuantifiedWhereClause { + let (bound, binders) = bound.as_ref().into_value_and_skipped_binders(); + match bound { + rust_ir::InlineBound::TraitBound(trait_bound) => { + let trait_ref = TraitRef { + trait_id: trait_bound.trait_id, + substitution: Substitution::from_iter( + Interner, + iter::once(self_ty.cast(Interner)) + .chain(trait_bound.args_no_self.iter().cloned()), + ), + }; + chalk_ir::Binders::new(binders, WhereClause::Implemented(trait_ref)) + } + rust_ir::InlineBound::AliasEqBound(alias_eq) => { + let substitution = Substitution::from_iter( + Interner, + alias_eq + .parameters + .iter() + .cloned() + .chain(iter::once(self_ty.cast(Interner))) + .chain(alias_eq.trait_bound.args_no_self.iter().cloned()), + ); + let alias = AliasEq { + ty: alias_eq.value.clone(), + alias: AliasTy::Projection(ProjectionTy { + associated_ty_id: alias_eq.associated_ty_id, + substitution, + }), + }; + chalk_ir::Binders::new(binders, WhereClause::AliasEq(alias)) + } + } +} + pub(crate) fn associated_ty_value_query( db: &dyn HirDatabase, krate: Crate, id: AssociatedTyValueId, ) -> Arc { - let type_alias: TypeAliasAsValue = from_chalk(db, id); - type_alias_associated_ty_value(db, krate, type_alias.0) + match from_assoc_type_value_id(db, id) { + AnyImplAssocType::Normal(type_alias) => { + type_alias_associated_ty_value(db, krate, type_alias) + } + AnyImplAssocType::Rpitit(assoc_type_id) => rpitit_associated_ty_value(db, assoc_type_id), + } +} + +fn rpitit_associated_ty_value( + db: &dyn HirDatabase, + assoc_type_id: RpititImplAssocTyId, +) -> Arc { + let assoc_type = assoc_type_id.loc(db); + let trait_assoc = assoc_type.trait_assoc.loc(db); + let all_method_assocs = + impl_method_rpitit_values(db, assoc_type.impl_id, trait_assoc.synthesized_from_method); + let trait_assoc_id = to_assoc_type_id_rpitit(assoc_type.trait_assoc); + all_method_assocs + .iter() + .find(|method_assoc| method_assoc.associated_ty_id == trait_assoc_id) + .cloned() + .unwrap_or_else(|| { + let impl_id = hir_def::ImplId::to_chalk(assoc_type.impl_id, db); + let trait_method_generics = + generics(db.upcast(), trait_assoc.synthesized_from_method.into()); + let impl_generics = generics(db.upcast(), assoc_type.impl_id.into()); + // In this situation, we don't know even that the trait and impl generics match, therefore + // the only binders we can give to comply with the trait's binders are the trait's binders. + // However, for impl associated types chalk wants only their own generics, excluding + // those of the impl (unlike in traits), therefore we filter them here. + // Completely unlike the docs, Chalk requires both the impl generics and the associated type + // generics in the binder. + let value = Binders::new( + VariableKinds::from_iter( + Interner, + trait_assoc.bounds.binders.as_slice(Interner) + [..trait_method_generics.len_self()] + .iter() + .cloned() + .chain(variable_kinds_from_generics(db, impl_generics.iter_id())), + ), + rust_ir::AssociatedTyValueBound { ty: TyKind::Error.intern(Interner) }, + ); + Arc::new(AssociatedTyValue { associated_ty_id: trait_assoc_id, impl_id, value }) + }) } fn type_alias_associated_ty_value( @@ -1025,8 +1170,16 @@ pub(super) fn generic_predicate_to_inline_bound( Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - let generics = - generics(db.upcast(), from_assoc_type_id(projection_ty.associated_ty_id).into()); + let generic_def = match from_assoc_type_id(db, projection_ty.associated_ty_id) { + AnyTraitAssocType::Normal(type_alias) => type_alias.into(), + AnyTraitAssocType::Rpitit(_) => { + unreachable!( + "there is no way to refer to a RPITIT synthesized \ + associated type on associated type's self bounds (`type Assoc: Bound`)" + ) + } + }; + let generics = generics(db.upcast(), generic_def); let (assoc_args, trait_args) = projection_ty.substitution.as_slice(Interner).split_at(generics.len_self()); let (self_ty, args_no_self) = diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 49dde303099b..42f2e2be311c 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -4,7 +4,7 @@ use chalk_ir::{ FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy, cast::Cast, }; use hir_def::{ - DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, + DefWithBodyId, FunctionId, HasModule, ItemContainerId, Lookup, TraitId, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, generics::TypeOrConstParamData, lang_item::LangItem, @@ -12,11 +12,11 @@ use hir_def::{ }; use crate::{ - AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, - ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, + AdtId, AliasEq, AliasTy, AssocTypeId, Binders, CallableDefId, CallableSig, Canonical, + CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, + ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, + WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, + generics::generics, mapping::AnyTraitAssocType, to_chalk_trait_id, utils::ClosureSubst, }; pub trait TyExt { @@ -39,7 +39,6 @@ pub trait TyExt { fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>; fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>; - fn as_generic_def(&self, db: &dyn HirDatabase) -> Option; fn callable_def(&self, db: &dyn HirDatabase) -> Option; fn callable_sig(&self, db: &dyn HirDatabase) -> Option; @@ -187,19 +186,6 @@ impl TyExt for Ty { } } - fn as_generic_def(&self, db: &dyn HirDatabase) -> Option { - match *self.kind(Interner) { - TyKind::Adt(AdtId(adt), ..) => Some(adt.into()), - TyKind::FnDef(callable, ..) => Some(GenericDefId::from_callable( - db.upcast(), - db.lookup_intern_callable_def(callable.into()), - )), - TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()), - TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()), - _ => None, - } - } - fn callable_def(&self, db: &dyn HirDatabase) -> Option { match self.kind(Interner) { &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())), @@ -348,20 +334,9 @@ impl TyExt for Ty { fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option { match self.kind(Interner) { - TyKind::AssociatedType(id, ..) => { - match from_assoc_type_id(*id).lookup(db.upcast()).container { - ItemContainerId::TraitId(trait_id) => Some(trait_id), - _ => None, - } - } + TyKind::AssociatedType(id, ..) => Some(assoc_type_parent_trait(db, *id)), TyKind::Alias(AliasTy::Projection(projection_ty)) => { - match from_assoc_type_id(projection_ty.associated_ty_id) - .lookup(db.upcast()) - .container - { - ItemContainerId::TraitId(trait_id) => Some(trait_id), - _ => None, - } + Some(assoc_type_parent_trait(db, projection_ty.associated_ty_id)) } _ => None, } @@ -413,6 +388,16 @@ impl TyExt for Ty { } } +fn assoc_type_parent_trait(db: &dyn HirDatabase, id: AssocTypeId) -> TraitId { + match from_assoc_type_id(db, id) { + AnyTraitAssocType::Normal(type_alias) => match type_alias.lookup(db.upcast()).container { + ItemContainerId::TraitId(trait_id) => trait_id, + _ => panic!("`AssocTypeId` without parent trait"), + }, + AnyTraitAssocType::Rpitit(assoc_type) => assoc_type.loc(db).trait_id, + } +} + pub trait ProjectionTyExt { fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef; fn trait_(&self, db: &dyn HirDatabase) -> TraitId; @@ -422,7 +407,15 @@ pub trait ProjectionTyExt { impl ProjectionTyExt for ProjectionTy { fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { // FIXME: something like `Split` trait from chalk-solve might be nice. - let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into()); + let generic_def = match from_assoc_type_id(db, self.associated_ty_id) { + AnyTraitAssocType::Normal(type_alias) => type_alias.into(), + // FIXME: This isn't entirely correct, the generics of the RPITIT assoc type may differ from its method + // wrt. lifetimes, but we don't handle that currently. See https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html. + AnyTraitAssocType::Rpitit(assoc_type) => { + assoc_type.loc(db).synthesized_from_method.into() + } + }; + let generics = generics(db.upcast(), generic_def); let substitution = Substitution::from_iter( Interner, self.substitution.iter(Interner).skip(generics.len_self()), @@ -431,10 +424,7 @@ impl ProjectionTyExt for ProjectionTy { } fn trait_(&self, db: &dyn HirDatabase) -> TraitId { - match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container { - ItemContainerId::TraitId(it) => it, - _ => panic!("projection ty without parent trait"), - } + assoc_type_parent_trait(db, self.associated_ty_id) } fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty { diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index d72b1955246e..e07921416aad 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -46,6 +46,7 @@ use crate::{ LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, + chalk_db::inline_bound_to_generic_predicate, consteval::try_const_usize, db::{HirDatabase, InternedClosure}, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, @@ -53,9 +54,9 @@ use crate::{ infer::normalize, layout::Layout, lt_from_placeholder_idx, - mapping::from_chalk, + mapping::{AnyTraitAssocType, from_chalk}, mir::pad16, - primitive, to_assoc_type_id, + primitive, utils::{self, ClosureSubst, detect_variant_from_bytes}, }; @@ -607,21 +608,55 @@ impl HirDisplay for ProjectionTy { } } - write!(f, "<")?; - self_ty.hir_fmt(f)?; - write!(f, " as ")?; - trait_ref.hir_fmt(f)?; - write!( - f, - ">::{}", - f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)) - .name - .display(f.db.upcast(), f.edition()) - )?; - let proj_params_count = - self.substitution.len(Interner) - trait_ref.substitution.len(Interner); - let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; - hir_fmt_generics(f, proj_params, None, None) + match from_assoc_type_id(f.db, self.associated_ty_id) { + AnyTraitAssocType::Normal(type_alias) => { + write!(f, "<")?; + self_ty.hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; + write!( + f, + ">::{}", + f.db.type_alias_data(type_alias).name.display(f.db.upcast(), f.edition()) + )?; + let proj_params_count = + self.substitution.len(Interner) - trait_ref.substitution.len(Interner); + let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; + hir_fmt_generics(f, proj_params, None, None) + } + AnyTraitAssocType::Rpitit(assoc_type) => { + // Format RPITIT as `impl Trait`. + // FIXME: In some cases, it makes more sense to show this as RTN (`Trait::method(..)`). + // However not *all* associated types are the same as the corresponding RTN (the `impl Trait` + // can be nested). Figuring out when we should display RTN will be tricky. + let assoc_type = assoc_type.loc(f.db); + f.format_bounds_with(self.clone(), |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), + ), + &assoc_type + .bounds + .clone() + .substitute(Interner, &self.substitution) + .iter() + .map(|bound| { + // We ignore `Self` anyway when formatting, so it's fine put an error type in it. + inline_bound_to_generic_predicate( + bound, + TyKind::Error.intern(Interner), + ) + }) + .collect::>(), + SizedByDefault::Sized { + anchor: assoc_type.trait_id.lookup(f.db.upcast()).container.krate(), + }, + ) + }) + } + } } } @@ -1259,35 +1294,62 @@ impl HirDisplay for Ty { } f.end_location_link(); - let generic_def = self.as_generic_def(db); - - hir_fmt_generics(f, parameters.as_slice(Interner), generic_def, None)?; + hir_fmt_generics(f, parameters.as_slice(Interner), Some((*def_id).into()), None)?; } TyKind::AssociatedType(assoc_type_id, parameters) => { - let type_alias = from_assoc_type_id(*assoc_type_id); - let trait_ = match type_alias.lookup(db.upcast()).container { - ItemContainerId::TraitId(it) => it, - _ => panic!("not an associated type"), - }; - let trait_data = db.trait_data(trait_); - let type_alias_data = db.type_alias_data(type_alias); - // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) if f.display_kind.is_test() { - f.start_location_link(trait_.into()); - write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?; - f.end_location_link(); - write!(f, "::")?; + match from_assoc_type_id(f.db, *assoc_type_id) { + AnyTraitAssocType::Normal(type_alias) => { + let trait_ = match type_alias.lookup(db.upcast()).container { + ItemContainerId::TraitId(it) => it, + _ => panic!("not an associated type"), + }; + let trait_data = db.trait_data(trait_); + let type_alias_data = db.type_alias_data(type_alias); + + f.start_location_link(trait_.into()); + write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?; + f.end_location_link(); + write!(f, "::")?; - f.start_location_link(type_alias.into()); - write!(f, "{}", type_alias_data.name.display(f.db.upcast(), f.edition()))?; - f.end_location_link(); - // Note that the generic args for the associated type come before those for the - // trait (including the self type). - hir_fmt_generics(f, parameters.as_slice(Interner), None, None) + f.start_location_link(type_alias.into()); + write!( + f, + "{}", + type_alias_data.name.display(f.db.upcast(), f.edition()) + )?; + f.end_location_link(); + // Note that the generic args for the associated type come before those for the + // trait (including the self type). + hir_fmt_generics(f, parameters.as_slice(Interner), None, None) + } + AnyTraitAssocType::Rpitit(assoc_type) => { + // In tests show the associated type as is. + let assoc_type = assoc_type.loc(f.db); + + let trait_data = f.db.trait_data(assoc_type.trait_id); + let method_data = + f.db.function_data(assoc_type.synthesized_from_method); + + f.start_location_link(assoc_type.trait_id.into()); + write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?; + f.end_location_link(); + write!(f, "::")?; + + f.start_location_link(assoc_type.synthesized_from_method.into()); + write!( + f, + "__{}_rpitit", + method_data.name.display(f.db.upcast(), f.edition()) + )?; + f.end_location_link(); + hir_fmt_generics(f, parameters.as_slice(Interner), None, None) + } + } } else { let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(type_alias), + associated_ty_id: *assoc_type_id, substitution: parameters.clone(), }; @@ -1748,7 +1810,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( } } -fn write_bounds_like_dyn_trait( +pub(crate) fn write_bounds_like_dyn_trait( f: &mut HirFormatter<'_>, this: Either<&Ty, &Lifetime>, predicates: &[QuantifiedWhereClause], @@ -1860,7 +1922,14 @@ fn write_bounds_like_dyn_trait( angle_open = true; } if let AliasTy::Projection(proj) = alias { - let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id); + let assoc_ty_id = match from_assoc_type_id(f.db, proj.associated_ty_id) { + AnyTraitAssocType::Normal(it) => it, + AnyTraitAssocType::Rpitit(_) => { + unreachable!( + "Rust does not currently have a way to specify alias equation on RPITIT" + ) + } + }; let type_alias = f.db.type_alias_data(assoc_ty_id); f.start_location_link(assoc_ty_id.into()); write!(f, "{}", type_alias.name.display(f.db.upcast(), f.edition()))?; @@ -1940,7 +2009,14 @@ impl HirDisplay for WhereClause { write!(f, " as ")?; trait_ref.hir_fmt(f)?; write!(f, ">::",)?; - let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); + let type_alias = match from_assoc_type_id(f.db, projection_ty.associated_ty_id) { + AnyTraitAssocType::Normal(it) => it, + AnyTraitAssocType::Rpitit(_) => { + unreachable!( + "Rust does not currently have a way to specify alias equation on RPITIT" + ) + } + }; f.start_location_link(type_alias.into()); write!( f, diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index cb186c45ad00..0569f7b260d4 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs @@ -19,7 +19,7 @@ use crate::{ AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, + from_chalk_trait_id, generics::{generics, trait_self_param_idx}, lower::callable_item_sig, to_assoc_type_id, to_chalk_trait_id, @@ -173,18 +173,17 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { AssocItemId::TypeAliasId(id) => { let assoc_ty_id = to_assoc_type_id(id); let assoc_ty_data = db.associated_ty_data(assoc_ty_id); - Some(assoc_ty_data) + Some((id, assoc_ty_data)) } _ => None, }) - .any(|assoc_ty_data| { + .any(|(assoc_ty_id, assoc_ty_data)| { assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { - let def = from_assoc_type_id(assoc_ty_data.id).into(); match bound.skip_binders() { InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { contains_illegal_self_type_reference( db, - def, + assoc_ty_id.into(), trait_, arg, DebruijnIndex::ONE, @@ -194,7 +193,7 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { contains_illegal_self_type_reference( db, - def, + assoc_ty_id.into(), trait_, arg, DebruijnIndex::ONE, diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index 9ed9817dfa07..8338134bb93a 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs @@ -51,6 +51,10 @@ where } impl Generics { + pub(crate) fn self_params(&self) -> &GenericParams { + &self.params + } + pub(crate) fn def(&self) -> GenericDefId { self.def } diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs index bd4a53603d1a..30d018dd8e5d 100644 --- a/crates/hir-ty/src/interner.rs +++ b/crates/hir-ty/src/interner.rs @@ -9,8 +9,7 @@ use crate::{ TyData, TyKind, VariableKind, VariableKinds, chalk_db, tls, }; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; -use hir_def::TypeAliasId; -use intern::{Interned, impl_internable}; +use intern::{Interned, Symbol, impl_internable}; use smallvec::SmallVec; use std::fmt; use triomphe::Arc; @@ -69,7 +68,7 @@ impl chalk_ir::interner::Interner for Interner { type InternedVariances = SmallVec<[Variance; 16]>; type DefId = salsa::Id; type InternedAdtId = hir_def::AdtId; - type Identifier = TypeAliasId; + type Identifier = Symbol; type FnAbi = FnAbi; fn debug_adt_id( diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 8292e80c1e19..42a9b2c4709e 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -30,6 +30,7 @@ mod inhabitedness; mod interner; mod lower; mod mapping; +mod rpitit; mod target_feature; mod tls; mod utils; @@ -62,7 +63,10 @@ use chalk_ir::{ interner::HasInterner, }; use either::Either; -use hir_def::{CallableDefId, GeneralConstId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness}; +use hir_def::{ + CallableDefId, GeneralConstId, GenericParamId, TypeOrConstParamId, hir::ExprId, + type_ref::Rawness, +}; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; use intern::{Symbol, sym}; @@ -98,8 +102,9 @@ pub use lower::{ associated_type_shorthand_candidates, diagnostics::*, }; pub use mapping::{ - from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, - lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, + AnyImplAssocType, AnyTraitAssocType, from_assoc_type_id, from_assoc_type_value_id, + from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, + lt_to_placeholder_idx, to_assoc_type_id, to_assoc_type_value_id, to_chalk_trait_id, to_foreign_def_id, to_placeholder_idx, }; pub use method_resolution::check_orphan_rules; @@ -342,24 +347,24 @@ pub(crate) fn make_single_type_binders>( ) } +pub(crate) fn variable_kinds_from_generics( + db: &dyn HirDatabase, + generics: impl Iterator, +) -> impl Iterator { + generics.map(|x| match x { + GenericParamId::ConstParamId(id) => VariableKind::Const(db.const_param_ty(id)), + GenericParamId::TypeParamId(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), + GenericParamId::LifetimeParamId(_) => VariableKind::Lifetime, + }) +} + pub(crate) fn make_binders>( db: &dyn HirDatabase, generics: &Generics, value: T, ) -> Binders { Binders::new( - VariableKinds::from_iter( - Interner, - generics.iter_id().map(|x| match x { - hir_def::GenericParamId::ConstParamId(id) => { - chalk_ir::VariableKind::Const(db.const_param_ty(id)) - } - hir_def::GenericParamId::TypeParamId(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime, - }), - ), + VariableKinds::from_iter(Interner, variable_kinds_from_generics(db, generics.iter_id())), value, ) } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index e5f3c4cfc8fc..be29be04d8cb 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -22,11 +22,13 @@ use chalk_ir::{ interner::HasInterner, }; +use chalk_solve::rust_ir; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, - FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, LocalFieldId, - Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, UnionId, VariantId, + FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, + LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, + UnionId, VariantId, builtin_type::BuiltinType, data::{TraitFlags, adt::StructKind}, expander::Expander, @@ -48,16 +50,17 @@ use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashSet; use rustc_pattern_analysis::Captures; use salsa::Cycle; -use stdx::{impl_from, never}; +use stdx::{impl_from, never, thin_vec::EmptyOptimizedThinVec}; use syntax::ast; use triomphe::{Arc, ThinArc}; use crate::{ AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, - LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, QuantifiedWhereClause, - QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, - TyKind, WhereClause, all_super_traits, + LifetimeData, LifetimeOutlives, ParamKind, PlaceholderIndex, PolyFnSig, ProgramClause, + ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, + TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, VariableKinds, WhereClause, all_super_traits, + chalk_db::generic_predicate_to_inline_bound, consteval::{ intern_const_ref, intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic, @@ -70,9 +73,11 @@ use crate::{ path::{PathDiagnosticCallback, PathLoweringContext}, }, make_binders, - mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx}, + mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx, to_assoc_type_id_rpitit}, + rpitit::{RpititTraitAssocTy, RpititTraitAssocTyId}, static_lifetime, to_chalk_trait_id, to_placeholder_idx, utils::{InTypeConstIdMetadata, all_super_trait_refs}, + variable_kinds_from_generics, }; #[derive(Debug, Default)] @@ -84,16 +89,24 @@ struct ImplTraitLoweringState { // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. opaque_type_data: Arena, param_and_variable_counter: u16, + /// The associated types that were synthesized for `impl Trait`s if `mode` is [`ImplTraitLoweringMode::AssocType`]. + synthesized_assoc_types: Vec, } impl ImplTraitLoweringState { fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState { - Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } + Self { + mode, + opaque_type_data: Arena::new(), + param_and_variable_counter: 0, + synthesized_assoc_types: Vec::new(), + } } fn param(counter: u16) -> Self { Self { mode: ImplTraitLoweringMode::Param, opaque_type_data: Arena::new(), param_and_variable_counter: counter, + synthesized_assoc_types: Vec::new(), } } fn variable(counter: u16) -> Self { @@ -101,6 +114,7 @@ impl ImplTraitLoweringState { mode: ImplTraitLoweringMode::Variable, opaque_type_data: Arena::new(), param_and_variable_counter: counter, + synthesized_assoc_types: Vec::new(), } } } @@ -237,6 +251,12 @@ pub enum ImplTraitLoweringMode { /// of functions we're calling, and the return type of the function we're /// currently checking. Variable, + /// `impl Trait` gets lowered into a synthesized associated type, represented as + /// [`RpititTraitAssocTy`]. This is used when lowering RPITIT (Return Position Impl + /// Trait In Traits) in traits (not impls; inside an impl, RPITIT gets lowered into + /// an opaque then the return type is unified with that of the trait method to tell + /// the value of the associated types). + AssocType, /// `impl Trait` is disallowed and will be an error. #[default] Disallowed, @@ -370,9 +390,7 @@ impl<'a> TyLoweringContext<'a> { |a| ImplTraitId::TypeAliasImplTrait(a, idx), ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = - generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); - let parameters = generics.bound_vars_subst(self.db, self.in_binders); + let parameters = self.subst_for_generics(); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } ImplTraitLoweringMode::Param => { @@ -432,6 +450,7 @@ impl<'a> TyLoweringContext<'a> { // FIXME: report error TyKind::Error.intern(Interner) } + ImplTraitLoweringMode::AssocType => self.lower_rpitit_in_trait(bounds), } } TypeRef::Macro(macro_call) => { @@ -513,6 +532,143 @@ impl<'a> TyLoweringContext<'a> { (ty, res) } + /// Lowers a Return Position Impl Trait In Traits in the trait (not the impl). + /// + /// RPITITs create a synthesized associated type for each `impl Trait`. For example, + /// for the following trait: + /// ```ignore + /// trait Trait<'a, T, const N: usize> { + /// fn foo<'b, U>(&self) -> impl Future; + /// } + /// ``` + /// We desugar it to the following: + /// ```ignore + /// trait Trait<'a, T, const N: usize> { + /// type FooRpitit1<'b, U>: Display; + /// type FooRpitit2<'b, U>: Future>; + /// fn foo<'b, U>(&self) -> Self::FooRpitit2<'b, U>; + /// } + /// ``` + /// Actually, lifetime parameters are lowered somewhat differently in rustc, but I didn't duplicate that here + /// (because we don't handle lifetimes generally yet). + /// + /// The way we implement this is that when we lower a trait method and encounter an `impl Trait`, + /// we intern a [`RpititTraitAssocTyId`] containing the bounds, and we collect all such instances + /// within a method. When asking for the trait datum, we walk its method and collect all of their + /// RPITITs. + /// + /// Then, we need to infer the value for these associated types for an impl. We do that in `impl_rpitit_values()`, + /// but the outline of the process is as follows: we walk the methods, and for each method we take its return + /// type in the impl, and equate with the its return type in the trait with all RPITITs swapped with inference vars. + /// Then those inference vars are the values for the associated types. + /// + /// For example, consider: + /// ```ignore + /// trait Trait { + /// fn foo(&self) -> impl Debug; + /// } + /// + /// impl Trait for Foo { + /// fn foo(&self) -> Option; + /// } + /// ``` + /// The equation will tell us that the hidden associated type has value `Option` (note: this + /// `impl Debug` is **not** a RPITIT, it's a normal function RPIT!). + fn lower_rpitit_in_trait(&mut self, bounds: &[TypeBound]) -> Ty { + let method_generics = + self.generics().expect("`ImplTraitLoweringMode::AssocType` used outside a method"); + let Some(GenericDefId::FunctionId(method_id)) = self.resolver.generic_def() else { + panic!("`ImplTraitLoweringMode::AssocType` used outside a method"); + }; + let ItemContainerId::TraitId(trait_id) = method_id.loc(self.db).container else { + panic!("`ImplTraitLoweringMode::AssocType` used outside a trait method"); + }; + + let assoc_type_binders = VariableKinds::from_iter( + Interner, + variable_kinds_from_generics(self.db, method_generics.iter_id()), + ); + + let returned_subst = self.subst_for_generics(); + + // This is a placeholder (pun intended): we insert it and then remove it. + // Ideally it'd be a projection `Self::SynthesizedAssoc`, but we have no way to refer + // to the associated type here because it was not created yet! + let self_ty = TyKind::Placeholder(PlaceholderIndex { + ui: chalk_ir::UniverseIndex::ROOT, + idx: usize::MAX, + }) + .intern(Interner); + let mut assoc_type_bounds = Vec::new(); + let db = self.db; + // FIXME: `DebruijnIndex::INNERMOST` does not seem correct here, we need level 1 binder + // (level 0 is the bound itself binders). But `lower_type_bound()` shifts the bound in. + // I guess what we actually need is for `ParamLoweringMode::Variable` to contain the debruijn + // index we want to lower generic parameters to, then another field for binders of HRTB. + // But since we don't handle HRTB at all currently this should be fine for now. + self.with_debruijn(DebruijnIndex::INNERMOST, |this| { + let old_param_lowering_mode = + mem::replace(&mut this.type_param_mode, ParamLoweringMode::Variable); + for bound in bounds { + for bound in this.lower_type_bound(bound, self_ty.clone(), false) { + let bound = generic_predicate_to_inline_bound(db, &bound, &self_ty); + if let Some(bound) = bound { + assoc_type_bounds.push(bound); + }; + } + } + + if !this.unsized_types.contains(&self_ty) { + let sized_trait = db + .lang_item(this.resolver.krate(), LangItem::Sized) + .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); + let sized_bound = sized_trait.map(|sized_trait| { + let trait_bound = rust_ir::TraitBound { + trait_id: sized_trait, + args_no_self: Default::default(), + }; + let inline_bound = rust_ir::InlineBound::TraitBound(trait_bound); + chalk_ir::Binders::empty(Interner, inline_bound) + }); + if let Some(sized_bound) = sized_bound { + assoc_type_bounds.push(sized_bound); + } + } else { + // Because we used a placeholder, we must remove it before we proceed, otherwise it can affect other RPITITs. + this.unsized_types.remove(&self_ty); + } + + this.type_param_mode = old_param_lowering_mode; + }); + assoc_type_bounds.shrink_to_fit(); + + let assoc_type = RpititTraitAssocTyId::new( + self.db, + RpititTraitAssocTy { + trait_id, + synthesized_from_method: method_id, + bounds: Binders::new(assoc_type_binders, assoc_type_bounds), + }, + ); + self.impl_trait_mode.synthesized_assoc_types.push(assoc_type); + + // Now, in the place of the RPITIT, we insert a projection into this synthesized associated type. + TyKind::Alias(AliasTy::Projection(ProjectionTy { + associated_ty_id: to_assoc_type_id_rpitit(assoc_type), + substitution: returned_subst, + })) + .intern(Interner) + } + + /// Returns a `Substitution` for the current owner, with the expected param lowering mode. + fn subst_for_generics(&mut self) -> Substitution { + let generics = self.generics().expect("no generics in `subst_for_generics()`"); + match self.type_param_mode { + ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db), + ParamLoweringMode::Variable => generics.bound_vars_subst(self.db, self.in_binders), + } + } + /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. @@ -868,12 +1024,28 @@ impl<'a> TyLoweringContext<'a> { /// Build the signature of a callable item (function, struct or enum variant). pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig { match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), + CallableDefId::FunctionId(f) => { + let container = f.loc(db).container; + match container { + ItemContainerId::TraitId(_) => trait_fn_signature(db, f).0.clone(), + _ => fn_sig_for_fn(db, f, ImplTraitLoweringMode::Opaque).0, + } + } CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), } } +#[salsa::tracked(return_ref)] +pub(crate) fn trait_fn_signature( + db: &dyn HirDatabase, + def: FunctionId, +) -> (PolyFnSig, EmptyOptimizedThinVec) { + let (sig, rpitit_assoc_types) = fn_sig_for_fn(db, def, ImplTraitLoweringMode::AssocType); + let rpitit_assoc_types = EmptyOptimizedThinVec::from_iter(rpitit_assoc_types); + (sig, rpitit_assoc_types) +} + pub fn associated_type_shorthand_candidates( db: &dyn HirDatabase, def: GenericDefId, @@ -1431,7 +1603,11 @@ pub(crate) fn generic_defaults_with_diagnostics_recover( (defaults, None) } -fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { +fn fn_sig_for_fn( + db: &dyn HirDatabase, + def: FunctionId, + return_type_impl_trait_mode: ImplTraitLoweringMode, +) -> (PolyFnSig, Vec) { let data = db.function_data(def); let resolver = def.resolver(db.upcast()); let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) @@ -1439,9 +1615,10 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { .with_type_param_mode(ParamLoweringMode::Variable); let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_impl_trait_mode(return_type_impl_trait_mode) .with_type_param_mode(ParamLoweringMode::Variable); let ret = ctx_ret.lower_ty(data.ret_type); + let rpitit_assoc_types = ctx_ret.impl_trait_mode.synthesized_assoc_types; let generics = generics(db.upcast(), def.into()); let sig = CallableSig::from_params_and_return( params, @@ -1450,7 +1627,8 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), ); - make_binders(db, &generics, sig) + let sig = make_binders(db, &generics, sig); + (sig, rpitit_assoc_types) } /// Build the declared type of a function. This should not need to look at the diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index 042567030887..c9620646f93e 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -837,7 +837,12 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { if let Some(type_ref) = binding.type_ref { match (&self.ctx.types_map[type_ref], self.ctx.impl_trait_mode.mode) { (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), - (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { + ( + _, + ImplTraitLoweringMode::Disallowed + | ImplTraitLoweringMode::Opaque + | ImplTraitLoweringMode::AssocType, + ) => { let ty = self.ctx.lower_ty(type_ref); let alias_eq = AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; diff --git a/crates/hir-ty/src/mapping.rs b/crates/hir-ty/src/mapping.rs index f7511e5f63a3..6f9eb828e432 100644 --- a/crates/hir-ty/src/mapping.rs +++ b/crates/hir-ty/src/mapping.rs @@ -13,7 +13,9 @@ use salsa::{ use crate::{ AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId, Interner, OpaqueTyId, - PlaceholderIndex, chalk_db, db::HirDatabase, + PlaceholderIndex, chalk_db, + db::HirDatabase, + rpitit::{RpititImplAssocTyId, RpititTraitAssocTyId}, }; pub(crate) trait ToChalk { @@ -22,6 +24,18 @@ pub(crate) trait ToChalk { fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum AnyTraitAssocType { + Normal(TypeAliasId), + Rpitit(RpititTraitAssocTyId), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum AnyImplAssocType { + Normal(TypeAliasId), + Rpitit(RpititImplAssocTyId), +} + pub(crate) fn from_chalk(db: &dyn HirDatabase, chalk: ChalkT) -> T where T: ToChalk, @@ -53,23 +67,6 @@ impl ToChalk for CallableDefId { } } -pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId); - -impl ToChalk for TypeAliasAsValue { - type Chalk = chalk_db::AssociatedTyValueId; - - fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::AssociatedTyValueId { - rust_ir::AssociatedTyValueId(self.0.as_id()) - } - - fn from_chalk( - _db: &dyn HirDatabase, - assoc_ty_value_id: chalk_db::AssociatedTyValueId, - ) -> TypeAliasAsValue { - TypeAliasAsValue(TypeAliasId::from_id(assoc_ty_value_id.0)) - } -} - impl From for crate::db::InternedCallableDefId { fn from(fn_def_id: FnDefId) -> Self { Self::from_id(fn_def_id.0) @@ -130,8 +127,29 @@ pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { chalk_ir::AssocTypeId(id.as_id()) } -pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { - FromId::from_id(id.0) +pub(crate) fn to_assoc_type_id_rpitit(id: RpititTraitAssocTyId) -> AssocTypeId { + chalk_ir::AssocTypeId(id.as_id()) +} + +pub fn from_assoc_type_id(db: &dyn HirDatabase, id: AssocTypeId) -> AnyTraitAssocType { + salsa::plumbing::FromIdWithDb::from_id(id.0, db) +} + +pub fn to_assoc_type_value_id(id: TypeAliasId) -> chalk_db::AssociatedTyValueId { + rust_ir::AssociatedTyValueId(id.as_id()) +} + +pub(crate) fn to_assoc_type_value_id_rpitit( + id: RpititImplAssocTyId, +) -> chalk_db::AssociatedTyValueId { + rust_ir::AssociatedTyValueId(id.as_id()) +} + +pub fn from_assoc_type_value_id( + db: &dyn HirDatabase, + id: chalk_db::AssociatedTyValueId, +) -> AnyImplAssocType { + salsa::plumbing::FromIdWithDb::from_id(id.0, db) } pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId { diff --git a/crates/hir-ty/src/rpitit.rs b/crates/hir-ty/src/rpitit.rs new file mode 100644 index 000000000000..706161c629e9 --- /dev/null +++ b/crates/hir-ty/src/rpitit.rs @@ -0,0 +1,507 @@ +//! This module contains the implementation of Return Position Impl Trait In Traits. + +use std::{iter, sync::Arc}; + +use base_db::impl_intern_key_ref; +use chalk_ir::{ + BoundVar, DebruijnIndex, + cast::Cast, + fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}, +}; +use chalk_solve::rust_ir::AssociatedTyValueBound; +use hir_def::{ + AssocItemId, FunctionId, GenericDefId, GenericParamId, ImplId, TraitId, + generics::{GenericParams, TypeOrConstParamData}, + resolver::HasResolver, +}; +use rustc_hash::FxHashMap; +use stdx::thin_vec::EmptyOptimizedThinVec; + +use crate::{ + AliasTy, AnyTraitAssocType, Binders, Const, ConstData, ConstValue, DomainGoal, Goal, GoalData, + ImplTraitLoweringMode, InferenceTable, Interner, Lifetime, LifetimeData, ParamLoweringMode, + PlaceholderIndex, ProjectionTy, Substitution, TraitRef, Ty, TyKind, TyLoweringContext, + VariableKinds, + chalk_db::{AssociatedTyValue, inline_bound_to_generic_predicate}, + db::HirDatabase, + from_assoc_type_id, from_placeholder_idx, + generics::{Generics, generics}, + lt_from_placeholder_idx, + mapping::{ToChalk, to_assoc_type_id_rpitit}, + variable_kinds_from_generics, +}; + +/// An associated type synthesized from a Return Position Impl Trait In Trait +/// of the trait (not the impls). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RpititTraitAssocTy { + pub trait_id: TraitId, + /// The method that contains this RPITIT. + pub synthesized_from_method: FunctionId, + /// The bounds of this associated type (coming from the `impl Bounds`). + /// + /// The generics are the generics of the method (with some modifications that we + /// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html). + pub bounds: Binders>>, +} + +impl_intern_key_ref!(RpititTraitAssocTyId, RpititTraitAssocTy); + +/// An associated type synthesized from a Return Position Impl Trait In Trait +/// of the impl (not the trait). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RpititImplAssocTy { + pub impl_id: ImplId, + /// The definition of this associated type in the trait. + pub trait_assoc: RpititTraitAssocTyId, +} + +impl_intern_key_ref!(RpititImplAssocTyId, RpititImplAssocTy); + +// We return a list and not a hashmap because the number of RPITITs in a function should be small. +#[salsa::tracked(return_ref)] +pub(crate) fn impl_method_rpitit_values( + db: &dyn HirDatabase, + impl_id: ImplId, + trait_method_id: FunctionId, +) -> Box<[Arc]> { + tracing::error!("impl_method_rpitit_values()"); + let impl_items = db.impl_items(impl_id); + let trait_method = db.function_data(trait_method_id); + let impl_trait_ref = db.impl_trait(impl_id).expect("invalid impl passed to Chalk"); + let trait_method_generics = generics(db.upcast(), trait_method_id.into()); + let impl_method = impl_items.items.iter().find_map(|(name, id)| { + if *name == trait_method.name { + match *id { + AssocItemId::FunctionId(it) => Some(it), + _ => None, + } + } else { + None + } + }); + let impl_method = match impl_method { + Some(impl_method) => impl_method, + None => { + // Method not in the impl, so it is defaulted. + return defaulted_impl_method_rpitit_values( + db, + impl_id, + trait_method_id, + impl_trait_ref, + &trait_method_generics, + ); + } + }; + let impl_method_generics = generics(db.upcast(), impl_method.into()); + + // First, just so we won't ICE, check that the impl method generics match the trait method generics. + if !check_method_generics_are_structurally_compatible( + trait_method_generics.self_params(), + impl_method_generics.self_params(), + ) { + return Box::default(); + } + + // The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var, + // then we equate the return type of the trait method with the return type of the impl method. The values + // of the inference vars now represent the value of the RPITIT assoc types. + let mut table = InferenceTable::new(db, db.trait_environment(impl_method.into())); + let impl_method_placeholder_subst = impl_method_generics.placeholder_subst(db); + + let impl_method_ret = db + .callable_item_signature(impl_method.into()) + .substitute(Interner, &impl_method_placeholder_subst) + .ret() + .clone(); + let impl_method_ret = table.normalize_associated_types_in(impl_method_ret); + + // Create mapping from trait to impl (i.e. impl trait header + impl method identity args). + let trait_ref_placeholder_subst = + &impl_method_placeholder_subst.as_slice(Interner)[impl_method_generics.len_self()..]; + // We want to substitute the TraitRef with placeholders, but placeholders from the method, not the impl. + let impl_trait_ref = impl_trait_ref.substitute(Interner, trait_ref_placeholder_subst); + let trait_to_impl_args = Substitution::from_iter( + Interner, + impl_method_placeholder_subst.as_slice(Interner)[..impl_method_generics.len_self()] + .iter() + .chain(impl_trait_ref.substitution.as_slice(Interner)), + ); + let trait_method_ret = db + .callable_item_signature(trait_method_id.into()) + .substitute(Interner, &trait_to_impl_args) + .ret() + .clone(); + let mut rpitit_to_infer_var_folder = RpititToInferVarFolder { + db, + table: &mut table, + trait_method_id, + trait_rpitit_to_infer_var: FxHashMap::default(), + }; + let trait_method_ret = + trait_method_ret.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST); + let trait_rpitit_to_infer_var = rpitit_to_infer_var_folder.trait_rpitit_to_infer_var; + let trait_method_ret = table.normalize_associated_types_in(trait_method_ret); + + table.resolve_obligations_as_possible(); + // Even if unification fails, we want to continue. We will fill the RPITITs with error types. + table.unify(&trait_method_ret, &impl_method_ret); + table.resolve_obligations_as_possible(); + + trait_rpitit_to_infer_var + .into_iter() + .map(|(trait_assoc_id, infer_var)| { + let impl_rpitit = table.resolve_completely(infer_var); + let impl_rpitit = impl_rpitit.fold_with( + &mut PlaceholderToBoundVarFolder { + db, + method: impl_method.into(), + method_generics: impl_method_generics.self_params(), + parent: impl_id.into(), + parent_generics: impl_method_generics + .parent_generics() + .expect("parent should be an impl") + .self_params(), + }, + DebruijnIndex::INNERMOST, + ); + let trait_assoc = trait_assoc_id.loc(db); + // Completely unlike the docs, Chalk requires both the impl generics and the associated type + // generics in the binder. + let impl_rpitit_binders = VariableKinds::from_iter( + Interner, + trait_assoc.bounds.binders.as_slice(Interner)[..trait_method_generics.len()] + .iter() + .cloned() + .chain(variable_kinds_from_generics(db, impl_method_generics.iter_parent_id())), + ); + let impl_rpitit = + Binders::new(impl_rpitit_binders, AssociatedTyValueBound { ty: impl_rpitit }); + Arc::new(AssociatedTyValue { + associated_ty_id: to_assoc_type_id_rpitit(trait_assoc_id), + impl_id: ImplId::to_chalk(impl_id, db), + value: impl_rpitit, + }) + }) + .collect() +} + +fn defaulted_impl_method_rpitit_values( + db: &dyn HirDatabase, + impl_id: ImplId, + trait_method_id: FunctionId, + impl_trait_ref: Binders, + trait_method_generics: &Generics, +) -> Box<[Arc]> { + let defaulted_rpitit_values = defaulted_trait_method_rpitit_values(db, trait_method_id); + let impl_generics = generics(db.upcast(), impl_id.into()); + // The associated type generics as the same as the trait method's, but we take the impl as + // the parent instead of the trait. + // The impl generics need to be shifted to account for the associated type generics. + let trait_method_subst = trait_method_generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); + let impl_subst = Substitution::from_iter( + Interner, + impl_generics.iter_id().enumerate().map(|(idx, id)| match id { + GenericParamId::ConstParamId(id) => { + BoundVar::new(DebruijnIndex::INNERMOST, idx + trait_method_generics.len_self()) + .to_const(Interner, db.const_param_ty(id)) + .cast(Interner) + } + GenericParamId::TypeParamId(_) => { + BoundVar::new(DebruijnIndex::INNERMOST, idx + trait_method_generics.len_self()) + .to_ty(Interner) + .cast(Interner) + } + GenericParamId::LifetimeParamId(_) => { + BoundVar::new(DebruijnIndex::INNERMOST, idx + trait_method_generics.len_self()) + .to_lifetime(Interner) + .cast(Interner) + } + }), + ); + let impl_trait_ref = impl_trait_ref.substitute(Interner, &impl_subst); + let impl_rpitit_subst = Substitution::from_iter( + Interner, + trait_method_subst.as_slice(Interner)[..trait_method_generics.len_self()] + .iter() + .chain(impl_trait_ref.substitution.as_slice(Interner)), + ); + let binders = VariableKinds::from_iter( + Interner, + variable_kinds_from_generics( + db, + trait_method_generics.iter_self_id().chain(impl_generics.iter_id()), + ), + ); + defaulted_rpitit_values + .iter() + .map(|(trait_assoc, trait_rpitit)| { + let impl_rpitit = trait_rpitit.clone().substitute(Interner, &impl_rpitit_subst); + Arc::new(AssociatedTyValue { + associated_ty_id: to_assoc_type_id_rpitit(*trait_assoc), + impl_id: ImplId::to_chalk(impl_id, db), + value: Binders::new(binders.clone(), AssociatedTyValueBound { ty: impl_rpitit }), + }) + }) + .collect() +} + +/// This is called only for defaulted trait methods, as there the value of the RPITIT associated +/// items on an impl (if the method body is left defaulted) is the same as with the trait method. +// This returns an `EmptyOptimizedThinVec` and not `Box<[]>` because this is called from inference, +// and most methods don't have RPITITs. +#[salsa::tracked(return_ref)] +pub(crate) fn defaulted_trait_method_rpitit_values( + db: &dyn HirDatabase, + method_id: FunctionId, +) -> EmptyOptimizedThinVec<(RpititTraitAssocTyId, Binders)> { + tracing::error!("defaulted_trait_method_rpitit_values()"); + let method_generics = generics(db.upcast(), method_id.into()); + let mut table = InferenceTable::new(db, db.trait_environment(method_id.into())); + + let data = db.function_data(method_id); + let resolver = method_id.resolver(db.upcast()); + let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, method_id.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Placeholder); + // This is the return type of the method, with RPITIT lowered as opaques. In other words, like if it was written + // in an impl. + let method_opaques_ret = ctx_ret.lower_ty(data.ret_type); + tracing::error!("method_opaques_ret={method_opaques_ret:?}"); + let method_opaques_ret = table.normalize_associated_types_in(method_opaques_ret); + tracing::error!("after normalization: method_opaques_ret={method_opaques_ret:?}"); + + // This is the return type of the method, with RPITITs lowered as associated types. In other words, like in its + // signature. + let method_assocs_ret = db + .callable_item_signature(method_id.into()) + .substitute(Interner, &method_generics.placeholder_subst(db)) + .ret() + .clone(); + tracing::error!("method_assocs_ret={method_assocs_ret:?}"); + let mut rpitit_to_infer_var_folder = RpititToInferVarFolder { + db, + table: &mut table, + trait_method_id: method_id, + trait_rpitit_to_infer_var: FxHashMap::default(), + }; + let method_assocs_ret = + method_assocs_ret.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST); + tracing::error!("after folding: method_assocs_ret={method_assocs_ret:?}"); + let trait_rpitit_to_infer_var = rpitit_to_infer_var_folder.trait_rpitit_to_infer_var; + let method_assocs_ret = table.normalize_associated_types_in(method_assocs_ret); + tracing::error!("after normalization: method_assocs_ret={method_assocs_ret:?}"); + + table.resolve_obligations_as_possible(); + // Even if unification fails, we want to continue. We will fill the RPITITs with error types. + table.unify(&method_assocs_ret, &method_opaques_ret); + table.resolve_obligations_as_possible(); + + EmptyOptimizedThinVec::from_iter(trait_rpitit_to_infer_var.into_iter().map( + |(trait_assoc_id, infer_var)| { + let trait_assoc = trait_assoc_id.loc(db); + let rpitit = table.resolve_completely(infer_var); + let rpitit = rpitit.fold_with( + &mut PlaceholderToBoundVarFolder { + db, + method: method_id.into(), + method_generics: method_generics.self_params(), + parent: trait_assoc.trait_id.into(), + parent_generics: method_generics + .parent_generics() + .expect("method should be inside trait") + .self_params(), + }, + DebruijnIndex::INNERMOST, + ); + let impl_rpitit = trait_assoc.bounds.as_ref().map(|_| rpitit); + (trait_assoc_id, impl_rpitit) + }, + )) +} + +fn check_method_generics_are_structurally_compatible( + trait_method_generics: &GenericParams, + impl_method_generics: &GenericParams, +) -> bool { + if trait_method_generics.len_type_or_consts() != impl_method_generics.len_type_or_consts() { + return false; + } + + for ((_, trait_arg), (_, impl_arg)) in iter::zip( + trait_method_generics.iter_type_or_consts(), + impl_method_generics.iter_type_or_consts(), + ) { + match (trait_arg, impl_arg) { + (TypeOrConstParamData::TypeParamData(_), TypeOrConstParamData::TypeParamData(_)) + | (TypeOrConstParamData::ConstParamData(_), TypeOrConstParamData::ConstParamData(_)) => { + } + _ => return false, + } + } + + true +} + +#[derive(chalk_derive::FallibleTypeFolder)] +#[has_interner(Interner)] +struct RpititToInferVarFolder<'a, 'b> { + db: &'a dyn HirDatabase, + table: &'a mut InferenceTable<'b>, + trait_rpitit_to_infer_var: FxHashMap, + trait_method_id: FunctionId, +} +impl TypeFolder for RpititToInferVarFolder<'_, '_> { + fn as_dyn(&mut self) -> &mut dyn TypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { + let result = match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(ProjectionTy { associated_ty_id, substitution })) + | TyKind::AssociatedType(associated_ty_id, substitution) => { + if let AnyTraitAssocType::Rpitit(assoc_id) = + from_assoc_type_id(self.db, *associated_ty_id) + { + let assoc = assoc_id.loc(self.db); + if assoc.synthesized_from_method == self.trait_method_id { + if let Some(ty) = self.trait_rpitit_to_infer_var.get(&assoc_id) { + return ty.clone(); + } + + // Replace with new infer var. + // This needs to come before we fold the bounds, because they also contain this associated type. + let var = self.table.new_type_var(); + self.trait_rpitit_to_infer_var.insert(assoc_id, var.clone()); + + // Recurse into bounds, so that nested RPITITs will be handled correctly. + for bound in assoc.bounds.clone().substitute(Interner, substitution) { + let bound = inline_bound_to_generic_predicate(&bound, var.clone()); + tracing::error!("before folding: bound={bound:?}"); + // This is an unrelated binder, therefore `DebruijnIndex::INNERMOST`. + let bound = bound.fold_with(self, DebruijnIndex::INNERMOST); + tracing::error!("bound={bound:?}"); + let bound = self.table.normalize_associated_types_in(bound); + self.table.register_obligation(Goal::new( + Interner, + GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + bound.map(|bound| { + Goal::new( + Interner, + GoalData::DomainGoal(DomainGoal::Holds(bound)), + ) + }), + ), + )); + } + + return var; + } + } + ty.clone() + } + _ => ty.clone(), + }; + result.super_fold_with(self, outer_binder) + } +} + +#[derive(chalk_derive::FallibleTypeFolder)] +#[has_interner(Interner)] +struct PlaceholderToBoundVarFolder<'a> { + db: &'a dyn HirDatabase, + method: GenericDefId, + method_generics: &'a GenericParams, + parent: GenericDefId, + parent_generics: &'a GenericParams, +} +impl TypeFolder for PlaceholderToBoundVarFolder<'_> { + fn as_dyn(&mut self) -> &mut dyn TypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn fold_free_placeholder_ty( + &mut self, + universe: PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> Ty { + let placeholder = from_placeholder_idx(self.db, universe); + if placeholder.parent == self.method { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize + + self.method_generics.len_lifetimes(), + ) + .to_ty(Interner) + } else if placeholder.parent == self.parent { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize + + self.method_generics.len() + + self.parent_generics.len_lifetimes(), + ) + .to_ty(Interner) + } else { + TyKind::Placeholder(universe).intern(Interner) + } + } + + fn fold_free_placeholder_const( + &mut self, + ty: Ty, + universe: PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> Const { + let placeholder = from_placeholder_idx(self.db, universe); + if placeholder.parent == self.method { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize + + self.method_generics.len_lifetimes(), + ) + .to_const(Interner, ty) + } else if placeholder.parent == self.parent { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize + + self.method_generics.len() + + self.parent_generics.len_lifetimes(), + ) + .to_const(Interner, ty) + } else { + Const::new(Interner, ConstData { ty, value: ConstValue::Placeholder(universe) }) + } + } + + fn fold_free_placeholder_lifetime( + &mut self, + universe: PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> Lifetime { + let placeholder = lt_from_placeholder_idx(self.db, universe); + if placeholder.parent == self.method { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize, + ) + .to_lifetime(Interner) + } else if placeholder.parent == self.parent { + BoundVar::new( + DebruijnIndex::INNERMOST, + placeholder.local_id.into_raw().into_u32() as usize + self.method_generics.len(), + ) + .to_lifetime(Interner) + } else { + Lifetime::new(Interner, LifetimeData::Placeholder(universe)) + } + } +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index dda7bfb2baf9..a9ac4e32f145 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1,5 +1,25 @@ +use base_db::RootQueryDb; use cov_mark::check; -use expect_test::expect; +use either::Either; +use expect_test::{Expect, expect}; +use hir_def::db::DefDatabase; +use hir_def::nameres::DefMap; +use hir_def::{ImplId, ModuleDefId}; +use itertools::Itertools; +use test_fixture::WithFixture; + +use crate::chalk_db::inline_bound_to_generic_predicate; +use crate::db::HirDatabase; +use crate::display::{ + DisplayTarget, HirDisplay, HirDisplayError, HirFormatter, SizedByDefault, + write_bounds_like_dyn_trait, +}; +use crate::mapping::ToChalk; +use crate::test_db::TestDB; +use crate::{ + AnyImplAssocType, AnyTraitAssocType, Binders, Interner, TyKind, from_assoc_type_id, + from_assoc_type_value_id, to_chalk_trait_id, +}; use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; @@ -4884,3 +4904,237 @@ async fn baz i32>(c: T) { "#]], ); } + +#[test] +fn infer_rpitit() { + check_infer( + r#" +trait Trait<'a, T, const N: usize> { + fn foo<'b, U, const M: usize>(&self) -> (impl Trait<'b, U, M>,); +} + +fn bar<'a, 'b, T, const N: usize, U, const M: usize, Ty: Trait<'a, T, N>>(v: &Ty) { + let _ = v.foo::<'b, U, M>(); +} + "#, + // The `{unknown}` is not related to RPITIT, see https://github.com/rust-lang/rust-analyzer/issues/19392. + expect![[r#" + 72..76 'self': &'? Self + 183..184 'v': &'? Ty + 191..227 '{ ...>(); }': () + 201..202 '_': (Trait::__foo_rpitit<'b, U, M, Ty, '?, {unknown}, _>,) + 205..206 'v': &'? Ty + 205..224 'v.foo:..., M>()': (Trait::__foo_rpitit<'b, U, M, Ty, '?, {unknown}, _>,) + "#]], + ); +} + +#[track_caller] +fn check_rpitit(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let (db, _) = TestDB::with_many_files(ra_fixture); + let test_crate = *db.all_crates().last().unwrap(); + let def_map = db.crate_def_map(test_crate); + + crate::tls::set_current_program(&db, || { + let trait_ = def_map[DefMap::ROOT] + .scope + .declarations() + .filter_map(|decl| match decl { + ModuleDefId::TraitId(it) => Some(it), + _ => None, + }) + .at_most_one() + .unwrap_or_else(|_| panic!("at most one trait is supported for `check_rpitit()`")); + let trait_rpitits = trait_.into_iter().flat_map(|trait_| { + let trait_datum = db.trait_datum(test_crate, to_chalk_trait_id(trait_)); + trait_datum + .associated_ty_ids + .iter() + .copied() + .filter_map(|assoc_id| { + let assoc = match from_assoc_type_id(&db, assoc_id) { + AnyTraitAssocType::Rpitit(assoc_id) => Some(assoc_id.loc(&db)), + AnyTraitAssocType::Normal(_) => None, + }?; + let method_name = + db.function_data(assoc.synthesized_from_method).name.symbol().clone(); + let bounds = AssocTypeBounds(&assoc.bounds); + let bounds = + bounds.display_test(&db, DisplayTarget::from_crate(&db, test_crate)); + let method_name = method_name; + let description = format!("type __{method_name}_rpitit: {bounds};\n"); + Some(description) + }) + .collect::>() + }); + + let impl_ = def_map[DefMap::ROOT] + .scope + .impls() + .at_most_one() + .unwrap_or_else(|_| panic!("at most one impl is supported for `check_rpitit()`")); + let impl_rpitits = impl_.into_iter().flat_map(|impl_| { + let trait_datum = db.impl_datum(test_crate, ImplId::to_chalk(impl_, &db)); + trait_datum + .associated_ty_value_ids + .iter() + .copied() + .filter_map(|assoc_id| { + let trait_assoc = match from_assoc_type_value_id(&db, assoc_id) { + AnyImplAssocType::Rpitit(assoc) => assoc.loc(&db).trait_assoc, + AnyImplAssocType::Normal(_) => return None, + }; + let assoc_datum = db.associated_ty_value(test_crate, assoc_id); + let ty = assoc_datum + .value + .skip_binders() + .ty + .display_test(&db, DisplayTarget::from_crate(&db, test_crate)); + let method_name = db + .function_data(trait_assoc.loc(&db).synthesized_from_method) + .name + .symbol() + .clone(); + let description = format!("type __{method_name}_rpitit = {ty};\n"); + Some(description) + }) + .collect::>() + }); + + let all_rpitits = + trait_rpitits.chain(std::iter::once("\n".to_owned())).chain(impl_rpitits).join(""); + expect.assert_eq(&all_rpitits); + }); + + struct AssocTypeBounds<'a>( + &'a Binders>>>, + ); + impl HirDisplay for AssocTypeBounds<'_> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + let bounds = self + .0 + .skip_binders() + .iter() + .map(|bound| { + inline_bound_to_generic_predicate(bound, TyKind::Error.intern(Interner)) + }) + .collect::>(); + write_bounds_like_dyn_trait( + f, + Either::Left(&TyKind::Error.intern(Interner)), + &bounds, + SizedByDefault::Sized { anchor: f.krate() }, + ) + } + } +} + +#[test] +fn check_rpitit_trait_bounds_and_impl_value() { + check_rpitit( + r#" +//- minicore: sized +//- /helpers.rs crate:helpers +pub struct Foo(*mut T); +pub trait T1 {} +pub trait T2 {} +pub trait T3<'a, 'b, const N: usize> {} +pub trait T4 { + type Assoc; +} + +//- /lib.rs crate:lib deps:helpers +use helpers::*; +trait Trait { + fn foo<'a, B>() -> (impl T1, Foo + T2 + ?Sized>); + fn bar<'a>(&'a self) -> impl T2 + T3<'a, 'a, 123> + Trait; + fn baz() -> impl T4; +} +impl Trait for Foo { + fn foo<'a, B>() -> (impl T1, Foo) {} + fn bar<'a>(&'a self) -> impl T2 {} + fn baz() -> impl T4 {} +} + "#, + expect![[r#" + type __foo_rpitit: T1; + type __foo_rpitit: T2 + T2 + ?Sized; + type __bar_rpitit: T2 + T3 + Trait; + type __baz_rpitit: T1; + type __baz_rpitit: T4; + + type __foo_rpitit = impl T1; + type __foo_rpitit = ?0.1; + type __bar_rpitit = impl T2; + type __baz_rpitit = (); + type __baz_rpitit = impl T4; + "#]], + ); +} + +#[test] +fn rpitit_referring_self_assoc_type_in_impl_does_not_cycle() { + check_rpitit( + r#" +//- minicore: sized +trait Trait { + type Assoc; + fn foo() -> impl Sized; +} +impl Trait for () { + type Assoc = (); + fn foo() -> Self::Assoc; +} + "#, + expect![[r#" + type __foo_rpitit: Sized; + + type __foo_rpitit = (); + "#]], + ); +} + +#[test] +fn defaulted_method_with_rpitit() { + check_rpitit( + r#" +//- minicore: sized +//- /helpers.rs crate:helpers +pub trait Bar<'a, B: ?Sized, C: ?Sized, D: ?Sized> {} + +//- /lib.rs crate:library deps:helpers +use helpers::*; +trait Trait { + fn foo<'a, B>() -> impl Bar<'a, B, Self, T>; +} +struct Foo(T); +impl Trait<(Foo<()>, U)> for Foo {} + "#, + // The debruijn index in the value is 2, but should be 0 to refer to the associated + // type generics. It is 2 because opaques are wrapped in two binders, and so the 0 + // is shifted in twice. Since users are not expected to see debruijn indices anyway, + // this does not matter. + expect![[r#" + type __foo_rpitit: Bar; + + type __foo_rpitit = impl Bar, (Foo<()>, ?2.3)>; + "#]], + ); +} + +#[test] +fn check_foo() { + check_rpitit( + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () {} + "#, + expect![[r#""#]], + ); +} diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs index b718556c8ae0..0912dc73721e 100644 --- a/crates/hir-ty/src/tls.rs +++ b/crates/hir-ty/src/tls.rs @@ -4,11 +4,12 @@ use std::fmt::{self, Display}; use itertools::Itertools; use span::Edition; +use crate::mapping::AnyTraitAssocType; use crate::{ CallableDefId, Interner, ProjectionTyExt, chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk, }; -use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; +use hir_def::{AdtId, ItemContainerId, Lookup}; pub(crate) use unsafe_tls::{set_current_program, with_current_program}; @@ -45,20 +46,35 @@ impl DebugContext<'_> { id: chalk_db::AssocTypeId, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error> { - let type_alias: TypeAliasId = from_assoc_type_id(id); - let type_alias_data = self.0.type_alias_data(type_alias); - let trait_ = match type_alias.lookup(self.0.upcast()).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - let trait_data = self.0.trait_data(trait_); - write!( - fmt, - "{}::{}", - trait_data.name.display(self.0.upcast(), Edition::LATEST), - type_alias_data.name.display(self.0.upcast(), Edition::LATEST) - )?; - Ok(()) + match from_assoc_type_id(self.0, id) { + AnyTraitAssocType::Normal(type_alias) => { + let type_alias_data = self.0.type_alias_data(type_alias); + let trait_ = match type_alias.lookup(self.0.upcast()).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + let trait_data = self.0.trait_data(trait_); + write!( + fmt, + "{}::{}", + trait_data.name.display(self.0.upcast(), Edition::LATEST), + type_alias_data.name.display(self.0.upcast(), Edition::LATEST) + )?; + Ok(()) + } + AnyTraitAssocType::Rpitit(assoc_type) => { + let assoc_type = assoc_type.loc(self.0); + let method_data = self.0.function_data(assoc_type.synthesized_from_method); + let trait_data = self.0.trait_data(assoc_type.trait_id); + write!( + fmt, + "{}::__{}_rpitit", + trait_data.name.display(self.0.upcast(), Edition::LATEST), + method_data.name.display(self.0.upcast(), Edition::LATEST) + )?; + Ok(()) + } + } } pub(crate) fn debug_projection_ty( @@ -66,12 +82,28 @@ impl DebugContext<'_> { projection_ty: &chalk_ir::ProjectionTy, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error> { - let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); - let type_alias_data = self.0.type_alias_data(type_alias); - let trait_ = match type_alias.lookup(self.0.upcast()).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; + let (trait_, assoc_type_name) = + match from_assoc_type_id(self.0, projection_ty.associated_ty_id) { + AnyTraitAssocType::Normal(type_alias) => { + let type_alias_data = self.0.type_alias_data(type_alias); + let trait_ = match type_alias.lookup(self.0.upcast()).container { + ItemContainerId::TraitId(it) => it, + _ => panic!("associated type not in trait"), + }; + let type_alias_name = + type_alias_data.name.display(self.0.upcast(), Edition::LATEST).to_string(); + (trait_, type_alias_name) + } + AnyTraitAssocType::Rpitit(assoc_type) => { + let assoc_type = assoc_type.loc(self.0); + let method_data = self.0.function_data(assoc_type.synthesized_from_method); + let placeholder_assoc_type_name = format!( + "__{}_rpitit", + method_data.name.display(self.0.upcast(), Edition::LATEST) + ); + (assoc_type.trait_id, placeholder_assoc_type_name) + } + }; let trait_name = &self.0.trait_data(trait_).name; let trait_ref = projection_ty.trait_ref(self.0); let trait_params = trait_ref.substitution.as_slice(Interner); @@ -84,7 +116,7 @@ impl DebugContext<'_> { trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), )?; } - write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast(), Edition::LATEST))?; + write!(fmt, ">::{}", assoc_type_name)?; let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len(); let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count]; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ed0743def6bb..727026f493b5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2374,33 +2374,30 @@ impl Function { } } - pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool { - if self.is_async(db) { - return true; - } + /// Returns `Future::Output`. + pub fn returns_impl_future(self, db: &dyn HirDatabase) -> Option { + let future_trait_id = + db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())?; - let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false }; - let Some(future_trait_id) = - db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait()) - else { - return false; - }; - let Some(sized_trait_id) = - db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait()) - else { - return false; - }; + let ret_type = self.ret_type(db); + let canonical_ty = + Canonical { value: ret_type.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; + // The `is_async()` is an optimization. + if !self.is_async(db) + && !method_resolution::implements_trait_unique( + &canonical_ty, + db, + &ret_type.env, + future_trait_id, + ) + { + return None; + } - let mut has_impl_future = false; - impl_traits - .filter(|t| { - let fut = t.id == future_trait_id; - has_impl_future |= fut; - !fut && t.id != sized_trait_id - }) - // all traits but the future trait must be auto traits - .all(|t| t.is_auto(db)) - && has_impl_future + let future_output = db + .lang_item(self.ty(db).env.krate, LangItem::FutureOutput) + .and_then(|t| t.as_type_alias())?; + ret_type.normalize_trait_assoc_type(db, &[], future_output.into()) } /// Does this function have `#[test]` attribute? diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index ce9f56993e3c..0b1d0c133db8 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -34,8 +34,8 @@ use hir_expand::{ name::{AsName, Name}, }; use hir_ty::{ - Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, TyExt, TyKind, - TyLoweringContext, + Adjustment, AnyTraitAssocType, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, + TyExt, TyKind, TyLoweringContext, diagnostics::{ InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations, @@ -1025,11 +1025,14 @@ impl SourceAnalyzer { PathResolution::Def(ModuleDef::Adt(adt_id.0.into())), ), TyKind::AssociatedType(assoc_id, subst) => { - let assoc_id = from_assoc_type_id(*assoc_id); - ( - GenericSubstitution::new(assoc_id.into(), subst.clone(), env), - PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), - ) + match from_assoc_type_id(db, *assoc_id) { + AnyTraitAssocType::Normal(assoc_id) => ( + GenericSubstitution::new(assoc_id.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), + ), + // Showing substitution for RPITIT assoc types which are rendered as `impl Trait` will be very confusing. + AnyTraitAssocType::Rpitit(_) => return None, + } } TyKind::FnDef(fn_id, subst) => { let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id); diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index e08271f39a32..cf8f45373056 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,6 +31,7 @@ //! } //! ``` +use hir::HirDisplay; use hir::{MacroFileId, Name, db::ExpandDatabase}; use ide_db::text_edit::TextEdit; use ide_db::{ @@ -39,7 +40,7 @@ use ide_db::{ }; use syntax::{ AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr, - ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make}, + ast::{self, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make}, format_smolstr, ted, }; @@ -185,12 +186,12 @@ fn add_function_impl( let fn_name = &func.name(ctx.db); let sugar: &[_] = if func.is_async(ctx.db) { &[AsyncSugaring::Async, AsyncSugaring::Desugar] - } else if func.returns_impl_future(ctx.db) { - &[AsyncSugaring::Plain, AsyncSugaring::Resugar] + } else if let Some(future_output) = func.returns_impl_future(ctx.db) { + &[AsyncSugaring::Plain, AsyncSugaring::Resugar { future_output }] } else { &[AsyncSugaring::Plain] }; - for &sugaring in sugar { + for sugaring in sugar { add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); } } @@ -202,9 +203,9 @@ fn add_function_impl_( func: hir::Function, impl_def: hir::Impl, fn_name: &Name, - async_sugaring: AsyncSugaring, + async_sugaring: &AsyncSugaring, ) { - let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { + let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar { .. } = async_sugaring { "async " } else { "" @@ -248,10 +249,10 @@ fn add_function_impl_( } } -#[derive(Copy, Clone)] +#[derive(Clone)] enum AsyncSugaring { Desugar, - Resugar, + Resugar { future_output: hir::Type }, Async, Plain, } @@ -285,7 +286,7 @@ fn get_transformed_fn( ctx: &CompletionContext<'_>, fn_: ast::Fn, impl_def: hir::Impl, - async_: AsyncSugaring, + async_: &AsyncSugaring, ) -> Option { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(fn_.syntax())?; @@ -323,31 +324,16 @@ fn get_transformed_fn( } fn_.async_token().unwrap().detach(); } - AsyncSugaring::Resugar => { - let ty = fn_.ret_type()?.ty()?; - match &ty { - // best effort guessing here - ast::Type::ImplTraitType(t) => { - let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? { - ast::Type::PathType(p) => { - let p = p.path()?.segment()?; - if p.name_ref()?.text() != "Future" { - return None; - } - match p.generic_arg_list()?.generic_args().next()? { - ast::GenericArg::AssocTypeArg(a) - if a.name_ref()?.text() == "Output" => - { - a.ty() - } - _ => None, - } - } - _ => None, - })?; - ted::replace(ty.syntax(), output.syntax()); - } - _ => (), + AsyncSugaring::Resugar { future_output } => { + let ast_ret = fn_.ret_type()?; + if future_output.is_unit() { + ted::remove(ast_ret.syntax()); + } else { + let ret = future_output + .display_source_code(ctx.db, ctx.module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); + let ast_ret_ty = ast_ret.ty()?; + ted::replace(ast_ret_ty.syntax(), make::ty(&ret).syntax().clone_for_update()); } ted::prepend_child(fn_.syntax(), make::token(T![async])); } diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index cc9b3ef45736..612e3ab9ba74 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -133,6 +133,7 @@ define_symbols! { vectorcall_dash_unwind = "vectorcall-unwind", win64_dash_unwind = "win64-unwind", x86_dash_interrupt = "x86-interrupt", + synthesized_rpitit_assoc = "$synthesized_RPITIT_assoc$", @PLAIN: __ra_fixup, diff --git a/crates/stdx/src/thin_vec.rs b/crates/stdx/src/thin_vec.rs index 69d8ee7d9068..3549be72836b 100644 --- a/crates/stdx/src/thin_vec.rs +++ b/crates/stdx/src/thin_vec.rs @@ -183,6 +183,7 @@ unsafe impl<'a, T: Clone + 'a, I: TrustedLen> TrustedLen for std:: unsafe impl T> TrustedLen for std::iter::Map {} unsafe impl TrustedLen for std::vec::Drain<'_, T> {} unsafe impl TrustedLen for std::array::IntoIter {} +unsafe impl TrustedLen for std::collections::hash_map::IntoIter {} impl Clone for ThinVecWithHeader { #[inline]