From 8878a4030acf5285a48e7e248e5f96e4d01060ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 28 Feb 2017 12:35:00 +0100 Subject: [PATCH] rustup to rustc 1.17.0-nightly (60a0edc6c 2017-02-26) --- src/eval_context.rs | 17 +- src/memory.rs | 29 ++- src/terminator/mod.rs | 230 +++++++++++------- src/traits.rs | 23 +- .../run-pass/non_capture_closure_to_fn_ptr.rs | 16 ++ tests/run-pass/recursive_static.rs | 2 - 6 files changed, 215 insertions(+), 102 deletions(-) create mode 100644 tests/run-pass/non_capture_closure_to_fn_ptr.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ab6e85e389..1a1102c13a1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -649,16 +649,21 @@ pub(super) fn eval_rvalue_into_lvalue( }, 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), + }, } } diff --git a/src/memory.rs b/src/memory.rs index 459a9bed412..fd035b7fcba 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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), @@ -238,6 +234,23 @@ pub fn create_closure_ptr(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, })) } + 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(), + })) + } + 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, @@ -535,6 +548,10 @@ pub fn dump_allocs(&self, mut allocs: Vec) { 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; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 183a3f54fb6..0befb5ba9fc 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,14 +2,14 @@ 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; @@ -61,35 +61,54 @@ pub(super) fn eval_terminator( }; 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 + // 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, .. } => { @@ -138,17 +157,16 @@ pub(super) fn eval_terminator( 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, @@ -157,18 +175,19 @@ fn eval_fn_call( 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)?; @@ -185,7 +204,7 @@ fn eval_fn_call( }; // 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); @@ -240,66 +259,105 @@ fn eval_fn_call( 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> { diff --git a/src/traits.rs b/src/traits.rs index 733095322d0..36293dde532 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -130,6 +130,13 @@ pub(crate) fn trait_method( ); Ok((fn_def.def_id, fn_def.substs, Vec::new())) }, + Function::NonCaptureClosureAsFnPtr(fn_def) => { + args.insert(0, ( + Value::ByVal(PrimVal::Undef), + fn_def.sig.inputs()[0], + )); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + } Function::Closure(fn_def) => { self.unpack_fn_args(args)?; Ok((fn_def.def_id, fn_def.substs, Vec::new())) @@ -140,8 +147,20 @@ pub(crate) fn trait_method( args.remove(0); self.unpack_fn_args(args)?; let fn_ptr = self.memory.read_ptr(self_ptr)?; - let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; - assert_eq!(sig, fn_def.sig); + let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { + Function::Concrete(fn_def) => { + assert_eq!(sig, fn_def.sig); + fn_def + }, + Function::NonCaptureClosureAsFnPtr(fn_def) => { + args.insert(0, ( + Value::ByVal(PrimVal::Undef), + fn_def.sig.inputs()[0], + )); + fn_def + }, + other => bug!("FnPtrAsTraitObject for {:?}", other), + }; Ok((fn_def.def_id, fn_def.substs, Vec::new())) } } diff --git a/tests/run-pass/non_capture_closure_to_fn_ptr.rs b/tests/run-pass/non_capture_closure_to_fn_ptr.rs new file mode 100644 index 00000000000..6f73a3d09dd --- /dev/null +++ b/tests/run-pass/non_capture_closure_to_fn_ptr.rs @@ -0,0 +1,16 @@ +#![feature(closure_to_fn_coercion)] + +// allow(const_err) to work around a bug in warnings +#[allow(const_err)] +static FOO: fn() = || { assert_ne!(42, 43) }; +#[allow(const_err)] +static BAR: fn(i32, i32) = |a, b| { assert_ne!(a, b) }; + +fn main() { + FOO(); + BAR(44, 45); + let bar: unsafe fn(i32, i32) = BAR; + unsafe { bar(46, 47) }; + let boo: &Fn(i32, i32) = &BAR; + boo(48, 49); +} diff --git a/tests/run-pass/recursive_static.rs b/tests/run-pass/recursive_static.rs index 5b27324964b..77f2902917a 100644 --- a/tests/run-pass/recursive_static.rs +++ b/tests/run-pass/recursive_static.rs @@ -1,5 +1,3 @@ -#![feature(static_recursion)] - struct S(&'static S); static S1: S = S(&S2); static S2: S = S(&S1); -- GitLab