diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 2c60ede79758c..8334957e026c2 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -3,6 +3,7 @@ use std::fmt; use std::hash::Hash; use std::ops::ControlFlow; +use either::Either; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::IndexEntry; @@ -14,6 +15,7 @@ use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; +use rustc_middle::ty::Ty; use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -191,6 +193,16 @@ impl interpret::MayLeak for ! { } } +#[derive(Debug, Copy, Clone)] +pub enum ExtraFnVal<'tcx> { + /// `#[rustc_const_panic_str]` or `#[lang = "begin_panic"]` + BeginPanic, + /// `#[lang = "panic_fmt"]` + PanicFmt(ty::Instance<'tcx>), + /// `#[lang = "align_offset"]` + AlignOffset(ty::Instance<'tcx>), +} + impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); @@ -212,56 +224,29 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { /// "Intercept" a function call, because we have something special to do for it. /// All `#[rustc_do_not_const_check]` functions should be hooked here. - /// If this returns `Some` function, which may be `instance` or a different function with - /// compatible arguments, then evaluation should continue with that function. - /// If this returns `None`, the function call has been handled and the function has returned. - fn hook_special_const_fn( - &mut self, - instance: ty::Instance<'tcx>, - args: &[FnArg<'tcx>], - dest: &PlaceTy<'tcx>, - ret: Option, - ) -> InterpResult<'tcx, Option>> { + /// + /// If this returns `Some`, the function should be executed via [`call_extra_fn`]. + /// If this returns `None`, the function should be executed as normal. + /// + /// [`call_extra_fn`]: interpret::Machine::call_extra_fn + fn hook_special_const_fn(&mut self, instance: ty::Instance<'tcx>) -> Option> { let def_id = instance.def_id(); if self.tcx.has_attr(def_id, sym::rustc_const_panic_str) || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { - let args = self.copy_fn_args(args)?; - // &str or &&str - assert!(args.len() == 1); + return Some(ExtraFnVal::BeginPanic); + } - let mut msg_place = self.deref_pointer(&args[0])?; - while msg_place.layout.ty.is_ref() { - msg_place = self.deref_pointer(&msg_place)?; - } + if Some(def_id) == self.tcx.lang_items().panic_fmt() { + return Some(ExtraFnVal::PanicFmt(instance)); + } - let msg = Symbol::intern(self.read_str(&msg_place)?); - let span = self.find_closest_untracked_caller_location(); - let (file, line, col) = self.location_triple_for_span(span); - return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); - } else if Some(def_id) == self.tcx.lang_items().panic_fmt() { - // For panic_fmt, call const_panic_fmt instead. - let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); - let new_instance = ty::Instance::resolve( - *self.tcx, - ty::ParamEnv::reveal_all(), - const_def_id, - instance.args, - ) - .unwrap() - .unwrap(); - - return Ok(Some(new_instance)); - } else if Some(def_id) == self.tcx.lang_items().align_offset_fn() { - let args = self.copy_fn_args(args)?; - // For align_offset, we replace the function call if the pointer has no address. - match self.align_offset(instance, &args, dest, ret)? { - ControlFlow::Continue(()) => return Ok(Some(instance)), - ControlFlow::Break(()) => return Ok(None), - } + if Some(def_id) == self.tcx.lang_items().align_offset_fn() { + return Some(ExtraFnVal::AlignOffset(instance)); } - Ok(Some(instance)) + + None } /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer @@ -371,6 +356,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, compile_time_machine!(<'mir, 'tcx>); type MemoryKind = MemoryKind; + type ExtraFnVal = ExtraFnVal<'tcx>; const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error @@ -399,7 +385,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, .delayed_bug("This is likely a const item that is missing from its impl"); throw_inval!(AlreadyReported(guar.into())); } else { - // `find_mir_or_eval_fn` checks that this is a const fn before even calling us, + // `find_mir_or_extra_fn` checks that this is a const fn before even calling us, // so this should be unreachable. let path = ecx.tcx.def_path_str(def); bug!("trying to call extern function `{path}` at compile-time"); @@ -409,22 +395,16 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } - fn find_mir_or_eval_fn( + fn find_mir_or_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, - orig_instance: ty::Instance<'tcx>, - _abi: CallAbi, - args: &[FnArg<'tcx>], - dest: &PlaceTy<'tcx>, - ret: Option, - _unwind: mir::UnwindAction, // unwinding is not supported in consts - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { - debug!("find_mir_or_eval_fn: {:?}", orig_instance); + instance: ty::Instance<'tcx>, + ) -> InterpResult<'tcx, Either<&'mir mir::Body<'tcx>, Self::ExtraFnVal>> { + debug!("find_mir_or_extra_fn: {:?}", instance); // Replace some functions. - let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else { - // Call has already been handled. - return Ok(None); - }; + if let Some(extra) = ecx.hook_special_const_fn(instance) { + return Ok(Either::Right(extra)); + } // Only check non-glue functions if let ty::InstanceDef::Item(def) = instance.def { @@ -442,10 +422,82 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } - // This is a const fn. Call it. - // In case of replacement, we return the *original* instance to make backtraces work out - // (and we hope this does not confuse the FnAbi checks too much). - Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) + // This is a const fn. Return its mir to be called. + ecx.load_mir(instance.def, None).map(Either::Left) + } + + #[inline(always)] + fn call_extra_fn( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + fn_val: Self::ExtraFnVal, + abis: (CallAbi, &rustc_target::abi::call::FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx>], + destination: &PlaceTy<'tcx, Self::Provenance>, + target: Option, + unwind: mir::UnwindAction, + ) -> InterpResult<'tcx> { + match fn_val { + ExtraFnVal::BeginPanic => { + let args = ecx.copy_fn_args(args)?; + // &str or &&str + assert!(args.len() == 1); + + let mut msg_place = ecx.deref_pointer(&args[0])?; + while msg_place.layout.ty.is_ref() { + msg_place = ecx.deref_pointer(&msg_place)?; + } + + let msg = Symbol::intern(ecx.read_str(&msg_place)?); + let span = ecx.find_closest_untracked_caller_location(); + let (file, line, col) = ecx.location_triple_for_span(span); + return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); + } + ExtraFnVal::PanicFmt(instance) => { + // For panic_fmt, call const_panic_fmt instead. + let const_def_id = ecx.tcx.require_lang_item(LangItem::ConstPanicFmt, None); + let new_instance = ty::Instance::resolve( + *ecx.tcx, + ty::ParamEnv::reveal_all(), + const_def_id, + instance.args, + ) + .unwrap() + .unwrap(); + + ecx.eval_fn_call( + FnVal::Instance(new_instance), + abis, + args, + true, + destination, + target, + unwind, + ) + } + ExtraFnVal::AlignOffset(instance) => { + let args2 = ecx.copy_fn_args(args)?; + // For align_offset, we replace the function call if the pointer has no address. + match ecx.align_offset(instance, &args2, destination, target)? { + ControlFlow::Continue(()) => { + // Can't use `eval_fn_call` here because `eval_fn_call` tries to call + // const eval extra fn which ends up here, so calling `eval_fn_call` + // would cause infinite recursion and stack overflow. + let body = ecx.load_mir(instance.def, None)?; + ecx.eval_body( + instance, + body, + abis, + args, + false, + destination, + target, + unwind, + ) + } + ControlFlow::Break(()) => Ok(()), + } + } + } } fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 959ec2ca86558..a31465b29c5eb 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -24,12 +24,12 @@ use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceT use crate::const_eval; use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal}; -pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< +pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T, U = const_eval::ExtraFnVal<'tcx>> = Machine< 'mir, 'tcx, MemoryKind = T, Provenance = CtfeProvenance, - ExtraFnVal = !, + ExtraFnVal = U, FrameExtra = (), AllocExtra = (), MemoryMap = FxIndexMap, Allocation)>, @@ -42,7 +42,7 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>( +fn intern_shallow<'rt, 'mir, 'tcx, T, U, M: CompileTimeMachine<'mir, 'tcx, T, U>>( ecx: &'rt mut InterpCx<'mir, 'tcx, M>, alloc_id: AllocId, mutability: Mutability, @@ -236,7 +236,8 @@ pub fn intern_const_alloc_for_constprop< 'mir, 'tcx: 'mir, T, - M: CompileTimeMachine<'mir, 'tcx, T>, + U, + M: CompileTimeMachine<'mir, 'tcx, T, U>, >( ecx: &mut InterpCx<'mir, 'tcx, M>, alloc_id: AllocId, @@ -255,7 +256,7 @@ pub fn intern_const_alloc_for_constprop< Ok(()) } -impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> +impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !, !>> InterpCx<'mir, 'tcx, M> { /// A helper function that allocates memory for the layout given and gives you access to mutate diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 0106ec425bc50..79433b1094643 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -6,12 +6,13 @@ use std::borrow::{Borrow, Cow}; use std::fmt::Debug; use std::hash::Hash; +use either::Either; use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::mir; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_target::abi::{Align, Size}; @@ -183,30 +184,22 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Entry point to all function calls. /// - /// Returns either the mir to use for the call, or `None` if execution should - /// just proceed (which usually means this hook did all the work that the - /// called function should usually have done). In the latter case, it is - /// this hook's responsibility to advance the instruction pointer! - /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR - /// nor just jump to `ret`, but instead push their own stack frame.) - /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them - /// was used. - fn find_mir_or_eval_fn( + /// Returns either the mir to use for the call, or an [`ExtraFnVal`] for special functions + /// handled by [`call_extra_fn`]. + /// + /// [`ExtraFnVal`]: Machine::ExtraFnVal + /// [`call_extra_fn`]: Machine::call_extra_fn + fn find_mir_or_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - abi: CallAbi, - args: &[FnArg<'tcx, Self::Provenance>], - destination: &PlaceTy<'tcx, Self::Provenance>, - target: Option, - unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>; + ) -> InterpResult<'tcx, Either<&'mir mir::Body<'tcx>, Self::ExtraFnVal>>; /// Execute `fn_val`. It is the hook's responsibility to advance the instruction /// pointer as appropriate. fn call_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, - abi: CallAbi, + abis: (CallAbi, &rustc_target::abi::call::FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, Self::Provenance>], destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, @@ -555,8 +548,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type Provenance = CtfeProvenance; type ProvenanceExtra = bool; // the "immutable" flag - type ExtraFnVal = !; - type MemoryMap = rustc_data_structures::fx::FxIndexMap, Allocation)>; const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory @@ -578,19 +569,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { unreachable!("unwinding cannot happen during compile-time evaluation") } - #[inline(always)] - fn call_extra_fn( - _ecx: &mut InterpCx<$mir, $tcx, Self>, - fn_val: !, - _abi: CallAbi, - _args: &[FnArg<$tcx>], - _destination: &PlaceTy<$tcx, Self::Provenance>, - _target: Option, - _unwind: mir::UnwindAction, - ) -> InterpResult<$tcx> { - match fn_val {} - } - #[inline(always)] fn adjust_allocation<'b>( _ecx: &InterpCx<$mir, $tcx, Self>, diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index e72ace8be3559..106b971fd9e8f 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use either::Either; use rustc_ast::ast::InlineAsmOptions; use rustc_middle::{ mir, @@ -497,7 +498,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// `with_caller_location` indicates whether the caller passed a caller location. Miri /// implements caller locations without argument passing, but to match `FnAbi` we need to know /// when those arguments are present. - pub(crate) fn eval_fn_call( + pub fn eval_fn_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), @@ -505,7 +506,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { with_caller_location: bool, destination: &PlaceTy<'tcx, M::Provenance>, target: Option, - mut unwind: mir::UnwindAction, + unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { trace!("eval_fn_call: {:#?}", fn_val); @@ -515,7 +516,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return M::call_extra_fn( self, extra, - caller_abi, + (caller_abi, caller_fn_abi), args, destination, target, @@ -547,204 +548,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::FnPtrAddrShim(..) | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::Item(_) => { - // We need MIR for this fn - let Some((body, instance)) = M::find_mir_or_eval_fn( - self, + | ty::InstanceDef::Item(_) => match M::find_mir_or_extra_fn(self, instance)? { + Either::Left(body) => self.eval_body( instance, - caller_abi, + body, + (caller_abi, caller_fn_abi), args, + with_caller_location, destination, target, unwind, - )? - else { - return Ok(()); - }; - - // Compute callee information using the `instance` returned by - // `find_mir_or_eval_fn`. - // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - - if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { - throw_unsup_format!("calling a c-variadic function is not supported"); - } - - if M::enforce_abi(self) { - if caller_fn_abi.conv != callee_fn_abi.conv { - throw_ub_custom!( - fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), - ) - } - } - - // Check that all target features required by the callee (i.e., from - // the attribute `#[target_feature(enable = ...)]`) are enabled at - // compile time. - self.check_fn_target_features(instance)?; - - if !callee_fn_abi.can_unwind { - // The callee cannot unwind, so force the `Unreachable` unwind handling. - unwind = mir::UnwindAction::Unreachable; - } - - self.push_stack_frame( - instance, - body, + ), + Either::Right(f) => M::call_extra_fn( + self, + f, + (caller_abi, caller_fn_abi), + args, destination, - StackPopCleanup::Goto { ret: target, unwind }, - )?; - - // If an error is raised here, pop the frame again to get an accurate backtrace. - // To this end, we wrap it all in a `try` block. - let res: InterpResult<'tcx> = try { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| ( - arg.layout().ty, - match arg { - FnArg::Copy(op) => format!("copy({:?})", *op), - FnArg::InPlace(place) => format!("in-place({:?})", *place), - } - )) - .collect::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty - )) - .collect::>() - ); - - // In principle, we have two iterators: Where the arguments come from, and where - // they go to. - - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from( - args.iter() - .map(|a| Ok(a.clone())) - .chain( - (0..untuple_arg.layout().fields.count()) - .map(|i| self.fn_arg_field(untuple_arg, i)), - ) - .collect::>>()?, - ) - } else { - // Plain arg passing - Cow::from(args) - }; - // If `with_caller_location` is set we pretend there is an extra argument (that - // we will not pass). - assert_eq!( - caller_args.len() + if with_caller_location { 1 } else { 0 }, - caller_fn_abi.args.len(), - "mismatch between caller ABI and caller arguments", - ); - let mut caller_args = caller_args - .iter() - .zip(caller_fn_abi.args.iter()) - .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `pass_argument` would be the loop body. It takes care to - // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter(); - for local in body.args_iter() { - // Construct the destination place for this argument. At this point all - // locals are still dead, so we cannot construct a `PlaceTy`. - let dest = mir::Place::from(local); - // `layout_of_local` does more than just the instantiation we need to get the - // type, but the result gets cached so this avoids calling the instantiation - // query *again* the next time this local is accessed. - let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if Some(local) == body.spread_arg { - // Make the local live once, then fill in the value field by field. - self.storage_live(local)?; - // Must be a tuple - let ty::Tuple(fields) = ty.kind() else { - span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") - }; - for (i, field_ty) in fields.iter().enumerate() { - let dest = dest.project_deeper( - &[mir::ProjectionElem::Field( - FieldIdx::from_usize(i), - field_ty, - )], - *self.tcx, - ); - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - field_ty, - /* already_live */ true, - )?; - } - } else { - // Normal argument. Cannot mark it as live yet, it might be unsized! - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - ty, - /* already_live */ false, - )?; - } - } - // If the callee needs a caller location, pretend we consume one more argument from the ABI. - if instance.def.requires_caller_location(*self.tcx) { - callee_args_abis.next().unwrap(); - } - // Now we should have no more caller args or callee arg ABIs - assert!( - callee_args_abis.next().is_none(), - "mismatch between callee ABI and callee body arguments" - ); - if caller_args.next().is_some() { - throw_ub_custom!(fluent::const_eval_too_many_caller_args); - } - // Don't forget to check the return type! - if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { - throw_ub!(AbiMismatchReturn { - caller_ty: caller_fn_abi.ret.layout.ty, - callee_ty: callee_fn_abi.ret.layout.ty - }); - } - // Protect return place for in-place return value passing. - M::protect_in_place_function_argument(self, destination)?; - - // Don't forget to mark "initially live" locals as live. - self.storage_live_for_always_live_locals()?; - }; - match res { - Err(err) => { - self.stack_mut().pop(); - Err(err) - } - Ok(()) => Ok(()), - } - } + target, + unwind, + ), + }, // `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be // codegen'd / interpreted as virtual calls through the vtable. ty::InstanceDef::Virtual(def_id, idx) => { @@ -885,6 +709,198 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + pub(crate) fn eval_body( + &mut self, + instance: Instance<'tcx>, + body: &'mir mir::Body<'tcx>, + (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx, M::Provenance>], + with_caller_location: bool, + destination: &PlaceTy<'tcx, M::Provenance>, + target: Option, + mut unwind: mir::UnwindAction, + ) -> InterpResult<'tcx> { + // Compute callee information using the `instance` returned by + // `find_mir_or_extra_fn`. + // FIXME: for variadic support, do we have to somehow determine callee's extra_args? + let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + + if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { + throw_unsup_format!("calling a c-variadic function is not supported"); + } + + if M::enforce_abi(self) { + if caller_fn_abi.conv != callee_fn_abi.conv { + throw_ub_custom!( + fluent::const_eval_incompatible_calling_conventions, + callee_conv = format!("{:?}", callee_fn_abi.conv), + caller_conv = format!("{:?}", caller_fn_abi.conv), + ) + } + } + + // Check that all target features required by the callee (i.e., from + // the attribute `#[target_feature(enable = ...)]`) are enabled at + // compile time. + self.check_fn_target_features(instance)?; + + if !callee_fn_abi.can_unwind { + // The callee cannot unwind, so force the `Unreachable` unwind handling. + unwind = mir::UnwindAction::Unreachable; + } + + self.push_stack_frame( + instance, + body, + destination, + StackPopCleanup::Goto { ret: target, unwind }, + )?; + + // If an error is raised here, pop the frame again to get an accurate backtrace. + // To this end, we wrap it all in a `try` block. + let res: InterpResult<'tcx> = try { + trace!( + "caller ABI: {:?}, args: {:#?}", + caller_abi, + args.iter() + .map(|arg| ( + arg.layout().ty, + match arg { + FnArg::Copy(op) => format!("copy({:?})", *op), + FnArg::InPlace(place) => format!("in-place({:?})", *place), + } + )) + .collect::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty + )) + .collect::>() + ); + + // In principle, we have two iterators: Where the arguments come from, and where + // they go to. + + // For where they come from: If the ABI is RustCall, we untuple the + // last incoming argument. These two iterators do not have the same type, + // so to keep the code paths uniform we accept an allocation + // (for RustCall ABI only). + let caller_args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (untuple_arg, args) = args.split_last().unwrap(); + trace!("eval_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|a| Ok(a.clone())) + .chain( + (0..untuple_arg.layout().fields.count()) + .map(|i| self.fn_arg_field(untuple_arg, i)), + ) + .collect::>>()?, + ) + } else { + // Plain arg passing + Cow::from(args) + }; + // If `with_caller_location` is set we pretend there is an extra argument (that + // we will not pass). + assert_eq!( + caller_args.len() + if with_caller_location { 1 } else { 0 }, + caller_fn_abi.args.len(), + "mismatch between caller ABI and caller arguments", + ); + let mut caller_args = caller_args + .iter() + .zip(caller_fn_abi.args.iter()) + .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); + + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `pass_argument` would be the loop body. It takes care to + // not advance `caller_iter` for ignored arguments. + let mut callee_args_abis = callee_fn_abi.args.iter(); + for local in body.args_iter() { + // Construct the destination place for this argument. At this point all + // locals are still dead, so we cannot construct a `PlaceTy`. + let dest = mir::Place::from(local); + // `layout_of_local` does more than just the instantiation we need to get the + // type, but the result gets cached so this avoids calling the instantiation + // query *again* the next time this local is accessed. + let ty = self.layout_of_local(self.frame(), local, None)?.ty; + if Some(local) == body.spread_arg { + // Make the local live once, then fill in the value field by field. + self.storage_live(local)?; + // Must be a tuple + let ty::Tuple(fields) = ty.kind() else { + span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") + }; + for (i, field_ty) in fields.iter().enumerate() { + let dest = dest.project_deeper( + &[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)], + *self.tcx, + ); + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + field_ty, + /* already_live */ true, + )?; + } + } else { + // Normal argument. Cannot mark it as live yet, it might be unsized! + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + ty, + /* already_live */ false, + )?; + } + } + // If the callee needs a caller location, pretend we consume one more argument from the ABI. + if instance.def.requires_caller_location(*self.tcx) { + callee_args_abis.next().unwrap(); + } + // Now we should have no more caller args or callee arg ABIs + assert!( + callee_args_abis.next().is_none(), + "mismatch between callee ABI and callee body arguments" + ); + if caller_args.next().is_some() { + throw_ub_custom!(fluent::const_eval_too_many_caller_args); + } + // Don't forget to check the return type! + if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { + throw_ub!(AbiMismatchReturn { + caller_ty: caller_fn_abi.ret.layout.ty, + callee_ty: callee_fn_abi.ret.layout.ty + }); + } + // Protect return place for in-place return value passing. + M::protect_in_place_function_argument(self, destination)?; + + // Don't forget to mark "initially live" locals as live. + self.storage_live_for_always_live_locals()?; + }; + match res { + Err(err) => { + self.stack_mut().pop(); + Err(err) + } + Ok(()) => Ok(()), + } + } + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 86e99a8a5b5ce..d6e687198a143 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,6 +2,7 @@ //! //! Currently, this pass only propagates scalar values. +use either::Either; use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable, }; @@ -867,6 +868,7 @@ pub(crate) struct DummyMachine; impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); type MemoryKind = !; + type ExtraFnVal = !; const PANIC_ON_ALLOC_FAIL: bool = true; #[inline(always)] @@ -899,15 +901,10 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm Ok(()) } - fn find_mir_or_eval_fn( + fn find_mir_or_extra_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, - _abi: rustc_target::spec::abi::Abi, - _args: &[rustc_const_eval::interpret::FnArg<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::PlaceTy<'tcx, Self::Provenance>, - _target: Option, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> interpret::InterpResult<'tcx, Either<&'mir Body<'tcx>, Self::ExtraFnVal>> { unimplemented!() } @@ -1016,4 +1013,16 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm > { unimplemented!() } + + fn call_extra_fn( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + fn_val: Self::ExtraFnVal, + _: (rustc_target::spec::abi::Abi, &rustc_target::abi::call::FnAbi<'tcx, Ty<'tcx>>), + _args: &[rustc_const_eval::interpret::FnArg<'tcx, Self::Provenance>], + _destination: &PlaceTy<'tcx, Self::Provenance>, + _target: Option, + _unwind: rustc_middle::mir::UnwindAction, + ) -> InterpResult<'tcx> { + match fn_val {} + } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 0819334b600b6..03d0b36956609 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -258,6 +258,7 @@ fn panic_in_cleanup() -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] +#[track_caller] #[rustc_const_unstable(feature = "panic_internals", issue = "none")] pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index d9b4363d604b4..707a8b08875c3 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -997,7 +997,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { link_name: Symbol, ) -> InterpResult<'tcx, ()> { self.check_abi(abi, exp_abi)?; - if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? { + if let Some(instance) = self.eval_context_mut().lookup_exported_symbol(link_name)? { // If compiler-builtins is providing the symbol, then don't treat it as a clash. // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased // performance. Note that this means we won't catch any undefined behavior in @@ -1007,6 +1007,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(()); } + let body = self.eval_context_mut().load_mir(instance.def, None)?; + throw_machine_stop!(TerminationInfo::SymbolShimClashing { link_name, span: body.span.data(), diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 40d041c8fdb16..81875d79ead3c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -28,9 +28,11 @@ use rustc_middle::{ }; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{Span, SpanData, Symbol}; +use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; +use crate::shims::foreign_items::ExtraFnVal; use crate::{ concurrency::{data_race, weak_memory}, shims::unix::FileHandler, @@ -853,7 +855,7 @@ impl<'mir, 'tcx> MiriInterpCxExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> { /// Machine hook implementations. impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { type MemoryKind = MiriMemoryKind; - type ExtraFnVal = DynSym; + type ExtraFnVal = ExtraFnVal; type FrameExtra = FrameExtra<'tcx>; type AllocExtra = AllocExtra<'tcx>; @@ -938,44 +940,39 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn find_mir_or_eval_fn( + fn find_mir_or_extra_fn( ecx: &mut MiriInterpCx<'mir, 'tcx>, instance: ty::Instance<'tcx>, - abi: Abi, - args: &[FnArg<'tcx, Provenance>], - dest: &PlaceTy<'tcx, Provenance>, - ret: Option, - unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { - // For foreign items, try to see if we can emulate them. + ) -> InterpResult<'tcx, Either<&'mir mir::Body<'tcx>, ExtraFnVal>> { + // For foreign items, we return `ExtraFnVal::ForeignFn` to later try to see if we can + // emulate them. if ecx.tcx.is_foreign_item(instance.def_id()) { - // An external function call that does not have a MIR body. We either find MIR elsewhere - // or emulate its effect. - // This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need - // to run extra MIR), and Ok(Some(body)) if we found MIR to run for the - // foreign function - // Any needed call to `goto_block` will be performed by `emulate_foreign_item`. - let args = ecx.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? let link_name = ecx.item_link_name(instance.def_id()); - return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind); + return Ok(Either::Right(ExtraFnVal::ForeignFn { link_name })); } // Otherwise, load the MIR. - Ok(Some((ecx.load_mir(instance.def, None)?, instance))) + ecx.load_mir(instance.def, None).map(Either::Left) } #[inline(always)] fn call_extra_fn( ecx: &mut MiriInterpCx<'mir, 'tcx>, - fn_val: DynSym, - abi: Abi, + fn_val: ExtraFnVal, + abis: (Abi, &FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - let args = ecx.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? - ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind) + match fn_val { + // An external function call that does not have a MIR body. We either find MIR elsewhere + // or emulate its effect. + // Any needed call to `goto_block` will be performed by `emulate_foreign_item`. + ExtraFnVal::ForeignFn { link_name } => + ecx.emulate_foreign_item(link_name, abis, &args, dest, ret, unwind).map(|_| ()), + ExtraFnVal::DynSym(sym) => ecx.emulate_dyn_sym(sym, abis, &args, dest, ret, unwind), + } } #[inline(always)] diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index 0284e5b606ced..145cae45d5c16 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -1,6 +1,6 @@ //! Provides the `extern static` that this platform expects. -use crate::*; +use crate::{shims::foreign_items::ExtraFnVal, *}; impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { fn alloc_extern_static( @@ -62,7 +62,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // "signal" -- just needs a non-zero pointer value (function does not even get called), // but we arrange for this to call the `signal` function anyway. let layout = this.machine.layouts.const_raw_ptr; - let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str("signal"))); + let ptr = this.fn_ptr(FnVal::Other(ExtraFnVal::DynSym(DynSym::from_str("signal")))); let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); Self::alloc_extern_static(this, "signal", val)?; } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 0645c1f176ef7..27c45fad27fe5 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -6,16 +6,19 @@ use rustc_hir::{ def::DefKind, def_id::{CrateNum, LOCAL_CRATE}, }; -use rustc_middle::middle::{ - codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage, - exported_symbols::ExportedSymbol, -}; use rustc_middle::mir; use rustc_middle::ty; +use rustc_middle::{ + middle::{ + codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage, + exported_symbols::ExportedSymbol, + }, + ty::Ty, +}; use rustc_session::config::CrateType; use rustc_span::Symbol; use rustc_target::{ - abi::{Align, Size}, + abi::{call::FnAbi, Align, Size}, spec::abi::Abi, }; @@ -23,6 +26,12 @@ use super::backtrace::EvalContextExt as _; use crate::*; use helpers::{ToHost, ToSoft}; +#[derive(Debug, Copy, Clone)] +pub enum ExtraFnVal { + ForeignFn { link_name: Symbol }, + DynSym(DynSym), +} + /// Type of dynamic symbols (for `dlsym` et al) #[derive(Debug, Copy, Clone)] pub struct DynSym(Symbol); @@ -55,12 +64,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn emulate_foreign_item( &mut self, link_name: Symbol, - abi: Abi, - args: &[OpTy<'tcx, Provenance>], + (abi, fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option<()>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -69,26 +78,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None => match link_name.as_str() { "miri_start_panic" => { + let args = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? + // `check_shim` happens inside `handle_miri_start_panic`. - this.handle_miri_start_panic(abi, link_name, args, unwind)?; + this.handle_miri_start_panic(abi, link_name, &args, unwind)?; return Ok(None); } // This matches calls to the foreign item `panic_impl`. // The implementation is provided by the function with the `#[panic_handler]` attribute. "panic_impl" => { // We don't use `check_shim` here because we are just forwarding to the lang - // item. Argument count checking will be performed when the returned `Body` is - // called. + // item. Argument count checking will be performed in `eval_fn_call`. this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?; let panic_impl_id = tcx.lang_items().panic_impl().unwrap(); let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id); - return Ok(Some(( - this.load_mir(panic_impl_instance.def, None)?, - panic_impl_instance, - ))); + + this.eval_fn_call( + FnVal::Instance(panic_impl_instance), + (abi, fn_abi), + args, + false, + dest, + ret, + unwind, + )?; + + return Ok(Some(())); } #[rustfmt::skip] - | "exit" + "exit" | "ExitProcess" => { let exp_abi = if link_name.as_str() == "exit" { @@ -96,24 +114,39 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else { Abi::System { unwind: false } }; - let [code] = this.check_shim(abi, exp_abi, link_name, args)?; + let args = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? + let [code] = this.check_shim(abi, exp_abi, link_name, &args)?; // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); } "abort" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let args = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? + let [] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, &args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() )) } _ => { - if let Some(body) = this.lookup_exported_symbol(link_name)? { - return Ok(Some(body)); + if let Some(instance) = this.lookup_exported_symbol(link_name)? { + this.eval_fn_call( + FnVal::Instance(instance), + (abi, fn_abi), + args, + false, + dest, + ret, + unwind, + )?; + + return Ok(Some(())); } + this.handle_unsupported(format!( "can't call (diverging) foreign function: {link_name}" ))?; + return Ok(None); } }, @@ -121,15 +154,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Second: functions that return immediately. - match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { + let args2 = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit? + match this.emulate_foreign_item_inner(link_name, abi, &args2, dest)? { EmulateForeignItemResult::NeedsJumping => { trace!("{:?}", this.dump_place(dest)); this.go_to_block(ret); } EmulateForeignItemResult::AlreadyJumped => (), EmulateForeignItemResult::NotSupported => { - if let Some(body) = this.lookup_exported_symbol(link_name)? { - return Ok(Some(body)); + if let Some(instance) = this.lookup_exported_symbol(link_name)? { + this.eval_fn_call( + FnVal::Instance(instance), + (abi, fn_abi), + args, + false, + dest, + Some(ret), + unwind, + )?; + + return Ok(Some(())); } this.handle_unsupported(format!( @@ -147,13 +191,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn emulate_dyn_sym( &mut self, sym: DynSym, - abi: Abi, - args: &[OpTy<'tcx, Provenance>], + abis: (Abi, &FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?; + let res = self.emulate_foreign_item(sym.0, abis, args, dest, ret, unwind)?; assert!(res.is_none(), "DynSyms that delegate are not supported"); Ok(()) } @@ -162,7 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn lookup_exported_symbol( &mut self, link_name: Symbol, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -246,10 +290,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { e.insert(instance_and_crate.map(|ic| ic.0)) } }; - match instance { - None => Ok(None), // no symbol with this name - Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))), - } + + Ok(instance) } fn malloc( diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index b5cd18396a286..de466f66f7010 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -6,6 +6,7 @@ use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; +use crate::shims::foreign_items::ExtraFnVal; use crate::*; use shims::foreign_items::EmulateForeignItemResult; use shims::unix::fs::EvalContextExt as _; @@ -308,7 +309,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; if let Ok(name) = str::from_utf8(name) && is_dyn_sym(name, &this.tcx.sess.target.os) { - let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name))); + let ptr = this.fn_ptr(FnVal::Other(ExtraFnVal::DynSym(DynSym::from_str(name)))); this.write_pointer(ptr, dest)?; } else { this.write_null(dest)?; diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index bddc30b8379b2..394ea7df3ea2f 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -5,6 +5,7 @@ use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use crate::shims::foreign_items::ExtraFnVal; use crate::*; use shims::foreign_items::EmulateForeignItemResult; use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; @@ -366,7 +367,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let Ok(name) = str::from_utf8(name) && is_dyn_sym(name) { - let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name))); + let ptr = this.fn_ptr(FnVal::Other(ExtraFnVal::DynSym(DynSym::from_str(name)))); this.write_pointer(ptr, dest)?; } else { this.write_null(dest)?;