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