Skip to content

Typesafe fn calls #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions benches/fibonacci_helper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#![feature(custom_attribute)]
#![allow(unused_attributes)]

#[miri_run]
#[inline(never)]
pub fn main() {
assert_eq!(fib(10), 55);
Expand Down
4 changes: 0 additions & 4 deletions benches/fibonacci_helper_iterative.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#![feature(custom_attribute)]
#![allow(unused_attributes)]

#[miri_run]
#[inline(never)]
pub fn main() {
assert_eq!(fib(10), 55);
Expand Down
4 changes: 0 additions & 4 deletions benches/smoke_helper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#![feature(custom_attribute)]
#![allow(unused_attributes)]

#[miri_run]
#[inline(never)]
pub fn main() {
}
73 changes: 32 additions & 41 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ use rustc::session::Session;
use rustc_driver::{driver, CompilerCalls};
use rustc::ty::{TyCtxt, subst};
use rustc::mir::mir_map::MirMap;
use rustc::mir::repr::Mir;
use rustc::hir::def_id::DefId;
use rustc::hir::{map, ItemFn, Item};
use syntax::codemap::Span;

struct MiriCompilerCalls;

Expand All @@ -34,58 +37,46 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {

control.after_analysis.callback = Box::new(|state| {
state.session.abort_if_errors();
interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap());

let tcx = state.tcx.unwrap();
let mir_map = state.mir_map.unwrap();
let (span, mir, def_id) = get_main(tcx, mir_map);
println!("found `main` function at: {:?}", span);

let mut ecx = EvalContext::new(tcx, mir_map);
let substs = tcx.mk_substs(subst::Substs::empty());
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging");

ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr));

loop {
match step(&mut ecx) {
Ok(true) => {}
Ok(false) => break,
// FIXME: diverging functions can end up here in some future miri
Err(e) => {
report(tcx, &ecx, e);
break;
}
}
}
});

control
}
}



