use crate::clauses::ClauseBuilder;
use crate::rust_ir::{ClosureKind, FnDefInputsAndOutputDatum, WellKnownTrait};
use crate::{Interner, RustIrDatabase, TraitRef};
use chalk_ir::cast::Cast;
use chalk_ir::{
AliasTy, Binders, Normalize, ProjectionTy, Safety, Substitution, TraitId, Ty, TyKind,
};
fn push_clauses<I: Interner>(
db: &dyn RustIrDatabase<I>,
builder: &mut ClauseBuilder<'_, I>,
well_known: WellKnownTrait,
trait_id: TraitId<I>,
self_ty: Ty<I>,
arg_sub: Substitution<I>,
return_type: Ty<I>,
) {
let interner = db.interner();
let tupled = TyKind::Tuple(arg_sub.len(interner), arg_sub).intern(interner);
let substitution =
Substitution::from_iter(interner, &[self_ty.cast(interner), tupled.cast(interner)]);
builder.push_fact(TraitRef {
trait_id,
substitution: substitution.clone(),
});
if let WellKnownTrait::FnOnce = well_known {
let trait_datum = db.trait_datum(trait_id);
assert_eq!(
trait_datum.associated_ty_ids.len(),
1,
"FnOnce trait should have exactly one associated type, found {:?}",
trait_datum.associated_ty_ids
);
let output_id = trait_datum.associated_ty_ids[0];
let alias = AliasTy::Projection(ProjectionTy {
associated_ty_id: output_id,
substitution,
});
builder.push_fact(Normalize {
alias,
ty: return_type,
});
}
}
fn push_clauses_for_apply<I: Interner>(
db: &dyn RustIrDatabase<I>,
builder: &mut ClauseBuilder<'_, I>,
well_known: WellKnownTrait,
trait_id: TraitId<I>,
self_ty: Ty<I>,
inputs_and_output: Binders<FnDefInputsAndOutputDatum<I>>,
) {
let interner = db.interner();
builder.push_binders(inputs_and_output, |builder, inputs_and_output| {
let arg_sub = inputs_and_output
.argument_types
.iter()
.cloned()
.map(|ty| ty.cast(interner));
let arg_sub = Substitution::from_iter(interner, arg_sub);
let output_ty = inputs_and_output.return_type;
push_clauses(
db, builder, well_known, trait_id, self_ty, arg_sub, output_ty,
);
});
}
pub fn add_fn_trait_program_clauses<I: Interner>(
db: &dyn RustIrDatabase<I>,
builder: &mut ClauseBuilder<'_, I>,
well_known: WellKnownTrait,
self_ty: Ty<I>,
) {
let interner = db.interner();
let trait_id = db.well_known_trait_id(well_known).unwrap();
match self_ty.kind(interner) {
TyKind::FnDef(fn_def_id, substitution) => {
let fn_def_datum = builder.db.fn_def_datum(*fn_def_id);
if fn_def_datum.sig.safety == Safety::Safe && !fn_def_datum.sig.variadic {
let bound = fn_def_datum
.binders
.clone()
.substitute(builder.interner(), &substitution);
push_clauses_for_apply(
db,
builder,
well_known,
trait_id,
self_ty,
bound.inputs_and_output,
);
}
}
TyKind::Closure(closure_id, substitution) => {
let closure_kind = db.closure_kind(*closure_id, substitution);
let trait_matches = matches!(
(well_known, closure_kind),
(WellKnownTrait::Fn, ClosureKind::Fn)
| (WellKnownTrait::FnMut, ClosureKind::FnMut | ClosureKind::Fn)
| (WellKnownTrait::FnOnce, _)
);
if !trait_matches {
return;
}
let closure_inputs_and_output = db.closure_inputs_and_output(*closure_id, substitution);
push_clauses_for_apply(
db,
builder,
well_known,
trait_id,
self_ty,
closure_inputs_and_output,
);
}
TyKind::Function(fn_val) if fn_val.sig.safety == Safety::Safe && !fn_val.sig.variadic => {
let bound_ref = fn_val.clone().into_binders(interner);
builder.push_binders(bound_ref, |builder, orig_sub| {
let (arg_sub, fn_output_ty) = orig_sub
.0
.as_slice(interner)
.split_at(orig_sub.0.len(interner) - 1);
let arg_sub = Substitution::from_iter(interner, arg_sub);
let output_ty = fn_output_ty[0].assert_ty_ref(interner).clone();
push_clauses(
db,
builder,
well_known,
trait_id,
self_ty.clone(),
arg_sub,
output_ty,
);
});
}
_ => {}
}
}