-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
support c-variadic functions in rustc_const_eval
#150601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
93991f2
7922c56
a0035e8
c4a6ae9
264c10c
a0d6871
883d140
3c22fc1
9830c16
7eb7c91
08857bb
d4f0534
6c68a71
0f4af92
b10a090
1f9b9e7
d31c38b
40b40ef
17d7cf4
7aae090
ce3a748
8a49abe
4915062
7c4e3e0
bba1abe
d20c217
f9dbc2c
e0246b7
27f2fb1
7832e24
229f0cf
4bc2318
24b5666
03543fb
cc7d5b5
5e6519c
49ba67a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ use std::borrow::Cow; | |
| use either::{Left, Right}; | ||
| use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; | ||
| use rustc_data_structures::assert_matches; | ||
| use rustc_hir::def::DefKind; | ||
| use rustc_hir::def_id::DefId; | ||
| use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; | ||
| use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; | ||
|
|
@@ -17,7 +18,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::interpret::EnteredTraceSpan; | ||
| use crate::{enter_trace_span, fluent_generated as fluent}; | ||
|
|
@@ -349,13 +350,27 @@ 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())?; | ||
| // The check for the DefKind is so that we don't request the fn_sig of a closure. | ||
| // Otherwise, we hit: | ||
| // | ||
| // DefId(1:180 ~ std[269c]::rt::lang_start_internal::{closure#0}) does not have a "fn_sig" | ||
| let (fixed_count, callee_c_variadic_args) = | ||
| if matches!(self.tcx.def_kind(instance.def_id()), DefKind::Fn) | ||
| && let callee_fn_sig = self.tcx.fn_sig(instance.def_id()).skip_binder() | ||
| && callee_fn_sig.c_variadic() | ||
|
Comment on lines
+353
to
+360
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very odd and seems to break our usual abstractions. How does codegen deal with this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure, I asked in #t-compiler/help > no `fn_sig`for `lang_start_internal` closure. My impression is that those functions just never get
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently checking for the |
||
| { | ||
| // A mismatch in caller and callee fixed_count will error below. | ||
| let fixed_count = callee_fn_sig.inputs().skip_binder().len(); | ||
| let extra_tys = caller_fn_abi.args[caller_fn_abi.fixed_count as usize..] | ||
| .iter() | ||
| .map(|arg_abi| arg_abi.layout.ty); | ||
|
|
||
| (fixed_count, self.tcx.mk_type_list_from_iter(extra_tys)) | ||
| } else { | ||
| (caller_fn_abi.fixed_count.try_into().unwrap(), 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"); | ||
| } | ||
| let callee_fn_abi = self.fn_abi_of_instance(instance, callee_c_variadic_args)?; | ||
|
|
||
| if caller_fn_abi.conv != callee_fn_abi.conv { | ||
| throw_ub_custom!( | ||
|
|
@@ -365,6 +380,20 @@ 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. | ||
|
|
@@ -436,16 +465,54 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | |
| // this is a single iterator (that handles `spread_arg`), then | ||
| // `pass_argument` would be the loop body. It takes care to | ||
| // not advance `caller_iter` for ignored arguments. | ||
| let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); | ||
| for local in body.args_iter() { | ||
| let mut callee_args_abis = if caller_fn_abi.c_variadic { | ||
| // Only the fixed arguments are passed normally. C-variadic functions cannot be | ||
| // `extern "Rust"` and `#[track_caller]` can only be applied to `extern "Rust"`, to | ||
| // the extra caller location argument is not relevant here. | ||
| assert!(!instance.def.requires_caller_location(*self.tcx)); | ||
| callee_fn_abi.args[..fixed_count].iter().enumerate() | ||
| } else { | ||
| // NOTE: this handles the extra caller location argument that is passed when | ||
| // `#[track_caller]` is used. The `fixed_count` does not account for this argument. | ||
| // This attribute is only allowed on `extern "Rust"` functions, so the c-variadic | ||
| // case does not need to handle the extra argument. | ||
| callee_fn_abi.args.iter().enumerate() | ||
| }; | ||
|
|
||
| let mut it = body.args_iter().peekable(); | ||
| while let Some(local) = it.next() { | ||
| // Construct the destination place for this argument. At this point all | ||
| // locals are still dead, so we cannot construct a `PlaceTy`. | ||
| let dest = mir::Place::from(local); | ||
| // `layout_of_local` does more than just the instantiation we need to get the | ||
| // type, but the result gets cached so this avoids calling the instantiation | ||
| // query *again* the next time this local is accessed. | ||
| let ty = self.layout_of_local(self.frame(), local, None)?.ty; | ||
| if Some(local) == body.spread_arg { | ||
| if caller_fn_abi.c_variadic && it.peek().is_none() { | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // 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)?; | ||
| // 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 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.