]> git.proxmox.com Git - rustc.git/blob - vendor/chalk-solve/src/clauses/builtin_traits/fn_family.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / vendor / chalk-solve / src / clauses / builtin_traits / fn_family.rs
1 use crate::clauses::ClauseBuilder;
2 use crate::infer::instantiate::IntoBindersAndValue;
3 use crate::rust_ir::{ClosureKind, FnDefInputsAndOutputDatum, WellKnownTrait};
4 use crate::{Interner, RustIrDatabase, TraitRef};
5 use chalk_ir::cast::Cast;
6 use chalk_ir::{
7 AliasTy, Binders, Floundered, Normalize, ProjectionTy, Safety, Substitution, TraitId, Ty,
8 TyKind, VariableKinds,
9 };
10
11 fn push_clauses<I: Interner>(
12 db: &dyn RustIrDatabase<I>,
13 builder: &mut ClauseBuilder<'_, I>,
14 well_known: WellKnownTrait,
15 trait_id: TraitId<I>,
16 self_ty: Ty<I>,
17 arg_sub: Substitution<I>,
18 return_type: Ty<I>,
19 ) {
20 let interner = db.interner();
21 let tupled = TyKind::Tuple(arg_sub.len(interner), arg_sub).intern(interner);
22 let substitution =
23 Substitution::from_iter(interner, &[self_ty.cast(interner), tupled.cast(interner)]);
24 builder.push_fact(TraitRef {
25 trait_id,
26 substitution: substitution.clone(),
27 });
28
29 // The `Output` type is defined on the `FnOnce`
30 if let WellKnownTrait::FnOnce = well_known {
31 let trait_datum = db.trait_datum(trait_id);
32 assert_eq!(
33 trait_datum.associated_ty_ids.len(),
34 1,
35 "FnOnce trait should have exactly one associated type, found {:?}",
36 trait_datum.associated_ty_ids
37 );
38 // Constructs the alias. For `Fn`, for example, this would look like
39 // `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B)`
40 let output_id = trait_datum.associated_ty_ids[0];
41 let alias = AliasTy::Projection(ProjectionTy {
42 associated_ty_id: output_id,
43 substitution,
44 });
45 builder.push_fact(Normalize {
46 alias,
47 ty: return_type,
48 });
49 }
50 }
51
52 fn push_clauses_for_apply<I: Interner>(
53 db: &dyn RustIrDatabase<I>,
54 builder: &mut ClauseBuilder<'_, I>,
55 well_known: WellKnownTrait,
56 trait_id: TraitId<I>,
57 self_ty: Ty<I>,
58 inputs_and_output: &Binders<FnDefInputsAndOutputDatum<I>>,
59 ) {
60 let interner = db.interner();
61 builder.push_binders(inputs_and_output, |builder, inputs_and_output| {
62 let arg_sub = inputs_and_output
63 .argument_types
64 .iter()
65 .cloned()
66 .map(|ty| ty.cast(interner));
67 let arg_sub = Substitution::from_iter(interner, arg_sub);
68 let output_ty = inputs_and_output.return_type;
69
70 push_clauses(
71 db, builder, well_known, trait_id, self_ty, arg_sub, output_ty,
72 );
73 });
74 }
75
76 /// Handles clauses for FnOnce/FnMut/Fn.
77 /// If `self_ty` is a function, we push a clause of the form
78 /// `fn(A1, A2, ..., AN) -> O: FnTrait<(A1, A2, ..., AN)>`, where `FnTrait`
79 /// is the trait corresponding to `trait_id` (FnOnce/FnMut/Fn)
80 ///
81 /// If `trait_id` is `FnOnce`, we also push a clause for the output type of the form:
82 /// `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B)`
83 /// We do not add the usual `Implemented(fn(A) -> b as FnOnce<(A,)>` clause
84 /// as a condition, since we already called `push_fact` with it
85 pub fn add_fn_trait_program_clauses<I: Interner>(
86 db: &dyn RustIrDatabase<I>,
87 builder: &mut ClauseBuilder<'_, I>,
88 well_known: WellKnownTrait,
89 self_ty: Ty<I>,
90 ) -> Result<(), Floundered> {
91 let interner = db.interner();
92 let trait_id = db.well_known_trait_id(well_known).unwrap();
93
94 match self_ty.kind(interner) {
95 TyKind::FnDef(fn_def_id, substitution) => {
96 let fn_def_datum = builder.db.fn_def_datum(*fn_def_id);
97 if fn_def_datum.sig.safety == Safety::Safe && !fn_def_datum.sig.variadic {
98 let bound = fn_def_datum
99 .binders
100 .substitute(builder.interner(), &substitution);
101 push_clauses_for_apply(
102 db,
103 builder,
104 well_known,
105 trait_id,
106 self_ty,
107 &bound.inputs_and_output,
108 );
109 }
110 Ok(())
111 }
112 TyKind::Closure(closure_id, substitution) => {
113 let closure_kind = db.closure_kind(*closure_id, &substitution);
114 let trait_matches = match (well_known, closure_kind) {
115 (WellKnownTrait::Fn, ClosureKind::Fn) => true,
116 (WellKnownTrait::FnMut, ClosureKind::FnMut)
117 | (WellKnownTrait::FnMut, ClosureKind::Fn) => true,
118 (WellKnownTrait::FnOnce, _) => true,
119 _ => false,
120 };
121 if !trait_matches {
122 return Ok(());
123 }
124 let closure_inputs_and_output =
125 db.closure_inputs_and_output(*closure_id, &substitution);
126 push_clauses_for_apply(
127 db,
128 builder,
129 well_known,
130 trait_id,
131 self_ty,
132 &closure_inputs_and_output,
133 );
134 Ok(())
135 }
136 TyKind::Function(fn_val) if fn_val.sig.safety == Safety::Safe && !fn_val.sig.variadic => {
137 let (binders, orig_sub) = fn_val.into_binders_and_value(interner);
138 let bound_ref = Binders::new(VariableKinds::from_iter(interner, binders), orig_sub);
139 builder.push_binders(&bound_ref, |builder, orig_sub| {
140 // The last parameter represents the function return type
141 let (arg_sub, fn_output_ty) = orig_sub
142 .as_slice(interner)
143 .split_at(orig_sub.len(interner) - 1);
144 let arg_sub = Substitution::from_iter(interner, arg_sub);
145 let output_ty = fn_output_ty[0].assert_ty_ref(interner).clone();
146
147 push_clauses(
148 db,
149 builder,
150 well_known,
151 trait_id,
152 self_ty.clone(),
153 arg_sub,
154 output_ty,
155 );
156 });
157 Ok(())
158 }
159 // Function traits are non-enumerable
160 TyKind::InferenceVar(..) | TyKind::Alias(..) => Err(Floundered),
161 _ => Ok(()),
162 }
163 }