@@ -31,7 +31,7 @@ use rustc::mir::interpret::{
3131use syntax:: source_map:: { self , Span } ;
3232
3333use super :: {
34- Value , Operand , MemPlace , MPlaceTy , Place , ScalarMaybeUndef ,
34+ Value , Operand , MemPlace , MPlaceTy , Place , PlaceTy , ScalarMaybeUndef ,
3535 Memory , Machine
3636} ;
3737
@@ -73,8 +73,9 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=()> {
7373 /// Work to perform when returning from this function
7474 pub return_to_block : StackPopCleanup ,
7575
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 > > ,
7879
7980 /// The list of locals for this stack frame, stored in order as
8081 /// `[return_ptr, arguments..., variables..., temporaries...]`.
@@ -97,7 +98,8 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=()> {
9798#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
9899pub enum StackPopCleanup {
99100 /// 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.
101103 Goto ( Option < mir:: BasicBlock > ) ,
102104 /// Just do nohing: Used by Main and for the box_alloc hook in miri.
103105 /// `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
330332 }
331333
332334 /// 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.
334337 pub ( super ) fn size_and_align_of (
335338 & self ,
336339 metadata : Option < Scalar < M :: PointerTag > > ,
337340 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+ }
349345 match layout. ty . sty {
350346 ty:: Adt ( ..) | ty:: Tuple ( ..) => {
351347 // 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
365361 ) ;
366362
367363 // 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?
369366 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" ) ;
371369
372370 // FIXME (#26403, #27023): We should be adding padding
373371 // 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
394392 //
395393 // `(size + (align-1)) & -align`
396394
397- Ok ( ( size. abi_align ( align) , align) )
395+ Ok ( Some ( ( size. abi_align ( align) , align) ) )
398396 }
399397 ty:: Dynamic ( ..) => {
400- let vtable = metadata. to_ptr ( ) ?;
398+ let vtable = metadata. expect ( "dyn trait fat ptr must have vtable" ) . to_ptr ( ) ?;
401399 // 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) ? ) )
403401 }
404402
405403 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 ) ?;
407405 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 )
409411 }
410412
411413 _ => 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
415417 pub fn size_and_align_of_mplace (
416418 & self ,
417419 mplace : MPlaceTy < ' tcx , M :: PointerTag >
418- ) -> EvalResult < ' tcx , ( Size , Align ) > {
420+ ) -> EvalResult < ' tcx , Option < ( Size , Align ) > > {
419421 self . size_and_align_of ( mplace. meta , mplace. layout )
420422 }
421423
@@ -424,7 +426,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
424426 instance : ty:: Instance < ' tcx > ,
425427 span : source_map:: Span ,
426428 mir : & ' mir mir:: Mir < ' tcx > ,
427- return_place : Place < M :: PointerTag > ,
429+ return_place : Option < PlaceTy < ' tcx , M :: PointerTag > > ,
428430 return_to_block : StackPopCleanup ,
429431 ) -> EvalResult < ' tcx > {
430432 :: log_settings:: settings ( ) . indentation += 1 ;
@@ -509,15 +511,38 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
509511 }
510512 StackPopCleanup :: None { cleanup } => {
511513 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.
513517 return Ok ( ( ) ) ;
514518 }
515519 }
516520 }
517- // deallocate all locals that are backed by an allocation
521+ // Deallocate all locals that are backed by an allocation.
518522 for local in frame. locals {
519523 self . deallocate_local ( local) ?;
520524 }
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+ }
521546
522547 Ok ( ( ) )
523548 }
0 commit comments