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( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, trait_id: TraitId, self_ty: Ty, arg_sub: Substitution, return_type: Ty, ) { 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(), }); // The `Output` type is defined on the `FnOnce` 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 ); // Constructs the alias. For `Fn`, for example, this would look like // `Normalize( B as FnOnce<(A,)>>::Output -> B)` 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( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, trait_id: TraitId, self_ty: Ty, inputs_and_output: Binders>, ) { 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, ); }); } /// Handles clauses for FnOnce/FnMut/Fn. /// If `self_ty` is a function, we push a clause of the form /// `fn(A1, A2, ..., AN) -> O: FnTrait<(A1, A2, ..., AN)>`, where `FnTrait` /// is the trait corresponding to `trait_id` (FnOnce/FnMut/Fn) /// /// If `trait_id` is `FnOnce`, we also push a clause for the output type of the form: /// `Normalize( B as FnOnce<(A,)>>::Output -> B)` /// We do not add the usual `Implemented(fn(A) -> b as FnOnce<(A,)>` clause /// as a condition, since we already called `push_fact` with it pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, self_ty: Ty, ) { 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| { // The last parameter represents the function return type 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, ); }); } _ => {} } }