Skip to content

Commit a20437a

Browse files
authored
Avoid mismatch when generating structs that represent scalar data but also include ZSTs (rust-lang#2794)
This change considers another case for the code generation of scalar data. In particular, when the expected type is an ADT, we previously considered two cases: either there is no field or there is one. But in cases where the ADT includes a ZST, the ADT will contain multiple fields: one associated to the scalar data, and other fields associated to the ZSTs. In those cases, we ended up crashing as in rust-lang#2680: ```rust error: internal compiler error: Kani unexpectedly panicked at panicked at cprover_bindings/src/goto_program/expr.rs:804:9: assertion failed: `(left == right)` left: `2`, right: `1`: Error in struct_expr; mismatch in number of fields and values. StructTag("tag-_161424092592971241517604180463813633314") [Expr { value: IntConstant(0), typ: Unsignedbv { width: 8 }, location: None, size_of_annotation: None }]. thread 'rustc' panicked at cprover_bindings/src/goto_program/expr.rs:804:9: assertion failed: `(left == right)` left: `2`, right: `1`: Error in struct_expr; mismatch in number of fields and values. StructTag("tag-_161424092592971241517604180463813633314") [Expr { value: IntConstant(0), typ: Unsignedbv { width: 8 }, location: None, size_of_annotation: None }] ``` With the changes in this PR, that's no longer the case. Resolves rust-lang#2364 Resolves rust-lang#2680
1 parent dba1674 commit a20437a

File tree

7 files changed

+98
-26
lines changed

7 files changed

+98
-26
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -303,38 +303,39 @@ impl<'tcx> GotocCtx<'tcx> {
303303
}
304304
}
305305
(Scalar::Int(_), ty::Adt(adt, subst)) => {
306-
if adt.is_struct() || adt.is_union() {
307-
// in this case, we must have a one variant ADT. there are two cases
308-
let variant = &adt.variants().raw[0];
309-
// if there is no field, then it's just a ZST
310-
if variant.fields.is_empty() {
311-
if adt.is_struct() {
312-
let overall_t = self.codegen_ty(ty);
313-
Expr::struct_expr_from_values(overall_t, vec![], &self.symbol_table)
314-
} else {
315-
unimplemented!()
316-
}
317-
} else {
318-
// otherwise, there is just one field, which is stored as the scalar data
319-
let field = &variant.fields[0usize.into()];
320-
let fty = field.ty(self.tcx, subst);
321-
322-
let overall_t = self.codegen_ty(ty);
323-
if adt.is_struct() {
324-
self.codegen_single_variant_single_field(s, span, overall_t, fty)
325-
} else {
326-
unimplemented!()
327-
}
328-
}
329-
} else {
330-
// if it's an enum
306+
if adt.is_struct() {
307+
// In this case, we must have a one variant ADT.
308+
let variant = adt.non_enum_variant();
309+
let overall_type = self.codegen_ty(ty);
310+
// There must be at least one field associated with the scalar data.
311+
// Any additional fields correspond to ZSTs.
312+
let field_types: Vec<Ty<'_>> =
313+
variant.fields.iter().map(|f| f.ty(self.tcx, subst)).collect();
314+
// Check that there is a single non-ZST field.
315+
let non_zst_types: Vec<_> =
316+
field_types.iter().filter(|t| !self.is_zst(**t)).collect();
317+
assert!(
318+
non_zst_types.len() == 1,
319+
"error: expected exactly one field whose type is not a ZST"
320+
);
321+
let field_values: Vec<Expr> = field_types
322+
.iter()
323+
.map(|t| {
324+
if self.is_zst(*t) {
325+
Expr::init_unit(self.codegen_ty(*t), &self.symbol_table)
326+
} else {
327+
self.codegen_scalar(s, *t, span)
328+
}
329+
})
330+
.collect();
331+
Expr::struct_expr_from_values(overall_type, field_values, &self.symbol_table)
332+
} else if adt.is_enum() {
331333
let layout = self.layout_of(ty);
332334
let overall_t = self.codegen_ty(ty);
333335
match &layout.variants {
334336
Variants::Single { index } => {
335337
// here we must have one variant
336338
let variant = &adt.variants()[*index];
337-
338339
match variant.fields.len() {
339340
0 => Expr::struct_expr_from_values(
340341
overall_t,
@@ -398,6 +399,9 @@ impl<'tcx> GotocCtx<'tcx> {
398399
}
399400
},
400401
}
402+
} else {
403+
// if it's a union
404+
unimplemented!()
401405
}
402406
}
403407
(Scalar::Int(int), ty::Tuple(_)) => {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "codegen-scalar-with-phantom"
6+
version = "0.1.0"
7+
edition = "2021"
8+
9+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10+
11+
[dependencies]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Status: SUCCESS\
2+
Description: "assertion failed: C.x == 0"
3+
4+
VERIFICATION:- SUCCESSFUL
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Check that we can codegen structs with scalar and phantom data.
5+
//!
6+
//! Note: Phantom data is represented with ZSTs, which are already covered by
7+
//! the test `codegen-scalar-with-zsts`, but we include this one as well for
8+
//! completeness.
9+
10+
use std::marker::PhantomData;
11+
12+
pub struct Foo<R> {
13+
x: u8,
14+
_t: PhantomData<R>,
15+
}
16+
17+
#[kani::proof]
18+
fn check_phantom_data() {
19+
const C: Foo<usize> = Foo { x: 0, _t: PhantomData };
20+
assert_eq!(C.x, 0);
21+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "codegen-scalar-with-zsts"
6+
version = "0.1.0"
7+
edition = "2021"
8+
9+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10+
11+
[dependencies]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Status: SUCCESS\
2+
Description: "assertion failed: C.x == 0"
3+
4+
VERIFICATION:- SUCCESSFUL
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Check that we can codegen structs with scalar and ZSTs.
5+
6+
struct Empty {}
7+
8+
pub struct Foo {
9+
x: u8,
10+
_t: Empty,
11+
}
12+
13+
#[kani::proof]
14+
fn check_zst() {
15+
const C: Foo = Foo { x: 0, _t: Empty {} };
16+
assert_eq!(C.x, 0);
17+
}

0 commit comments

Comments
 (0)