2016-06-23 01:02:47 -05:00
//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.
2016-06-03 08:48:56 -05:00
use rustc ::hir ::def_id ::DefId ;
2016-09-09 10:44:04 -05:00
use rustc ::hir ;
2016-06-03 08:48:56 -05:00
use rustc ::mir ::visit ::{ Visitor , LvalueContext } ;
2017-07-13 16:05:45 -05:00
use rustc ::mir ;
2017-06-02 20:00:35 -05:00
use rustc ::traits ::Reveal ;
2017-07-14 01:42:18 -05:00
use rustc ::ty ;
2016-12-19 01:31:23 -06:00
use rustc ::ty ::layout ::Layout ;
2017-07-19 21:45:25 -05:00
use rustc ::ty ::subst ::Substs ;
2016-12-07 22:30:37 -06:00
2017-07-21 06:39:06 -05:00
use super ::{
EvalResult , EvalError ,
EvalContext , StackPopCleanup , TyAndPacked ,
Global , GlobalId , Lvalue ,
Value , PrimVal ,
HasMemory ,
2017-07-21 10:25:30 -05:00
Machine ,
2017-07-21 06:39:06 -05:00
} ;
2017-07-27 01:43:13 -05:00
use syntax ::codemap ::Span ;
use syntax ::ast ::Mutability ;
2017-07-21 10:25:30 -05:00
impl < ' a , ' tcx , M : Machine < ' tcx > > EvalContext < ' a , ' tcx , M > {
2017-02-04 15:09:10 -06:00
pub fn inc_step_counter_and_check_limit ( & mut self , n : u64 ) -> EvalResult < ' tcx > {
2016-11-17 10:22:34 -06:00
self . steps_remaining = self . steps_remaining . saturating_sub ( n ) ;
if self . steps_remaining > 0 {
Ok ( ( ) )
} else {
Err ( EvalError ::ExecutionTimeLimitReached )
}
}
2016-06-23 01:02:47 -05:00
/// Returns true as long as there are more things to do.
pub fn step ( & mut self ) -> EvalResult < ' tcx , bool > {
2016-11-17 10:22:34 -06:00
self . inc_step_counter_and_check_limit ( 1 ) ? ;
2016-06-23 01:02:47 -05:00
if self . stack . is_empty ( ) {
2016-06-09 03:52:45 -05:00
return Ok ( false ) ;
2016-06-03 08:48:56 -05:00
}
2016-06-23 01:02:47 -05:00
let block = self . frame ( ) . block ;
2016-09-06 09:04:51 -05:00
let stmt_id = self . frame ( ) . stmt ;
2016-06-23 01:02:47 -05:00
let mir = self . mir ( ) ;
2016-06-11 13:38:28 -05:00
let basic_block = & mir . basic_blocks ( ) [ block ] ;
2016-06-01 10:05:20 -05:00
2016-09-13 06:03:42 -05:00
if let Some ( stmt ) = basic_block . statements . get ( stmt_id ) {
2016-07-05 03:47:10 -05:00
let mut new = Ok ( 0 ) ;
2016-06-03 08:48:56 -05:00
ConstantExtractor {
2016-06-11 13:38:28 -05:00
span : stmt . source_info . span ,
2017-03-21 07:53:55 -05:00
instance : self . frame ( ) . instance ,
2016-06-23 01:02:47 -05:00
ecx : self ,
2017-05-04 10:42:43 -05:00
mir ,
2016-07-05 03:47:10 -05:00
new_constants : & mut new ,
2017-01-16 20:45:30 -06:00
} . visit_statement ( block , stmt , mir ::Location { block , statement_index : stmt_id } ) ;
2017-05-26 00:38:07 -05:00
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
2016-07-05 03:47:10 -05:00
if new ? = = 0 {
2016-06-09 03:52:45 -05:00
self . statement ( stmt ) ? ;
2016-06-03 08:48:56 -05:00
}
2016-06-09 03:52:45 -05:00
return Ok ( true ) ;
2016-06-01 10:05:20 -05:00
}
2016-06-03 08:48:56 -05:00
let terminator = basic_block . terminator ( ) ;
2016-07-05 03:47:10 -05:00
let mut new = Ok ( 0 ) ;
2016-06-03 08:48:56 -05:00
ConstantExtractor {
2016-06-11 13:38:28 -05:00
span : terminator . source_info . span ,
2017-03-21 07:53:55 -05:00
instance : self . frame ( ) . instance ,
2016-06-23 01:02:47 -05:00
ecx : self ,
2017-05-04 10:42:43 -05:00
mir ,
2016-07-05 03:47:10 -05:00
new_constants : & mut new ,
2017-01-16 20:45:30 -06:00
} . visit_terminator ( block , terminator , mir ::Location { block , statement_index : stmt_id } ) ;
2017-05-26 00:38:07 -05:00
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
2016-07-05 03:47:10 -05:00
if new ? = = 0 {
2016-06-09 03:52:45 -05:00
self . terminator ( terminator ) ? ;
2016-06-03 08:48:56 -05:00
}
2016-06-09 03:52:45 -05:00
Ok ( true )
2016-06-01 10:05:20 -05:00
}
2016-06-23 01:02:47 -05:00
2017-02-04 15:09:10 -06:00
fn statement ( & mut self , stmt : & mir ::Statement < ' tcx > ) -> EvalResult < ' tcx > {
2016-06-23 01:02:47 -05:00
trace! ( " {:?} " , stmt ) ;
2016-08-27 02:44:46 -05:00
2016-11-03 04:38:08 -05:00
use rustc ::mir ::StatementKind ::* ;
2016-08-27 02:44:46 -05:00
match stmt . kind {
2016-09-19 03:19:31 -05:00
Assign ( ref lvalue , ref rvalue ) = > self . eval_rvalue_into_lvalue ( rvalue , lvalue ) ? ,
2016-12-19 01:31:23 -06:00
SetDiscriminant { ref lvalue , variant_index } = > {
let dest = self . eval_lvalue ( lvalue ) ? ;
let dest_ty = self . lvalue_ty ( lvalue ) ;
let dest_layout = self . type_layout ( dest_ty ) ? ;
match * dest_layout {
2017-02-10 07:32:58 -06:00
Layout ::General { discr , .. } = > {
2017-07-20 09:05:14 -05:00
let discr_size = discr . size ( ) . bytes ( ) ;
let dest_ptr = self . force_allocation ( dest ) ? . to_ptr ( ) ? ;
self . memory . write_uint ( dest_ptr , variant_index as u128 , discr_size ) ?
2016-12-19 01:31:23 -06:00
}
Layout ::RawNullablePointer { nndiscr , .. } = > {
if variant_index as u64 ! = nndiscr {
2017-07-04 07:26:27 -05:00
self . write_null ( dest , dest_ty ) ? ;
2016-12-19 01:31:23 -06:00
}
}
2017-07-20 09:05:14 -05:00
Layout ::StructWrappedNullablePointer { nndiscr , ref discrfield , .. } = > {
if variant_index as u64 ! = nndiscr {
2017-07-28 21:43:05 -05:00
let ( offset , TyAndPacked { ty , packed } ) = self . nonnull_offset_and_ty ( dest_ty , nndiscr , discrfield ) ? ;
2017-07-24 02:56:02 -05:00
let nonnull = self . force_allocation ( dest ) ? . to_ptr ( ) ? . offset ( offset . bytes ( ) , & self ) ? ;
2017-07-20 09:05:14 -05:00
trace! ( " struct wrapped nullable pointer type: {} " , ty ) ;
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self . type_size ( ty ) ? . expect ( " bad StructWrappedNullablePointer discrfield " ) ;
2017-07-27 11:14:04 -05:00
self . write_maybe_aligned_mut ( ! packed , | ectx | ectx . memory . write_uint ( nonnull , 0 , discr_size ) ) ? ;
2017-07-20 09:05:14 -05:00
}
} ,
2016-12-19 01:31:23 -06:00
_ = > bug! ( " SetDiscriminant on {} represented as {:#?} " , dest_ty , dest_layout ) ,
}
}
2016-08-27 02:44:46 -05:00
2017-05-31 19:41:33 -05:00
// Mark locals as dead or alive.
StorageLive ( ref lvalue ) | StorageDead ( ref lvalue ) = > {
let ( frame , local ) = match self . eval_lvalue ( lvalue ) ? {
2017-06-16 19:58:18 -05:00
Lvalue ::Local { frame , local } if self . cur_frame ( ) = = frame = > ( frame , local ) ,
2017-06-01 23:53:52 -05:00
_ = > return Err ( EvalError ::Unimplemented ( " Storage annotations must refer to locals of the topmost stack frame. " . to_owned ( ) ) ) // FIXME maybe this should get its own error type
2017-05-31 19:41:33 -05:00
} ;
2017-06-01 19:59:00 -05:00
let old_val = match stmt . kind {
2017-05-31 19:41:33 -05:00
StorageLive ( _ ) = > self . stack [ frame ] . storage_live ( local ) ? ,
2017-06-01 19:59:00 -05:00
StorageDead ( _ ) = > self . stack [ frame ] . storage_dead ( local ) ? ,
_ = > bug! ( " We already checked that we are a storage stmt " )
2017-05-31 19:41:33 -05:00
} ;
2017-06-01 19:59:00 -05:00
self . deallocate_local ( old_val ) ? ;
2017-05-31 19:41:33 -05:00
}
2016-09-22 00:16:31 -05:00
2017-07-26 13:27:40 -05:00
// NOPs for now.
EndRegion ( _ce ) = > { }
2017-06-20 05:35:46 -05:00
2016-09-22 00:16:31 -05:00
// Defined to do nothing. These are added by optimization passes, to avoid changing the
// size of MIR constantly.
Nop = > { }
2017-02-24 03:39:55 -06:00
InlineAsm { .. } = > return Err ( EvalError ::InlineAsm ) ,
2016-08-27 02:44:46 -05:00
}
2016-06-23 01:02:47 -05:00
self . frame_mut ( ) . stmt + = 1 ;
Ok ( ( ) )
}
2017-02-04 15:09:10 -06:00
fn terminator ( & mut self , terminator : & mir ::Terminator < ' tcx > ) -> EvalResult < ' tcx > {
2016-06-23 01:02:47 -05:00
trace! ( " {:?} " , terminator . kind ) ;
self . eval_terminator ( terminator ) ? ;
if ! self . stack . is_empty ( ) {
trace! ( " // {:?} " , self . frame ( ) . block ) ;
}
Ok ( ( ) )
}
2016-06-03 08:48:56 -05:00
}
2016-06-02 10:05:17 -05:00
2016-06-10 09:56:04 -05:00
// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
2016-06-09 10:24:42 -05:00
// this includes any method that might access the stack
2016-10-14 04:31:45 -05:00
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
2016-06-09 10:24:42 -05:00
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
2017-07-21 10:25:30 -05:00
struct ConstantExtractor < ' a , ' b : ' a , ' tcx : ' b , M : Machine < ' tcx > + ' a > {
2016-06-03 08:48:56 -05:00
span : Span ,
2017-07-21 10:25:30 -05:00
ecx : & ' a mut EvalContext < ' b , ' tcx , M > ,
2017-05-04 10:42:43 -05:00
mir : & ' tcx mir ::Mir < ' tcx > ,
2017-03-21 07:53:55 -05:00
instance : ty ::Instance < ' tcx > ,
2016-07-05 03:47:10 -05:00
new_constants : & ' a mut EvalResult < ' tcx , u64 > ,
2016-06-02 10:05:17 -05:00
}
2017-07-21 10:25:30 -05:00
impl < ' a , ' b , ' tcx , M : Machine < ' tcx > > ConstantExtractor < ' a , ' b , ' tcx , M > {
2017-02-10 05:24:05 -06:00
fn global_item (
& mut self ,
def_id : DefId ,
2017-07-13 18:48:29 -05:00
substs : & ' tcx Substs < ' tcx > ,
2017-02-10 05:24:05 -06:00
span : Span ,
2017-07-13 10:25:17 -05:00
mutability : Mutability ,
2017-02-10 05:24:05 -06:00
) {
2017-03-21 07:53:55 -05:00
let instance = self . ecx . resolve_associated_const ( def_id , substs ) ;
let cid = GlobalId { instance , promoted : None } ;
2016-10-21 04:39:39 -05:00
if self . ecx . globals . contains_key ( & cid ) {
2016-06-03 10:41:36 -05:00
return ;
}
2017-05-25 17:19:38 -05:00
if self . ecx . tcx . has_attr ( def_id , " linkage " ) {
trace! ( " Initializing an extern global with NULL " ) ;
2017-07-13 10:25:17 -05:00
self . ecx . globals . insert ( cid , Global ::initialized ( self . ecx . tcx . type_of ( def_id ) , Value ::ByVal ( PrimVal ::Bytes ( 0 ) ) , mutability ) ) ;
2017-05-25 17:19:38 -05:00
return ;
}
2016-07-05 03:47:10 -05:00
self . try ( | this | {
2017-03-21 07:53:55 -05:00
let mir = this . ecx . load_mir ( instance . def ) ? ;
2016-10-21 04:39:39 -05:00
this . ecx . globals . insert ( cid , Global ::uninitialized ( mir . return_ty ) ) ;
2017-07-13 10:25:17 -05:00
let internally_mutable = ! mir . return_ty . is_freeze (
2017-04-23 12:45:04 -05:00
this . ecx . tcx ,
2017-06-02 20:00:35 -05:00
ty ::ParamEnv ::empty ( Reveal ::All ) ,
2017-04-23 12:45:04 -05:00
span ) ;
2017-07-13 10:25:17 -05:00
let mutability = if mutability = = Mutability ::Mutable | | internally_mutable {
Mutability ::Mutable
} else {
Mutability ::Immutable
} ;
let cleanup = StackPopCleanup ::MarkStatic ( mutability ) ;
2017-02-07 09:02:45 -06:00
let name = ty ::tls ::with ( | tcx | tcx . item_path_str ( def_id ) ) ;
trace! ( " pushing stack frame for global: {} " , name ) ;
2017-02-10 05:24:05 -06:00
this . ecx . push_stack_frame (
2017-03-21 07:53:55 -05:00
instance ,
2017-02-10 05:24:05 -06:00
span ,
mir ,
2017-06-19 06:29:04 -05:00
Lvalue ::Global ( cid ) ,
2017-02-10 05:24:05 -06:00
cleanup ,
)
2016-07-05 03:47:10 -05:00
} ) ;
}
2017-05-25 17:19:38 -05:00
2017-02-04 15:09:10 -06:00
fn try < F : FnOnce ( & mut Self ) -> EvalResult < ' tcx > > ( & mut self , f : F ) {
2016-07-05 03:47:10 -05:00
if let Ok ( ref mut n ) = * self . new_constants {
* n + = 1 ;
} else {
return ;
}
if let Err ( e ) = f ( self ) {
* self . new_constants = Err ( e ) ;
}
2016-06-03 08:48:56 -05:00
}
2016-06-02 10:05:17 -05:00
}
2017-07-21 10:25:30 -05:00
impl < ' a , ' b , ' tcx , M : Machine < ' tcx > > Visitor < ' tcx > for ConstantExtractor < ' a , ' b , ' tcx , M > {
2016-09-06 09:04:51 -05:00
fn visit_constant ( & mut self , constant : & mir ::Constant < ' tcx > , location : mir ::Location ) {
self . super_constant ( constant , location ) ;
2016-06-02 10:05:17 -05:00
match constant . literal {
// already computed by rustc
mir ::Literal ::Value { .. } = > { }
2016-06-03 08:48:56 -05:00
mir ::Literal ::Item { def_id , substs } = > {
2017-07-13 10:25:17 -05:00
self . global_item ( def_id , substs , constant . span , Mutability ::Immutable ) ;
2016-06-03 08:48:56 -05:00
} ,
2016-06-02 10:05:17 -05:00
mir ::Literal ::Promoted { index } = > {
2016-10-21 04:39:39 -05:00
let cid = GlobalId {
2017-03-21 07:53:55 -05:00
instance : self . instance ,
2016-10-21 04:54:38 -05:00
promoted : Some ( index ) ,
2016-06-03 10:41:36 -05:00
} ;
2016-10-21 04:39:39 -05:00
if self . ecx . globals . contains_key ( & cid ) {
2016-06-03 08:48:56 -05:00
return ;
}
2017-05-04 10:42:43 -05:00
let mir = & self . mir . promoted [ index ] ;
2016-07-05 03:47:10 -05:00
self . try ( | this | {
2017-03-21 07:53:55 -05:00
let ty = this . ecx . monomorphize ( mir . return_ty , this . instance . substs ) ;
2016-10-21 04:39:39 -05:00
this . ecx . globals . insert ( cid , Global ::uninitialized ( ty ) ) ;
2017-02-07 09:02:45 -06:00
trace! ( " pushing stack frame for {:?} " , index ) ;
2017-03-21 07:53:55 -05:00
this . ecx . push_stack_frame ( this . instance ,
2016-09-09 10:44:04 -05:00
constant . span ,
mir ,
2017-06-19 06:29:04 -05:00
Lvalue ::Global ( cid ) ,
2017-07-13 10:25:17 -05:00
StackPopCleanup ::MarkStatic ( Mutability ::Immutable ) ,
2017-05-31 19:41:33 -05:00
)
2016-07-05 03:47:10 -05:00
} ) ;
2016-06-02 10:05:17 -05:00
}
}
}
2016-09-22 00:16:31 -05:00
fn visit_lvalue (
& mut self ,
lvalue : & mir ::Lvalue < ' tcx > ,
context : LvalueContext < ' tcx > ,
location : mir ::Location
) {
2016-09-06 09:04:51 -05:00
self . super_lvalue ( lvalue , context , location ) ;
2017-03-14 03:23:15 -05:00
if let mir ::Lvalue ::Static ( ref static_ ) = * lvalue {
let def_id = static_ . def_id ;
2016-11-01 17:26:04 -05:00
let substs = self . ecx . tcx . intern_substs ( & [ ] ) ;
2016-06-03 08:48:56 -05:00
let span = self . span ;
2017-01-29 01:21:24 -06:00
if let Some ( node_item ) = self . ecx . tcx . hir . get_if_local ( def_id ) {
2016-09-27 10:02:04 -05:00
if let hir ::map ::Node ::NodeItem ( & hir ::Item { ref node , .. } ) = node_item {
if let hir ::ItemStatic ( _ , m , _ ) = * node {
2017-07-13 10:25:17 -05:00
self . global_item ( def_id , substs , span , if m = = hir ::MutMutable { Mutability ::Mutable } else { Mutability ::Immutable } ) ;
2016-09-27 10:02:04 -05:00
return ;
} else {
bug! ( " static def id doesn't point to static " ) ;
}
2016-09-09 10:44:04 -05:00
} else {
2016-09-27 10:02:04 -05:00
bug! ( " static def id doesn't point to item " ) ;
2016-09-09 10:44:04 -05:00
}
} else {
2017-05-02 03:44:35 -05:00
let def = self . ecx . tcx . describe_def ( def_id ) . expect ( " static not found " ) ;
2016-09-27 10:02:04 -05:00
if let hir ::def ::Def ::Static ( _ , mutable ) = def {
2017-07-13 10:25:17 -05:00
self . global_item ( def_id , substs , span , if mutable { Mutability ::Mutable } else { Mutability ::Immutable } ) ;
2016-09-27 10:02:04 -05:00
} else {
bug! ( " static found but isn't a static: {:?} " , def ) ;
}
2016-09-09 10:44:04 -05:00
}
2016-06-02 10:05:17 -05:00
}
}
2016-06-01 10:05:20 -05:00
}