Skip to content

Commit 176207f

Browse files
committed
Extract built-in trait implementations to separate module
This untangles the builtin logic from the Chalk translation.
1 parent 3376c08 commit 176207f

File tree

3 files changed

+219
-127
lines changed

3 files changed

+219
-127
lines changed

crates/ra_hir_ty/src/traits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty,
1515
use self::chalk::{from_chalk, ToChalk};
1616

1717
pub(crate) mod chalk;
18+
mod builtin;
1819

1920
#[derive(Debug, Clone)]
2021
pub struct TraitSolver {
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! This module provides the built-in trait implementations, e.g. to make
2+
//! closures implement `Fn`.
3+
use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
4+
use hir_expand::name;
5+
use ra_db::CrateId;
6+
7+
use super::{AssocTyValue, Impl};
8+
use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor};
9+
10+
pub(super) struct BuiltinImplData {
11+
pub num_vars: usize,
12+
pub trait_ref: TraitRef,
13+
pub where_clauses: Vec<super::GenericPredicate>,
14+
pub assoc_ty_values: Vec<AssocTyValue>,
15+
}
16+
17+
pub(super) struct BuiltinImplAssocTyValueData {
18+
pub impl_: Impl,
19+
pub assoc_ty_id: TypeAliasId,
20+
pub num_vars: usize,
21+
pub value: Ty,
22+
}
23+
24+
pub(super) fn get_builtin_impls(
25+
db: &impl HirDatabase,
26+
krate: CrateId,
27+
ty: &Ty,
28+
trait_: TraitId,
29+
mut callback: impl FnMut(Impl),
30+
) {
31+
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
32+
for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
33+
{
34+
if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) {
35+
if trait_ == actual_trait {
36+
let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
37+
callback(Impl::ClosureFnTraitImpl(impl_));
38+
}
39+
}
40+
}
41+
}
42+
}
43+
44+
pub(super) fn impl_datum(
45+
db: &impl HirDatabase,
46+
krate: CrateId,
47+
impl_: Impl,
48+
) -> Option<BuiltinImplData> {
49+
match impl_ {
50+
Impl::ImplBlock(_) => unreachable!(),
51+
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
52+
}
53+
}
54+
55+
pub(super) fn associated_ty_value(
56+
db: &impl HirDatabase,
57+
krate: CrateId,
58+
data: AssocTyValue,
59+
) -> BuiltinImplAssocTyValueData {
60+
match data {
61+
AssocTyValue::TypeAlias(_) => unreachable!(),
62+
AssocTyValue::ClosureFnTraitImplOutput(data) => {
63+
closure_fn_trait_output_assoc_ty_value(db, krate, data)
64+
}
65+
}
66+
}
67+
68+
fn closure_fn_trait_impl_datum(
69+
db: &impl HirDatabase,
70+
krate: CrateId,
71+
data: super::ClosureFnTraitImplData,
72+
) -> Option<BuiltinImplData> {
73+
// for some closure |X, Y| -> Z:
74+
// impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
75+
76+
let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait
77+
78+
// validate FnOnce trait, since we need it in the assoc ty value definition
79+
// and don't want to return a valid value only to find out later that FnOnce
80+
// is broken
81+
let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?;
82+
let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?;
83+
84+
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
85+
Expr::Lambda { args, .. } => args.len() as u16,
86+
_ => {
87+
log::warn!("closure for closure type {:?} not found", data);
88+
0
89+
}
90+
};
91+
92+
let arg_ty = Ty::apply(
93+
TypeCtor::Tuple { cardinality: num_args },
94+
Substs::builder(num_args as usize).fill_with_bound_vars(0).build(),
95+
);
96+
let sig_ty = Ty::apply(
97+
TypeCtor::FnPtr { num_args },
98+
Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(),
99+
);
100+
101+
let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
102+
103+
let trait_ref = TraitRef {
104+
trait_: trait_.into(),
105+
substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
106+
};
107+
108+
let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone());
109+
110+
Some(BuiltinImplData {
111+
num_vars: num_args as usize + 1,
112+
trait_ref,
113+
where_clauses: Vec::new(),
114+
assoc_ty_values: vec![output_ty_id],
115+
})
116+
}
117+
118+
fn closure_fn_trait_output_assoc_ty_value(
119+
db: &impl HirDatabase,
120+
krate: CrateId,
121+
data: super::ClosureFnTraitImplData,
122+
) -> BuiltinImplAssocTyValueData {
123+
let impl_ = Impl::ClosureFnTraitImpl(data.clone());
124+
125+
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
126+
Expr::Lambda { args, .. } => args.len() as u16,
127+
_ => {
128+
log::warn!("closure for closure type {:?} not found", data);
129+
0
130+
}
131+
};
132+
133+
let output_ty = Ty::Bound(num_args.into());
134+
135+
let fn_once_trait =
136+
get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist");
137+
138+
let output_ty_id = db
139+
.trait_data(fn_once_trait)
140+
.associated_type_by_name(&name::OUTPUT_TYPE)
141+
.expect("assoc ty value should not exist");
142+
143+
BuiltinImplAssocTyValueData {
144+
impl_,
145+
assoc_ty_id: output_ty_id,
146+
num_vars: num_args as usize + 1,
147+
value: output_ty,
148+
}
149+
}
150+
151+
fn get_fn_trait(
152+
db: &impl HirDatabase,
153+
krate: CrateId,
154+
fn_trait: super::FnTrait,
155+
) -> Option<TraitId> {
156+
let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
157+
match target {
158+
LangItemTarget::TraitId(t) => Some(t),
159+
_ => None,
160+
}
161+
}

0 commit comments

Comments
 (0)