@@ -12,25 +12,107 @@ use rustc_data_structures::fx::FxHashMap;
12
12
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
13
13
use rustc_middle:: { mir:: Body , ty:: TyCtxt } ;
14
14
15
+ /// Returns the name of the stub for the function/method identified by `def_id`,
16
+ /// and `None` if the function/method is not stubbed.
17
+ pub fn get_stub_name ( tcx : TyCtxt , def_id : DefId ) -> Option < String > {
18
+ let mapping = get_stub_mapping ( tcx) ?;
19
+ let name = tcx. def_path_str ( def_id) ;
20
+ mapping. get ( & name) . map ( String :: clone)
21
+ }
22
+
15
23
/// Returns the new body of a function/method if it has been stubbed out;
16
- /// otherwise, returns `None`.
17
- pub fn transform ( tcx : TyCtxt , def_id : DefId ) -> Option < & Body > {
18
- if let Some ( mapping) = get_stub_mapping ( tcx) {
19
- let name = tcx. def_path_str ( def_id) ;
20
- if let Some ( replacement) = mapping. get ( & name) {
21
- if let Some ( replacement_id) = get_def_id ( tcx, replacement) {
22
- // TODO: We need to perform validation here (e.g., check that
23
- // the replacement is compatible with the original function).
24
- // <https://github.com/model-checking/kani/issues/1892>
25
- let new_body = tcx. optimized_mir ( replacement_id) . clone ( ) ;
26
- return Some ( tcx. arena . alloc ( new_body) ) ;
24
+ /// otherwise, returns the old body.
25
+ pub fn transform < ' tcx > (
26
+ tcx : TyCtxt < ' tcx > ,
27
+ def_id : DefId ,
28
+ old_body : & ' tcx Body < ' tcx > ,
29
+ ) -> & ' tcx Body < ' tcx > {
30
+ if let Some ( replacement) = get_stub_name ( tcx, def_id) {
31
+ if let Some ( replacement_id) = get_def_id ( tcx, & replacement) {
32
+ let new_body = tcx. optimized_mir ( replacement_id) . clone ( ) ;
33
+ if check_compatibility ( tcx, def_id, old_body, replacement_id, & new_body) {
34
+ return tcx. arena . alloc ( new_body) ;
35
+ }
36
+ } else {
37
+ tcx. sess
38
+ . span_err ( tcx. def_span ( def_id) , format ! ( "unable to find stub: `{}`" , replacement) ) ;
39
+ } ;
40
+ }
41
+ old_body
42
+ }
43
+
44
+ /// Checks whether the stub is compatible with the original function/method: do
45
+ /// the arities and types (of the parameters and return values) match up? This
46
+ /// does **NOT** check whether the type variables are constrained to implement
47
+ /// the same traits; trait mismatches are checked during monomorphization.
48
+ fn check_compatibility < ' a , ' tcx > (
49
+ tcx : TyCtxt ,
50
+ old_def_id : DefId ,
51
+ old_body : & ' a Body < ' tcx > ,
52
+ stub_def_id : DefId ,
53
+ stub_body : & ' a Body < ' tcx > ,
54
+ ) -> bool {
55
+ // Check whether the arities match.
56
+ if old_body. arg_count != stub_body. arg_count {
57
+ tcx. sess . span_err (
58
+ tcx. def_span ( stub_def_id) ,
59
+ format ! (
60
+ "arity mismatch: original function/method `{}` takes {} argument(s), stub `{}` takes {}" ,
61
+ tcx. def_path_str( old_def_id) ,
62
+ old_body. arg_count,
63
+ tcx. def_path_str( stub_def_id) ,
64
+ stub_body. arg_count
65
+ ) ,
66
+ ) ;
67
+ return false ;
68
+ }
69
+ // Check whether the numbers of generic parameters match.
70
+ let old_num_generics = tcx. generics_of ( old_def_id) . count ( ) ;
71
+ let stub_num_generics = tcx. generics_of ( stub_def_id) . count ( ) ;
72
+ if old_num_generics != stub_num_generics {
73
+ tcx. sess . span_err (
74
+ tcx. def_span ( stub_def_id) ,
75
+ format ! (
76
+ "mismatch in the number of generic parameters: original function/method `{}` takes {} generic parameters(s), stub `{}` takes {}" ,
77
+ tcx. def_path_str( old_def_id) ,
78
+ old_num_generics,
79
+ tcx. def_path_str( stub_def_id) ,
80
+ stub_num_generics
81
+ ) ,
82
+ ) ;
83
+ return false ;
84
+ }
85
+ // Check whether the types match. Index 0 refers to the returned value,
86
+ // indices [1, `arg_count`] refer to the parameters.
87
+ // TODO: We currently force generic parameters in the stub to have exactly
88
+ // the same names as their counterparts in the original function/method;
89
+ // instead, we should be checking for the equivalence of types up to the
90
+ // renaming of generic parameters.
91
+ // <https://github.com/model-checking/kani/issues/1953>
92
+ let mut matches = true ;
93
+ for i in 0 ..=old_body. arg_count {
94
+ let old_arg = old_body. local_decls . get ( i. into ( ) ) . unwrap ( ) ;
95
+ let new_arg = stub_body. local_decls . get ( i. into ( ) ) . unwrap ( ) ;
96
+ if old_arg. ty != new_arg. ty {
97
+ let prefix = if i == 0 {
98
+ "return type differs" . to_string ( )
27
99
} else {
28
- tcx. sess
29
- . span_err ( tcx. def_span ( def_id) , format ! ( "Unable to find stub: {replacement}" ) ) ;
100
+ format ! ( "type of parameter {} differs" , i - 1 )
30
101
} ;
102
+ tcx. sess . span_err (
103
+ new_arg. source_info . span ,
104
+ format ! (
105
+ "{prefix}: stub `{}` has type `{}` where original function/method `{}` has type `{}`" ,
106
+ tcx. def_path_str( stub_def_id) ,
107
+ new_arg. ty,
108
+ tcx. def_path_str( old_def_id) ,
109
+ old_arg. ty
110
+ ) ,
111
+ ) ;
112
+ matches = false ;
31
113
}
32
114
}
33
- None
115
+ matches
34
116
}
35
117
36
118
/// The prefix we will use when serializing the stub mapping as a rustc argument.
0 commit comments