Skip to content

Commit

Permalink
Merge pull request rust-lang#24 from oli-obk/typesafe_fn_calls
Browse files Browse the repository at this point in the history
Typesafe fn calls
  • Loading branch information
solson authored Jun 15, 2016
2 parents 1146504 + c36dcff commit b24edd6
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 70 deletions.
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 @@ -163,7 +163,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 @@ -372,7 +372,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 @@ -438,7 +438,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 @@ -484,7 +487,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 @@ -563,7 +566,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 @@ -605,7 +608,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 @@ -633,7 +636,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 @@ -650,7 +653,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 @@ -796,7 +799,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 @@ -859,7 +862,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 @@ -870,7 +873,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 @@ -1099,8 +1102,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 @@ -1116,7 +1119,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 @@ -1137,7 +1140,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 @@ -1150,7 +1153,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 @@ -1166,7 +1169,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 @@ -1183,7 +1186,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 @@ -1217,7 +1220,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 @@ -1325,7 +1328,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 @@ -1334,7 +1337,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

0 comments on commit b24edd6

Please sign in to comment.