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