@@ -28,11 +28,30 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok
28
28
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
29
29
30
30
let body = fn_any_body ( & item_name, & derive_item. data ) ;
31
- let expanded = quote ! {
32
- // The generated implementation.
33
- impl #impl_generics kani:: Arbitrary for #item_name #ty_generics #where_clause {
34
- fn any( ) -> Self {
35
- #body
31
+
32
+ // Get the safety constraints (if any) to produce type-safe values
33
+ let safety_conds_opt = safety_conds ( & item_name, & derive_item. data ) ;
34
+
35
+ let expanded = if let Some ( safety_cond) = safety_conds_opt {
36
+ let field_refs = field_refs ( & item_name, & derive_item. data ) ;
37
+ quote ! {
38
+ // The generated implementation.
39
+ impl #impl_generics kani:: Arbitrary for #item_name #ty_generics #where_clause {
40
+ fn any( ) -> Self {
41
+ let obj = #body;
42
+ #field_refs
43
+ kani:: assume( #safety_cond) ;
44
+ obj
45
+ }
46
+ }
47
+ }
48
+ } else {
49
+ quote ! {
50
+ // The generated implementation.
51
+ impl #impl_generics kani:: Arbitrary for #item_name #ty_generics #where_clause {
52
+ fn any( ) -> Self {
53
+ #body
54
+ }
36
55
}
37
56
}
38
57
} ;
@@ -75,6 +94,103 @@ fn fn_any_body(ident: &Ident, data: &Data) -> TokenStream {
75
94
}
76
95
}
77
96
97
+ /// Parse the condition expressions in `#[safety_constraint(<cond>)]` attached to struct
98
+ /// fields and, it at least one was found, generate a conjunction to be assumed.
99
+ ///
100
+ /// For example, if we're deriving implementations for the struct
101
+ /// ```
102
+ /// #[derive(Arbitrary)]
103
+ /// #[derive(Invariant)]
104
+ /// struct PositivePoint {
105
+ /// #[safety_constraint(*x >= 0)]
106
+ /// x: i32,
107
+ /// #[safety_constraint(*y >= 0)]
108
+ /// y: i32,
109
+ /// }
110
+ /// ```
111
+ /// this function will generate the `TokenStream`
112
+ /// ```
113
+ /// *x >= 0 && *y >= 0
114
+ /// ```
115
+ /// which can be passed to `kani::assume` to constrain the values generated
116
+ /// through the `Arbitrary` impl so that they are type-safe by construction.
117
+ fn safety_conds ( ident : & Ident , data : & Data ) -> Option < TokenStream > {
118
+ match data {
119
+ Data :: Struct ( struct_data) => safety_conds_inner ( ident, & struct_data. fields ) ,
120
+ Data :: Enum ( _) => None ,
121
+ Data :: Union ( _) => None ,
122
+ }
123
+ }
124
+
125
+ /// Generates an expression resulting from the conjunction of conditions
126
+ /// specified as safety constraints for each field. See `safety_conds` for more details.
127
+ fn safety_conds_inner ( ident : & Ident , fields : & Fields ) -> Option < TokenStream > {
128
+ match fields {
129
+ Fields :: Named ( ref fields) => {
130
+ let conds: Vec < TokenStream > =
131
+ fields. named . iter ( ) . filter_map ( |field| parse_safety_expr ( ident, field) ) . collect ( ) ;
132
+ if !conds. is_empty ( ) { Some ( quote ! { #( #conds) &&* } ) } else { None }
133
+ }
134
+ Fields :: Unnamed ( _) => None ,
135
+ Fields :: Unit => None ,
136
+ }
137
+ }
138
+
139
+ /// Generates the sequence of expressions to initialize the variables used as
140
+ /// references to the struct fields.
141
+ ///
142
+ /// For example, if we're deriving implementations for the struct
143
+ /// ```
144
+ /// #[derive(Arbitrary)]
145
+ /// #[derive(Invariant)]
146
+ /// struct PositivePoint {
147
+ /// #[safety_constraint(*x >= 0)]
148
+ /// x: i32,
149
+ /// #[safety_constraint(*y >= 0)]
150
+ /// y: i32,
151
+ /// }
152
+ /// ```
153
+ /// this function will generate the `TokenStream`
154
+ /// ```
155
+ /// let x = &obj.x;
156
+ /// let y = &obj.y;
157
+ /// ```
158
+ /// which allows us to refer to the struct fields without using `self`.
159
+ /// Note that the actual stream is generated in the `field_refs_inner` function.
160
+ fn field_refs ( ident : & Ident , data : & Data ) -> TokenStream {
161
+ match data {
162
+ Data :: Struct ( struct_data) => field_refs_inner ( ident, & struct_data. fields ) ,
163
+ Data :: Enum ( _) => unreachable ! ( ) ,
164
+ Data :: Union ( _) => unreachable ! ( ) ,
165
+ }
166
+ }
167
+
168
+ /// Generates the sequence of expressions to initialize the variables used as
169
+ /// references to the struct fields. See `field_refs` for more details.
170
+ fn field_refs_inner ( _ident : & Ident , fields : & Fields ) -> TokenStream {
171
+ match fields {
172
+ Fields :: Named ( ref fields) => {
173
+ let field_refs: Vec < TokenStream > = fields
174
+ . named
175
+ . iter ( )
176
+ . map ( |field| {
177
+ let name = & field. ident ;
178
+ quote_spanned ! { field. span( ) =>
179
+ let #name = & obj. #name;
180
+ }
181
+ } )
182
+ . collect ( ) ;
183
+ if !field_refs. is_empty ( ) {
184
+ quote ! { #( #field_refs ) * }
185
+ } else {
186
+ quote ! { }
187
+ }
188
+ }
189
+ Fields :: Unnamed ( _) => quote ! { } ,
190
+ Fields :: Unit => quote ! { } ,
191
+ }
192
+ }
193
+
78
194
/// Generate an item initialization where an item can be a struct or a variant.
79
195
/// For named fields, this will generate: `Item { field1: kani::any(), field2: kani::any(), .. }`
80
196
/// For unnamed fields, this will generate: `Item (kani::any(), kani::any(), ..)`
@@ -115,6 +231,42 @@ fn init_symbolic_item(ident: &Ident, fields: &Fields) -> TokenStream {
115
231
}
116
232
}
117
233
234
+ /// Extract, parse and return the expression `cond` (i.e., `Some(cond)`) in the
235
+ /// `#[safety_constraint(<cond>)]` attribute helper associated with a given field.
236
+ /// Return `None` if the attribute isn't specified.
237
+ fn parse_safety_expr ( ident : & Ident , field : & syn:: Field ) -> Option < TokenStream > {
238
+ let name = & field. ident ;
239
+ let mut safety_helper_attr = None ;
240
+
241
+ // Keep the helper attribute if we find it
242
+ for attr in & field. attrs {
243
+ if attr. path ( ) . is_ident ( "safety_constraint" ) {
244
+ safety_helper_attr = Some ( attr) ;
245
+ }
246
+ }
247
+
248
+ // Parse the arguments in the `#[safety_constraint(...)]` attribute
249
+ if let Some ( attr) = safety_helper_attr {
250
+ let expr_args: Result < syn:: Expr , syn:: Error > = attr. parse_args ( ) ;
251
+
252
+ // Check if there was an error parsing the arguments
253
+ if let Err ( err) = expr_args {
254
+ abort ! ( Span :: call_site( ) , "Cannot derive impl for `{}`" , ident;
255
+ note = attr. span( ) =>
256
+ "safety constraint in field `{}` could not be parsed: {}" , name. as_ref( ) . unwrap( ) . to_string( ) , err
257
+ )
258
+ }
259
+
260
+ // Return the expression associated to the safety constraint
261
+ let safety_expr = expr_args. unwrap ( ) ;
262
+ Some ( quote_spanned ! { field. span( ) =>
263
+ #safety_expr
264
+ } )
265
+ } else {
266
+ None
267
+ }
268
+ }
269
+
118
270
/// Generate the body of the function `any()` for enums. The cases are:
119
271
/// 1. For zero-variants enumerations, this will encode a `panic!()` statement.
120
272
/// 2. For one or more variants, the code will be something like:
@@ -176,10 +328,14 @@ pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::Tok
176
328
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
177
329
178
330
let body = is_safe_body ( & item_name, & derive_item. data ) ;
331
+ let field_refs = field_refs ( & item_name, & derive_item. data ) ;
332
+
179
333
let expanded = quote ! {
180
334
// The generated implementation.
181
335
impl #impl_generics kani:: Invariant for #item_name #ty_generics #where_clause {
182
336
fn is_safe( & self ) -> bool {
337
+ let obj = self ;
338
+ #field_refs
183
339
#body
184
340
}
185
341
}
@@ -199,7 +355,7 @@ fn add_trait_bound_invariant(mut generics: Generics) -> Generics {
199
355
200
356
fn is_safe_body ( ident : & Ident , data : & Data ) -> TokenStream {
201
357
match data {
202
- Data :: Struct ( struct_data) => struct_safe_conjunction ( ident, & struct_data. fields ) ,
358
+ Data :: Struct ( struct_data) => struct_invariant_conjunction ( ident, & struct_data. fields ) ,
203
359
Data :: Enum ( _) => {
204
360
abort ! ( Span :: call_site( ) , "Cannot derive `Invariant` for `{}` enum" , ident;
205
361
note = ident. span( ) =>
@@ -215,21 +371,35 @@ fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream {
215
371
}
216
372
}
217
373
218
- /// Generates an expression that is the conjunction of `is_safe` calls for each field in the struct.
219
- fn struct_safe_conjunction ( _ident : & Ident , fields : & Fields ) -> TokenStream {
374
+ /// Generates an expression that is the conjunction of safety constraints for each field in the struct.
375
+ fn struct_invariant_conjunction ( ident : & Ident , fields : & Fields ) -> TokenStream {
220
376
match fields {
221
377
// Expands to the expression
378
+ // `true && <safety_cond1> && <safety_cond2> && ..`
379
+ // where `safety_condN` is
380
+ // - `self.fieldN.is_safe() && <cond>` if a condition `<cond>` was
381
+ // specified through the `#[safety_constraint(<cond>)]` helper attribute, or
382
+ // - `self.fieldN.is_safe()` otherwise
383
+ //
384
+ // Therefore, if `#[safety_constraint(<cond>)]` isn't specified for any field, this expands to
222
385
// `true && self.field1.is_safe() && self.field2.is_safe() && ..`
223
386
Fields :: Named ( ref fields) => {
224
- let safe_calls = fields. named . iter ( ) . map ( |field| {
225
- let name = & field. ident ;
226
- quote_spanned ! { field. span( ) =>
227
- self . #name. is_safe( )
228
- }
229
- } ) ;
387
+ let safety_conds: Vec < TokenStream > = fields
388
+ . named
389
+ . iter ( )
390
+ . map ( |field| {
391
+ let name = & field. ident ;
392
+ let default_expr = quote_spanned ! { field. span( ) =>
393
+ #name. is_safe( )
394
+ } ;
395
+ parse_safety_expr ( ident, field)
396
+ . map ( |expr| quote ! { #expr && #default_expr} )
397
+ . unwrap_or ( default_expr)
398
+ } )
399
+ . collect ( ) ;
230
400
// An initial value is required for empty structs
231
- safe_calls . fold ( quote ! { true } , |acc, call | {
232
- quote ! { #acc && #call }
401
+ safety_conds . iter ( ) . fold ( quote ! { true } , |acc, cond | {
402
+ quote ! { #acc && #cond }
233
403
} )
234
404
}
235
405
Fields :: Unnamed ( ref fields) => {
0 commit comments