@@ -447,6 +447,8 @@ impl<'tcx> GotocCtx<'tcx> {
447
447
e. member ( "case" , & self . symbol_table ) . cast_to ( self . codegen_ty ( res_ty) )
448
448
}
449
449
TagEncoding :: Niche { dataful_variant, niche_variants, niche_start } => {
450
+ // This code follows the logic in the cranelift codegen backend:
451
+ // https://github.com/rust-lang/rust/blob/05d22212e89588e7c443cc6b9bc0e4e02fdfbc8d/compiler/rustc_codegen_cranelift/src/discriminant.rs#L116
450
452
let offset = match & layout. fields {
451
453
FieldsShape :: Arbitrary { offsets, .. } => offsets[ 0 ] . bytes_usize ( ) ,
452
454
_ => unreachable ! ( "niche encoding must have arbitrary fields" ) ,
@@ -457,8 +459,7 @@ impl<'tcx> GotocCtx<'tcx> {
457
459
let relative_discr = if * niche_start == 0 {
458
460
niche_val
459
461
} else {
460
- // This should be a wrapping sub.
461
- niche_val. sub ( Expr :: int_constant ( * niche_start, discr_ty. clone ( ) ) )
462
+ wrapping_sub ( & niche_val, * niche_start)
462
463
} ;
463
464
let relative_max =
464
465
niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
@@ -467,8 +468,7 @@ impl<'tcx> GotocCtx<'tcx> {
467
468
} else {
468
469
relative_discr
469
470
. clone ( )
470
- . cast_to ( Type :: unsigned_int ( 64 ) )
471
- . le ( Expr :: int_constant ( relative_max, Type :: unsigned_int ( 64 ) ) )
471
+ . le ( Expr :: int_constant ( relative_max, relative_discr. typ ( ) . clone ( ) ) )
472
472
} ;
473
473
let niche_discr = {
474
474
let relative_discr = if relative_max == 0 {
@@ -1223,3 +1223,30 @@ impl<'tcx> GotocCtx<'tcx> {
1223
1223
}
1224
1224
}
1225
1225
}
1226
+
1227
+ /// Perform a wrapping subtraction of an Expr with a constant "expr - constant"
1228
+ /// where "-" is wrapping subtraction, i.e., the result should be interpreted as
1229
+ /// an unsigned value (2's complement).
1230
+ fn wrapping_sub ( expr : & Expr , constant : u128 ) -> Expr {
1231
+ // While the wrapping subtraction can be done through a regular subtraction
1232
+ // and then casting the result to an unsigned, doing so may result in CBMC
1233
+ // flagging the operation with a signed to unsigned overflow failure (see
1234
+ // https://github.com/model-checking/kani/issues/356).
1235
+ // To avoid those overflow failures, the wrapping subtraction operation is
1236
+ // computed as:
1237
+ // if expr >= constant {
1238
+ // // result is positive, so overflow may not occur
1239
+ // expr - constant
1240
+ // } else {
1241
+ // // compute the 2's complement to avoid overflow
1242
+ // expr - constant + 2^32
1243
+ // }
1244
+ let s64 = Type :: signed_int ( 64 ) ;
1245
+ let expr = expr. clone ( ) . cast_to ( s64. clone ( ) ) ;
1246
+ let twos_complement: i64 = u32:: MAX as i64 + 1 - i64:: try_from ( constant) . unwrap ( ) ;
1247
+ let constant = Expr :: int_constant ( constant, s64. clone ( ) ) ;
1248
+ expr. clone ( ) . ge ( constant. clone ( ) ) . ternary (
1249
+ expr. clone ( ) . sub ( constant) ,
1250
+ expr. plus ( Expr :: int_constant ( twos_complement, s64. clone ( ) ) ) ,
1251
+ )
1252
+ }
0 commit comments