mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 17:28:09 +00:00
Extract built-in trait implementations to separate module
This untangles the builtin logic from the Chalk translation.
This commit is contained in:
parent
3376c08052
commit
176207f1e8
3 changed files with 219 additions and 127 deletions
|
@ -15,6 +15,7 @@ use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty,
|
||||||
use self::chalk::{from_chalk, ToChalk};
|
use self::chalk::{from_chalk, ToChalk};
|
||||||
|
|
||||||
pub(crate) mod chalk;
|
pub(crate) mod chalk;
|
||||||
|
mod builtin;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TraitSolver {
|
pub struct TraitSolver {
|
||||||
|
|
161
crates/ra_hir_ty/src/traits/builtin.rs
Normal file
161
crates/ra_hir_ty/src/traits/builtin.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
//! This module provides the built-in trait implementations, e.g. to make
|
||||||
|
//! closures implement `Fn`.
|
||||||
|
use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
|
||||||
|
use hir_expand::name;
|
||||||
|
use ra_db::CrateId;
|
||||||
|
|
||||||
|
use super::{AssocTyValue, Impl};
|
||||||
|
use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor};
|
||||||
|
|
||||||
|
pub(super) struct BuiltinImplData {
|
||||||
|
pub num_vars: usize,
|
||||||
|
pub trait_ref: TraitRef,
|
||||||
|
pub where_clauses: Vec<super::GenericPredicate>,
|
||||||
|
pub assoc_ty_values: Vec<AssocTyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct BuiltinImplAssocTyValueData {
|
||||||
|
pub impl_: Impl,
|
||||||
|
pub assoc_ty_id: TypeAliasId,
|
||||||
|
pub num_vars: usize,
|
||||||
|
pub value: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_builtin_impls(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
ty: &Ty,
|
||||||
|
trait_: TraitId,
|
||||||
|
mut callback: impl FnMut(Impl),
|
||||||
|
) {
|
||||||
|
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
|
||||||
|
for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
|
||||||
|
{
|
||||||
|
if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) {
|
||||||
|
if trait_ == actual_trait {
|
||||||
|
let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
|
||||||
|
callback(Impl::ClosureFnTraitImpl(impl_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn impl_datum(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
impl_: Impl,
|
||||||
|
) -> Option<BuiltinImplData> {
|
||||||
|
match impl_ {
|
||||||
|
Impl::ImplBlock(_) => unreachable!(),
|
||||||
|
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn associated_ty_value(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
data: AssocTyValue,
|
||||||
|
) -> BuiltinImplAssocTyValueData {
|
||||||
|
match data {
|
||||||
|
AssocTyValue::TypeAlias(_) => unreachable!(),
|
||||||
|
AssocTyValue::ClosureFnTraitImplOutput(data) => {
|
||||||
|
closure_fn_trait_output_assoc_ty_value(db, krate, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closure_fn_trait_impl_datum(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
data: super::ClosureFnTraitImplData,
|
||||||
|
) -> Option<BuiltinImplData> {
|
||||||
|
// for some closure |X, Y| -> Z:
|
||||||
|
// impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
|
||||||
|
|
||||||
|
let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait
|
||||||
|
|
||||||
|
// validate FnOnce trait, since we need it in the assoc ty value definition
|
||||||
|
// and don't want to return a valid value only to find out later that FnOnce
|
||||||
|
// is broken
|
||||||
|
let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?;
|
||||||
|
let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?;
|
||||||
|
|
||||||
|
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
|
||||||
|
Expr::Lambda { args, .. } => args.len() as u16,
|
||||||
|
_ => {
|
||||||
|
log::warn!("closure for closure type {:?} not found", data);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg_ty = Ty::apply(
|
||||||
|
TypeCtor::Tuple { cardinality: num_args },
|
||||||
|
Substs::builder(num_args as usize).fill_with_bound_vars(0).build(),
|
||||||
|
);
|
||||||
|
let sig_ty = Ty::apply(
|
||||||
|
TypeCtor::FnPtr { num_args },
|
||||||
|
Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
|
||||||
|
|
||||||
|
let trait_ref = TraitRef {
|
||||||
|
trait_: trait_.into(),
|
||||||
|
substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone());
|
||||||
|
|
||||||
|
Some(BuiltinImplData {
|
||||||
|
num_vars: num_args as usize + 1,
|
||||||
|
trait_ref,
|
||||||
|
where_clauses: Vec::new(),
|
||||||
|
assoc_ty_values: vec![output_ty_id],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closure_fn_trait_output_assoc_ty_value(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
data: super::ClosureFnTraitImplData,
|
||||||
|
) -> BuiltinImplAssocTyValueData {
|
||||||
|
let impl_ = Impl::ClosureFnTraitImpl(data.clone());
|
||||||
|
|
||||||
|
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
|
||||||
|
Expr::Lambda { args, .. } => args.len() as u16,
|
||||||
|
_ => {
|
||||||
|
log::warn!("closure for closure type {:?} not found", data);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_ty = Ty::Bound(num_args.into());
|
||||||
|
|
||||||
|
let fn_once_trait =
|
||||||
|
get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist");
|
||||||
|
|
||||||
|
let output_ty_id = db
|
||||||
|
.trait_data(fn_once_trait)
|
||||||
|
.associated_type_by_name(&name::OUTPUT_TYPE)
|
||||||
|
.expect("assoc ty value should not exist");
|
||||||
|
|
||||||
|
BuiltinImplAssocTyValueData {
|
||||||
|
impl_,
|
||||||
|
assoc_ty_id: output_ty_id,
|
||||||
|
num_vars: num_args as usize + 1,
|
||||||
|
value: output_ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fn_trait(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
fn_trait: super::FnTrait,
|
||||||
|
) -> Option<TraitId> {
|
||||||
|
let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
|
||||||
|
match target {
|
||||||
|
LangItemTarget::TraitId(t) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,17 +8,16 @@ use chalk_ir::{
|
||||||
TypeName, UniverseIndex,
|
TypeName, UniverseIndex,
|
||||||
};
|
};
|
||||||
use chalk_rust_ir::{AssociatedTyDatum, AssociatedTyValue, ImplDatum, StructDatum, TraitDatum};
|
use chalk_rust_ir::{AssociatedTyDatum, AssociatedTyValue, ImplDatum, StructDatum, TraitDatum};
|
||||||
use ra_db::CrateId;
|
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
expr::Expr, lang_item::LangItemTarget, AssocItemId, AstItemDef, ContainerId, GenericDefId,
|
AssocItemId, AstItemDef, ContainerId, GenericDefId, ImplId, Lookup, TraitId, TypeAliasId,
|
||||||
ImplId, Lookup, TraitId, TypeAliasId,
|
};
|
||||||
|
use ra_db::{
|
||||||
|
salsa::{InternId, InternKey},
|
||||||
|
CrateId,
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
|
||||||
|
|
||||||
use ra_db::salsa::{InternId, InternKey};
|
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
|
||||||
|
|
||||||
use super::{AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, display::HirDisplay, ApplicationTy, GenericPredicate, ProjectionTy, Substs,
|
db::HirDatabase, display::HirDisplay, ApplicationTy, GenericPredicate, ProjectionTy, Substs,
|
||||||
TraitRef, Ty, TypeCtor, TypeWalk,
|
TraitRef, Ty, TypeCtor, TypeWalk,
|
||||||
|
@ -395,6 +394,51 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToChalk for builtin::BuiltinImplData {
|
||||||
|
type Chalk = chalk_rust_ir::ImplDatum<ChalkIr>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_rust_ir::ImplDatum<ChalkIr> {
|
||||||
|
let impl_type = chalk_rust_ir::ImplType::External;
|
||||||
|
let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
|
||||||
|
|
||||||
|
let impl_datum_bound =
|
||||||
|
chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
|
||||||
|
let associated_ty_value_ids =
|
||||||
|
self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
|
||||||
|
chalk_rust_ir::ImplDatum {
|
||||||
|
binders: make_binders(impl_datum_bound, self.num_vars),
|
||||||
|
impl_type,
|
||||||
|
polarity: chalk_rust_ir::Polarity::Positive,
|
||||||
|
associated_ty_value_ids,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(_db: &impl HirDatabase, _data: chalk_rust_ir::ImplDatum<ChalkIr>) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChalk for builtin::BuiltinImplAssocTyValueData {
|
||||||
|
type Chalk = chalk_rust_ir::AssociatedTyValue<ChalkIr>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_rust_ir::AssociatedTyValue<ChalkIr> {
|
||||||
|
let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: self.value.to_chalk(db) };
|
||||||
|
|
||||||
|
chalk_rust_ir::AssociatedTyValue {
|
||||||
|
associated_ty_id: self.assoc_ty_id.to_chalk(db),
|
||||||
|
impl_id: self.impl_.to_chalk(db),
|
||||||
|
value: make_binders(value_bound, self.num_vars),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(
|
||||||
|
_db: &impl HirDatabase,
|
||||||
|
_data: chalk_rust_ir::AssociatedTyValue<ChalkIr>,
|
||||||
|
) -> builtin::BuiltinImplAssocTyValueData {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
|
fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
|
||||||
chalk_ir::Binders {
|
chalk_ir::Binders {
|
||||||
value,
|
value,
|
||||||
|
@ -456,18 +500,10 @@ where
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone());
|
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone());
|
||||||
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
|
|
||||||
for &fn_trait in
|
builtin::get_builtin_impls(self.db, self.krate, &ty, trait_, |i| {
|
||||||
[super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
|
result.push(i.to_chalk(self.db))
|
||||||
{
|
});
|
||||||
if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) {
|
|
||||||
if trait_ == actual_trait {
|
|
||||||
let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait };
|
|
||||||
result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("impls_for_trait returned {} impls", result.len());
|
debug!("impls_for_trait returned {} impls", result.len());
|
||||||
result
|
result
|
||||||
|
@ -619,7 +655,7 @@ pub(crate) fn impl_datum_query(
|
||||||
let impl_: Impl = from_chalk(db, impl_id);
|
let impl_: Impl = from_chalk(db, impl_id);
|
||||||
match impl_ {
|
match impl_ {
|
||||||
Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block),
|
Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block),
|
||||||
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
|
_ => builtin::impl_datum(db, krate, impl_).map(|d| Arc::new(d.to_chalk(db))),
|
||||||
}
|
}
|
||||||
.unwrap_or_else(invalid_impl_datum)
|
.unwrap_or_else(invalid_impl_datum)
|
||||||
}
|
}
|
||||||
|
@ -700,63 +736,6 @@ fn invalid_impl_datum() -> Arc<ImplDatum<ChalkIr>> {
|
||||||
Arc::new(impl_datum)
|
Arc::new(impl_datum)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closure_fn_trait_impl_datum(
|
|
||||||
db: &impl HirDatabase,
|
|
||||||
krate: CrateId,
|
|
||||||
data: super::ClosureFnTraitImplData,
|
|
||||||
) -> Option<Arc<ImplDatum<ChalkIr>>> {
|
|
||||||
// for some closure |X, Y| -> Z:
|
|
||||||
// impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
|
|
||||||
|
|
||||||
let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait
|
|
||||||
|
|
||||||
// validate FnOnce trait, since we need it in the assoc ty value definition
|
|
||||||
// and don't want to return a valid value only to find out later that FnOnce
|
|
||||||
// is broken
|
|
||||||
let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?;
|
|
||||||
let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?;
|
|
||||||
|
|
||||||
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
|
|
||||||
Expr::Lambda { args, .. } => args.len() as u16,
|
|
||||||
_ => {
|
|
||||||
log::warn!("closure for closure type {:?} not found", data);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_ty = Ty::apply(
|
|
||||||
TypeCtor::Tuple { cardinality: num_args },
|
|
||||||
Substs::builder(num_args as usize).fill_with_bound_vars(0).build(),
|
|
||||||
);
|
|
||||||
let sig_ty = Ty::apply(
|
|
||||||
TypeCtor::FnPtr { num_args },
|
|
||||||
Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
|
|
||||||
|
|
||||||
let trait_ref = TraitRef {
|
|
||||||
trait_: trait_.into(),
|
|
||||||
substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()).to_chalk(db);
|
|
||||||
|
|
||||||
let impl_type = chalk_rust_ir::ImplType::External;
|
|
||||||
|
|
||||||
let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
|
|
||||||
trait_ref: trait_ref.to_chalk(db),
|
|
||||||
where_clauses: Vec::new(),
|
|
||||||
};
|
|
||||||
let impl_datum = ImplDatum {
|
|
||||||
binders: make_binders(impl_datum_bound, num_args as usize + 1),
|
|
||||||
impl_type,
|
|
||||||
polarity: chalk_rust_ir::Polarity::Positive,
|
|
||||||
associated_ty_value_ids: vec![output_ty_id],
|
|
||||||
};
|
|
||||||
Some(Arc::new(impl_datum))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn associated_ty_value_query(
|
pub(crate) fn associated_ty_value_query(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
|
@ -767,9 +746,7 @@ pub(crate) fn associated_ty_value_query(
|
||||||
AssocTyValue::TypeAlias(type_alias) => {
|
AssocTyValue::TypeAlias(type_alias) => {
|
||||||
type_alias_associated_ty_value(db, krate, type_alias)
|
type_alias_associated_ty_value(db, krate, type_alias)
|
||||||
}
|
}
|
||||||
AssocTyValue::ClosureFnTraitImplOutput(data) => {
|
_ => Arc::new(builtin::associated_ty_value(db, krate, data).to_chalk(db)),
|
||||||
closure_fn_trait_output_assoc_ty_value(db, krate, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -802,53 +779,6 @@ fn type_alias_associated_ty_value(
|
||||||
Arc::new(value)
|
Arc::new(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closure_fn_trait_output_assoc_ty_value(
|
|
||||||
db: &impl HirDatabase,
|
|
||||||
krate: CrateId,
|
|
||||||
data: super::ClosureFnTraitImplData,
|
|
||||||
) -> Arc<AssociatedTyValue<ChalkIr>> {
|
|
||||||
let impl_id = Impl::ClosureFnTraitImpl(data.clone()).to_chalk(db);
|
|
||||||
|
|
||||||
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
|
|
||||||
Expr::Lambda { args, .. } => args.len() as u16,
|
|
||||||
_ => {
|
|
||||||
log::warn!("closure for closure type {:?} not found", data);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let output_ty = Ty::Bound(num_args.into());
|
|
||||||
|
|
||||||
let fn_once_trait =
|
|
||||||
get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist");
|
|
||||||
|
|
||||||
let output_ty_id = db
|
|
||||||
.trait_data(fn_once_trait)
|
|
||||||
.associated_type_by_name(&name::OUTPUT_TYPE)
|
|
||||||
.expect("assoc ty value should not exist");
|
|
||||||
|
|
||||||
let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) };
|
|
||||||
|
|
||||||
let value = chalk_rust_ir::AssociatedTyValue {
|
|
||||||
associated_ty_id: output_ty_id.to_chalk(db),
|
|
||||||
impl_id,
|
|
||||||
value: make_binders(value_bound, num_args as usize + 1),
|
|
||||||
};
|
|
||||||
Arc::new(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_fn_trait(
|
|
||||||
db: &impl HirDatabase,
|
|
||||||
krate: CrateId,
|
|
||||||
fn_trait: super::FnTrait,
|
|
||||||
) -> Option<TraitId> {
|
|
||||||
let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
|
|
||||||
match target {
|
|
||||||
LangItemTarget::TraitId(t) => Some(t),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T {
|
fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T {
|
||||||
T::from_intern_id(InternId::from(chalk_id.index))
|
T::from_intern_id(InternId::from(chalk_id.index))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue