1
1
use crate :: clauses:: ClauseBuilder ;
2
2
use crate :: infer:: instantiate:: IntoBindersAndValue ;
3
+ use crate :: rust_ir:: WellKnownTrait ;
3
4
use crate :: { Interner , RustIrDatabase , TraitRef } ;
4
- use chalk_ir:: { ApplicationTy , Binders , Substitution , Ty , TyData , TypeName , VariableKinds } ;
5
+ use chalk_ir:: {
6
+ AliasTy , ApplicationTy , Binders , Normalize , ProjectionTy , Substitution , TraitId , Ty , TyData ,
7
+ TypeName , VariableKinds ,
8
+ } ;
5
9
6
- // Handles clauses for FnOnce/FnMut/Fn
10
+ /// Handles clauses for FnOnce/FnMut/Fn.
11
+ /// If `assoc_output` is `true`, we push a clause of the form
12
+ /// `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B) :- Implemented(fn(A) -> B as FnOnce<(A,)>`
13
+ ///
14
+ /// If `assoc_output` is `false`, we push a clause of the form
15
+ /// `Implemented(fn(A) -> B as FnOnce<(A,)>)`
7
16
pub fn add_fn_trait_program_clauses < I : Interner > (
8
17
db : & dyn RustIrDatabase < I > ,
9
18
builder : & mut ClauseBuilder < ' _ , I > ,
10
- trait_ref : & TraitRef < I > ,
19
+ trait_id : TraitId < I > ,
11
20
ty : & TyData < I > ,
21
+ assoc_output : bool ,
12
22
) {
13
23
match ty {
14
24
TyData :: Function ( fn_val) => {
15
25
let interner = db. interner ( ) ;
16
- let ( binders, sub) = fn_val. into_binders_and_value ( interner) ;
26
+ let ( binders, orig_sub) = fn_val. into_binders_and_value ( interner) ;
27
+ // Take all of the arguments except for the last one, which
28
+ // represents the return type
29
+ let arg_sub = Substitution :: from (
30
+ interner,
31
+ orig_sub. iter ( interner) . take ( orig_sub. len ( interner) - 1 ) ,
32
+ ) ;
33
+ let fn_output_ty = orig_sub
34
+ . at ( interner, orig_sub. len ( interner) - 1 )
35
+ . assert_ty_ref ( interner) ;
17
36
18
37
// We are constructing a reference to `FnOnce<Args>`, where
19
38
// `Args` is a tuple of the function's argument types
20
39
let tupled = Ty :: new (
21
40
interner,
22
41
TyData :: Apply ( ApplicationTy {
23
- name : TypeName :: Tuple ( sub . len ( interner) ) ,
24
- substitution : sub . clone ( ) ,
42
+ name : TypeName :: Tuple ( arg_sub . len ( interner) ) ,
43
+ substitution : arg_sub . clone ( ) ,
25
44
} ) ,
26
45
) ;
27
46
@@ -31,16 +50,42 @@ pub fn add_fn_trait_program_clauses<I: Interner>(
31
50
// Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef`
32
51
// of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>`
33
52
let new_trait_ref = TraitRef {
34
- trait_id : trait_ref . trait_id ,
35
- substitution : tupled_sub,
53
+ trait_id,
54
+ substitution : tupled_sub. clone ( ) ,
36
55
} ;
37
56
38
57
// Functions types come with a binder, which we push so
39
58
// that the `TraitRef` properly references any bound lifetimes
40
59
// (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`)
41
60
let bound_ref = Binders :: new ( VariableKinds :: from ( interner, binders) , new_trait_ref) ;
42
61
builder. push_binders ( & bound_ref, |this, inner_trait| {
43
- this. push_fact ( inner_trait) ;
62
+ if assoc_output {
63
+ //The `Output` type is defined on the `FnOnceTrait`
64
+ let fn_once = db. trait_datum ( trait_id) ;
65
+ assert_eq ! ( fn_once. well_known, Some ( WellKnownTrait :: FnOnceTrait ) ) ;
66
+ let assoc_types = & fn_once. associated_ty_ids ;
67
+ if assoc_types. len ( ) != 1 {
68
+ panic ! (
69
+ "FnOnce trait should have exactly one associated type, found {:?}" ,
70
+ assoc_types
71
+ ) ;
72
+ }
73
+
74
+ // Construct `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B)`
75
+ let assoc_output_ty = assoc_types[ 0 ] ;
76
+ let proj_ty = ProjectionTy {
77
+ associated_ty_id : assoc_output_ty,
78
+ substitution : tupled_sub,
79
+ } ;
80
+ let normalize = Normalize {
81
+ alias : AliasTy :: Projection ( proj_ty) ,
82
+ ty : fn_output_ty. clone ( ) ,
83
+ } ;
84
+
85
+ this. push_clause ( normalize, std:: iter:: once ( inner_trait) ) ;
86
+ } else {
87
+ this. push_fact ( inner_trait) ;
88
+ }
44
89
} )
45
90
}
46
91
_ => { }
0 commit comments