Rollup merge of #150601 - folkertdev:c-variadic-const-eval, r=RalfJung

support c-variadic functions in `rustc_const_eval`

tracking issue: https://github.com/rust-lang/rust/issues/44930

The new `GlobalAlloc::VaList` is used to create an `AllocId` that represents the variable argument list of a frame. The allocation itself does not store any data, all we need is the unique identifier.

The actual variable argument list is stored in `Memory`, and keyed by the `AllocId`. The `Frame` also stores this `AllocId`, so that when a frame is popped, it can deallocate the variable arguments.

At "runtime" a `VaList` value stores a pointer to the global allocation in its first bytes. The provenance on this pointer can be used to retrieve its `AllocId`, and the offset of the pointer is used to store the index of the next argument to read from the variable argument list.

Miri does not yet support `va_arg`, but I think that can be done separetely?

r? @RalfJung
cc @workingjubilee
This commit is contained in:
Jacob Pratt 2026-02-16 04:28:56 -05:00 committed by GitHub
commit 494c6da389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1306 additions and 72 deletions

View file

@ -698,13 +698,11 @@ impl<'a> AstValidator<'a> {
unreachable!("C variable argument list cannot be used in closures")
};
// C-variadics are not yet implemented in const evaluation.
if let Const::Yes(const_span) = sig.header.constness {
self.dcx().emit_err(errors::ConstAndCVariadic {
spans: vec![const_span, variadic_param.span],
const_span,
variadic_span: variadic_param.span,
});
if let Const::Yes(_) = sig.header.constness
&& !self.features.enabled(sym::const_c_variadic)
{
let msg = format!("c-variadic const function definitions are unstable");
feature_err(&self.sess, sym::const_c_variadic, sig.span, msg).emit();
}
if let Some(coroutine_kind) = sig.header.coroutine_kind {

View file

@ -823,17 +823,6 @@ pub(crate) struct ConstAndCoroutine {
pub coroutine_kind: &'static str,
}
#[derive(Diagnostic)]
#[diag("functions cannot be both `const` and C-variadic")]
pub(crate) struct ConstAndCVariadic {
#[primary_span]
pub spans: Vec<Span>,
#[label("`const` because of this")]
pub const_span: Span,
#[label("C-variadic because of this")]
pub variadic_span: Span,
}
#[derive(Diagnostic)]
#[diag("functions cannot be both `{$coroutine_kind}` and C-variadic")]
pub(crate) struct CoroutineAndCVariadic {

View file

@ -815,6 +815,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
});
}
if self.tcx.fn_sig(callee).skip_binder().c_variadic() {
self.check_op(ops::FnCallCVariadic)
}
// At this point, we are calling a function, `callee`, whose `DefId` is known...
// `begin_panic` and `panic_display` functions accept generic

View file

@ -75,6 +75,27 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
}
}
/// A c-variadic function call.
#[derive(Debug)]
pub(crate) struct FnCallCVariadic;
impl<'tcx> NonConstOp<'tcx> for FnCallCVariadic {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable {
gate: sym::const_c_variadic,
gate_already_checked: false,
safe_to_expose_on_stable: false,
is_function_call: true,
}
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx.sess.create_feature_err(
errors::NonConstCVariadicCall { span, kind: ccx.const_kind() },
sym::const_c_variadic,
)
}
}
/// A call to a function that is in a trait, or has trait bounds that make it conditionally-const.
#[derive(Debug)]
pub(crate) struct ConditionallyConstCall<'tcx> {

View file

@ -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,
};

View file