fn interpret_start_points<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir_map: &MirMap<'tcx>,
) {
let initial_indentation = ::log_settings::settings().indentation;
fn get_main<'a, 'b, 'tcx: 'b>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'b MirMap<'tcx>) -> (Span, &'b Mir<'tcx>, DefId) {
for (&id, mir) in &mir_map.map {
for attr in tcx.map.attrs(id) {
use syntax::attr::AttrMetaMethods;
if attr.check_name("miri_run") {
let item = tcx.map.expect_item(id);

::log_settings::settings().indentation = initial_indentation;

debug!("Interpreting: {}", item.name);

let mut ecx = EvalContext::new(tcx, mir_map);
let substs = tcx.mk_substs(subst::Substs::empty());
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs);

ecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr);

loop {
match step(&mut ecx) {
Ok(true) => {}
Ok(false) => {
match return_ptr {
Some(ptr) => if log_enabled!(::log::LogLevel::Debug) {
ecx.memory().dump(ptr.alloc_id);
},
None => warn!("diverging function returned"),
}
break;
}
// FIXME: diverging functions can end up here in some future miri
Err(e) => {
report(tcx, &ecx, e);
break;
}
}
if let map::Node::NodeItem(&Item { name, span, ref node, .. }) = tcx.map.get(id) {
if let ItemFn(..) = *node {
if name.as_str() == "main" {
return (span, mir, tcx.map.local_def_id(id));
}
}
}
}
panic!("no main function found");
}

fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
Expand Down
14 changes: 10 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::error::Error;
use std::fmt;
use rustc::mir::repr as mir;
use rustc::ty::BareFnTy;
use memory::Pointer;

#[derive(Clone, Debug)]
pub enum EvalError {
pub enum EvalError<'tcx> {
FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>),
DanglingPointerDeref,
InvalidFunctionPointer,
InvalidBool,
Expand All @@ -24,11 +26,13 @@ pub enum EvalError {
ExecuteMemory,
}

pub type EvalResult<T> = Result<T, EvalError>;
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;

impl Error for EvalError {
impl<'tcx> Error for EvalError<'tcx> {
fn description(&self) -> &str {
match *self {
EvalError::FunctionPointerTyMismatch(..) =>
"tried to call a function through a function pointer of a different type",
EvalError::DanglingPointerDeref =>
"dangling pointer was dereferenced",
EvalError::InvalidFunctionPointer =>
Expand Down Expand Up @@ -60,13 +64,15 @@ impl Error for EvalError {
fn cause(&self) -> Option<&Error> { None }
}

impl fmt::Display for EvalError {
impl<'tcx> fmt::Display for EvalError<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
EvalError::PointerOutOfBounds { ptr, size, allocation_size } => {
write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}",
ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size)
},
EvalError::FunctionPointerTyMismatch(expected, got) =>
write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got),
_ => write!(f, "{}", self.description()),
}
}
Expand Down
49 changes: 26 additions & 23 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ use syntax::attr;
use syntax::codemap::{self, DUMMY_SP, Span};

use error::{EvalError, EvalResult};
use memory::{Memory, Pointer};
use memory::{Memory, Pointer, FunctionDefinition};
use primval::{self, PrimVal};

use std::collections::HashMap;

mod stepper;

pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult<bool> {
pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, bool> {
stepper::Stepper::new(ecx).step()
}

Expand Down Expand Up @@ -159,7 +159,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

// TODO(solson): Try making const_to_primval instead.
fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<Pointer> {
fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> {
use rustc::middle::const_val::ConstVal::*;
match *const_val {
Float(_f) => unimplemented!(),
Expand Down Expand Up @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>)
-> EvalResult<()> {
-> EvalResult<'tcx, ()> {
use rustc::mir::repr::TerminatorKind::*;
match terminator.kind {
Return => self.pop_stack_frame(),
Expand Down Expand Up @@ -434,7 +434,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ptr = self.eval_operand(func)?;
assert_eq!(ptr.offset, 0);
let fn_ptr = self.memory.read_ptr(ptr)?;
let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?;
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
if fn_ty != bare_fn_ty {
return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty));
}
self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args,
terminator.source_info.span)?
},
Expand Down Expand Up @@ -480,7 +483,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
return_ptr: Option<Pointer>,
args: &[mir::Operand<'tcx>],
span: Span,
) -> EvalResult<()> {
) -> EvalResult<'tcx, ()> {
use syntax::abi::Abi;
match fn_ty.abi {
Abi::RustIntrinsic => {
Expand Down Expand Up @@ -559,7 +562,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}

fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> {
fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
if !self.type_needs_drop(ty) {
debug!("no need to drop {:?}", ty);
return Ok(());
Expand Down Expand Up @@ -601,7 +604,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(())
}

fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<u64> {
fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> {
use rustc::ty::layout::Layout::*;
let adt_layout = self.type_layout(adt_ty);

Expand Down Expand Up @@ -629,7 +632,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(discr_val)
}

fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<u64> {
fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> {
let not_null = match self.memory.read_usize(ptr) {
Ok(0) => false,
Ok(_) | Err(EvalError::ReadPointerAsBytes) => true,
Expand All @@ -646,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
args: &[mir::Operand<'tcx>],
dest: Pointer,
dest_size: usize
) -> EvalResult<()> {
) -> EvalResult<'tcx, ()> {
let args_res: EvalResult<Vec<Pointer>> = args.iter()
.map(|arg| self.eval_operand(arg))
.collect();
Expand Down Expand Up @@ -792,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
args: &[mir::Operand<'tcx>],
dest: Pointer,
dest_size: usize,
) -> EvalResult<()> {
) -> EvalResult<'tcx, ()> {
let name = self.tcx.item_name(def_id);
let attrs = self.tcx.get_attrs(def_id);
let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
Expand Down Expand Up @@ -855,7 +858,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
dest: Pointer,
offsets: I,
operands: &[mir::Operand<'tcx>],
) -> EvalResult<()> {
) -> EvalResult<'tcx, ()> {
for (offset, operand) in offsets.into_iter().zip(operands) {
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);
Expand All @@ -866,7 +869,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>)
-> EvalResult<()>
-> EvalResult<'tcx, ()>
{
let dest = self.eval_lvalue(lvalue)?.to_ptr();
let dest_ty = self.lvalue_ty(lvalue);
Expand Down Expand Up @@ -1095,8 +1098,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

ReifyFnPointer => match self.operand_ty(operand).sty {
ty::TyFnDef(def_id, substs, _) => {
let fn_ptr = self.memory.create_fn_ptr(def_id, substs);
ty::TyFnDef(def_id, substs, fn_ty) => {
let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty);
self.memory.write_ptr(dest, fn_ptr)?;
},
ref other => panic!("reify fn pointer on {:?}", other),
Expand All @@ -1112,7 +1115,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(())
}

fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<Size> {
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> {
// Skip the constant 0 at the start meant for LLVM GEP.
let mut path = discrfield.iter().skip(1).map(|&i| i as usize);

Expand All @@ -1133,7 +1136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.field_path_offset(inner_ty, path)
}

fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<Size> {
fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, Size> {
let mut offset = Size::from_bytes(0);

// Skip the initial 0 intended for LLVM GEP.
Expand All @@ -1146,7 +1149,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(offset)
}

fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Ty<'tcx>> {
fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
match ty.sty {
ty::TyStruct(adt_def, substs) => {
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
Expand All @@ -1162,7 +1165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}

fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Size> {
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> {
let layout = self.type_layout(ty);

use rustc::ty::layout::Layout::*;
Expand All @@ -1179,7 +1182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}

fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<Pointer> {
fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> {
use rustc::mir::repr::Operand::*;
match *op {
Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()),
Expand Down Expand Up @@ -1213,7 +1216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}

fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<Lvalue> {
fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
use rustc::mir::repr::Lvalue::*;
let ptr = match *lvalue {
ReturnPointer => self.frame().return_ptr
Expand Down Expand Up @@ -1321,7 +1324,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.monomorphize(self.mir().operand_ty(self.tcx, operand), self.substs())
}

fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> {
fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
let size = self.type_size(ty);
self.memory.copy(src, dest, size)?;
if self.type_needs_drop(ty) {
Expand All @@ -1330,7 +1333,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(())
}

pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<PrimVal> {
pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use syntax::ast::{IntTy, UintTy};
let val = match (self.memory.pointer_size, &ty.sty) {
(_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?),
Expand Down
6 changes: 3 additions & 3 deletions src/interpreter/stepper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> {
}
}

fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> {
trace!("{:?}", stmt);
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
self.ecx.eval_assignment(lvalue, rvalue)?;
self.ecx.frame_mut().stmt += 1;
Ok(())
}

fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> {
// after a terminator we go to a new block
self.ecx.frame_mut().stmt = 0;
trace!("{:?}", terminator.kind);
Expand All @@ -43,7 +43,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> {
}

// returns true as long as there are more things to do
pub(super) fn step(&mut self) -> EvalResult<bool> {
pub(super) fn step(&mut self) -> EvalResult<'tcx, bool> {
if self.ecx.stack.is_empty() {
return Ok(false);
}
Expand Down
Loading