44//! This file contains functions related to codegenning MIR functions into gotoc
55
66use crate :: codegen_cprover_gotoc:: GotocCtx ;
7- use crate :: kani_middle:: attributes:: { extract_integer_argument , partition_kanitool_attributes } ;
7+ use crate :: kani_middle:: attributes:: { extract_harness_attributes , is_test_harness_closure } ;
88use cbmc:: goto_program:: { Expr , Stmt , Symbol } ;
99use cbmc:: InternString ;
10- use kani_metadata:: { CbmcSolver , HarnessMetadata } ;
11- use kani_queries:: UserInput ;
12- use rustc_ast:: { Attribute , MetaItemKind } ;
13- use rustc_hir:: def:: DefKind ;
14- use rustc_hir:: def_id:: DefId ;
10+ use kani_metadata:: { HarnessAttributes , HarnessMetadata } ;
1511use rustc_middle:: mir:: traversal:: reverse_postorder;
1612use rustc_middle:: mir:: { Body , HasLocalDecls , Local } ;
17- use rustc_middle:: ty:: layout:: FnAbiOf ;
1813use rustc_middle:: ty:: { self , Instance } ;
1914use std:: collections:: BTreeMap ;
20- use std:: convert:: TryInto ;
2115use std:: iter:: FromIterator ;
22- use std:: str:: FromStr ;
2316use tracing:: { debug, debug_span} ;
2417
2518/// Codegen MIR functions into gotoc
@@ -97,7 +90,7 @@ impl<'tcx> GotocCtx<'tcx> {
9790 let body = Stmt :: block ( stmts, loc) ;
9891 self . symbol_table . update_fn_declaration_with_definition ( & name, body) ;
9992
100- self . handle_kanitool_attributes ( ) ;
93+ self . record_kani_attributes ( ) ;
10194 self . record_test_harness_metadata ( ) ;
10295 }
10396 self . reset_current_fn ( ) ;
@@ -252,90 +245,6 @@ impl<'tcx> GotocCtx<'tcx> {
252245 self . reset_current_fn ( ) ;
253246 }
254247
255- /// Check that if an item is tagged with a proof_attribute, it is a valid harness.
256- fn check_proof_attribute ( & self , def_id : DefId , proof_attributes : Vec < & Attribute > ) {
257- assert ! ( !proof_attributes. is_empty( ) ) ;
258- let span = proof_attributes. first ( ) . unwrap ( ) . span ;
259- if proof_attributes. len ( ) > 1 {
260- self . tcx . sess . span_warn ( proof_attributes[ 0 ] . span , "Duplicate attribute" ) ;
261- }
262-
263- if self . tcx . def_kind ( def_id) != DefKind :: Fn {
264- self . tcx
265- . sess
266- . span_err ( span, "The kani::proof attribute can only be applied to functions." ) ;
267- } else if self . tcx . generics_of ( def_id) . requires_monomorphization ( self . tcx ) {
268- self . tcx
269- . sess
270- . span_err ( span, "The proof attribute cannot be applied to generic functions." ) ;
271- } else {
272- let instance = Instance :: mono ( self . tcx , def_id) ;
273- if !self . fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) . args . is_empty ( ) {
274- self . tcx
275- . sess
276- . span_err ( span, "Functions used as harnesses can not have any arguments." ) ;
277- }
278- }
279- }
280-
281- pub fn is_proof_harness ( & self , def_id : DefId ) -> bool {
282- let all_attributes = self . tcx . get_attrs_unchecked ( def_id) ;
283- let ( proof_attributes, _) = partition_kanitool_attributes ( all_attributes) ;
284- !proof_attributes. is_empty ( )
285- }
286-
287- /// Check that all attributes assigned to an item is valid.
288- /// Errors will be added to the session. Invoke self.tcx.sess.abort_if_errors() to terminate
289- /// the session in case of an error.
290- pub fn check_attributes ( & self , def_id : DefId ) {
291- let all_attributes = self . tcx . get_attrs_unchecked ( def_id) ;
292- let ( proof_attributes, other_attributes) = partition_kanitool_attributes ( all_attributes) ;
293- if !proof_attributes. is_empty ( ) {
294- self . check_proof_attribute ( def_id, proof_attributes) ;
295- } else if !other_attributes. is_empty ( ) {
296- self . tcx . sess . span_err (
297- other_attributes[ 0 ] . 1 . span ,
298- format ! (
299- "The {} attribute also requires the '#[kani::proof]' attribute" ,
300- other_attributes[ 0 ] . 0
301- )
302- . as_str ( ) ,
303- ) ;
304- }
305- }
306-
307- /// Does this `def_id` have `#[rustc_test_marker]`?
308- pub fn is_test_harness_description ( & self , def_id : DefId ) -> bool {
309- let attrs = self . tcx . get_attrs_unchecked ( def_id) ;
310-
311- self . tcx . sess . contains_name ( attrs, rustc_span:: symbol:: sym:: rustc_test_marker)
312- }
313- /// Is this the closure inside of a test description const (i.e. macro expanded from a `#[test]`)?
314- ///
315- /// We're trying to detect the closure (`||`) inside code like:
316- ///
317- /// ```ignore
318- /// #[rustc_test_marker]
319- /// pub const check_2: test::TestDescAndFn = test::TestDescAndFn {
320- /// desc: ...,
321- /// testfn: test::StaticTestFn(|| test::assert_test_result(check_2())),
322- /// };
323- /// ```
324- pub fn is_test_harness_closure ( & self , def_id : DefId ) -> bool {
325- if !def_id. is_local ( ) {
326- return false ;
327- }
328-
329- let local_def_id = def_id. expect_local ( ) ;
330- let hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
331-
332- // The parent item of the closure appears to reliably be the `const` declaration item.
333- let parent_id = self . tcx . hir ( ) . get_parent_item ( hir_id) ;
334- let parent_def_id = parent_id. to_def_id ( ) ;
335-
336- self . is_test_harness_description ( parent_def_id)
337- }
338-
339248 /// We record test harness information in kani-metadata, just like we record
340249 /// proof harness information. This is used to support e.g. cargo-kani assess.
341250 ///
@@ -345,64 +254,25 @@ impl<'tcx> GotocCtx<'tcx> {
345254 /// as it add asserts for tests that return `Result` types.
346255 fn record_test_harness_metadata ( & mut self ) {
347256 let def_id = self . current_fn ( ) . instance ( ) . def_id ( ) ;
348- if self . is_test_harness_closure ( def_id) {
349- let loc = self . codegen_span ( & self . current_fn ( ) . mir ( ) . span ) ;
350- self . test_harnesses . push ( HarnessMetadata {
351- pretty_name : self . current_fn ( ) . readable_name ( ) . to_owned ( ) ,
352- mangled_name : self . current_fn ( ) . name ( ) ,
353- crate_name : self . current_fn ( ) . krate ( ) ,
354- original_file : loc. filename ( ) . unwrap ( ) ,
355- original_start_line : loc. start_line ( ) . unwrap ( ) as usize ,
356- original_end_line : loc. end_line ( ) . unwrap ( ) as usize ,
357- solver : None ,
358- unwind_value : None ,
359- // We record the actual path after codegen before we dump the metadata into a file.
360- goto_file : None ,
361- } )
257+ if is_test_harness_closure ( self . tcx , def_id) {
258+ self . test_harnesses . push ( self . generate_metadata ( None ) )
362259 }
363260 }
364261
365262 /// This updates the goto context with any information that should be accumulated from a function's
366263 /// attributes.
367264 ///
368265 /// Handle all attributes i.e. `#[kani::x]` (which kani_macros translates to `#[kanitool::x]` for us to handle here)
369- fn handle_kanitool_attributes ( & mut self ) {
266+ fn record_kani_attributes ( & mut self ) {
370267 let def_id = self . current_fn ( ) . instance ( ) . def_id ( ) ;
371- let all_attributes = self . tcx . get_attrs_unchecked ( def_id) ;
372- let ( proof_attributes, other_attributes) = partition_kanitool_attributes ( all_attributes) ;
373- if !proof_attributes. is_empty ( ) {
374- self . create_proof_harness ( other_attributes) ;
375- }
376- }
377-
378- /// Create the proof harness struct using the handler methods for various attributes
379- fn create_proof_harness ( & mut self , other_attributes : Vec < ( String , & Attribute ) > ) {
380- let mut harness = self . default_kanitool_proof ( ) ;
381- for attr in other_attributes. iter ( ) {
382- match attr. 0 . as_str ( ) {
383- "solver" => self . handle_kanitool_solver ( attr. 1 , & mut harness) ,
384- "stub" => {
385- if !self . queries . get_stubbing_enabled ( ) {
386- self . tcx . sess . span_warn (
387- attr. 1 . span ,
388- "Stubbing is not enabled; attribute `kani::stub` will be ignored" ,
389- )
390- }
391- }
392- "unwind" => self . handle_kanitool_unwind ( attr. 1 , & mut harness) ,
393- _ => {
394- self . tcx . sess . span_err (
395- attr. 1 . span ,
396- format ! ( "Unsupported Annotation -> {}" , attr. 0 . as_str( ) ) . as_str ( ) ,
397- ) ;
398- }
399- }
268+ let attributes = extract_harness_attributes ( self . tcx , def_id) ;
269+ if attributes. is_some ( ) {
270+ self . proof_harnesses . push ( self . generate_metadata ( attributes) ) ;
400271 }
401- self . proof_harnesses . push ( harness) ;
402272 }
403273
404274 /// Create the default proof harness for the current function
405- fn default_kanitool_proof ( & mut self ) -> HarnessMetadata {
275+ fn generate_metadata ( & self , attributes : Option < HarnessAttributes > ) -> HarnessMetadata {
406276 let current_fn = self . current_fn ( ) ;
407277 let pretty_name = current_fn. readable_name ( ) . to_owned ( ) ;
408278 let mangled_name = current_fn. name ( ) ;
@@ -415,102 +285,9 @@ impl<'tcx> GotocCtx<'tcx> {
415285 original_file : loc. filename ( ) . unwrap ( ) ,
416286 original_start_line : loc. start_line ( ) . unwrap ( ) as usize ,
417287 original_end_line : loc. end_line ( ) . unwrap ( ) as usize ,
418- solver : None ,
419- unwind_value : None ,
288+ attributes : attributes. unwrap_or_default ( ) ,
420289 // We record the actual path after codegen before we dump the metadata into a file.
421290 goto_file : None ,
422291 }
423292 }
424-
425- /// Updates the proof harness with new unwind value
426- fn handle_kanitool_unwind ( & mut self , attr : & Attribute , harness : & mut HarnessMetadata ) {
427- // If some unwind value already exists, then the current unwind being handled is a duplicate
428- if harness. unwind_value . is_some ( ) {
429- self . tcx . sess . span_err ( attr. span , "Only one '#[kani::unwind]' allowed" ) ;
430- return ;
431- }
432- // Get Attribute value and if it's not none, assign it to the metadata
433- match extract_integer_argument ( attr) {
434- None => {
435- // There are no integers or too many arguments given to the attribute
436- self . tcx
437- . sess
438- . span_err ( attr. span , "Exactly one Unwind Argument as Integer accepted" ) ;
439- }
440- Some ( unwind_integer_value) => {
441- let val: Result < u32 , _ > = unwind_integer_value. try_into ( ) ;
442- if val. is_err ( ) {
443- self . tcx
444- . sess
445- . span_err ( attr. span , "Value above maximum permitted value - u32::MAX" ) ;
446- return ;
447- }
448- harness. unwind_value = Some ( val. unwrap ( ) ) ;
449- }
450- }
451- }
452-
453- /// Set the solver for this proof harness
454- fn handle_kanitool_solver ( & mut self , attr : & Attribute , harness : & mut HarnessMetadata ) {
455- // Make sure the solver is not already set
456- if harness. solver . is_some ( ) {
457- self . tcx
458- . sess
459- . span_err ( attr. span , "only one '#[kani::solver]' attribute is allowed per harness" ) ;
460- return ;
461- }
462- harness. solver = self . extract_solver_argument ( attr) ;
463- }
464-
465- fn extract_solver_argument ( & mut self , attr : & Attribute ) -> Option < CbmcSolver > {
466- // TODO: Argument validation should be done as part of the `kani_macros` crate
467- // <https://github.com/model-checking/kani/issues/2192>
468- const ATTRIBUTE : & str = "#[kani::solver]" ;
469- let invalid_arg_err = |attr : & Attribute | {
470- self . tcx . sess . span_err (
471- attr. span ,
472- format ! ( "invalid argument for `{ATTRIBUTE}` attribute, expected one of the supported solvers (e.g. `kissat`) or a SAT solver binary (e.g. `bin=\" <SAT_SOLVER_BINARY>\" `)" )
473- )
474- } ;
475-
476- let attr_args = attr. meta_item_list ( ) . unwrap ( ) ;
477- if attr_args. len ( ) != 1 {
478- self . tcx . sess . span_err (
479- attr. span ,
480- format ! (
481- "the `{ATTRIBUTE}` attribute expects a single argument. Got {} arguments." ,
482- attr_args. len( )
483- ) ,
484- ) ;
485- return None ;
486- }
487- let attr_arg = & attr_args[ 0 ] ;
488- let meta_item = attr_arg. meta_item ( ) ;
489- if meta_item. is_none ( ) {
490- invalid_arg_err ( attr) ;
491- return None ;
492- }
493- let meta_item = meta_item. unwrap ( ) ;
494- let ident = meta_item. ident ( ) . unwrap ( ) ;
495- let ident_str = ident. as_str ( ) ;
496- match & meta_item. kind {
497- MetaItemKind :: Word => {
498- let solver = CbmcSolver :: from_str ( ident_str) ;
499- match solver {
500- Ok ( solver) => Some ( solver) ,
501- Err ( _) => {
502- self . tcx . sess . span_err ( attr. span , format ! ( "unknown solver `{ident_str}`" ) ) ;
503- None
504- }
505- }
506- }
507- MetaItemKind :: NameValue ( lit) if ident_str == "bin" && lit. kind . is_str ( ) => {
508- Some ( CbmcSolver :: Binary ( lit. token_lit . symbol . to_string ( ) ) )
509- }
510- _ => {
511- invalid_arg_err ( attr) ;
512- None
513- }
514- }
515- }
516293}
0 commit comments