@@ -31,7 +31,7 @@ use rustc::mir::interpret::{
31
31
use syntax:: source_map:: { self , Span } ;
32
32
33
33
use super :: {
34
- Value , Operand , MemPlace , MPlaceTy , Place , ScalarMaybeUndef ,
34
+ Value , Operand , MemPlace , MPlaceTy , Place , PlaceTy , ScalarMaybeUndef ,
35
35
Memory , Machine
36
36
} ;
37
37
@@ -73,8 +73,9 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=()> {
73
73
/// Work to perform when returning from this function
74
74
pub return_to_block : StackPopCleanup ,
75
75
76
- /// The location where the result of the current stack frame should be written to.
77
- pub return_place : Place < Tag > ,
76
+ /// The location where the result of the current stack frame should be written to,
77
+ /// and its layout in the caller.
78
+ pub return_place : Option < PlaceTy < ' tcx , Tag > > ,
78
79
79
80
/// The list of locals for this stack frame, stored in order as
80
81
/// `[return_ptr, arguments..., variables..., temporaries...]`.
@@ -97,7 +98,8 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=()> {
97
98
#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
98
99
pub enum StackPopCleanup {
99
100
/// Jump to the next block in the caller, or cause UB if None (that's a function
100
- /// that may never return).
101
+ /// that may never return). Also store layout of return place so
102
+ /// we can validate it at that layout.
101
103
Goto ( Option < mir:: BasicBlock > ) ,
102
104
/// Just do nohing: Used by Main and for the box_alloc hook in miri.
103
105
/// `cleanup` says whether locals are deallocated. Static computation
@@ -330,22 +332,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
330
332
}
331
333
332
334
/// Return the actual dynamic size and alignment of the place at the given type.
333
- /// Only the `meta` part of the place matters.
335
+ /// Only the "meta" (metadata) part of the place matters.
336
+ /// This can fail to provide an answer for extern types.
334
337
pub ( super ) fn size_and_align_of (
335
338
& self ,
336
339
metadata : Option < Scalar < M :: PointerTag > > ,
337
340
layout : TyLayout < ' tcx > ,
338
- ) -> EvalResult < ' tcx , ( Size , Align ) > {
339
- let metadata = match metadata {
340
- None => {
341
- assert ! ( !layout. is_unsized( ) ) ;
342
- return Ok ( layout. size_and_align ( ) )
343
- }
344
- Some ( metadata) => {
345
- assert ! ( layout. is_unsized( ) ) ;
346
- metadata
347
- }
348
- } ;
341
+ ) -> EvalResult < ' tcx , Option < ( Size , Align ) > > {
342
+ if !layout. is_unsized ( ) {
343
+ return Ok ( Some ( layout. size_and_align ( ) ) ) ;
344
+ }
349
345
match layout. ty . sty {
350
346
ty:: Adt ( ..) | ty:: Tuple ( ..) => {
351
347
// First get the size of all statically known fields.
@@ -365,9 +361,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
365
361
) ;
366
362
367
363
// Recurse to get the size of the dynamically sized field (must be
368
- // the last field).
364
+ // the last field). Can't have foreign types here, how would we
365
+ // adjust alignment and size for them?
369
366
let field = layout. field ( self , layout. fields . count ( ) - 1 ) ?;
370
- let ( unsized_size, unsized_align) = self . size_and_align_of ( Some ( metadata) , field) ?;
367
+ let ( unsized_size, unsized_align) = self . size_and_align_of ( metadata, field) ?
368
+ . expect ( "Fields cannot be extern types" ) ;
371
369
372
370
// FIXME (#26403, #27023): We should be adding padding
373
371
// to `sized_size` (to accommodate the `unsized_align`
@@ -394,18 +392,22 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
394
392
//
395
393
// `(size + (align-1)) & -align`
396
394
397
- Ok ( ( size. abi_align ( align) , align) )
395
+ Ok ( Some ( ( size. abi_align ( align) , align) ) )
398
396
}
399
397
ty:: Dynamic ( ..) => {
400
- let vtable = metadata. to_ptr ( ) ?;
398
+ let vtable = metadata. expect ( "dyn trait fat ptr must have vtable" ) . to_ptr ( ) ?;
401
399
// the second entry in the vtable is the dynamic size of the object.
402
- self . read_size_and_align_from_vtable ( vtable)
400
+ Ok ( Some ( self . read_size_and_align_from_vtable ( vtable) ? ) )
403
401
}
404
402
405
403
ty:: Slice ( _) | ty:: Str => {
406
- let len = metadata. to_usize ( self ) ?;
404
+ let len = metadata. expect ( "slice fat ptr must have vtable" ) . to_usize ( self ) ?;
407
405
let ( elem_size, align) = layout. field ( self , 0 ) ?. size_and_align ( ) ;
408
- Ok ( ( elem_size * len, align) )
406
+ Ok ( Some ( ( elem_size * len, align) ) )
407
+ }
408
+
409
+ ty:: Foreign ( _) => {
410
+ Ok ( None )
409
411
}
410
412
411
413
_ => bug ! ( "size_and_align_of::<{:?}> not supported" , layout. ty) ,
@@ -415,7 +417,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
415
417
pub fn size_and_align_of_mplace (
416
418
& self ,
417
419
mplace : MPlaceTy < ' tcx , M :: PointerTag >
418
- ) -> EvalResult < ' tcx , ( Size , Align ) > {
420
+ ) -> EvalResult < ' tcx , Option < ( Size , Align ) > > {
419
421
self . size_and_align_of ( mplace. meta , mplace. layout )
420
422
}
421
423
@@ -424,7 +426,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
424
426
instance : ty:: Instance < ' tcx > ,
425
427
span : source_map:: Span ,
426
428
mir : & ' mir mir:: Mir < ' tcx > ,
427
- return_place : Place < M :: PointerTag > ,
429
+ return_place : Option < PlaceTy < ' tcx , M :: PointerTag > > ,
428
430
return_to_block : StackPopCleanup ,
429
431
) -> EvalResult < ' tcx > {
430
432
:: log_settings:: settings ( ) . indentation += 1 ;
@@ -509,15 +511,38 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
509
511
}
510
512
StackPopCleanup :: None { cleanup } => {
511
513
if !cleanup {
512
- // Leak the locals
514
+ // Leak the locals. Also skip validation, this is only used by
515
+ // static/const computation which does its own (stronger) final
516
+ // validation.
513
517
return Ok ( ( ) ) ;
514
518
}
515
519
}
516
520
}
517
- // deallocate all locals that are backed by an allocation
521
+ // Deallocate all locals that are backed by an allocation.
518
522
for local in frame. locals {
519
523
self . deallocate_local ( local) ?;
520
524
}
525
+ // Validate the return value.
526
+ if let Some ( return_place) = frame. return_place {
527
+ if M :: enforce_validity ( self ) {
528
+ // Data got changed, better make sure it matches the type!
529
+ // It is still possible that the return place held invalid data while
530
+ // the function is running, but that's okay because nobody could have
531
+ // accessed that same data from the "outside" to observe any broken
532
+ // invariant -- that is, unless a function somehow has a ptr to
533
+ // its return place... but the way MIR is currently generated, the
534
+ // return place is always a local and then this cannot happen.
535
+ self . validate_operand (
536
+ self . place_to_op ( return_place) ?,
537
+ & mut vec ! [ ] ,
538
+ None ,
539
+ /*const_mode*/ false ,
540
+ ) ?;
541
+ }
542
+ } else {
543
+ // Uh, that shouln't happen... the function did not intend to return
544
+ return err ! ( Unreachable ) ;
545
+ }
521
546
522
547
Ok ( ( ) )
523
548
}
0 commit comments