@@ -23,7 +23,7 @@ use tracing::{debug, trace, warn};
23
23
24
24
/// A projection in Kani can either be to a type (the normal case),
25
25
/// or a variant in the case of a downcast.
26
- #[ derive( Debug ) ]
26
+ #[ derive( Copy , Clone , Debug ) ]
27
27
pub enum TypeOrVariant < ' tcx > {
28
28
Type ( Ty < ' tcx > ) ,
29
29
Variant ( & ' tcx VariantDef ) ,
@@ -235,15 +235,21 @@ impl<'tcx> TypeOrVariant<'tcx> {
235
235
}
236
236
237
237
impl < ' tcx > GotocCtx < ' tcx > {
238
+ /// Codegen field access for types that allow direct field projection.
239
+ ///
240
+ /// I.e.: Algebraic data types, closures, and generators.
241
+ ///
242
+ /// Other composite types such as array only support index projection.
238
243
fn codegen_field (
239
244
& mut self ,
240
- res : Expr ,
241
- t : TypeOrVariant < ' tcx > ,
242
- f : & FieldIdx ,
245
+ parent_expr : Expr ,
246
+ parent_ty_or_var : TypeOrVariant < ' tcx > ,
247
+ field : & FieldIdx ,
248
+ field_ty_or_var : TypeOrVariant < ' tcx > ,
243
249
) -> Result < Expr , UnimplementedData > {
244
- match t {
245
- TypeOrVariant :: Type ( t ) => {
246
- match t . kind ( ) {
250
+ match parent_ty_or_var {
251
+ TypeOrVariant :: Type ( parent_ty ) => {
252
+ match parent_ty . kind ( ) {
247
253
ty:: Alias ( ..)
248
254
| ty:: Bool
249
255
| ty:: Char
@@ -254,56 +260,98 @@ impl<'tcx> GotocCtx<'tcx> {
254
260
| ty:: Never
255
261
| ty:: FnDef ( ..)
256
262
| ty:: GeneratorWitness ( ..)
263
+ | ty:: GeneratorWitnessMIR ( ..)
257
264
| ty:: Foreign ( ..)
258
265
| ty:: Dynamic ( ..)
259
266
| ty:: Bound ( ..)
260
267
| ty:: Placeholder ( ..)
261
268
| ty:: Param ( _)
262
269
| ty:: Infer ( _)
263
- | ty:: Error ( _) => unreachable ! ( "type {:?} does not have a field" , t) ,
264
- ty:: Tuple ( _) => {
265
- Ok ( res. member ( & Self :: tuple_fld_name ( f. index ( ) ) , & self . symbol_table ) )
266
- }
267
- ty:: Adt ( def, _) if def. repr ( ) . simd ( ) => {
268
- // this is a SIMD vector - the index represents one
269
- // of the elements, so we want to index as an array
270
- // Example:
271
- // pub struct i64x2(i64, i64);
272
- // fn main() {
273
- // let v = i64x2(1, 2);
274
- // assert!(v.0 == 1); // refers to the first i64
275
- // assert!(v.1 == 2);
276
- // }
277
- let size_index = Expr :: int_constant ( f. index ( ) , Type :: size_t ( ) ) ;
278
- Ok ( res. index_array ( size_index) )
279
- }
270
+ | ty:: Error ( _) => unreachable ! ( "type {parent_ty:?} does not have a field" ) ,
271
+ ty:: Tuple ( _) => Ok ( parent_expr
272
+ . member ( & Self :: tuple_fld_name ( field. index ( ) ) , & self . symbol_table ) ) ,
273
+ ty:: Adt ( def, _) if def. repr ( ) . simd ( ) => Ok ( self . codegen_simd_field (
274
+ parent_expr,
275
+ * field,
276
+ field_ty_or_var. expect_type ( ) ,
277
+ ) ) ,
280
278
// if we fall here, then we are handling either a struct or a union
281
279
ty:: Adt ( def, _) => {
282
- let field = & def. variants ( ) . raw [ 0 ] . fields [ * f] ;
283
- Ok ( res. member ( & field. name . to_string ( ) , & self . symbol_table ) )
280
+ let field = & def. variants ( ) . raw [ 0 ] . fields [ * field] ;
281
+ Ok ( parent_expr. member ( & field. name . to_string ( ) , & self . symbol_table ) )
282
+ }
283
+ ty:: Closure ( ..) => {
284
+ Ok ( parent_expr. member ( & field. index ( ) . to_string ( ) , & self . symbol_table ) )
284
285
}
285
- ty:: Closure ( ..) => Ok ( res. member ( & f. index ( ) . to_string ( ) , & self . symbol_table ) ) ,
286
286
ty:: Generator ( ..) => {
287
- let field_name = self . generator_field_name ( f . as_usize ( ) ) ;
288
- Ok ( res
287
+ let field_name = self . generator_field_name ( field . as_usize ( ) ) ;
288
+ Ok ( parent_expr
289
289
. member ( "direct_fields" , & self . symbol_table )
290
290
. member ( field_name, & self . symbol_table ) )
291
291
}
292
- _ => unimplemented ! ( ) ,
292
+ ty:: Str | ty:: Array ( _, _) | ty:: Slice ( _) | ty:: RawPtr ( _) | ty:: Ref ( _, _, _) => {
293
+ unreachable ! (
294
+ "element of {parent_ty:?} is not accessed via field projection"
295
+ )
296
+ }
293
297
}
294
298
}
295
299
// if we fall here, then we are handling an enum
296
- TypeOrVariant :: Variant ( v ) => {
297
- let field = & v . fields [ * f ] ;
298
- Ok ( res . member ( & field. name . to_string ( ) , & self . symbol_table ) )
300
+ TypeOrVariant :: Variant ( parent_var ) => {
301
+ let field = & parent_var . fields [ * field ] ;
302
+ Ok ( parent_expr . member ( & field. name . to_string ( ) , & self . symbol_table ) )
299
303
}
300
304
TypeOrVariant :: GeneratorVariant ( _var_idx) => {
301
- let field_name = self . generator_field_name ( f . index ( ) ) ;
302
- Ok ( res . member ( field_name, & self . symbol_table ) )
305
+ let field_name = self . generator_field_name ( field . index ( ) ) ;
306
+ Ok ( parent_expr . member ( field_name, & self . symbol_table ) )
303
307
}
304
308
}
305
309
}
306
310
311
+ /// This is a SIMD vector, which has 2 possible internal representations:
312
+ /// 1- Multi-field representation (original and currently deprecated)
313
+ /// In this case, a field is one lane (i.e.: one element)
314
+ /// Example:
315
+ /// ```ignore
316
+ /// pub struct i64x2(i64, i64);
317
+ /// fn main() {
318
+ /// let v = i64x2(1, 2);
319
+ /// assert!(v.0 == 1); // refers to the first i64
320
+ /// assert!(v.1 == 2);
321
+ /// }
322
+ /// ```
323
+ /// 2- Array-based representation
324
+ /// In this case, the projection refers to the entire array.
325
+ /// ```ignore
326
+ /// pub struct i64x2([i64; 2]);
327
+ /// fn main() {
328
+ /// let v = i64x2([1, 2]);
329
+ /// assert!(v.0 == [1, 2]); // refers to the entire array
330
+ /// }
331
+ /// ```
332
+ /// * Note that projection inside SIMD structs may eventually become illegal.
333
+ /// See <https://github.com/rust-lang/stdarch/pull/1422#discussion_r1176415609> thread.
334
+ ///
335
+ /// Since the goto representation for both is the same, we use the expected type to decide
336
+ /// what to return.
337
+ fn codegen_simd_field (
338
+ & mut self ,
339
+ parent_expr : Expr ,
340
+ field : FieldIdx ,
341
+ field_ty : Ty < ' tcx > ,
342
+ ) -> Expr {
343
+ if matches ! ( field_ty. kind( ) , ty:: Array { .. } ) {
344
+ // Array based
345
+ assert_eq ! ( field. index( ) , 0 ) ;
346
+ let field_typ = self . codegen_ty ( field_ty) ;
347
+ parent_expr. reinterpret_cast ( field_typ)
348
+ } else {
349
+ // Return the given field.
350
+ let index_expr = Expr :: int_constant ( field. index ( ) , Type :: size_t ( ) ) ;
351
+ parent_expr. index_array ( index_expr)
352
+ }
353
+ }
354
+
307
355
/// If a local is a function definition, ignore the local variable name and
308
356
/// generate a function call based on the def id.
309
357
///
@@ -424,7 +472,8 @@ impl<'tcx> GotocCtx<'tcx> {
424
472
}
425
473
ProjectionElem :: Field ( f, t) => {
426
474
let typ = TypeOrVariant :: Type ( t) ;
427
- let expr = self . codegen_field ( before. goto_expr , before. mir_typ_or_variant , & f) ?;
475
+ let expr =
476
+ self . codegen_field ( before. goto_expr , before. mir_typ_or_variant , & f, typ) ?;
428
477
ProjectedPlace :: try_new (
429
478
expr,
430
479
typ,
0 commit comments