Skip to content

rustup to rustc 1.17.0-nightly (60a0edc6c 2017-02-26) #147

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 8 commits into from
Mar 14, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 11 additions & 6 deletions src/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,16 +649,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
},

UnsafeFnPointer => match dest_ty.sty {
ty::TyFnPtr(unsafe_fn_ty) => {
ty::TyFnPtr(_) => {
let src = self.eval_operand(operand)?;
let ptr = src.read_ptr(&self.memory)?;
let fn_def = self.memory.get_fn(ptr.alloc_id)?.expect_concrete()?;
let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty);
let fn_ptr = self.memory.create_fn_ptr(self.tcx, fn_def.def_id, fn_def.substs, unsafe_fn_ty);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
self.write_value(src, dest, dest_ty)?;
},
ref other => bug!("fn to unsafe fn cast on {:?}", other),
},

ClosureFnPointer => match self.operand_ty(operand).sty {
ty::TyClosure(def_id, substs) => {
let fn_ty = self.tcx.closure_type(def_id, substs);
let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(self.tcx, def_id, substs, fn_ty);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
},
ref other => bug!("reify fn pointer on {:?}", other),
},
}
}

Expand Down
29 changes: 23 additions & 6 deletions src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,11 @@ pub enum Function<'tcx> {
FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>),
/// Glue for Closures
Closure(FunctionDefinition<'tcx>),
/// Glue for noncapturing closures casted to function pointers
NonCaptureClosureAsFnPtr(FunctionDefinition<'tcx>),
}

impl<'tcx> Function<'tcx> {
pub fn expect_concrete(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> {
match self {
Function::Concrete(fn_def) => Ok(fn_def),
other => Err(EvalError::ExpectedConcreteFunction(other)),
}
}
pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> {
match self {
Function::DropGlue(real_ty) => Ok(real_ty),
Expand Down Expand Up @@ -238,6 +234,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}))
}

pub fn create_fn_ptr_from_noncapture_closure(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer {
// FIXME: this is a hack
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
unsafety: fn_ty.unsafety,
abi: fn_ty.abi,
sig: fn_ty.sig,
});
self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition {
def_id,
substs: substs.substs,
abi: Abi::Rust, // adjust abi
// FIXME: why doesn't this compile?
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
sig: fn_ty.sig.skip_binder(),
}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the way to go. Did you see how this was implemented in librustc_trans?
That is, it relies on an existing shim- oh, drat, you have that extra argument to take care of, nevermind me!

}

pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition {
def_id,
Expand Down Expand Up @@ -535,6 +548,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
trace!("{} closure glue for {}", msg, dump_fn_def(fn_def));
continue;
},
(None, Some(&Function::NonCaptureClosureAsFnPtr(fn_def))) => {
trace!("{} non-capture closure as fn ptr glue for {}", msg, dump_fn_def(fn_def));
continue;
},
(None, None) => {
trace!("{} (deallocated)", msg);
continue;
Expand Down
230 changes: 144 additions & 86 deletions src/terminator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, BareFnTy};
use rustc::ty::{self, Ty};
use syntax::codemap::Span;
use syntax::attr;

use error::{EvalError, EvalResult};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
use lvalue::Lvalue;
use memory::{Pointer, FunctionDefinition};
use memory::{Pointer, FunctionDefinition, Function};
use value::PrimVal;
use value::Value;

Expand Down Expand Up @@ -61,35 +61,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};

