diff options
Diffstat (limited to 'vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs')
-rw-r--r-- | vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs b/vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs new file mode 100644 index 000000000..f2358bc73 --- /dev/null +++ b/vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs @@ -0,0 +1,155 @@ +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(), + }); + + // 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(<fn(A) -> 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<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, + ); + }); +} + +/// 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(<fn(A) -> 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<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| { + // 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, + ); + }); + } + _ => {} + } +} |