c-variadic functions in rustc_const_eval
This commit is contained in:
parent
ce693807f6
commit
02c4af397e
16 changed files with 908 additions and 33 deletions
|
|
@ -9,7 +9,7 @@ use rustc_errors::msg;
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo};
|
||||
use rustc_middle::mir::interpret::ReportedErrorInfo;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
|
@ -22,7 +22,7 @@ use super::error::*;
|
|||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
use crate::interpret::{
|
||||
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
|
||||
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
|
||||
throw_ub_custom, throw_unsup, throw_unsup_format,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -757,6 +757,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
WriteToReadOnly(_) => msg!("writing to {$allocation} which is read-only"),
|
||||
DerefFunctionPointer(_) => msg!("accessing {$allocation} which contains a function"),
|
||||
DerefVTablePointer(_) => msg!("accessing {$allocation} which contains a vtable"),
|
||||
DerefVaListPointer(_) => msg!("accessing {$allocation} which contains a variable argument list"),
|
||||
DerefTypeIdPointer(_) => msg!("accessing {$allocation} which contains a `TypeId`"),
|
||||
InvalidBool(_) => msg!("interpreting an invalid 8-bit value as a bool: 0x{$value}"),
|
||||
InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"),
|
||||
|
|
@ -776,6 +777,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
}
|
||||
AbiMismatchArgument { .. } => msg!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"),
|
||||
AbiMismatchReturn { .. } => msg!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"),
|
||||
VaArgOutOfBounds => "more C-variadic arguments read than were passed".into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -800,6 +802,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
| InvalidMeta(InvalidMetaKind::TooBig)
|
||||
| InvalidUninitBytes(None)
|
||||
| DeadLocal
|
||||
| VaArgOutOfBounds
|
||||
| UninhabitedEnumVariantWritten(_)
|
||||
| UninhabitedEnumVariantRead(_) => {}
|
||||
|
||||
|
|
@ -874,6 +877,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
WriteToReadOnly(alloc)
|
||||
| DerefFunctionPointer(alloc)
|
||||
| DerefVTablePointer(alloc)
|
||||
| DerefVaListPointer(alloc)
|
||||
| DerefTypeIdPointer(alloc) => {
|
||||
diag.arg("allocation", alloc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, HasDataLayout, Integer, Size, VariantIdx};
|
||||
use rustc_data_structures::assert_matches;
|
||||
use rustc_errors::msg;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
|
@ -17,9 +17,9 @@ use tracing::field::Empty;
|
|||
use tracing::{info, instrument, trace};
|
||||
|
||||
use super::{
|
||||
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy,
|
||||
Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok,
|
||||
throw_ub, throw_ub_custom, throw_unsup_format,
|
||||
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy,
|
||||
PlaceTy, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo,
|
||||
interp_ok, throw_ub, throw_ub_custom,
|
||||
};
|
||||
use crate::enter_trace_span;
|
||||
use crate::interpret::EnteredTraceSpan;
|
||||
|
|
@ -354,12 +354,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty);
|
||||
|
||||
// Compute callee information.
|
||||
// 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())?;
|
||||
let (fixed_count, c_variadic_args) = if caller_fn_abi.c_variadic {
|
||||
let sig = self.tcx.fn_sig(instance.def_id()).skip_binder();
|
||||
let fixed_count = sig.inputs().skip_binder().len();
|
||||
assert!(caller_fn_abi.args.len() >= fixed_count);
|
||||
let extra_tys: Vec<Ty<'tcx>> =
|
||||
caller_fn_abi.args[fixed_count..].iter().map(|arg_abi| arg_abi.layout.ty).collect();
|
||||
|
||||
if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic {
|
||||
throw_unsup_format!("calling a c-variadic function is not supported");
|
||||
(fixed_count, self.tcx.mk_type_list(&extra_tys))
|
||||
} else {
|
||||
(caller_fn_abi.args.len(), ty::List::empty())
|
||||
};
|
||||
|
||||
let callee_fn_abi = self.fn_abi_of_instance(instance, c_variadic_args)?;
|
||||
|
||||
if callee_fn_abi.c_variadic ^ caller_fn_abi.c_variadic {
|
||||
unreachable!("caller and callee disagree on being c-variadic");
|
||||
}
|
||||
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
|
|
@ -443,8 +453,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// 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().enumerate();
|
||||
for local in body.args_iter() {
|
||||
let mut callee_args_abis = if caller_fn_abi.c_variadic {
|
||||
callee_fn_abi.args[..fixed_count].iter().enumerate()
|
||||
} else {
|
||||
callee_fn_abi.args.iter().enumerate()
|
||||
};
|
||||
|
||||
let mut it = body.args_iter().peekable();
|
||||
while let Some(local) = it.next() {
|
||||
// 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);
|
||||
|
|
@ -452,7 +468,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// 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 {
|
||||
if caller_fn_abi.c_variadic && it.peek().is_none() {
|
||||
// The callee's signature has an additional VaList argument, that the caller
|
||||
// won't actually pass. Here we synthesize a `VaList` value, whose leading bytes
|
||||
// are a pointer that can be mapped to the corresponding variable argument list.
|
||||
self.storage_live(local)?;
|
||||
|
||||
let place = self.eval_place(dest)?;
|
||||
let mplace = self.force_allocation(&place)?;
|
||||
|
||||
// Consume the remaining arguments and store them in a global allocation.
|
||||
let mut varargs = Vec::new();
|
||||
for (fn_arg, abi) in &mut caller_args {
|
||||
let op = self.copy_fn_arg(fn_arg);
|
||||
let mplace = self.allocate(abi.layout, MemoryKind::Stack)?;
|
||||
self.copy_op(&op, &mplace)?;
|
||||
|
||||
varargs.push(mplace);
|
||||
}
|
||||
|
||||
// When the frame is dropped, this ID is used to deallocate the variable arguments list.
|
||||
self.frame_mut().va_list = varargs.clone();
|
||||
|
||||
// This is a new VaList, so start at index 0.
|
||||
let ptr = self.va_list_ptr(varargs, 0);
|
||||
let addr = Scalar::from_pointer(ptr, self);
|
||||
|
||||
// Zero the mplace, so it is fully initialized.
|
||||
self.write_bytes_ptr(
|
||||
mplace.ptr(),
|
||||
(0..mplace.layout.size.bytes()).map(|_| 0u8),
|
||||
)?;
|
||||
|
||||
// Store the pointer to the global variable arguments list allocation in the
|
||||
// first bytes of the `VaList` value.
|
||||
let mut alloc = self
|
||||
.get_ptr_alloc_mut(mplace.ptr(), self.data_layout().pointer_size())?
|
||||
.expect("not a ZST");
|
||||
alloc.write_ptr_sized(Size::ZERO, addr)?;
|
||||
} else 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
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use super::util::ensure_monomorphic_enough;
|
|||
use super::{
|
||||
AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer,
|
||||
PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval,
|
||||
throw_ub_custom, throw_ub_format,
|
||||
throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format,
|
||||
};
|
||||
use crate::interpret::Writeable;
|
||||
|
||||
|
|
@ -750,6 +750,116 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
|
||||
sym::va_copy => {
|
||||
// fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f>
|
||||
let src_ptr = self.read_pointer(&args[0])?;
|
||||
|
||||
// Read the token pointer from the src VaList (alloc_id + offset-as-index).
|
||||
let src_va_list_ptr = {
|
||||
let pointer_size = tcx.data_layout.pointer_size();
|
||||
let alloc = self
|
||||
.get_ptr_alloc(src_ptr, pointer_size)?
|
||||
.expect("va_list storage should not be a ZST");
|
||||
let scalar = alloc.read_pointer(Size::ZERO)?;
|
||||
scalar.to_pointer(self)?
|
||||
};
|
||||
|
||||
let (prov, offset) = src_va_list_ptr.into_raw_parts();
|
||||
let src_alloc_id = prov.unwrap().get_alloc_id().unwrap();
|
||||
let index = offset.bytes();
|
||||
|
||||
// Look up arguments without consuming src.
|
||||
let Some(arguments) = self.get_va_list_alloc(src_alloc_id) else {
|
||||
throw_unsup_format!("va_copy on unknown va_list allocation {:?}", src_alloc_id);
|
||||
};
|
||||
|
||||
// Create a new allocation pointing at the same index.
|
||||
let new_va_list_ptr = self.va_list_ptr(arguments.to_vec(), index);
|
||||
let addr = Scalar::from_pointer(new_va_list_ptr, self);
|
||||
|
||||
// Now overwrite the token pointer stored inside the VaList.
|
||||
let mplace = self.force_allocation(dest)?;
|
||||
let mut alloc = self.get_place_alloc_mut(&mplace)?.unwrap();
|
||||
alloc.write_ptr_sized(Size::ZERO, addr)?;
|
||||
}
|
||||
|
||||
sym::va_end => {
|
||||
let ptr_size = self.tcx.data_layout.pointer_size();
|
||||
|
||||
// The only argument is a `&mut VaList`.
|
||||
let ap_ref = self.read_pointer(&args[0])?;
|
||||
|
||||
// The first bytes of the `VaList` value store a pointer. The `AllocId` of this
|
||||
// pointer is a key into a global map of variable argument lists. The offset is
|
||||
// used as the index of the argument to read.
|
||||
let va_list_ptr = {
|
||||
let alloc = self
|
||||
.get_ptr_alloc(ap_ref, ptr_size)?
|
||||
.expect("va_list storage should not be a ZST");
|
||||
let scalar = alloc.read_pointer(Size::ZERO)?;
|
||||
scalar.to_pointer(self)?
|
||||
};
|
||||
|
||||
let (prov, _offset) = va_list_ptr.into_raw_parts();
|
||||
let alloc_id = prov.unwrap().get_alloc_id().unwrap();
|
||||
|
||||
let Some(_) = self.remove_va_list_alloc(alloc_id) else {
|
||||
throw_unsup_format!("va_end on unknown va_list allocation {:?}", alloc_id)
|
||||
};
|
||||
}
|
||||
|
||||
sym::va_arg => {
|
||||
let ptr_size = self.tcx.data_layout.pointer_size();
|
||||
|
||||
// The only argument is a `&mut VaList`.
|
||||
let ap_ref = self.read_pointer(&args[0])?;
|
||||
|
||||
// The first bytes of the `VaList` value store a pointer. The `AllocId` of this
|
||||
// pointer is a key into a global map of variable argument lists. The offset is
|
||||
// used as the index of the argument to read.
|
||||
let va_list_ptr = {
|
||||
let alloc = self
|
||||
.get_ptr_alloc(ap_ref, ptr_size)?
|
||||
.expect("va_list storage should not be a ZST");
|
||||
let scalar = alloc.read_pointer(Size::ZERO)?;
|
||||
scalar.to_pointer(self)?
|
||||
};
|
||||
|
||||
let (prov, offset) = va_list_ptr.into_raw_parts();
|
||||
let alloc_id = prov.unwrap().get_alloc_id().unwrap();
|
||||
let index = offset.bytes();
|
||||
|
||||
let Some(varargs) = self.remove_va_list_alloc(alloc_id) else {
|
||||
throw_unsup_format!("va_arg on unknown va_list allocation {:?}", alloc_id)
|
||||
};
|
||||
|
||||
let Some(src_mplace) = varargs.get(offset.bytes_usize()).cloned() else {
|
||||
throw_ub!(VaArgOutOfBounds)
|
||||
};
|
||||
|
||||
// Update the offset in this `VaList` value so that a subsequent call to `va_arg`
|
||||
// reads the next argument.
|
||||
let new_va_list_ptr = self.va_list_ptr(varargs, index + 1);
|
||||
let addr = Scalar::from_pointer(new_va_list_ptr, self);
|
||||
let mut alloc = self
|
||||
.get_ptr_alloc_mut(ap_ref, ptr_size)?
|
||||
.expect("va_list storage should not be a ZST");
|
||||
alloc.write_ptr_sized(Size::ZERO, addr)?;
|
||||
|
||||
// NOTE: In C some type conversions are allowed (e.g. casting between signed and
|
||||
// unsigned integers). For now we require c-variadic arguments to be read with the
|
||||
// exact type they were passed as.
|
||||
if src_mplace.layout.ty != dest.layout.ty {
|
||||
throw_unsup_format!(
|
||||
"va_arg type mismatch: requested `{}`, but next argument is `{}`",
|
||||
dest.layout.ty,
|
||||
src_mplace.layout.ty
|
||||
);
|
||||
}
|
||||
|
||||
self.copy_op(&src_mplace, dest)?;
|
||||
}
|
||||
|
||||
// Unsupported intrinsic: skip the return_to_block below.
|
||||
_ => return interp_ok(false),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ use tracing::{debug, instrument, trace};
|
|||
|
||||
use super::{
|
||||
AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg,
|
||||
CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak,
|
||||
Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
|
||||
CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine,
|
||||
MayLeak, Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
|
||||
err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
|
||||
};
|
||||
use crate::const_eval::ConstEvalErrKind;
|
||||
|
|
@ -67,6 +67,8 @@ pub enum AllocKind {
|
|||
LiveData,
|
||||
/// A function allocation (that fn ptrs point to).
|
||||
Function,
|
||||
/// A variable argument list allocation (used by c-variadic functions).
|
||||
VaList,
|
||||
/// A vtable allocation.
|
||||
VTable,
|
||||
/// A TypeId allocation.
|
||||
|
|
@ -126,6 +128,9 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
|
|||
/// Map for "extra" function pointers.
|
||||
extra_fn_ptr_map: FxIndexMap<AllocId, M::ExtraFnVal>,
|
||||
|
||||
/// Map storing variable argument lists.
|
||||
va_list_map: FxIndexMap<AllocId, Vec<MPlaceTy<'tcx, M::Provenance>>>,
|
||||
|
||||
/// To be able to compare pointers with null, and to check alignment for accesses
|
||||
/// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
|
||||
/// that do not exist any more.
|
||||
|
|
@ -161,6 +166,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
|
|||
Memory {
|
||||
alloc_map: M::MemoryMap::default(),
|
||||
extra_fn_ptr_map: FxIndexMap::default(),
|
||||
va_list_map: FxIndexMap::default(),
|
||||
dead_alloc_map: FxIndexMap::default(),
|
||||
validation_in_progress: Cell::new(false),
|
||||
}
|
||||
|
|
@ -199,9 +205,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
return M::extern_static_pointer(self, def_id);
|
||||
}
|
||||
None => {
|
||||
let is_fn_ptr = self.memory.extra_fn_ptr_map.contains_key(&alloc_id);
|
||||
let is_va_list = self.memory.va_list_map.contains_key(&alloc_id);
|
||||
assert!(
|
||||
self.memory.extra_fn_ptr_map.contains_key(&alloc_id),
|
||||
"{alloc_id:?} is neither global nor a function pointer"
|
||||
is_fn_ptr || is_va_list,
|
||||
"{alloc_id:?} is neither global, va_list nor a function pointer"
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -229,6 +237,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.global_root_pointer(Pointer::from(id)).unwrap()
|
||||
}
|
||||
|
||||
pub fn va_list_ptr(
|
||||
&mut self,
|
||||
varargs: Vec<MPlaceTy<'tcx, M::Provenance>>,
|
||||
index: u64,
|
||||
) -> Pointer<M::Provenance> {
|
||||
let id = self.tcx.reserve_alloc_id();
|
||||
let old = self.memory.va_list_map.insert(id, varargs);
|
||||
assert!(old.is_none());
|
||||
// The offset is used to store the current index.
|
||||
let ptr = Pointer::new(id.into(), Size::from_bytes(index));
|
||||
// Variable argument lists are global allocations, so make sure we get the right root
|
||||
// pointer. We know this is not an `extern static` so this cannot fail.
|
||||
self.global_root_pointer(ptr).unwrap()
|
||||
}
|
||||
|
||||
pub fn allocate_ptr(
|
||||
&mut self,
|
||||
size: Size,
|
||||
|
|
@ -956,6 +979,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn is_alloc_live(&self, id: AllocId) -> bool {
|
||||
self.memory.alloc_map.contains_key_ref(&id)
|
||||
|| self.memory.extra_fn_ptr_map.contains_key(&id)
|
||||
|| self.memory.va_list_map.contains_key(&id)
|
||||
// We check `tcx` last as that has to acquire a lock in `many-seeds` mode.
|
||||
// This also matches the order in `get_alloc_info`.
|
||||
|| self.tcx.try_get_global_alloc(id).is_some()
|
||||
|
|
@ -995,6 +1019,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not);
|
||||
}
|
||||
|
||||
// # Variable argument lists
|
||||
if let Some(_) = self.get_va_list_alloc(id) {
|
||||
return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::VaList, Mutability::Not);
|
||||
}
|
||||
|
||||
// # Global allocations
|
||||
if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
|
||||
// NOTE: `static` alignment from attributes has already been applied to the allocation.
|
||||
|
|
@ -1042,6 +1071,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_va_list_alloc(&self, id: AllocId) -> Option<&[MPlaceTy<'tcx, M::Provenance>]> {
|
||||
self.memory.va_list_map.get(&id).map(|v| &**v)
|
||||
}
|
||||
|
||||
pub fn remove_va_list_alloc(
|
||||
&mut self,
|
||||
id: AllocId,
|
||||
) -> Option<Vec<MPlaceTy<'tcx, M::Provenance>>> {
|
||||
self.memory.dead_alloc_map.insert(id, (Size::ZERO, Align::ONE));
|
||||
self.memory.va_list_map.swap_remove(&id)
|
||||
}
|
||||
|
||||
/// Takes a pointer that is the first chunk of a `TypeId` and return the type that its
|
||||
/// provenance refers to, as well as the segment of the hash that this pointer covers.
|
||||
pub fn get_ptr_type_id(
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ use tracing::field::Empty;
|
|||
use tracing::{info_span, instrument, trace};
|
||||
|
||||
use super::{
|
||||
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta,
|
||||
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
|
||||
interp_ok, throw_ub, throw_unsup,
|
||||
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar,
|
||||
from_known_layout, interp_ok, throw_ub, throw_unsup,
|
||||
};
|
||||
use crate::{enter_trace_span, errors};
|
||||
|
||||
|
|
@ -91,6 +91,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
|||
/// Do *not* access this directly; always go through the machine hook!
|
||||
pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
|
||||
|
||||
/// The complete variable argument list of this frame. Its elements must be dropped when the
|
||||
/// frame is popped.
|
||||
pub(super) va_list: Vec<MPlaceTy<'tcx, Prov>>,
|
||||
|
||||
/// The span of the `tracing` crate is stored here.
|
||||
/// When the guard is dropped, the span is exited. This gives us
|
||||
/// a full stack trace on all tracing statements.
|
||||
|
|
@ -259,6 +263,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
|
|||
return_cont: self.return_cont,
|
||||
return_place: self.return_place,
|
||||
locals: self.locals,
|
||||
va_list: self.va_list,
|
||||
loc: self.loc,
|
||||
extra,
|
||||
tracing_span: self.tracing_span,
|
||||
|
|
@ -377,6 +382,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
return_cont,
|
||||
return_place: return_place.clone(),
|
||||
locals,
|
||||
va_list: vec![],
|
||||
instance,
|
||||
tracing_span: SpanGuard::new(),
|
||||
extra: (),
|
||||
|
|
@ -454,6 +460,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.deallocate_local(local.value)?;
|
||||
}
|
||||
|
||||
// Deallocate any c-variadic arguments.
|
||||
for mplace in &frame.va_list {
|
||||
self.deallocate_vararg(mplace)?;
|
||||
}
|
||||
|
||||
// Call the machine hook, which determines the next steps.
|
||||
let return_action = M::after_stack_pop(self, frame, unwinding)?;
|
||||
assert_ne!(return_action, ReturnAction::NoCleanup);
|
||||
|
|
@ -599,6 +610,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
fn deallocate_vararg(&mut self, vararg: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
let ptr = vararg.ptr();
|
||||
|
||||
// FIXME: is the `unwrap` valid here?
|
||||
trace!(
|
||||
"deallocating vararg {:?}: {:?}",
|
||||
vararg,
|
||||
// FIXME: what do we do with this comment?
|
||||
// Locals always have a `alloc_id` (they are never the result of a int2ptr).
|
||||
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
|
||||
);
|
||||
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
/// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/)
|
||||
/// to analyze all the locals in a stack frame.
|
||||
#[inline(always)]
|
||||
|
|
|
|||
|
|
@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
DerefFunctionPointer(AllocId),
|
||||
/// Trying to access the data behind a vtable pointer.
|
||||
DerefVTablePointer(AllocId),
|
||||
/// Trying to access the data behind a va_list pointer.
|
||||
DerefVaListPointer(AllocId),
|
||||
/// Trying to access the actual type id.
|
||||
DerefTypeIdPointer(AllocId),
|
||||
/// Using a non-boolean `u8` as bool.
|
||||
|
|
@ -434,6 +436,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
},
|
||||
/// ABI-incompatible return types.
|
||||
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
/// `va_arg` was called on an exhausted `VaList`.
|
||||
VaArgOutOfBounds,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
|
|||
// No data to be accessed here. But vtables are pointer-aligned.
|
||||
(Size::ZERO, tcx.data_layout.pointer_align().abi)
|
||||
}
|
||||
// Fake allocation, there's nothing to access here
|
||||
// Fake allocation, there's nothing to access here.
|
||||
GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,12 +200,13 @@ impl fmt::Debug for VaList<'_> {
|
|||
|
||||
impl VaList<'_> {
|
||||
// Helper used in the implementation of the `va_copy` intrinsic.
|
||||
pub(crate) fn duplicate(&self) -> Self {
|
||||
Self { inner: self.inner.clone(), _marker: self._marker }
|
||||
pub(crate) const fn duplicate(&self) -> Self {
|
||||
Self { inner: self.inner, _marker: self._marker }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VaList<'_> {
|
||||
#[rustc_const_unstable(feature = "c_variadic_const", issue = "none")]
|
||||
impl<'f> const Clone for VaList<'f> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
// We only implement Clone and not Copy because some future target might not be able to
|
||||
|
|
|
|||
|
|
@ -3484,7 +3484,7 @@ pub const unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
|
|||
/// when a variable argument list is used incorrectly.
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> {
|
||||
pub const fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> {
|
||||
src.duplicate()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
#[cfg(not(all(unix, feature = "native-lib")))]
|
||||
AllocKind::Function => dummy_alloc(params),
|
||||
AllocKind::VTable => dummy_alloc(params),
|
||||
AllocKind::TypeId | AllocKind::Dead => unreachable!(),
|
||||
AllocKind::TypeId | AllocKind::Dead | AllocKind::VaList => unreachable!(),
|
||||
};
|
||||
// We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
|
||||
return interp_ok(base_ptr.addr().to_u64());
|
||||
|
|
@ -363,8 +363,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ProvenanceMode::Default => {
|
||||
// The first time this happens at a particular location, print a warning.
|
||||
static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new();
|
||||
this.dedup_diagnostic(&DEDUP, |first| {
|
||||
NonHaltingDiagnostic::Int2Ptr { details: first }
|
||||
this.dedup_diagnostic(&DEDUP, |first| NonHaltingDiagnostic::Int2Ptr {
|
||||
details: first,
|
||||
});
|
||||
}
|
||||
ProvenanceMode::Strict => {
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
|
|||
dcx.log_protector();
|
||||
}
|
||||
},
|
||||
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
|
||||
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead | AllocKind::VaList => {
|
||||
// No stacked borrows on these allocations.
|
||||
}
|
||||
}
|
||||
|
|
@ -1010,7 +1010,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
|
||||
alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
|
||||
}
|
||||
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
|
||||
AllocKind::Function
|
||||
| AllocKind::VTable
|
||||
| AllocKind::TypeId
|
||||
| AllocKind::Dead
|
||||
| AllocKind::VaList => {
|
||||
// No stacked borrows on these allocations.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -576,7 +576,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let protected = protected_tags.contains_key(&tag);
|
||||
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
|
||||
}
|
||||
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
|
||||
AllocKind::Function
|
||||
| AllocKind::VTable
|
||||
| AllocKind::TypeId
|
||||
| AllocKind::Dead
|
||||
| AllocKind::VaList => {
|
||||
// No tree borrows on these allocations.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
147
tests/ui/consts/const-eval/c-variadic-fail.rs
Normal file
147
tests/ui/consts/const-eval/c-variadic-fail.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
//@ build-fail
|
||||
|
||||
#![feature(c_variadic)]
|
||||
#![feature(c_variadic_const)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(const_clone)]
|
||||
|
||||
use std::ffi::VaList;
|
||||
|
||||
const unsafe extern "C" fn read_n<const N: usize>(mut ap: ...) {
|
||||
let mut i = N;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
let _ = ap.arg::<i32>();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn read_too_many() {
|
||||
// None passed, none read.
|
||||
const { read_n::<0>() }
|
||||
|
||||
// One passed, none read. Ignoring arguments is fine.
|
||||
const { read_n::<0>(1) }
|
||||
|
||||
// None passed, one read.
|
||||
const { read_n::<1>() }
|
||||
//~^ ERROR more C-variadic arguments read than were passed
|
||||
|
||||
// One passed, two read.
|
||||
const { read_n::<2>(1) }
|
||||
//~^ ERROR more C-variadic arguments read than were passed
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn read_as<T: core::ffi::VaArgSafe>(mut ap: ...) -> T {
|
||||
ap.arg::<T>()
|
||||
}
|
||||
|
||||
unsafe fn read_cast() {
|
||||
const { read_as::<i32>(1i32) };
|
||||
const { read_as::<u32>(1u32) };
|
||||
|
||||
const { read_as::<i32>(1i32, 2u64, 3.0f64) };
|
||||
const { read_as::<u32>(1u32, 2u64, 3.0f64) };
|
||||
|
||||
const { read_as::<i64>(1i64) };
|
||||
const { read_as::<u64>(1u64) };
|
||||
|
||||
const { read_as::<u32>(1i32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `u32`, but next argument is `i32`
|
||||
|
||||
const { read_as::<i32>(1u32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u32`
|
||||
|
||||
const { read_as::<i32>(1u64) };
|
||||
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u64`
|
||||
|
||||
const { read_as::<f64>(1i32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `f64`, but next argument is `i32`
|
||||
|
||||
const { read_as::<*const u8>(1i32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `*const u8`, but next argument is `i32`
|
||||
}
|
||||
|
||||
fn use_after_free() {
|
||||
const unsafe extern "C" fn helper(ap: ...) -> [u8; size_of::<VaList>()] {
|
||||
unsafe { std::mem::transmute(ap) }
|
||||
}
|
||||
|
||||
const {
|
||||
unsafe {
|
||||
let ap = helper(1, 2, 3);
|
||||
let mut ap = std::mem::transmute::<_, VaList>(ap);
|
||||
ap.arg::<i32>();
|
||||
//~^ ERROR memory access failed: ALLOC0 has been freed, so this pointer is dangling [E0080]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! va_list_copy {
|
||||
($ap:expr) => {{
|
||||
// A copy created using Clone is valid, and can be used to read arguments.
|
||||
let mut copy = $ap.clone();
|
||||
assert!(copy.arg::<i32>() == 1i32);
|
||||
|
||||
let mut u = core::mem::MaybeUninit::uninit();
|
||||
unsafe { core::ptr::copy_nonoverlapping(&$ap, u.as_mut_ptr(), 1) };
|
||||
|
||||
// Manually creating the copy is fine.
|
||||
unsafe { u.assume_init() }
|
||||
}};
|
||||
}
|
||||
|
||||
fn manual_copy_drop() {
|
||||
const unsafe extern "C" fn helper(ap: ...) {
|
||||
let mut copy: VaList = va_list_copy!(ap);
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
drop(copy);
|
||||
|
||||
// But then using the original is UB.
|
||||
drop(ap);
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080]
|
||||
}
|
||||
|
||||
fn manual_copy_forget() {
|
||||
const unsafe extern "C" fn helper(ap: ...) {
|
||||
let mut copy: VaList = va_list_copy!(ap);
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
std::mem::forget(copy);
|
||||
|
||||
// The read (via `copy`) deallocated the original allocation.
|
||||
drop(ap);
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080]
|
||||
}
|
||||
|
||||
fn manual_copy_read() {
|
||||
const unsafe extern "C" fn helper(mut ap: ...) {
|
||||
let mut copy: VaList = va_list_copy!(ap);
|
||||
|
||||
// Reading from `ap` after reading from `copy` is UB.
|
||||
let _ = copy.arg::<i32>();
|
||||
let _ = ap.arg::<i32>();
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_arg on unknown va_list allocation ALLOC0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
read_too_many();
|
||||
read_cast();
|
||||
manual_copy_read();
|
||||
manual_copy_drop();
|
||||
manual_copy_forget();
|
||||
}
|
||||
}
|
||||
324
tests/ui/consts/const-eval/c-variadic-fail.stderr
Normal file
324
tests/ui/consts/const-eval/c-variadic-fail.stderr
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
error[E0080]: more C-variadic arguments read than were passed
|
||||
--> $DIR/c-variadic-fail.rs:27:13
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#2}` failed inside this call
|
||||
|
|
||||
note: inside `read_n::<1>`
|
||||
--> $DIR/c-variadic-fail.rs:15:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:27:5
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:27:5
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: more C-variadic arguments read than were passed
|
||||
--> $DIR/c-variadic-fail.rs:31:13
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#3}` failed inside this call
|
||||
|
|
||||
note: inside `read_n::<2>`
|
||||
--> $DIR/c-variadic-fail.rs:15:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:31:5
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:31:5
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg type mismatch: requested `u32`, but next argument is `i32`
|
||||
--> $DIR/c-variadic-fail.rs:49:13
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#6}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<u32>`
|
||||
--> $DIR/c-variadic-fail.rs:36:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<u32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:49:5
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:49:5
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u32`
|
||||
--> $DIR/c-variadic-fail.rs:52:13
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#7}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<i32>`
|
||||
--> $DIR/c-variadic-fail.rs:36:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:52:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:52:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u64`
|
||||
--> $DIR/c-variadic-fail.rs:55:13
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#8}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<i32>`
|
||||
--> $DIR/c-variadic-fail.rs:36:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:55:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:55:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg type mismatch: requested `f64`, but next argument is `i32`
|
||||
--> $DIR/c-variadic-fail.rs:58:13
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#9}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<f64>`
|
||||
--> $DIR/c-variadic-fail.rs:36:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<f64>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:58:5
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:58:5
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg type mismatch: requested `*const u8`, but next argument is `i32`
|
||||
--> $DIR/c-variadic-fail.rs:61:13
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#10}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<*const u8>`
|
||||
--> $DIR/c-variadic-fail.rs:36:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<*const u8>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:61:5
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:61:5
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling
|
||||
--> $DIR/c-variadic-fail.rs:74:13
|
||||
|
|
||||
LL | ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call
|
||||
|
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:70:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | unsafe {
|
||||
LL | | let ap = helper(1, 2, 3);
|
||||
LL | | let mut ap = std::mem::transmute::<_, VaList>(ap);
|
||||
... |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:70:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | unsafe {
|
||||
LL | | let ap = helper(1, 2, 3);
|
||||
LL | | let mut ap = std::mem::transmute::<_, VaList>(ap);
|
||||
... |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_end on unknown va_list allocation ALLOC1
|
||||
--> $DIR/c-variadic-fail.rs:106:22
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `manual_copy_drop::{constant#0}` failed inside this call
|
||||
|
|
||||
note: inside `manual_copy_drop::helper`
|
||||
--> $DIR/c-variadic-fail.rs:103:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
note: inside `std::mem::drop::<VaList<'_>>`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
note: inside `drop_in_place::<VaList<'_>> - shim(Some(VaList<'_>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `<VaList<'_> as Drop>::drop`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:106:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:106:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_end on unknown va_list allocation ALLOC2
|
||||
--> $DIR/c-variadic-fail.rs:122:22
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `manual_copy_forget::{constant#0}` failed inside this call
|
||||
|
|
||||
note: inside `manual_copy_forget::helper`
|
||||
--> $DIR/c-variadic-fail.rs:119:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
note: inside `std::mem::drop::<VaList<'_>>`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
note: inside `drop_in_place::<VaList<'_>> - shim(Some(VaList<'_>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `<VaList<'_> as Drop>::drop`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:122:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:122:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0080]: va_arg on unknown va_list allocation ALLOC3
|
||||
--> $DIR/c-variadic-fail.rs:135:22
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `manual_copy_read::{constant#0}` failed inside this call
|
||||
|
|
||||
note: inside `manual_copy_read::helper`
|
||||
--> $DIR/c-variadic-fail.rs:132:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:135:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:135:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
155
tests/ui/consts/const-eval/c-variadic.rs
Normal file
155
tests/ui/consts/const-eval/c-variadic.rs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//@ edition: 2021
|
||||
//@ run-pass
|
||||
//@ ignore-backends: gcc
|
||||
|
||||
#![feature(c_variadic)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(c_variadic_const)]
|
||||
#![feature(const_cmp)]
|
||||
#![feature(const_trait_impl)]
|
||||
|
||||
use std::ffi::*;
|
||||
|
||||
fn ignores_arguments() {
|
||||
const unsafe extern "C" fn variadic(_: ...) {}
|
||||
|
||||
const {
|
||||
unsafe { variadic() };
|
||||
unsafe { variadic(1, 2, 3) };
|
||||
}
|
||||
}
|
||||
|
||||
fn echo() {
|
||||
const unsafe extern "C" fn variadic(mut ap: ...) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic(1) }, 1);
|
||||
assert_eq!(unsafe { variadic(3, 2, 1) }, 3);
|
||||
|
||||
const {
|
||||
assert!(unsafe { variadic(1) } == 1);
|
||||
assert!(unsafe { variadic(3, 2, 1) } == 3);
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_by_val() {
|
||||
const unsafe fn helper(mut ap: VaList) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn variadic(ap: ...) -> i32 {
|
||||
helper(ap)
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic(1) }, 1);
|
||||
assert_eq!(unsafe { variadic(3, 2, 1) }, 3);
|
||||
|
||||
const {
|
||||
assert!(unsafe { variadic(1) } == 1);
|
||||
assert!(unsafe { variadic(3, 2, 1) } == 3);
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_by_ref() {
|
||||
const unsafe fn helper(ap: &mut VaList) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn variadic(mut ap: ...) -> i32 {
|
||||
helper(&mut ap)
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic(1) }, 1);
|
||||
assert_eq!(unsafe { variadic(3, 2, 1) }, 3);
|
||||
|
||||
const {
|
||||
assert!(unsafe { variadic(1) } == 1);
|
||||
assert!(unsafe { variadic(3, 2, 1) } == 3);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
fn nested() {
|
||||
const unsafe fn helper(mut ap1: VaList, mut ap2: VaList) -> (i32, i32) {
|
||||
(ap1.arg(), ap2.arg())
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn variadic2(ap1: VaList, ap2: ...) -> (i32, i32) {
|
||||
helper(ap1, ap2)
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn variadic1(ap1: ...) -> (i32, i32) {
|
||||
variadic2(ap1, 2, 2)
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic1(1) }, (1, 2));
|
||||
|
||||
const {
|
||||
let (a, b) = unsafe { variadic1(1, 1) };
|
||||
|
||||
assert!(a != 2);
|
||||
assert!(a == 1);
|
||||
assert!(b != 1);
|
||||
assert!(b == 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn various_types() {
|
||||
const unsafe extern "C" fn check_list_2(mut ap: ...) {
|
||||
macro_rules! continue_if {
|
||||
($cond:expr) => {
|
||||
if !($cond) {
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool {
|
||||
match CStr::from_ptr(ptr).to_str() {
|
||||
Ok(cstr) => cstr == val,
|
||||
Err(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
continue_if!(ap.arg::<c_double>().floor() == 3.14f64.floor());
|
||||
continue_if!(ap.arg::<c_long>() == 12);
|
||||
continue_if!(ap.arg::<c_int>() == 'a' as c_int);
|
||||
continue_if!(ap.arg::<c_double>().floor() == 6.18f64.floor());
|
||||
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello"));
|
||||
continue_if!(ap.arg::<c_int>() == 42);
|
||||
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "World"));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
check_list_2(
|
||||
3.14 as c_double,
|
||||
12 as c_long,
|
||||
b'a' as c_int,
|
||||
6.28 as c_double,
|
||||
c"Hello".as_ptr(),
|
||||
42 as c_int,
|
||||
c"World".as_ptr(),
|
||||
);
|
||||
const {
|
||||
check_list_2(
|
||||
3.14 as c_double,
|
||||
12 as c_long,
|
||||
b'a' as c_int,
|
||||
6.28 as c_double,
|
||||
c"Hello".as_ptr(),
|
||||
42 as c_int,
|
||||
c"World".as_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
ignores_arguments();
|
||||
echo();
|
||||
forward_by_val();
|
||||
forward_by_ref();
|
||||
nested();
|
||||
various_types();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue