4
4
//! This file contains functions related to codegenning MIR functions into gotoc
5
5
6
6
use 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 } ;
8
8
use cbmc:: goto_program:: { Expr , Stmt , Symbol } ;
9
9
use 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 } ;
15
11
use rustc_middle:: mir:: traversal:: reverse_postorder;
16
12
use rustc_middle:: mir:: { Body , HasLocalDecls , Local } ;
17
- use rustc_middle:: ty:: layout:: FnAbiOf ;
18
13
use rustc_middle:: ty:: { self , Instance } ;
19
14
use std:: collections:: BTreeMap ;
20
- use std:: convert:: TryInto ;
21
15
use std:: iter:: FromIterator ;
22
- use std:: str:: FromStr ;
23
16
use tracing:: { debug, debug_span} ;
24
17
25
18
/// Codegen MIR functions into gotoc
@@ -97,7 +90,7 @@ impl<'tcx> GotocCtx<'tcx> {
97
90
let body = Stmt :: block ( stmts, loc) ;
98
91
self . symbol_table . update_fn_declaration_with_definition ( & name, body) ;
99
92
100
- self . handle_kanitool_attributes ( ) ;
93
+ self . record_kani_attributes ( ) ;
101
94
self . record_test_harness_metadata ( ) ;
102
95
}
103
96
self . reset_current_fn ( ) ;
@@ -252,90 +245,6 @@ impl<'tcx> GotocCtx<'tcx> {
252
245
self . reset_current_fn ( ) ;
253
246
}
254
247
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
-
339
248
/// We record test harness information in kani-metadata, just like we record
340
249
/// proof harness information. This is used to support e.g. cargo-kani assess.
341
250
///
@@ -345,64 +254,25 @@ impl<'tcx> GotocCtx<'tcx> {
345
254
/// as it add asserts for tests that return `Result` types.
346
255
fn record_test_harness_metadata ( & mut self ) {
347
256
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 ) )
362
259
}
363
260
}
364
261
365
262
/// This updates the goto context with any information that should be accumulated from a function's
366
263
/// attributes.
367
264
///
368
265
/// 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 ) {
370
267
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) ) ;
400
271
}
401
- self . proof_harnesses . push ( harness) ;
402
272
}
403
273
404
274
/// 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 {
406
276
let current_fn = self . current_fn ( ) ;
407
277
let pretty_name = current_fn. readable_name ( ) . to_owned ( ) ;
408
278
let mangled_name = current_fn. name ( ) ;
@@ -415,102 +285,9 @@ impl<'tcx> GotocCtx<'tcx> {
415
285
original_file : loc. filename ( ) . unwrap ( ) ,
416
286
original_start_line : loc. start_line ( ) . unwrap ( ) as usize ,
417
287
original_end_line : loc. end_line ( ) . unwrap ( ) as usize ,
418
- solver : None ,
419
- unwind_value : None ,
288
+ attributes : attributes. unwrap_or_default ( ) ,
420
289
// We record the actual path after codegen before we dump the metadata into a file.
421
290
goto_file : None ,
422
291
}
423
292
}
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
- }
516
293
}
0 commit comments