@ -531,6 +531,19 @@ pub struct NonConstClosure {
pub non_or_conditionally: &'static str,
}
#[derive(Diagnostic)]
#[diag(r#"calling const c-variadic functions is unstable in {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}s"#, code = E0015)]
pub struct NonConstCVariadicCall {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Subdiagnostic)]
pub enum NonConstClosureNote {
#[note("function defined here, but it is not `const`")]
@ -757,11 +770,13 @@ 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}"),
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}"),
@ -776,6 +791,9 @@ 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}"),
}
}
@ -800,6 +818,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
| InvalidMeta(InvalidMetaKind::TooBig)
| InvalidUninitBytes(None)
| DeadLocal
| VaArgOutOfBounds
| UninhabitedEnumVariantWritten(_)
| UninhabitedEnumVariantRead(_) => {}
@ -820,7 +839,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 } => {
@ -874,6 +896,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
WriteToReadOnly(alloc)
| DerefFunctionPointer(alloc)
| DerefVTablePointer(alloc)
| DerefVaListPointer(alloc)
| DerefTypeIdPointer(alloc) => {
diag.arg("allocation", alloc);
}
@ -910,6 +933,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);
}
}
}
}

View file

@ -19,7 +19,7 @@ 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,
throw_ub, throw_ub_custom,
};
use crate::enter_trace_span;
use crate::interpret::EnteredTraceSpan;
@ -354,13 +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);
// 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())?;
if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic {
throw_unsup_format!("calling a c-variadic function is not supported");
}
// 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 {
ty::List::empty()
};
let callee_fn_abi = self.fn_abi_of_instance(instance, extra_tys)?;
if caller_fn_abi.conv != callee_fn_abi.conv {
throw_ub_custom!(
@ -372,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.
@ -444,6 +462,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// `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();
// 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`.
@ -452,7 +474,31 @@ 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 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 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());
// Zero the VaList, so it is fully initialized.
self.write_bytes_ptr(
mplace.ptr(),
(0..mplace.layout.size.bytes()).map(|_| 0u8),
)?;
// 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)?;
// Must be a tuple
@ -491,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"

View file

@ -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_custom, throw_ub_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;
@ -750,6 +750,57 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Nondeterministic)?
}
sym::va_copy => {
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)?;
let varargs = self.get_ptr_va_list(key)?;
let copy_key = self.va_list_ptr(varargs.clone());
let copy_key_mplace = self.va_list_key_field(dest)?;
self.write_pointer(copy_key, &copy_key_mplace)?;
}
sym::va_end => {
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)?;
self.deallocate_va_list(key)?;
}
sym::va_arg => {
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)?;
// 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)?;
let Some(arg_mplace) = varargs.pop_front() else {
throw_ub!(VaArgOutOfBounds);
};
// 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 arg_mplace.layout.ty != dest.layout.ty {
throw_unsup_format!(
"va_arg type mismatch: requested `{}`, but next argument is `{}`",
dest.layout.ty,
arg_mplace.layout.ty
);
}
// Copy the argument.
self.copy_op(&arg_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.
_ => return interp_ok(false),
}
@ -1230,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");
}
}

View file

@ -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, 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
/// 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,19 @@ 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: 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());
// 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(Pointer::from(id)).unwrap()
}
pub fn allocate_ptr(
&mut self,
size: Size,
@ -956,6 +977,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 +1017,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not);
}
// # Variable argument lists
if self.memory.va_list_map.contains_key(&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.
@ -1069,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(

View file

@ -12,13 +12,14 @@ 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, Machine, MemPlace, MemPlaceMeta,
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
interp_ok, throw_ub, throw_unsup,
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,
};
use crate::{enter_trace_span, errors};
@ -91,6 +92,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 +264,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 +383,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: (),
@ -449,11 +456,13 @@ 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.
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)?;
assert_ne!(return_action, ReturnAction::NoCleanup);
@ -626,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,

View file

@ -420,6 +420,8 @@ declare_features! (
(unstable, const_async_blocks, "1.53.0", Some(85368)),
/// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items.
(unstable, const_block_items, "CURRENT_RUSTC_VERSION", Some(149226)),
/// Allows defining and calling c-variadic functions in const contexts.
(unstable, const_c_variadic, "CURRENT_RUSTC_VERSION", Some(151787)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
/// Allows using `[const] Destruct` bounds and calling drop impls in const contexts.

View file

@ -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.
@ -402,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.
@ -434,6 +438,12 @@ 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,
/// 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)]

View file

@ -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),
}
}

