8
8
use super :: typ:: TypeExt ;
9
9
use crate :: codegen_cprover_gotoc:: utils:: slice_fat_ptr;
10
10
use crate :: codegen_cprover_gotoc:: GotocCtx ;
11
- use cbmc:: goto_program:: { Expr , Type } ;
11
+ use cbmc:: goto_program:: { Expr , Location , Type } ;
12
12
use rustc_hir:: Mutability ;
13
13
use rustc_middle:: ty:: layout:: LayoutOf ;
14
14
use rustc_middle:: {
@@ -26,6 +26,19 @@ pub enum TypeOrVariant<'tcx> {
26
26
Variant ( & ' tcx VariantDef ) ,
27
27
}
28
28
29
+ /// A struct for storing the data for passing to `codegen_unimplemented`
30
+ #[ derive( Debug ) ]
31
+ pub struct UnimplementedData {
32
+ /// The specific operation that is not supported
33
+ pub operation : String ,
34
+ /// URL for issue on Kani github page
35
+ pub bug_url : String ,
36
+ /// The resulting goto type of the operation
37
+ pub goto_type : Type ,
38
+ /// Location of operation
39
+ pub loc : Location ,
40
+ }
41
+
29
42
/// Relevent information about a projected place (i.e. an lvalue).
30
43
#[ derive( Debug ) ]
31
44
pub struct ProjectedPlace < ' tcx > {
@@ -290,9 +303,10 @@ impl<'tcx> GotocCtx<'tcx> {
290
303
/// the return value is the expression after.
291
304
fn codegen_projection (
292
305
& mut self ,
293
- before : ProjectedPlace < ' tcx > ,
306
+ before : Result < ProjectedPlace < ' tcx > , UnimplementedData > ,
294
307
proj : ProjectionElem < Local , Ty < ' tcx > > ,
295
- ) -> ProjectedPlace < ' tcx > {
308
+ ) -> Result < ProjectedPlace < ' tcx > , UnimplementedData > {
309
+ let before = before?;
296
310
match proj {
297
311
ProjectionElem :: Deref => {
298
312
let base_type = before. mir_typ ( ) ;
@@ -360,18 +374,18 @@ impl<'tcx> GotocCtx<'tcx> {
360
374
_ => inner_goto_expr. dereference ( ) ,
361
375
} ;
362
376
let typ = TypeOrVariant :: Type ( inner_mir_typ) ;
363
- ProjectedPlace :: new ( expr, typ, fat_ptr_goto_expr, fat_ptr_mir_typ, self )
377
+ Ok ( ProjectedPlace :: new ( expr, typ, fat_ptr_goto_expr, fat_ptr_mir_typ, self ) )
364
378
}
365
379
ProjectionElem :: Field ( f, t) => {
366
380
let typ = TypeOrVariant :: Type ( t) ;
367
381
let expr = self . codegen_field ( before. goto_expr , before. mir_typ_or_variant , & f) ;
368
- ProjectedPlace :: new (
382
+ Ok ( ProjectedPlace :: new (
369
383
expr,
370
384
typ,
371
385
before. fat_ptr_goto_expr ,
372
386
before. fat_ptr_mir_typ ,
373
387
self ,
374
- )
388
+ ) )
375
389
}
376
390
ProjectionElem :: Index ( i) => {
377
391
let base_type = before. mir_typ ( ) ;
@@ -385,21 +399,20 @@ impl<'tcx> GotocCtx<'tcx> {
385
399
ty:: Slice ( ..) => before. goto_expr . index ( idxe) ,
386
400
_ => unreachable ! ( "must index an array" ) ,
387
401
} ;
388
- ProjectedPlace :: new (
402
+ Ok ( ProjectedPlace :: new (
389
403
expr,
390
404
typ,
391
405
before. fat_ptr_goto_expr ,
392
406
before. fat_ptr_mir_typ ,
393
407
self ,
394
- )
408
+ ) )
395
409
}
396
410
ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
397
- self . codegen_constant_index ( before, offset, min_length, from_end)
411
+ Ok ( self . codegen_constant_index ( before, offset, min_length, from_end) )
398
412
}
399
413
// Best effort to codegen subslice projection.
400
- // This is known to fail with a CBMC invariant violation
401
- // in some cases. Full support to be added in
402
- // https://github.com/model-checking/kani/issues/357
414
+ // Full support to be added in
415
+ // https://github.com/model-checking/kani/issues/707
403
416
ProjectionElem :: Subslice { from, to, from_end } => {
404
417
// https://rust-lang.github.io/rfcs/2359-subslice-pattern-syntax.html
405
418
match before. mir_typ ( ) . kind ( ) {
@@ -425,13 +438,13 @@ impl<'tcx> GotocCtx<'tcx> {
425
438
let from_elem = before. goto_expr . index ( index) ;
426
439
let data = from_elem. address_of ( ) ;
427
440
let fat_ptr = slice_fat_ptr ( goto_type, data, len, & self . symbol_table ) ;
428
- ProjectedPlace :: new (
441
+ Ok ( ProjectedPlace :: new (
429
442
fat_ptr. clone ( ) ,
430
443
TypeOrVariant :: Type ( ptr_typ) ,
431
444
Some ( fat_ptr) ,
432
445
Some ( ptr_typ) ,
433
446
self ,
434
- )
447
+ ) )
435
448
}
436
449
_ => unreachable ! ( "must be array or slice" ) ,
437
450
}
@@ -456,13 +469,13 @@ impl<'tcx> GotocCtx<'tcx> {
456
469
TagEncoding :: Niche { .. } => before. goto_expr ,
457
470
} ,
458
471
} ;
459
- ProjectedPlace :: new (
472
+ Ok ( ProjectedPlace :: new (
460
473
expr,
461
474
typ,
462
475
before. fat_ptr_goto_expr ,
463
476
before. fat_ptr_mir_typ ,
464
477
self ,
465
- )
478
+ ) )
466
479
}
467
480
_ => unreachable ! ( "it's a bug to reach here!" ) ,
468
481
}
@@ -476,15 +489,18 @@ impl<'tcx> GotocCtx<'tcx> {
476
489
/// This function follows the MIR projection to get the final useable lvalue.
477
490
/// If it passes through a fat pointer along the way, it stores info about it,
478
491
/// which can be useful in reconstructing fat pointer operations.
479
- pub fn codegen_place ( & mut self , p : & Place < ' tcx > ) -> ProjectedPlace < ' tcx > {
492
+ pub fn codegen_place (
493
+ & mut self ,
494
+ p : & Place < ' tcx > ,
495
+ ) -> Result < ProjectedPlace < ' tcx > , UnimplementedData > {
480
496
debug ! ( place=?p, "codegen_place" ) ;
481
497
let initial_expr = self . codegen_local ( p. local ) ;
482
498
let initial_typ = TypeOrVariant :: Type ( self . local_ty ( p. local ) ) ;
483
499
debug ! ( ?initial_typ, ?initial_expr, "codegen_place" ) ;
484
500
let initial_projection = ProjectedPlace :: new ( initial_expr, initial_typ, None , None , self ) ;
485
501
p. projection
486
502
. iter ( )
487
- . fold ( initial_projection, |accum, proj| self . codegen_projection ( accum, proj) )
503
+ . fold ( Ok ( initial_projection) , |accum, proj| self . codegen_projection ( accum, proj) )
488
504
}
489
505
490
506
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.ProjectionElem.html
@@ -550,3 +566,40 @@ impl<'tcx> GotocCtx<'tcx> {
550
566
arr. member ( "0" , & self . symbol_table ) . index_array ( idx)
551
567
}
552
568
}
569
+
570
+ /// A convenience macro that unwraps a `Result<ProjectPlace<'tcx>,
571
+ /// Err<UnimplementedData>` if it is `Ok` and returns an `codegen_unimplemented`
572
+ /// expression otherwise.
573
+ /// Note that this macro affects the control flow since it calls `return`
574
+ #[ macro_export]
575
+ macro_rules! unwrap_or_return_codegen_unimplemented {
576
+ ( $ctx: expr, $pp_result: expr) => { {
577
+ if let Err ( err) = $pp_result {
578
+ return $ctx. codegen_unimplemented(
579
+ err. operation. as_str( ) ,
580
+ err. goto_type,
581
+ err. loc,
582
+ err. bug_url. as_str( ) ,
583
+ ) ;
584
+ }
585
+ $pp_result. unwrap( )
586
+ } } ;
587
+ }
588
+
589
+ /// Same as the above macro, but returns a goto program `Stmt` instead
590
+ #[ macro_export]
591
+ macro_rules! unwrap_or_return_codegen_unimplemented_stmt {
592
+ ( $ctx: expr, $pp_result: expr) => { {
593
+ if let Err ( err) = $pp_result {
594
+ return $ctx
595
+ . codegen_unimplemented(
596
+ err. operation. as_str( ) ,
597
+ err. goto_type,
598
+ err. loc,
599
+ err. bug_url. as_str( ) ,
600
+ )
601
+ . as_stmt( err. loc) ;
602
+ }
603
+ $pp_result. unwrap( )
604
+ } } ;
605
+ }
0 commit comments