Skip to content

Commit e1c784c

Browse files
authored
Handle enums with zero or one variants (#4171)
Handle enums with zero or one variants when deriving `BoundedArbitrary`. Resolves #4170 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent c5021a8 commit e1c784c

File tree

3 files changed

+62
-11
lines changed

3 files changed

+62
-11
lines changed

library/kani_macros/src/derive_bounded.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,40 @@ fn generate_type_constructor(type_name: TokenStream, fields: &syn::Fields) -> To
9292
/// Generates a `match` case to construct each variant of the given type. Uses a
9393
/// symbolic `usize` to decide which variant to construct.
9494
fn enum_constructor(ident: &syn::Ident, data_enum: &syn::DataEnum) -> TokenStream {
95-
let variant_constructors = data_enum.variants.iter().map(|variant| {
95+
if data_enum.variants.is_empty() {
96+
let msg = format!(
97+
"Cannot create symbolic enum `{ident}`. Enums with zero-variants cannot be instantiated"
98+
);
99+
quote! {
100+
panic!(#msg)
101+
}
102+
} else if data_enum.variants.len() == 1 {
103+
let variant = data_enum.variants.first().unwrap();
96104
let variant_name = &variant.ident;
97-
generate_type_constructor(quote!(#ident::#variant_name), &variant.fields)
98-
});
99-
let n_variants = data_enum.variants.len();
100-
let cases = variant_constructors.enumerate().map(|(idx, var_constr)| {
101-
if idx < n_variants - 1 { quote!(#idx => #var_constr) } else { quote!(_ => #var_constr) }
102-
});
105+
let variant_constructor =
106+
generate_type_constructor(quote!(#ident::#variant_name), &variant.fields);
107+
quote! {
108+
#variant_constructor
109+
}
110+
} else {
111+
let variant_constructors = data_enum.variants.iter().map(|variant| {
112+
let variant_name = &variant.ident;
113+
generate_type_constructor(quote!(#ident::#variant_name), &variant.fields)
114+
});
115+
let n_variants = data_enum.variants.len();
116+
let cases = variant_constructors.enumerate().map(|(idx, var_constr)| {
117+
if idx < n_variants - 1 {
118+
quote!(#idx => #var_constr)
119+
} else {
120+
quote!(_ => #var_constr)
121+
}
122+
});
103123

104-
let kani_path = kani_path();
105-
quote! {
106-
match #kani_path::any() {
107-
#(#cases),* ,
124+
let kani_path = kani_path();
125+
quote! {
126+
match #kani_path::any() {
127+
#(#cases),* ,
128+
}
108129
}
109130
}
110131
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Checking harness check_enum...
2+
3+
** 5 of 5 cover properties satisfied
4+
5+
VERIFICATION:- SUCCESSFUL
6+
7+
Complete - 1 successfully verified harnesses, 0 failures, 1 total.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Check that derive BoundedArbitrary macro works on enums with a single variant
5+
//! See https://github.com/model-checking/kani/issues/4170
6+
7+
#[allow(unused)]
8+
#[derive(kani::BoundedArbitrary)]
9+
enum Foo {
10+
A(#[bounded] String),
11+
}
12+
13+
#[kani::proof]
14+
#[kani::unwind(6)]
15+
fn check_enum() {
16+
let any_enum: Foo = kani::bounded_any::<_, 4>();
17+
let Foo::A(s) = any_enum;
18+
kani::cover!(s.len() == 0);
19+
kani::cover!(s.len() == 1);
20+
kani::cover!(s.len() == 2);
21+
kani::cover!(s.len() == 3);
22+
kani::cover!(s.len() == 4);
23+
}

0 commit comments

Comments
 (0)