@@ -188,22 +188,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
188
188
189
189
"size_of_val" => {
190
190
let ty = substs. type_at ( 0 ) ;
191
- if self . type_is_sized ( ty) {
192
- let size = self . type_size ( ty) as u64 ;
193
- self . memory . write_uint ( dest, size, pointer_size) ?;
194
- } else {
195
- match ty. sty {
196
- ty:: TySlice ( _) | ty:: TyStr => {
197
- let elem_ty = ty. sequence_element_type ( self . tcx ) ;
198
- let elem_size = self . type_size ( elem_ty) as u64 ;
199
- let ptr_size = self . memory . pointer_size ( ) as isize ;
200
- let n = self . memory . read_usize ( args_ptrs[ 0 ] . offset ( ptr_size) ) ?;
201
- self . memory . write_uint ( dest, n * elem_size, pointer_size) ?;
202
- }
203
-
204
- _ => return Err ( EvalError :: Unimplemented ( format ! ( "unimplemented: size_of_val::<{:?}>" , ty) ) ) ,
205
- }
206
- }
191
+ let ( size, _) = self . size_and_align_of_dst ( ty, args_ptrs[ 0 ] ) ?;
192
+ self . memory . write_uint ( dest, size, pointer_size) ?;
207
193
}
208
194
// FIXME: wait for eval_operand_to_ptr to be gone
209
195
/*
@@ -248,4 +234,114 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
248
234
// current frame.
249
235
Ok ( ( ) )
250
236
}
237
+
238
+ fn size_and_align_of_dst (
239
+ & self ,
240
+ ty : ty:: Ty < ' tcx > ,
241
+ value : Pointer ,
242
+ ) -> EvalResult < ' tcx , ( u64 , u64 ) > {
243
+ let pointer_size = self . memory . pointer_size ( ) ;
244
+ if self . type_is_sized ( ty) {
245
+ Ok ( ( self . type_size ( ty) as u64 , self . type_align ( ty) as u64 ) )
246
+ } else {
247
+ match ty. sty {
248
+ ty:: TyAdt ( def, substs) => {
249
+ // First get the size of all statically known fields.
250
+ // Don't use type_of::sizing_type_of because that expects t to be sized,
251
+ // and it also rounds up to alignment, which we want to avoid,
252
+ // as the unsized field's alignment could be smaller.
253
+ assert ! ( !ty. is_simd( ) ) ;
254
+ let layout = self . type_layout ( ty) ;
255
+ debug ! ( "DST {} layout: {:?}" , ty, layout) ;
256
+
257
+ // Returns size in bytes of all fields except the last one
258
+ // (we will be recursing on the last one).
259
+ fn local_prefix_bytes ( variant : & ty:: layout:: Struct ) -> u64 {
260
+ let fields = variant. offset_after_field . len ( ) ;
261
+ if fields > 1 {
262
+ variant. offset_after_field [ fields - 2 ] . bytes ( )
263
+ } else {
264
+ 0
265
+ }
266
+ }
267
+
268
+ let ( sized_size, sized_align) = match * layout {
269
+ ty:: layout:: Layout :: Univariant { ref variant, .. } => {
270
+ ( local_prefix_bytes ( variant) , variant. align . abi ( ) )
271
+ }
272
+ _ => {
273
+ bug ! ( "size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}" ,
274
+ ty, layout) ;
275
+ }
276
+ } ;
277
+ debug ! ( "DST {} statically sized prefix size: {} align: {}" ,
278
+ ty, sized_size, sized_align) ;
279
+
280
+ // Recurse to get the size of the dynamically sized field (must be
281
+ // the last field).
282
+ let last_field = def. struct_variant ( ) . fields . last ( ) . unwrap ( ) ;
283
+ let field_ty = self . field_ty ( substs, last_field) ;
284
+ let ( unsized_size, unsized_align) = self . size_and_align_of_dst ( field_ty, value) ?;
285
+
286
+ // FIXME (#26403, #27023): We should be adding padding
287
+ // to `sized_size` (to accommodate the `unsized_align`
288
+ // required of the unsized field that follows) before
289
+ // summing it with `sized_size`. (Note that since #26403
290
+ // is unfixed, we do not yet add the necessary padding
291
+ // here. But this is where the add would go.)
292
+
293
+ // Return the sum of sizes and max of aligns.
294
+ let size = sized_size + unsized_size;
295
+
296
+ // Choose max of two known alignments (combined value must
297
+ // be aligned according to more restrictive of the two).
298
+ let align = :: std:: cmp:: max ( sized_align, unsized_align) ;
299
+
300
+ // Issue #27023: must add any necessary padding to `size`
301
+ // (to make it a multiple of `align`) before returning it.
302
+ //
303
+ // Namely, the returned size should be, in C notation:
304
+ //
305
+ // `size + ((size & (align-1)) ? align : 0)`
306
+ //
307
+ // emulated via the semi-standard fast bit trick:
308
+ //
309
+ // `(size + (align-1)) & -align`
310
+
311
+ if size & ( align - 1 ) != 0 {
312
+ Ok ( ( size + align, align) )
313
+ } else {
314
+ Ok ( ( size, align) )
315
+ }
316
+ }
317
+ ty:: TyTrait ( ..) => {
318
+ let ( _, vtable) = self . get_fat_ptr ( value) ;
319
+ let vtable = self . memory . read_ptr ( vtable) ?;
320
+ // the second entry in the vtable is the dynamic size of the object.
321
+ let size = self . memory . read_usize ( vtable. offset ( pointer_size as isize ) ) ?;
322
+ let align = self . memory . read_usize ( vtable. offset ( pointer_size as isize * 2 ) ) ?;
323
+ Ok ( ( size, align) )
324
+ }
325
+
326
+ ty:: TySlice ( _) | ty:: TyStr => {
327
+ let elem_ty = ty. sequence_element_type ( self . tcx ) ;
328
+ let elem_size = self . type_size ( elem_ty) as u64 ;
329
+ let ( _, len_ptr) = self . get_fat_ptr ( value) ;
330
+ let n = self . memory . read_usize ( len_ptr) ?;
331
+ let align = self . type_align ( elem_ty) ;
332
+ Ok ( ( n * elem_size, align as u64 ) )
333
+ }
334
+
335
+ _ => bug ! ( "size_of_val::<{:?}>" , ty) ,
336
+ }
337
+ }
338
+ }
339
+ /// Returns the normalized type of a struct field
340
+ fn field_ty (
341
+ & self ,
342
+ param_substs : & Substs < ' tcx > ,
343
+ f : ty:: FieldDef < ' tcx > ,
344
+ ) -> ty:: Ty < ' tcx > {
345
+ self . tcx . normalize_associated_type ( & f. ty ( self . tcx , param_substs) )
346
+ }
251
347
}
0 commit comments