4
4
//! These checks should only depend on StableMIR APIs. See other modules for tests that compare
5
5
//! the result between StableMIR and internal APIs.
6
6
use crate :: TestResult ;
7
- use stable_mir;
7
+ use stable_mir:: { self , mir, mir:: MirVisitor , ty} ;
8
+ use std:: collections:: HashSet ;
8
9
use std:: fmt:: Debug ;
9
- use std:: hint :: black_box ;
10
+ use std:: iter :: zip ;
10
11
11
12
fn check_equal < T > ( val : T , expected : T , msg : & str ) -> TestResult
12
13
where
13
14
T : Debug + PartialEq ,
14
15
{
15
16
if val != expected {
16
17
Err ( format ! (
17
- "{}: \n Expected: {:?}\n Found: {:?}" ,
18
+ "{}: \n Expected: ` {:?}` \n Found: ` {:?}` " ,
18
19
msg, expected, val
19
20
) )
20
21
} else {
@@ -34,11 +35,16 @@ pub fn check(val: bool, msg: String) -> TestResult {
34
35
pub fn test_entry_fn ( ) -> TestResult {
35
36
let entry_fn = stable_mir:: entry_fn ( ) ;
36
37
entry_fn. map_or ( Ok ( ( ) ) , |entry_fn| {
37
- check_body ( entry_fn. body ( ) ) ;
38
+ check_body ( & entry_fn. name ( ) , & entry_fn . body ( ) ) ? ;
38
39
let all_items = stable_mir:: all_local_items ( ) ;
39
40
check (
40
41
all_items. contains ( & entry_fn) ,
41
- format ! ( "Failed to find entry_fn `{:?}`" , entry_fn) ,
42
+ format ! ( "Failed to find entry_fn: `{:?}`" , entry_fn) ,
43
+ ) ?;
44
+ check_equal (
45
+ entry_fn. kind ( ) ,
46
+ stable_mir:: ItemKind :: Fn ,
47
+ "Entry must be a function" ,
42
48
)
43
49
} )
44
50
}
@@ -49,7 +55,7 @@ pub fn test_all_fns() -> TestResult {
49
55
for item in all_items {
50
56
// Get body and iterate over items
51
57
let body = item. body ( ) ;
52
- check_body ( body) ;
58
+ check_body ( & item . name ( ) , & body) ? ;
53
59
}
54
60
Ok ( ( ) )
55
61
}
@@ -75,7 +81,7 @@ pub fn test_traits() -> TestResult {
75
81
{
76
82
check (
77
83
all_traits. contains ( & trait_impl. value . def_id ) ,
78
- format ! ( "Failed to find trait definition {trait_impl:?}" ) ,
84
+ format ! ( "Failed to find trait definition: ` {trait_impl:?}` " ) ,
79
85
) ?;
80
86
}
81
87
Ok ( ( ) )
@@ -85,30 +91,127 @@ pub fn test_crates() -> TestResult {
85
91
for krate in stable_mir:: external_crates ( ) {
86
92
check (
87
93
stable_mir:: find_crates ( & krate. name . as_str ( ) ) . contains ( & krate) ,
88
- format ! ( "Cannot find {krate:?}" ) ,
94
+ format ! ( "Cannot find ` {krate:?}` " ) ,
89
95
) ?;
90
96
}
91
97
92
98
let local = stable_mir:: local_crate ( ) ;
93
99
check (
94
100
stable_mir:: find_crates ( & local. name . as_str ( ) ) . contains ( & local) ,
95
- format ! ( "Cannot find {local:?}" ) ,
101
+ format ! ( "Cannot find local: ` {local:?}` " ) ,
96
102
)
97
103
}
98
104
105
+ pub fn test_instances ( ) -> TestResult {
106
+ let all_items = stable_mir:: all_local_items ( ) ;
107
+ let mut queue = all_items
108
+ . iter ( )
109
+ . filter_map ( |item| {
110
+ ( item. kind ( ) == stable_mir:: ItemKind :: Fn )
111
+ . then ( || mir:: mono:: Instance :: try_from ( * item) . ok ( ) )
112
+ . flatten ( )
113
+ } )
114
+ . collect :: < Vec < _ > > ( ) ;
115
+
116
+ let mut visited = HashSet :: < mir:: mono:: Instance > :: default ( ) ;
117
+ while let Some ( next_item) = queue. pop ( ) {
118
+ if visited. insert ( next_item. clone ( ) ) {
119
+ let Some ( body) = next_item. body ( ) else {
120
+ continue ;
121
+ } ;
122
+ let visitor = check_body ( & next_item. mangled_name ( ) , & body) ?;
123
+ for term in visitor. terminators {
124
+ match & term. kind {
125
+ // We currently don't support Copy / Move `ty()` due to missing Place::ty().
126
+ // https://github.com/rust-lang/project-stable-mir/issues/49
127
+ mir:: TerminatorKind :: Call {
128
+ func : mir:: Operand :: Constant ( constant) ,
129
+ ..
130
+ } => {
131
+ match constant. ty ( ) . kind ( ) . rigid ( ) {
132
+ Some ( ty:: RigidTy :: FnDef ( def, args) ) => {
133
+ queue. push ( mir:: mono:: Instance :: resolve ( * def, & args) . unwrap ( ) ) ;
134
+ }
135
+ Some ( ty:: RigidTy :: FnPtr ( ..) ) => { /* ignore FnPtr for now */ }
136
+ ty => check ( false , format ! ( "Unexpected call: `{ty:?}`" ) ) ?,
137
+ }
138
+ }
139
+ _ => { /* Do nothing */ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ Ok ( ( ) )
145
+ }
146
+
99
147
/// Visit all local types, statements and terminator to ensure nothing crashes.
100
- fn check_body ( body : stable_mir:: mir:: Body ) {
101
- for bb in & body. blocks {
102
- for stable_mir:: mir:: Statement { kind, .. } in & bb. statements {
103
- black_box ( matches ! ( kind, stable_mir:: mir:: StatementKind :: Assign ( ..) ) ) ;
148
+ fn check_body ( name : & str , body : & mir:: Body ) -> Result < BodyVisitor , String > {
149
+ let mut visitor = BodyVisitor :: default ( ) ;
150
+ visitor. visit_body ( body) ;
151
+
152
+ check_equal (
153
+ body. blocks . len ( ) ,
154
+ visitor. statements . len ( ) ,
155
+ & format ! ( "Function `{name}`: Unexpected visited BB statements" ) ,
156
+ ) ?;
157
+ check_equal (
158
+ body. blocks . len ( ) ,
159
+ visitor. terminators . len ( ) ,
160
+ & format ! ( "Function `{name}`: Visited terminals" ) ,
161
+ ) ?;
162
+ for ( idx, bb) in body. blocks . iter ( ) . enumerate ( ) {
163
+ for ( stmt, visited_stmt) in zip ( & bb. statements , & visitor. statements [ idx] ) {
164
+ check_equal (
165
+ stmt,
166
+ visited_stmt,
167
+ & format ! ( "Function `{name}`: Visited statement" ) ,
168
+ ) ?;
104
169
}
105
- black_box ( matches ! (
106
- bb. terminator. kind,
107
- stable_mir:: mir:: TerminatorKind :: Goto { .. }
108
- ) ) ;
170
+ check_equal (
171
+ & bb. terminator ,
172
+ & visitor. terminators [ idx] ,
173
+ & format ! ( "Function `{name}`: Terminator" ) ,
174
+ ) ?;
109
175
}
110
176
111
177
for local in body. locals ( ) {
112
- black_box ( matches ! ( local. ty. kind( ) , stable_mir:: ty:: TyKind :: Alias ( ..) ) ) ;
178
+ if !visitor. types . contains ( & local. ty ) {
179
+ // Format fails due to unsupported CoroutineWitness.
180
+ // See https://github.com/rust-lang/project-stable-mir/issues/50.
181
+ check (
182
+ false ,
183
+ format ! ( "Function `{name}`: Missing type `{:?}`" , local. ty) ,
184
+ ) ?;
185
+ } ;
186
+ }
187
+ Ok ( visitor)
188
+ }
189
+
190
+ #[ derive( Debug , Default ) ]
191
+ struct BodyVisitor {
192
+ statements : Vec < Vec < mir:: Statement > > ,
193
+ terminators : Vec < mir:: Terminator > ,
194
+ types : HashSet < ty:: Ty > ,
195
+ }
196
+
197
+ impl mir:: MirVisitor for BodyVisitor {
198
+ fn visit_basic_block ( & mut self , bb : & mir:: BasicBlock ) {
199
+ assert_eq ! ( self . statements. len( ) , self . terminators. len( ) ) ;
200
+ self . statements . push ( vec ! [ ] ) ;
201
+ self . super_basic_block ( bb)
202
+ }
203
+ fn visit_statement ( & mut self , stmt : & mir:: Statement , loc : mir:: visit:: Location ) {
204
+ self . statements . last_mut ( ) . unwrap ( ) . push ( stmt. clone ( ) ) ;
205
+ self . super_statement ( stmt, loc)
206
+ }
207
+
208
+ fn visit_terminator ( & mut self , term : & mir:: Terminator , location : mir:: visit:: Location ) {
209
+ self . terminators . push ( term. clone ( ) ) ;
210
+ self . super_terminator ( term, location) ;
211
+ }
212
+
213
+ fn visit_ty ( & mut self , ty : & ty:: Ty , _location : mir:: visit:: Location ) {
214
+ self . types . insert ( * ty) ;
215
+ self . super_ty ( ty)
113
216
}
114
217
}
0 commit comments