44//! These checks should only depend on StableMIR APIs. See other modules for tests that compare
55//! the result between StableMIR and internal APIs.
66use crate :: TestResult ;
7- use stable_mir;
7+ use stable_mir:: { self , mir, mir:: MirVisitor , ty} ;
8+ use std:: collections:: HashSet ;
89use std:: fmt:: Debug ;
9- use std:: hint :: black_box ;
10+ use std:: iter :: zip ;
1011
1112fn check_equal < T > ( val : T , expected : T , msg : & str ) -> TestResult
1213where
1314 T : Debug + PartialEq ,
1415{
1516 if val != expected {
1617 Err ( format ! (
17- "{}: \n Expected: {:?}\n Found: {:?}" ,
18+ "{}: \n Expected: ` {:?}` \n Found: ` {:?}` " ,
1819 msg, expected, val
1920 ) )
2021 } else {
@@ -34,11 +35,16 @@ pub fn check(val: bool, msg: String) -> TestResult {
3435pub fn test_entry_fn ( ) -> TestResult {
3536 let entry_fn = stable_mir:: entry_fn ( ) ;
3637 entry_fn. map_or ( Ok ( ( ) ) , |entry_fn| {
37- check_body ( entry_fn. body ( ) ) ;
38+ check_body ( & entry_fn. name ( ) , & entry_fn . body ( ) ) ? ;
3839 let all_items = stable_mir:: all_local_items ( ) ;
3940 check (
4041 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" ,
4248 )
4349 } )
4450}
@@ -49,7 +55,7 @@ pub fn test_all_fns() -> TestResult {
4955 for item in all_items {
5056 // Get body and iterate over items
5157 let body = item. body ( ) ;
52- check_body ( body) ;
58+ check_body ( & item . name ( ) , & body) ? ;
5359 }
5460 Ok ( ( ) )
5561}
@@ -75,7 +81,7 @@ pub fn test_traits() -> TestResult {
7581 {
7682 check (
7783 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:?}` " ) ,
7985 ) ?;
8086 }
8187 Ok ( ( ) )
@@ -85,30 +91,127 @@ pub fn test_crates() -> TestResult {
8591 for krate in stable_mir:: external_crates ( ) {
8692 check (
8793 stable_mir:: find_crates ( & krate. name . as_str ( ) ) . contains ( & krate) ,
88- format ! ( "Cannot find {krate:?}" ) ,
94+ format ! ( "Cannot find ` {krate:?}` " ) ,
8995 ) ?;
9096 }
9197
9298 let local = stable_mir:: local_crate ( ) ;
9399 check (
94100 stable_mir:: find_crates ( & local. name . as_str ( ) ) . contains ( & local) ,
95- format ! ( "Cannot find {local:?}" ) ,
101+ format ! ( "Cannot find local: ` {local:?}` " ) ,
96102 )
97103}
98104
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+
99147/// 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+ ) ?;
104169 }
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+ ) ?;
109175 }
110176
111177 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)
113216 }
114217}
0 commit comments