summaryrefslogtreecommitdiffstats
path: root/vendor/chalk-solve-0.87.0/src/clauses/builtin_traits/fn_family.rs
diff options
context:
space:
mode:
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.rs155
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,
+ );
+ });
+ }
+ _ => {}
+ }
+}