View file

@ -742,6 +742,7 @@ symbols! {
const_allocate,
const_async_blocks,
const_block_items,
const_c_variadic,
const_closures,
const_compare_raw_pointers,
const_constructor,

View file

@ -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 = "const_c_variadic", issue = "151787")]
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
@ -216,7 +217,8 @@ impl Clone for VaList<'_> {
}
}
impl<'f> Drop for VaList<'f> {
#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")]
impl<'f> const Drop for VaList<'f> {
fn drop(&mut self) {
// SAFETY: this variable argument list is being dropped, so won't be read from again.
unsafe { va_end(self) }
@ -291,7 +293,8 @@ impl<'f> VaList<'f> {
///
/// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html
#[inline]
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")]
pub const unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
// SAFETY: the caller must uphold the safety contract for `va_arg`.
unsafe { va_arg(self) }
}

View file

@ -3485,7 +3485,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
///
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
pub const unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
/// Duplicates a variable argument list. The returned list is initially at the same position as
/// the one in `src`, but can be advanced independently.
@ -3497,7 +3497,7 @@ pub 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()
}
@ -3516,6 +3516,6 @@ pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> {
///
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn va_end(ap: &mut VaList<'_>) {
pub const unsafe fn va_end(ap: &mut VaList<'_>) {
/* deliberately does nothing */
}

View file

@ -184,7 +184,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::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`.

View file

@ -655,7 +655,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.
}
}
@ -1014,7 +1014,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.
}
}

View file

@ -577,7 +577,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.
}
}

View 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
}
}

View 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

View 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
}
}

View 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

View 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();
}

View 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

View file

@ -0,0 +1,115 @@
#![feature(c_variadic)]
use std::ffi::{CStr, VaList, c_char, c_double, c_int, c_long};
fn ignores_arguments() {
unsafe extern "C" fn variadic(_: ...) {}
unsafe { variadic() };
unsafe { variadic(1, 2, 3) };
}
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() {
ignores_arguments();
echo();
forward_by_val();
forward_by_ref();
nested();
various_types();
}

View file

@ -0,0 +1,147 @@
//@ build-fail
#![feature(c_variadic)]
#![feature(const_c_variadic)]
#![feature(const_trait_impl)]
#![feature(const_destruct)]
#![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;
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]
}
};
}
fn manual_copy_drop() {
const unsafe extern "C" fn helper(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>();
drop(copy);
// But then using the original is UB.
drop(ap);
}
const { unsafe { helper(1, 2, 3) } };
//~^ 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 = unsafe { std::mem::transmute_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 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 = unsafe { std::mem::transmute_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 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() {
unsafe {
read_too_many();
read_cast();
manual_copy_read();
manual_copy_drop();
manual_copy_forget();
drop_of_invalid();
}
}

View file

@ -0,0 +1,355 @@
error[E0080]: more C-variadic arguments read than were passed
--> $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:16: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:28:5
|
LL | const { read_n::<1>() }
| ^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:28: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: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:16: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:32:5
|
LL | const { read_n::<2>(1) }
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:32: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: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:37: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:50:5
|
LL | const { read_as::<u32>(1i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:50: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: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:37: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:53:5
|
LL | const { read_as::<i32>(1u32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:53: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: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:37: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:56:5
|
LL | const { read_as::<i32>(1u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:56: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: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:37: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:59:5
|
LL | const { read_as::<f64>(1i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:59: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: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:37: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:62:5
|
LL | const { read_as::<*const u8>(1i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/c-variadic-fail.rs:62: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:75: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:71: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:71: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]: 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:94: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:97:5
|
LL | const { unsafe { helper(1, 2, 3) } };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $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]: 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:110: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:113:5
|
LL | const { unsafe { helper(1, 2, 3) } };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $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]: 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:123: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:126:5
|
LL | const { unsafe { helper(1, 2, 3) } };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $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[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`.

View file

@ -0,0 +1,155 @@
//@ edition: 2021
//@ run-pass
//@ ignore-backends: gcc
#![feature(c_variadic)]
#![feature(const_c_variadic)]
#![feature(const_destruct)]
#![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();
}

View file

@ -0,0 +1,11 @@
#![feature(c_variadic)]
fn main() {
const unsafe extern "C" fn foo(ap: ...) {
//~^ ERROR c-variadic const function definitions are unstable
core::mem::forget(ap);
}
const { unsafe { foo() } }
//~^ ERROR calling const c-variadic functions is unstable in constants
}

View file

@ -0,0 +1,24 @@
error[E0658]: c-variadic const function definitions are unstable
--> $DIR/feature-gate-const-c-variadic.rs:4:5
|
LL | const unsafe extern "C" fn foo(ap: ...) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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[E0015]: calling const c-variadic functions is unstable in constants
--> $DIR/feature-gate-const-c-variadic.rs:9:22
|
LL | const { unsafe { foo() } }
| ^^^^^
|
= 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: aborting due to 2 previous errors
Some errors have detailed explanations: E0015, E0658.
For more information about an error, try `rustc --explain E0015`.

View file

@ -31,18 +31,18 @@ extern "C" fn f3_3(_: ..., x: isize) {}
//~^ ERROR `...` must be the last argument of a C-variadic function
const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
//~^ ERROR functions cannot be both `const` and C-variadic
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
//~^ 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 cannot be both `const` and C-variadic
//~| ERROR functions with a C variable argument list must be unsafe
//~^ 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 cannot be both `const` and C-variadic
//~| ERROR functions with a C variable argument list must be unsafe
//~^ 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);
@ -64,8 +64,8 @@ impl X {
//~| ERROR `...` must be the last argument of a C-variadic function
const fn i_f5(x: isize, _: ...) {}
//~^ ERROR `...` is not supported for non-extern functions
//~| ERROR functions cannot be both `const` and C-variadic
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
//~| ERROR c-variadic const function definitions are unstable
}
trait T {

View file

@ -80,17 +80,25 @@ error: `...` must be the last argument of a C-variadic function
LL | extern "C" fn f3_3(_: ..., x: isize) {}
| ^^^^^^
error: functions cannot be both `const` and C-variadic
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, _: ...) {}
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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 cannot be both `const` and C-variadic
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, _: ...) {}
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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:37:36
@ -109,11 +117,15 @@ error: `...` must be the last argument of a C-variadic function
LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
| ^^^^^^
error: functions cannot be both `const` and C-variadic
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, _: ...) {}
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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:42:44
@ -176,13 +188,15 @@ LL | fn i_f4(_: ..., x: isize, _: ...) {}
|
= help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
error: functions cannot be both `const` and C-variadic
error[E0658]: c-variadic const function definitions are unstable
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:5
|
LL | const fn i_f5(x: isize, _: ...) {}
| ^^^^^ ^^^^^^ C-variadic because of this
| |
| `const` because of this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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:65:29
@ -243,6 +257,10 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
| ^ - value is dropped here
| |
| the destructor for this type cannot be evaluated in constant functions
|
= note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
= 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[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
--> $DIR/variadic-ffi-semantic-restrictions.rs:37:36
@ -251,6 +269,10 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {}
| ^ - value is dropped here
| |
| the destructor for this type cannot be evaluated in constant functions
|
= note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
= 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[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:29
@ -259,7 +281,12 @@ LL | const fn i_f5(x: isize, _: ...) {}
| ^ - value is dropped here
| |
| the destructor for this type cannot be evaluated in constant functions
|
= note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
= 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 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`.