let func_ty = self.operand_ty(func);
match func_ty.sty {
let fn_def = match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?;
let FunctionDefinition {def_id, substs, abi, sig} = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?;
let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?;
let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig);
let bare_sig = self.tcx.erase_regions(&bare_sig);
// transmuting function pointers in miri is fine as long as the number of
// arguments and the abi don't change.
// FIXME: also check the size of the arguments' type and the return type
// Didn't get it to work, since that triggers an assertion in rustc which
// checks whether the type has escaping regions
if abi != bare_fn_ty.abi ||
sig.variadic != bare_sig.variadic ||
sig.inputs().len() != bare_sig.inputs().len() {
return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty));
match fn_def {
Function::Concrete(fn_def) => {
// transmuting function pointers in miri is fine as long as the number of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if we check ABI, number of arguments, and argument and return type sizes, we might still not match true target calling convention behaviour because of weird differences between, for example, T and struct Foo { x: T }. I think maybe we should be more conservative and prevent the calling of transmuted function pointers.

Specifically, error out at call time when the call-site function type doesn't match the type of the function actually being dynamically called, since Miri can remember that. We should still allow round-tripping transmutes that never call a fn ptr with the wrong type, just like we allow round trips with other transmutes.

Differences like unsafe vs. non-unsafe should be fine, though. Anything that clearly doesn't affect the calling convention.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea... I tried to do that check, but since we also don't care about lifetime changes I tried to erase the lifetimes, which kept causing rustc to panic in weird corner cases.

I'll change the FIXME text

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh... I remember sth else. Rustc tests whether we can transmute fn foo(*const i32) to fn foo(Option<&i32>) (or maybe the other direction).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to be more conservative than rustc about function pointer casts until a more formal definition comes along, unless it becomes essential for evaluating std code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reran on rust test suite:

   1 tried to call a function with sig fn(&[u8]) -> std::option::Option<u8> through a function pointer of type fn(&[u8]) -> std::option::Option<u8>
   1 tried to call a function with sig fn(&A<'_>) through a function pointer of type fn(&A)
   1 tried to call a function with sig extern "C" fn(usize) -> Foo through a function pointer of type extern "C" fn(usize) -> u32
   1 tried to call a function with sig extern "C" fn(&isize) -> std::option::Option<&isize> through a function pointer of type extern "C" fn(&isize) -> &isize

Copy link
Member

@solson solson Mar 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. fn(&[u8]) -> std::option::Option<u8>
    fn(&[u8]) -> std::option::Option<u8>
  2. fn(&A<'_>)
    fn(&A)
  3. extern "C" fn(usize) -> Foo
    extern "C" fn(usize) -> u32
  4. extern "C" fn(&isize) -> std::option::Option<&isize>
    extern "C" fn(&isize) -> &isize

Of these, I think we should make sure 1 and 2 work (ignore lifetimes). I don't care about supporting 3 and 4 for now.

// arguments and the abi don't change.
// FIXME: also check the size of the arguments' type and the return type
// Didn't get it to work, since that triggers an assertion in rustc which
// checks whether the type has escaping regions
if fn_def.abi != bare_fn_ty.abi ||
fn_def.sig.variadic != bare_sig.variadic ||
fn_def.sig.inputs().len() != bare_sig.inputs().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
}
},
Function::NonCaptureClosureAsFnPtr(fn_def) => {
if fn_def.abi != bare_fn_ty.abi ||
fn_def.sig.variadic != bare_sig.variadic ||
fn_def.sig.inputs().len() != 1 {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
}
if let ty::TyTuple(fields, _) = fn_def.sig.inputs()[0].sty {
if fields.len() != bare_sig.inputs().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
}
}
},
other => return Err(EvalError::ExpectedConcreteFunction(other)),
}
self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args,
terminator.source_info.span)?
self.memory.get_fn(fn_ptr.alloc_id)?
},
ty::TyFnDef(def_id, substs, fn_ty) => {
self.eval_fn_call(def_id, substs, fn_ty, destination, args,
terminator.source_info.span)?
}
ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition {
def_id,
substs,
abi: fn_ty.abi,
sig: fn_ty.sig.skip_binder(),
}),

_ => {
let msg = format!("can't handle callee of type {:?}", func_ty);
return Err(EvalError::Unimplemented(msg));
}
}
};
self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?;
}

Drop { ref location, target, .. } => {
Expand Down Expand Up @@ -138,17 +157,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {

fn eval_fn_call(
&mut self,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
fn_ty: &'tcx BareFnTy,
fn_def: Function<'tcx>,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
arg_operands: &[mir::Operand<'tcx>],
span: Span,
) -> EvalResult<'tcx> {
use syntax::abi::Abi;
match fn_ty.abi {
Abi::RustIntrinsic => {
let ty = fn_ty.sig.0.output();
match fn_def {
// Intrinsics can only be addressed directly
Function::Concrete(FunctionDefinition { def_id, substs, abi: Abi::RustIntrinsic, sig }) => {
let ty = sig.output();
let layout = self.type_layout(ty)?;
let (ret, target) = match destination {
Some(dest) if is_inhabited(self.tcx, ty) => dest,
Expand All @@ -157,18 +175,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?;
self.dump_local(ret);
Ok(())
}

Abi::C => {
let ty = fn_ty.sig.0.output();
},
// C functions can only be addressed directly
Function::Concrete(FunctionDefinition { def_id, abi: Abi::C, sig, ..}) => {
let ty = sig.output();
let (ret, target) = destination.unwrap();
self.call_c_abi(def_id, arg_operands, ret, ty)?;
self.dump_local(ret);
self.goto_block(target);
Ok(())
}

Abi::Rust | Abi::RustCall => {
},
Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue),
Function::Concrete(FunctionDefinition { def_id, abi: Abi::RustCall, sig, substs }) |
Function::Concrete(FunctionDefinition { def_id, abi: Abi::Rust, sig, substs }) => {
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
Expand All @@ -185,7 +204,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};

// FIXME(eddyb) Detect ADT constructors more efficiently.
if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() {
if let Some(adt_def) = sig.output().ty_adt_def() {
if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) {
let (lvalue, target) = destination.expect("tuple struct constructors can't diverge");
let dest_ty = self.tcx.item_type(adt_def.did);
Expand Down Expand Up @@ -240,66 +259,105 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
return Ok(());
}
}

let mir = match self.load_mir(resolved_def_id) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
match &path[..] {
// let's just ignore all output for now
"std::io::_print" => {
self.goto_block(destination.unwrap().1);
return Ok(());
},
"std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())),
"std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())),
"std::panicking::rust_panic_with_hook" |
"std::rt::begin_panic_fmt" => return Err(EvalError::Panic),
"std::panicking::panicking" |
"std::rt::panicking" => {
let (lval, block) = destination.expect("std::rt::panicking does not diverge");
// we abort on panic -> `std::rt::panicking` always returns false
let bool = self.tcx.types.bool;
self.write_primval(lval, PrimVal::from_bool(false), bool)?;
self.goto_block(block);
return Ok(());
}
_ => {},
}
return Err(EvalError::NoMirFor(path));
},
Err(other) => return Err(other),
};
let (return_lvalue, return_to_block) = match destination {
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => {
// FIXME(solson)
let lvalue = Lvalue::from_ptr(Pointer::never_ptr());
(lvalue, StackPopCleanup::None)
}
};

self.push_stack_frame(
self.eval_fn_call_inner(
resolved_def_id,
span,
mir,
resolved_substs,
return_lvalue,
return_to_block,
destination,
args,
temporaries,
)?;

let arg_locals = self.frame().mir.args_iter();
assert_eq!(self.frame().mir.arg_count, args.len());
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
span,
)
},
Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, abi: Abi::Rust, substs, sig }) => {
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
args.insert(0, (
Value::ByVal(PrimVal::Undef),
sig.inputs()[0],
));
self.eval_fn_call_inner(
def_id,
substs,
destination,
args,
Vec::new(),
span,
)
}
other => Err(EvalError::Unimplemented(format!("can't call function kind {:?}", other))),
}
}

Ok(())
fn eval_fn_call_inner(
&mut self,
resolved_def_id: DefId,
resolved_substs: &'tcx Substs,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
args: Vec<(Value, Ty<'tcx>)>,
temporaries: Vec<(Pointer, Ty<'tcx>)>,
span: Span,
) -> EvalResult<'tcx> {
trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", args, temporaries, destination);

let mir = match self.load_mir(resolved_def_id) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
match &path[..] {
// let's just ignore all output for now
"std::io::_print" => {
self.goto_block(destination.unwrap().1);
return Ok(());
},
"std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())),
"std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())),
"std::panicking::rust_panic_with_hook" |
"std::rt::begin_panic_fmt" => return Err(EvalError::Panic),
"std::panicking::panicking" |
"std::rt::panicking" => {
let (lval, block) = destination.expect("std::rt::panicking does not diverge");
// we abort on panic -> `std::rt::panicking` always returns false
let bool = self.tcx.types.bool;
self.write_primval(lval, PrimVal::from_bool(false), bool)?;
self.goto_block(block);
return Ok(());
}
_ => {},
}
return Err(EvalError::NoMirFor(path));
},
Err(other) => return Err(other),
};
let (return_lvalue, return_to_block) = match destination {
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => {
// FIXME(solson)
let lvalue = Lvalue::from_ptr(Pointer::never_ptr());
(lvalue, StackPopCleanup::None)
}
};

abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
self.push_stack_frame(
resolved_def_id,
span,
mir,
resolved_substs,
return_lvalue,
return_to_block,
temporaries,
)?;

let arg_locals = self.frame().mir.args_iter();
assert_eq!(self.frame().mir.arg_count, args.len());
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
}

Ok(())
}

pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
Expand Down
Loading