move va_list operations into InterpCx
This commit is contained in:
parent
ca3d1a3211
commit
f5bf3353e6
18 changed files with 577 additions and 283 deletions
|
|
@ -763,6 +763,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"),
|
||||
InvalidTag(_) => msg!("enum value has invalid tag: {$tag}"),
|
||||
InvalidFunctionPointer(_) => msg!("using {$pointer} as function pointer but it does not point to a function"),
|
||||
InvalidVaListPointer(_) => msg!("using {$pointer} as variable argument list pointer but it does not point to a variable argument list"),
|
||||
InvalidVTablePointer(_) => msg!("using {$pointer} as vtable pointer but it does not point to a vtable"),
|
||||
InvalidVTableTrait { .. } => msg!("using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected"),
|
||||
InvalidStr(_) => msg!("this string is not valid UTF-8: {$err}"),
|
||||
|
|
@ -778,6 +779,8 @@ 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(),
|
||||
CVariadicMismatch { ..} => "calling a function where the caller and callee disagree on whether the function is C-variadic".into(),
|
||||
CVariadicFixedCountMismatch { .. } => msg!("calling a C-variadic function with {$caller} fixed arguments, but the function expects {$callee}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -823,7 +826,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
diag.arg("len", len);
|
||||
diag.arg("index", index);
|
||||
}
|
||||
UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => {
|
||||
UnterminatedCString(ptr)
|
||||
| InvalidFunctionPointer(ptr)
|
||||
| InvalidVaListPointer(ptr)
|
||||
| InvalidVTablePointer(ptr) => {
|
||||
diag.arg("pointer", ptr);
|
||||
}
|
||||
InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => {
|
||||
|
|
@ -914,6 +920,14 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
diag.arg("caller_ty", caller_ty);
|
||||
diag.arg("callee_ty", callee_ty);
|
||||
}
|
||||
CVariadicMismatch { caller_is_c_variadic, callee_is_c_variadic } => {
|
||||
diag.arg("caller_is_c_variadic", caller_is_c_variadic);
|
||||
diag.arg("callee_is_c_variadic", callee_is_c_variadic);
|
||||
}
|
||||
CVariadicFixedCountMismatch { caller, callee } => {
|
||||
diag.arg("caller", caller);
|
||||
diag.arg("callee", callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, HasDataLayout, Integer, Size, VariantIdx};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, 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, MemoryKind, OpTy,
|
||||
PlaceTy, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo,
|
||||
interp_ok, throw_ub, throw_ub_custom,
|
||||
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, 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,23 +354,18 @@ 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);
|
||||
|
||||
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();
|
||||
|
||||
(fixed_count, self.tcx.mk_type_list(&extra_tys))
|
||||
// The first order of business is to figure out the callee signature.
|
||||
// However, that requires the list of variadic arguments.
|
||||
// We use the *caller* information to determine where to split the list of arguments,
|
||||
// and then later check that the callee indeed has the same number of fixed arguments.
|
||||
let extra_tys = if caller_fn_abi.c_variadic {
|
||||
let fixed_count = usize::try_from(caller_fn_abi.fixed_count).unwrap();
|
||||
let extra_tys = args[fixed_count..].iter().map(|arg| arg.layout().ty);
|
||||
self.tcx.mk_type_list_from_iter(extra_tys)
|
||||
} else {
|
||||
(caller_fn_abi.args.len(), ty::List::empty())
|
||||
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");
|
||||
}
|
||||
let callee_fn_abi = self.fn_abi_of_instance(instance, extra_tys)?;
|
||||
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
throw_ub_custom!(
|
||||
|
|
@ -382,6 +377,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
)
|
||||
}
|
||||
|
||||
if caller_fn_abi.c_variadic != callee_fn_abi.c_variadic {
|
||||
throw_ub!(CVariadicMismatch {
|
||||
caller_is_c_variadic: caller_fn_abi.c_variadic,
|
||||
callee_is_c_variadic: callee_fn_abi.c_variadic,
|
||||
});
|
||||
}
|
||||
if caller_fn_abi.c_variadic && caller_fn_abi.fixed_count != callee_fn_abi.fixed_count {
|
||||
throw_ub!(CVariadicFixedCountMismatch {
|
||||
caller: caller_fn_abi.fixed_count,
|
||||
callee: callee_fn_abi.fixed_count,
|
||||
});
|
||||
}
|
||||
|
||||
// Check that all target features required by the callee (i.e., from
|
||||
// the attribute `#[target_feature(enable = ...)]`) are enabled at
|
||||
// compile time.
|
||||
|
|
@ -453,14 +461,12 @@ 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 = 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() {
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
|
||||
// Determine whether there is a special VaList argument. This is always the
|
||||
// last argument, and since arguments start at index 1 that's `arg_count`.
|
||||
let va_list_arg =
|
||||
callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count));
|
||||
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);
|
||||
|
|
@ -468,44 +474,30 @@ 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 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.
|
||||
if Some(local) == va_list_arg {
|
||||
// This is the last callee-side argument of a variadic function.
|
||||
// This argument is a VaList holding the remaining caller-side arguments.
|
||||
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.
|
||||
// Consume the remaining arguments by putting them into the variable argument
|
||||
// list.
|
||||
let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?;
|
||||
// When the frame is dropped, these variable arguments are deallocated.
|
||||
self.frame_mut().va_list = varargs.clone();
|
||||
let key = self.va_list_ptr(varargs.into());
|
||||
|
||||
// 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.
|
||||
// Zero the VaList, 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)?;
|
||||
// Store the "key" pointer in the right field.
|
||||
let key_mplace = self.va_list_key_field(&mplace)?;
|
||||
self.write_pointer(key, &key_mplace)?;
|
||||
} else if Some(local) == body.spread_arg {
|
||||
// Make the local live once, then fill in the value field by field.
|
||||
self.storage_live(local)?;
|
||||
|
|
@ -545,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
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
|
||||
// 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"
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ use super::memory::MemoryKind;
|
|||
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, throw_ub_custom, throw_ub_format, throw_unsup_format,
|
||||
PointerArithmetic, Projectable, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok,
|
||||
throw_inval, throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format,
|
||||
};
|
||||
use crate::interpret::Writeable;
|
||||
|
||||
|
|
@ -751,113 +751,54 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
sym::va_copy => {
|
||||
// fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f>
|
||||
let src_ptr = self.read_pointer(&args[0])?;
|
||||
let va_list = self.deref_pointer(&args[0])?;
|
||||
let key_mplace = self.va_list_key_field(&va_list)?;
|
||||
let key = self.read_pointer(&key_mplace)?;
|
||||
|
||||
// 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 varargs = self.get_ptr_va_list(key)?;
|
||||
let copy_key = self.va_list_ptr(varargs.clone());
|
||||
|
||||
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)?;
|
||||
let copy_key_mplace = self.va_list_key_field(dest)?;
|
||||
self.write_pointer(copy_key, ©_key_mplace)?;
|
||||
}
|
||||
|
||||
sym::va_end => {
|
||||
let ptr_size = self.tcx.data_layout.pointer_size();
|
||||
let va_list = self.deref_pointer(&args[0])?;
|
||||
let key_mplace = self.va_list_key_field(&va_list)?;
|
||||
let key = self.read_pointer(&key_mplace)?;
|
||||
|
||||
// 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)
|
||||
};
|
||||
self.deallocate_va_list(key)?;
|
||||
}
|
||||
|
||||
sym::va_arg => {
|
||||
let ptr_size = self.tcx.data_layout.pointer_size();
|
||||
let va_list = self.deref_pointer(&args[0])?;
|
||||
let key_mplace = self.va_list_key_field(&va_list)?;
|
||||
let key = self.read_pointer(&key_mplace)?;
|
||||
|
||||
// The only argument is a `&mut VaList`.
|
||||
let ap_ref = self.read_pointer(&args[0])?;
|
||||
// Invalidate the old list and get its content. We'll recreate the
|
||||
// new list (one element shorter) below.
|
||||
let mut varargs = self.deallocate_va_list(key)?;
|
||||
|
||||
// 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 Some(arg_mplace) = varargs.pop_front() else {
|
||||
throw_ub!(VaArgOutOfBounds);
|
||||
};
|
||||
|
||||
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 {
|
||||
if arg_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
|
||||
arg_mplace.layout.ty
|
||||
);
|
||||
}
|
||||
// Copy the argument.
|
||||
self.copy_op(&arg_mplace, dest)?;
|
||||
|
||||
self.copy_op(&src_mplace, dest)?;
|
||||
// Update the VaList pointer.
|
||||
let new_key = self.va_list_ptr(varargs);
|
||||
self.write_pointer(new_key, &key_mplace)?;
|
||||
}
|
||||
|
||||
// Unsupported intrinsic: skip the return_to_block below.
|
||||
|
|
@ -1340,4 +1281,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the MPlace of the key from the place storing the VaList.
|
||||
pub(super) fn va_list_key_field<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
va_list: &P,
|
||||
) -> InterpResult<'tcx, P> {
|
||||
// The struct wrapped by VaList.
|
||||
let va_list_inner = self.project_field(va_list, FieldIdx::ZERO)?;
|
||||
|
||||
// Find the first pointer field in this struct. The exact index is target-specific.
|
||||
let ty::Adt(adt, substs) = va_list_inner.layout().ty.kind() else {
|
||||
bug!("invalid VaListImpl layout");
|
||||
};
|
||||
|
||||
for (i, field) in adt.non_enum_variant().fields.iter().enumerate() {
|
||||
if field.ty(*self.tcx, substs).is_raw_ptr() {
|
||||
return self.project_field(&va_list_inner, FieldIdx::from_usize(i));
|
||||
}
|
||||
}
|
||||
|
||||
bug!("no VaListImpl field is a pointer");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
|
|||
extra_fn_ptr_map: FxIndexMap<AllocId, M::ExtraFnVal>,
|
||||
|
||||
/// Map storing variable argument lists.
|
||||
va_list_map: FxIndexMap<AllocId, Vec<MPlaceTy<'tcx, M::Provenance>>>,
|
||||
va_list_map: FxIndexMap<AllocId, VecDeque<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
|
||||
|
|
@ -237,19 +237,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.global_root_pointer(Pointer::from(id)).unwrap()
|
||||
}
|
||||
|
||||
/// Insert a new variable argument list in the global map of variable argument lists.
|
||||
pub fn va_list_ptr(
|
||||
&mut self,
|
||||
varargs: Vec<MPlaceTy<'tcx, M::Provenance>>,
|
||||
index: u64,
|
||||
varargs: VecDeque<MPlaceTy<'tcx, M::Provenance>>,
|
||||
) -> 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()
|
||||
self.global_root_pointer(Pointer::from(id)).unwrap()
|
||||
}
|
||||
|
||||
pub fn allocate_ptr(
|
||||
|
|
@ -1020,7 +1018,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
// # Variable argument lists
|
||||
if let Some(_) = self.get_va_list_alloc(id) {
|
||||
if self.memory.va_list_map.contains_key(&id) {
|
||||
return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::VaList, Mutability::Not);
|
||||
}
|
||||
|
||||
|
|
@ -1071,18 +1069,6 @@ 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(
|
||||
|
|
@ -1110,6 +1096,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
.into()
|
||||
}
|
||||
|
||||
pub fn get_ptr_va_list(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, &VecDeque<MPlaceTy<'tcx, M::Provenance>>> {
|
||||
trace!("get_ptr_va_list({:?})", ptr);
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset)))
|
||||
}
|
||||
|
||||
let Some(va_list) = self.memory.va_list_map.get(&alloc_id) else {
|
||||
throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset)))
|
||||
};
|
||||
|
||||
interp_ok(va_list)
|
||||
}
|
||||
|
||||
/// Removes this VaList from the global map of variable argument lists. This does not deallocate
|
||||
/// the VaList elements, that happens when the Frame is popped.
|
||||
pub fn deallocate_va_list(
|
||||
&mut self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, VecDeque<MPlaceTy<'tcx, M::Provenance>>> {
|
||||
trace!("deallocate_va_list({:?})", ptr);
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset)))
|
||||
}
|
||||
|
||||
let Some(va_list) = self.memory.va_list_map.swap_remove(&alloc_id) else {
|
||||
throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset)))
|
||||
};
|
||||
|
||||
self.memory.dead_alloc_map.insert(alloc_id, (Size::ZERO, Align::ONE));
|
||||
interp_ok(va_list)
|
||||
}
|
||||
|
||||
/// Get the dynamic type of the given vtable pointer.
|
||||
/// If `expected_trait` is `Some`, it must be a vtable for the given trait.
|
||||
pub fn get_ptr_vtable_ty(
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
|
|||
use rustc_middle::{bug, mir};
|
||||
use rustc_mir_dataflow::impls::always_storage_live_locals;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::callconv::ArgAbi;
|
||||
use tracing::field::Empty;
|
||||
use tracing::{info_span, instrument, trace};
|
||||
|
||||
use super::{
|
||||
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
AllocId, CtfeProvenance, FnArg, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar,
|
||||
from_known_layout, interp_ok, throw_ub, throw_unsup,
|
||||
};
|
||||
|
|
@ -455,15 +456,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
|
||||
let return_action = if cleanup {
|
||||
// We need to take the locals out, since we need to mutate while iterating.
|
||||
for local in &frame.locals {
|
||||
self.deallocate_local(local.value)?;
|
||||
}
|
||||
|
||||
// Deallocate any c-variadic arguments.
|
||||
for mplace in &frame.va_list {
|
||||
self.deallocate_vararg(mplace)?;
|
||||
}
|
||||
self.deallocate_varargs(&frame.va_list)?;
|
||||
|
||||
// Call the machine hook, which determines the next steps.
|
||||
let return_action = M::after_stack_pop(self, frame, unwinding)?;
|
||||
|
|
@ -610,22 +608,6 @@ 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)]
|
||||
|
|
@ -653,6 +635,58 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Consume the arguments provided by the iterator and store them as a list
|
||||
/// of variadic arguments. Return a list of the places that hold those arguments.
|
||||
pub(crate) fn allocate_varargs<I, J>(
|
||||
&mut self,
|
||||
caller_args: &mut I,
|
||||
callee_abis: &mut J,
|
||||
) -> InterpResult<'tcx, Vec<MPlaceTy<'tcx, M::Provenance>>>
|
||||
where
|
||||
I: Iterator<Item = (&'a FnArg<'tcx, M::Provenance>, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
|
||||
J: Iterator<Item = (usize, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
|
||||
{
|
||||
// Consume the remaining arguments and store them in fresh allocations.
|
||||
let mut varargs = Vec::new();
|
||||
for (fn_arg, caller_abi) in caller_args {
|
||||
// The callee ABI is entirely computed based on which arguments the caller has
|
||||
// provided so it should not be possible to get a mismatch here.
|
||||
let (_idx, callee_abi) = callee_abis.next().unwrap();
|
||||
assert!(self.check_argument_compat(caller_abi, callee_abi)?);
|
||||
// FIXME: do we have to worry about in-place argument passing?
|
||||
let op = self.copy_fn_arg(fn_arg);
|
||||
let mplace = self.allocate(op.layout, MemoryKind::Stack)?;
|
||||
self.copy_op(&op, &mplace)?;
|
||||
|
||||
varargs.push(mplace);
|
||||
}
|
||||
assert!(callee_abis.next().is_none());
|
||||
|
||||
interp_ok(varargs)
|
||||
}
|
||||
|
||||
/// Deallocate the variadic arguments in the list (that must have been created with `allocate_varargs`).
|
||||
fn deallocate_varargs(
|
||||
&mut self,
|
||||
varargs: &[MPlaceTy<'tcx, M::Provenance>],
|
||||
) -> InterpResult<'tcx> {
|
||||
for vararg in varargs {
|
||||
let ptr = vararg.ptr();
|
||||
|
||||
trace!(
|
||||
"deallocating vararg {:?}: {:?}",
|
||||
vararg,
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
|
||||
pub(super) fn print(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -404,6 +404,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
InvalidTag(Scalar<AllocId>),
|
||||
/// Using a pointer-not-to-a-function as function pointer.
|
||||
InvalidFunctionPointer(Pointer<AllocId>),
|
||||
/// Using a pointer-not-to-a-va-list as variable argument list pointer.
|
||||
InvalidVaListPointer(Pointer<AllocId>),
|
||||
/// Using a pointer-not-to-a-vtable as vtable pointer.
|
||||
InvalidVTablePointer(Pointer<AllocId>),
|
||||
/// Using a vtable for the wrong trait.
|
||||
|
|
@ -438,6 +440,10 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
/// `va_arg` was called on an exhausted `VaList`.
|
||||
VaArgOutOfBounds,
|
||||
/// The caller and callee disagree on whether they are c-variadic or not.
|
||||
CVariadicMismatch { caller_is_c_variadic: bool, callee_is_c_variadic: bool },
|
||||
/// The caller and callee disagree on the number of fixed (i.e. non-c-variadic) arguments.
|
||||
CVariadicFixedCountMismatch { caller: u32, callee: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -184,8 +184,8 @@ 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 | AllocKind::VaList => unreachable!(),
|
||||
AllocKind::VTable | AllocKind::VaList => dummy_alloc(params),
|
||||
AllocKind::TypeId | AllocKind::Dead => 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 => {
|
||||
|
|
|
|||
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.rs
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#![feature(c_variadic)]
|
||||
|
||||
unsafe extern "C" fn helper(_: i32, _: ...) {}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let f = helper as *const ();
|
||||
let f = std::mem::transmute::<_, unsafe extern "C" fn(...)>(f);
|
||||
|
||||
f(1);
|
||||
//~^ ERROR: Undefined Behavior
|
||||
}
|
||||
}
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
error: Undefined Behavior: calling a C-variadic function with 0 fixed arguments, but the function expects 1
|
||||
--> tests/fail/c-variadic-mismatch-count.rs:LL:CC
|
||||
|
|
||||
LL | f(1);
|
||||
| ^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch.rs
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#![feature(c_variadic)]
|
||||
|
||||
unsafe extern "C" fn helper(_: i32, _: ...) {}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let f = helper as *const ();
|
||||
let f = std::mem::transmute::<_, unsafe extern "C" fn(_: i32, _: i64)>(f);
|
||||
|
||||
f(1i32, 1i64);
|
||||
//~^ ERROR: Undefined Behavior: calling a function where the caller and callee disagree on whether the function is C-variadic
|
||||
}
|
||||
}
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch.stderr
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch.stderr
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
error: Undefined Behavior: calling a function where the caller and callee disagree on whether the function is C-variadic
|
||||
--> tests/fail/c-variadic-mismatch.rs:LL:CC
|
||||
|
|
||||
LL | f(1i32, 1i64);
|
||||
| ^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
15
src/tools/miri/tests/fail/c-variadic.rs
Normal file
15
src/tools/miri/tests/fail/c-variadic.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#![feature(c_variadic)]
|
||||
|
||||
//@error-in-other-file: Undefined Behavior: more C-variadic arguments read than were passed
|
||||
|
||||
fn read_too_many() {
|
||||
unsafe extern "C" fn variadic(mut ap: ...) {
|
||||
ap.arg::<i32>();
|
||||
}
|
||||
|
||||
unsafe { variadic() };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
read_too_many();
|
||||
}
|
||||
22
src/tools/miri/tests/fail/c-variadic.stderr
Normal file
22
src/tools/miri/tests/fail/c-variadic.stderr
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
error: Undefined Behavior: more C-variadic arguments read than were passed
|
||||
--> RUSTLIB/core/src/ffi/va_list.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { va_arg(self) }
|
||||
| ^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: stack backtrace:
|
||||
0: std::ffi::VaList::<'_>::arg
|
||||
at RUSTLIB/core/src/ffi/va_list.rs:LL:CC
|
||||
1: read_too_many::variadic
|
||||
at tests/fail/c-variadic.rs:LL:CC
|
||||
2: read_too_many
|
||||
at tests/fail/c-variadic.rs:LL:CC
|
||||
3: main
|
||||
at tests/fail/c-variadic.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -1,18 +1,115 @@
|
|||
#![feature(c_variadic)]
|
||||
|
||||
use core::ffi::VaList;
|
||||
use std::ffi::{CStr, VaList, c_char, c_double, c_int, c_long};
|
||||
|
||||
fn helper(ap: VaList) -> i32 {
|
||||
// unsafe { ap.arg::<i32>() }
|
||||
let _ = ap;
|
||||
0
|
||||
fn ignores_arguments() {
|
||||
unsafe extern "C" fn variadic(_: ...) {}
|
||||
|
||||
unsafe { variadic() };
|
||||
unsafe { variadic(1, 2, 3) };
|
||||
}
|
||||
|
||||
unsafe extern "C" fn variadic(a: i32, ap: ...) -> i32 {
|
||||
assert_eq!(a, 42);
|
||||
helper(ap)
|
||||
fn echo() {
|
||||
unsafe extern "C" fn variadic(mut ap: ...) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic(1) }, 1);
|
||||
assert_eq!(unsafe { variadic(3, 2, 1) }, 3);
|
||||
}
|
||||
|
||||
fn forward_by_val() {
|
||||
unsafe fn helper(mut ap: VaList) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn variadic(ap: ...) -> i32 {
|
||||
helper(ap)
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic(1) }, 1);
|
||||
assert_eq!(unsafe { variadic(3, 2, 1) }, 3);
|
||||
}
|
||||
|
||||
fn forward_by_ref() {
|
||||
unsafe fn helper(ap: &mut VaList) -> i32 {
|
||||
ap.arg()
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
fn nested() {
|
||||
unsafe fn helper(mut ap1: VaList, mut ap2: VaList) -> (i32, i32) {
|
||||
(ap1.arg(), ap2.arg())
|
||||
}
|
||||
|
||||
unsafe extern "C" fn variadic2(ap1: VaList, ap2: ...) -> (i32, i32) {
|
||||
helper(ap1, ap2)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn variadic1(ap1: ...) -> (i32, i32) {
|
||||
variadic2(ap1, 2, 2)
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { variadic1(1) }, (1, 2));
|
||||
|
||||
let (a, b) = unsafe { variadic1(1, 1) };
|
||||
|
||||
assert_eq!(a, 1);
|
||||
assert_eq!(b, 2);
|
||||
}
|
||||
|
||||
fn various_types() {
|
||||
unsafe extern "C" fn check_list_2(mut ap: ...) {
|
||||
macro_rules! continue_if {
|
||||
($cond:expr) => {
|
||||
if !($cond) {
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(unsafe { variadic(42, 1) }, 1);
|
||||
ignores_arguments();
|
||||
echo();
|
||||
forward_by_val();
|
||||
forward_by_ref();
|
||||
nested();
|
||||
various_types();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#![feature(const_clone)]
|
||||
|
||||
use std::ffi::VaList;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
const unsafe extern "C" fn read_n<const N: usize>(mut ap: ...) {
|
||||
let mut i = N;
|
||||
|
|
@ -77,23 +78,13 @@ fn use_after_free() {
|
|||
};
|
||||
}
|
||||
|
||||
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);
|
||||
// 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 copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
|
|
@ -104,12 +95,12 @@ fn manual_copy_drop() {
|
|||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080]
|
||||
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
|
||||
}
|
||||
|
||||
fn manual_copy_forget() {
|
||||
const unsafe extern "C" fn helper(ap: ...) {
|
||||
let mut copy: VaList = va_list_copy!(ap);
|
||||
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
|
|
@ -120,12 +111,12 @@ fn manual_copy_forget() {
|
|||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080]
|
||||
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
|
||||
}
|
||||
|
||||
fn manual_copy_read() {
|
||||
const unsafe extern "C" fn helper(mut ap: ...) {
|
||||
let mut copy: VaList = va_list_copy!(ap);
|
||||
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Reading from `ap` after reading from `copy` is UB.
|
||||
let _ = copy.arg::<i32>();
|
||||
|
|
@ -133,7 +124,15 @@ fn manual_copy_read() {
|
|||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ ERROR va_arg on unknown va_list allocation ALLOC0
|
||||
//~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080]
|
||||
}
|
||||
|
||||
fn drop_of_invalid() {
|
||||
const {
|
||||
let mut invalid: MaybeUninit<VaList> = MaybeUninit::zeroed();
|
||||
let ap = unsafe { invalid.assume_init() };
|
||||
}
|
||||
//~^ ERROR pointer not dereferenceable: pointer must point to some allocation, but got null pointer [E0080]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -143,5 +142,6 @@ fn main() {
|
|||
manual_copy_read();
|
||||
manual_copy_drop();
|
||||
manual_copy_forget();
|
||||
drop_of_invalid();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error[E0080]: more C-variadic arguments read than were passed
|
||||
--> $DIR/c-variadic-fail.rs:27:13
|
||||
--> $DIR/c-variadic-fail.rs:28: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
|
||||
--> $DIR/c-variadic-fail.rs:16:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
@ -13,13 +13,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:28:5
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:27:5
|
||||
--> $DIR/c-variadic-fail.rs:28:5
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -27,13 +27,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:32: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
|
||||
--> $DIR/c-variadic-fail.rs:16:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
@ -41,13 +41,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:32:5
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:31:5
|
||||
--> $DIR/c-variadic-fail.rs:32:5
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -55,13 +55,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:50: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
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -69,13 +69,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:50:5
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:49:5
|
||||
--> $DIR/c-variadic-fail.rs:50:5
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -83,13 +83,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:53: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
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -97,13 +97,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:53:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:52:5
|
||||
--> $DIR/c-variadic-fail.rs:53:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -111,13 +111,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:56: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
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -125,13 +125,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:56:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:55:5
|
||||
--> $DIR/c-variadic-fail.rs:56:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -139,13 +139,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:59: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
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -153,13 +153,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:59:5
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:58:5
|
||||
--> $DIR/c-variadic-fail.rs:59:5
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -167,13 +167,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:62: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
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -181,13 +181,13 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:62:5
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:61:5
|
||||
--> $DIR/c-variadic-fail.rs:62:5
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -195,7 +195,7 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:75:13
|
||||
|
|
||||
LL | ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call
|
||||
|
|
@ -204,7 +204,7 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:71:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | unsafe {
|
||||
|
|
@ -215,7 +215,7 @@ LL | | };
|
|||
| |_____^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:70:5
|
||||
--> $DIR/c-variadic-fail.rs:71:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | unsafe {
|
||||
|
|
@ -227,14 +227,14 @@ 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
|
||||
error[E0080]: using ALLOC1 as variable argument list pointer but it does not point to a variable argument list
|
||||
--> $DIR/c-variadic-fail.rs:97: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
|
||||
--> $DIR/c-variadic-fail.rs:94:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
|
|
@ -246,27 +246,27 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:97:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:106:5
|
||||
--> $DIR/c-variadic-fail.rs:97: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
|
||||
error[E0080]: using ALLOC2 as variable argument list pointer but it does not point to a variable argument list
|
||||
--> $DIR/c-variadic-fail.rs:113: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
|
||||
--> $DIR/c-variadic-fail.rs:110:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
|
|
@ -278,27 +278,27 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:113:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:122:5
|
||||
--> $DIR/c-variadic-fail.rs:113: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
|
||||
error[E0080]: using ALLOC3 as variable argument list pointer but it does not point to a variable argument list
|
||||
--> $DIR/c-variadic-fail.rs:126: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
|
||||
--> $DIR/c-variadic-fail.rs:123:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
@ -306,19 +306,50 @@ 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
|
||||
--> $DIR/c-variadic-fail.rs:126:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:135:5
|
||||
--> $DIR/c-variadic-fail.rs:126: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
|
||||
error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer
|
||||
--> $DIR/c-variadic-fail.rs:134:5
|
||||
|
|
||||
LL | }
|
||||
| ^ evaluation of `drop_of_invalid::{constant#0}` failed inside this call
|
||||
|
|
||||
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:131:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | let mut invalid: MaybeUninit<VaList> = MaybeUninit::zeroed();
|
||||
LL | | let ap = unsafe { invalid.assume_init() };
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:131:5
|
||||
|
|
||||
LL | / const {
|
||||
LL | | let mut invalid: MaybeUninit<VaList> = MaybeUninit::zeroed();
|
||||
LL | | let ap = unsafe { invalid.assume_init() };
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
|
|||
|
|
@ -32,14 +32,17 @@ extern "C" fn f3_3(_: ..., x: isize) {}
|
|||
|
||||
const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
|
||||
//~^ ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
const extern "C" fn f4_2(x: isize, _: ...) {}
|
||||
//~^ ERROR functions with a C variable argument list must be unsafe
|
||||
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
||||
//~^ ERROR functions with a C variable argument list must be unsafe
|
||||
//~| ERROR `...` must be the last argument of a C-variadic function
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
extern "C" {
|
||||
fn e_f2(..., x: isize);
|
||||
|
|
@ -62,6 +65,7 @@ impl X {
|
|||
const fn i_f5(x: isize, _: ...) {}
|
||||
//~^ ERROR `...` is not supported for non-extern functions
|
||||
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
}
|
||||
|
||||
trait T {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,28 @@ error: `...` must be the last argument of a C-variadic function
|
|||
LL | extern "C" fn f3_3(_: ..., x: isize) {}
|
||||
| ^^^^^^
|
||||
|
||||
error[E0658]: c-variadic const function definitions are unstable
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:33:1
|
||||
|
|
||||
LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/151787> for more information
|
||||
= help: add `#![feature(const_c_variadic)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: c-variadic const function definitions are unstable
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:37:1
|
||||
|
|
||||
LL | const extern "C" fn f4_2(x: isize, _: ...) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/151787> for more information
|
||||
= help: add `#![feature(const_c_variadic)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: functions with a C variable argument list must be unsafe
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:36:36
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:37:36
|
||||
|
|
||||
LL | const extern "C" fn f4_2(x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -92,13 +112,23 @@ LL | const unsafe extern "C" fn f4_2(x: isize, _: ...) {}
|
|||
| ++++++
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:40:26
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:42:26
|
||||
|
|
||||
LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
||||
error[E0658]: c-variadic const function definitions are unstable
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:42:1
|
||||
|
|
||||
LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/151787> for more information
|
||||
= help: add `#![feature(const_c_variadic)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: functions with a C variable argument list must be unsafe
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:40:44
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:42:44
|
||||
|
|
||||
LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -109,13 +139,13 @@ LL | const unsafe extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
|||
| ++++++
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:45:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:48:13
|
||||
|
|
||||
LL | fn e_f2(..., x: isize);
|
||||
| ^^^
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:52:23
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:55:23
|
||||
|
|
||||
LL | fn i_f1(x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -123,7 +153,7 @@ LL | fn i_f1(x: isize, _: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:54:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:57:13
|
||||
|
|
||||
LL | fn i_f2(_: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -131,13 +161,13 @@ LL | fn i_f2(_: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:56:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:59:13
|
||||
|
|
||||
LL | fn i_f3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:56:31
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:59:31
|
||||
|
|
||||
LL | fn i_f3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -145,21 +175,31 @@ LL | fn i_f3(_: ..., x: isize, _: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:59:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:62:13
|
||||
|
|
||||
LL | fn i_f4(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:59:31
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:62:31
|
||||
|
|
||||
LL | fn i_f4(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error[E0658]: c-variadic const function definitions are unstable
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:5
|
||||
|
|
||||
LL | const fn i_f5(x: isize, _: ...) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/151787> for more information
|
||||
= help: add `#![feature(const_c_variadic)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:62:29
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:29
|
||||
|
|
||||
LL | const fn i_f5(x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -167,7 +207,7 @@ LL | const fn i_f5(x: isize, _: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:68:23
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:72:23
|
||||
|
|
||||
LL | fn t_f1(x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -175,7 +215,7 @@ LL | fn t_f1(x: isize, _: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:70:23
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:74:23
|
||||
|
|
||||
LL | fn t_f2(x: isize, _: ...);
|
||||
| ^^^^^^
|
||||
|
|
@ -183,7 +223,7 @@ LL | fn t_f2(x: isize, _: ...);
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:72:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:76:13
|
||||
|
|
||||
LL | fn t_f3(_: ...) {}
|
||||
| ^^^^^^
|
||||
|
|
@ -191,7 +231,7 @@ LL | fn t_f3(_: ...) {}
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` is not supported for non-extern functions
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:74:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:78:13
|
||||
|
|
||||
LL | fn t_f4(_: ...);
|
||||
| ^^^^^^
|
||||
|
|
@ -199,13 +239,13 @@ LL | fn t_f4(_: ...);
|
|||
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:76:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:80:13
|
||||
|
|
||||
LL | fn t_f5(_: ..., x: isize) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: `...` must be the last argument of a C-variadic function
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:78:13
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:82:13
|
||||
|
|
||||
LL | fn t_f6(_: ..., x: isize);
|
||||
| ^^^^^^
|
||||
|
|
@ -223,7 +263,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
|
|||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:36:36
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:37:36
|
||||
|
|
||||
LL | const extern "C" fn f4_2(x: isize, _: ...) {}
|
||||
| ^ - value is dropped here
|
||||
|
|
@ -235,7 +275,7 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {}
|
|||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:62:29
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:29
|
||||
|
|
||||
LL | const fn i_f5(x: isize, _: ...) {}
|
||||
| ^ - value is dropped here
|
||||
|
|
@ -246,6 +286,7 @@ LL | const fn i_f5(x: isize, _: ...) {}
|
|||
= help: add `#![feature(const_destruct)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
error: aborting due to 33 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0493`.
|
||||
Some errors have detailed explanations: E0493, E0658.
|
||||
For more information about an error, try `rustc --explain E0493`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue