unify passing of sized and unsized function arguments :-)
This commit is contained in:
parent
7cdeff266c
commit
f87e91de7d
2 changed files with 36 additions and 32 deletions
|
@ -912,6 +912,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn storage_live_dyn(
|
||||||
|
&mut self,
|
||||||
|
local: mir::Local,
|
||||||
|
meta: MemPlaceMeta<M::Provenance>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
trace!("{:?} is now live", local);
|
||||||
|
|
||||||
|
let layout = self.layout_of_local(self.frame(), local, None)?;
|
||||||
|
let local_val = LocalValue::Live(if layout.is_sized() {
|
||||||
|
assert!(matches!(meta, MemPlaceMeta::None)); // we're dropping the metadata
|
||||||
|
// Just make this an efficient immediate.
|
||||||
|
Operand::Immediate(Immediate::Uninit)
|
||||||
|
} else {
|
||||||
|
// Need to allocate some memory.
|
||||||
|
let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
|
||||||
|
Operand::Indirect(*dest_place)
|
||||||
|
});
|
||||||
|
|
||||||
|
// StorageLive expects the local to be dead, and marks it live.
|
||||||
|
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
|
||||||
|
if !matches!(old, LocalValue::Dead) {
|
||||||
|
throw_ub_custom!(fluent::const_eval_double_storage_live);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark a storage as live, killing the previous content.
|
/// Mark a storage as live, killing the previous content.
|
||||||
pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||||
trace!("{:?} is now live", local);
|
trace!("{:?} is now live", local);
|
||||||
|
@ -920,13 +946,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
throw_unsup!(UnsizedLocal);
|
throw_unsup!(UnsizedLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
let local_val = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
|
self.storage_live_dyn(local, MemPlaceMeta::None)
|
||||||
// StorageLive expects the local to be dead, and marks it live.
|
|
||||||
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
|
|
||||||
if !matches!(old, LocalValue::Dead) {
|
|
||||||
throw_ub_custom!(fluent::const_eval_double_storage_live);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use rustc_ast::ast::InlineAsmOptions;
|
use rustc_ast::ast::InlineAsmOptions;
|
||||||
|
@ -15,8 +14,8 @@ use rustc_target::abi::{self, FieldIdx};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AllocId, FnVal, ImmTy, InterpCx, InterpResult, LocalValue, MPlaceTy, Machine, MemoryKind, OpTy,
|
AllocId, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable,
|
||||||
Operand, PlaceTy, Provenance, Scalar, StackPopCleanup,
|
Provenance, Scalar, StackPopCleanup,
|
||||||
};
|
};
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
|
|
||||||
|
@ -394,28 +393,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// did in-place of by-copy argument passing, except for pointer equality tests.
|
// did in-place of by-copy argument passing, except for pointer equality tests.
|
||||||
let caller_arg_copy = self.copy_fn_arg(&caller_arg)?;
|
let caller_arg_copy = self.copy_fn_arg(&caller_arg)?;
|
||||||
if !already_live {
|
if !already_live {
|
||||||
// Special handling for unsized parameters: they are harder to make live.
|
let local = callee_arg.as_local().unwrap();
|
||||||
if caller_arg_copy.layout.is_unsized() {
|
let meta = caller_arg_copy.meta();
|
||||||
// `check_argument_compat` ensures that both have the same type, so we know they will use the metadata the same way.
|
// `check_argument_compat` ensures that if metadata is needed, both have the same type,
|
||||||
assert_eq!(caller_arg_copy.layout.ty, callee_ty);
|
// so we know they will use the metadata the same way.
|
||||||
// We have to properly pre-allocate the memory for the callee.
|
assert!(!meta.has_meta() || caller_arg_copy.layout.ty == callee_ty);
|
||||||
// So let's tear down some abstractions.
|
|
||||||
// This all has to be in memory, there are no immediate unsized values.
|
self.storage_live_dyn(local, meta)?;
|
||||||
let src = caller_arg_copy.assert_mem_place();
|
|
||||||
// The destination cannot be one of these "spread args".
|
|
||||||
let dest_local = callee_arg.as_local().expect("unsized arguments cannot be spread");
|
|
||||||
// Allocate enough memory to hold `src`.
|
|
||||||
let dest_place = self.allocate_dyn(src.layout, MemoryKind::Stack, src.meta)?;
|
|
||||||
// Update the local to be that new place. This is essentially a "dyn-sized StorageLive".
|
|
||||||
let old = mem::replace(
|
|
||||||
&mut self.frame_mut().locals[dest_local].value,
|
|
||||||
LocalValue::Live(Operand::Indirect(*dest_place)),
|
|
||||||
);
|
|
||||||
assert!(matches!(old, LocalValue::Dead));
|
|
||||||
} else {
|
|
||||||
// Just make the local live.
|
|
||||||
self.storage_live(callee_arg.as_local().unwrap())?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Now we can finally actually evaluate the callee place.
|
// Now we can finally actually evaluate the callee place.
|
||||||
let callee_arg = self.eval_place(*callee_arg)?;
|
let callee_arg = self.eval_place(*callee_arg)?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue