Skip to content

Conversation

@folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Jan 2, 2026

tracking issue: #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

@folkertdev folkertdev added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) F-c_variadic `#![feature(c_variadic)]` labels Jan 2, 2026
@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jan 2, 2026
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from d30044f to 5f98625 Compare January 2, 2026 18:57
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 5f98625 to 3368321 Compare January 2, 2026 19:09
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 3368321 to 41a34bc Compare January 2, 2026 19:32
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 41a34bc to 8608ba7 Compare January 2, 2026 20:51
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 8608ba7 to 207b032 Compare January 2, 2026 21:58
@rust-bors
Copy link
Contributor

rust-bors bot commented Jan 4, 2026

☔ The latest upstream changes made this pull request unmergeable. Please resolve the merge conflicts.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 207b032 to 1794556 Compare January 5, 2026 10:30
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch 2 times, most recently from b297adc to c081ba3 Compare January 5, 2026 11:14
@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from c081ba3 to 293ba18 Compare January 5, 2026 12:25
@folkertdev folkertdev marked this pull request as ready for review January 5, 2026 17:34
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 5, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 5, 2026

This PR changes rustc_public

cc @oli-obk, @celinval, @ouz-a, @makai410

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jan 5, 2026
@rust-bors
Copy link
Contributor

rust-bors bot commented Jan 10, 2026

☔ The latest upstream changes (presumably #146923) made this pull request unmergeable. Please resolve the merge conflicts.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 2763963 to 9e57c73 Compare January 21, 2026 09:52
@rustbot
Copy link
Collaborator

rustbot commented Jan 21, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

/// Map for "extra" function pointers.
extra_fn_ptr_map: FxIndexMap<AllocId, M::ExtraFnVal>,

pub(crate) va_list_map: FxIndexMap<AllocId, Vec<MPlaceTy<'tcx, M::Provenance>>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, you have a separate map here to give "meaning" to a VaList allocation... that's the part I missed earlier.

If you have that... why do you even need the new variant in GlobalAlloc? I know I told you to add that, but I also didn't expect a va_list_map field. I like the idea with the field, but it's not clear to me that we need both the field and the new kind of GlobalAlloc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An allocation is still needed to make the pointer. None of the other variants fit nicely I think, but maybe I'm just missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A GlobalAlloc is only needed for global allocations, as the name suggests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, how should I get an AllocId then? something like this?

                    let (alloc_id, _, _) = {
                        let f = self.layout_of(self.tcx.types.unit).unwrap();
                        let mplace = MPlaceTy::fake_alloc_zst(f);
                        let ptr = mplace.ptr();
                        self.ptr_get_alloc_id(ptr, 0)?
                    };

this specifically causes

pointer not dereferenceable: pointer must point to some allocation, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other approaches frustratingly create a Pointer instead of a Pointer:<M::Provemance>...

Copy link
Member

@RalfJung RalfJung Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your new va_list_map works basically like the existing extra_fn_ptr_map, so you can copy what we do for that:

let id = self.tcx.reserve_alloc_id();
let old = self.memory.extra_fn_ptr_map.insert(id, extra);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please keep va_list_map private to this file and have methods similar to the existing special allocation kinds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the above, you still get a Pointer<CmseProvenance>. Other code uses global_root_pointer to turn that into a Pointer<M::Provenance>, but that fails when the pointer is not actually a global allocation. Function pointers would still use

pub enum GlobalAlloc<'tcx> {
    /// The alloc ID is used as a function pointer.
    Function { instance: Instance<'tcx> },
    // ...
}

I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I found the assert that needs to change, nvm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will have to add a new variant to this enum and take that into account in Miri.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch 2 times, most recently from a3a75ad to 19347f2 Compare January 22, 2026 10:43
@RalfJung
Copy link
Member

RalfJung commented Jan 22, 2026

@rustbot author
Just so it is explicit when this is ready for review again :)

(I haven't yet had a chance to look at the code in detail, but given the larger structural issues it's probably better tog et those right before I do the detailed review.)

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 22, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 22, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the c-variadic-const-eval branch from 19347f2 to 83db12a Compare January 22, 2026 11:07
Comment on lines 490 to 496
// 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)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for const evaluation, but with Miri when I read this pointer value again in va_arg or va_endit has no provenance. Should I be storing the pointer in some other way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you push the code and tests? CI seems happy.

};

let (prov, _offset) = va_list_ptr.into_raw_parts();
let alloc_id = prov.unwrap().get_alloc_id().unwrap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in any case, the current example will fail here. The prov.unwrap() errors out:

Backtrace
error: no output was expected
Execute `./miri test --bless` to update `tests/pass/c-variadic.stderr` to the actual output
+++ <stderr output>

thread 'rustc' (330406) panicked at /home/folkertdev/rust/rust/compiler/rustc_const_eval/src/interpret/intrinsics.rs:788:37:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic
   3: core::option::unwrap_failed
   4: <rustc_const_eval::interpret::eval_context::InterpCx<miri::machine::MiriMachine>>::eval_intrinsic
   5: <rustc_const_eval::interpret::eval_context::InterpCx<miri::machine::MiriMachine>>::init_fn_call
   6: <rustc_const_eval::interpret::eval_context::InterpCx<miri::machine::MiriMachine> as miri::concurrency::thread::EvalContextExt>::run_threads
   7: miri::eval::eval_entry
   8: <miri::MiriCompilerCalls as rustc_driver_impl::Callbacks>::after_analysis
   9: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<<rustc_middle::ty::context::GlobalCtxt>::enter<rustc_interface::passes::create_and_enter_global_ctxt<core::option::Option<rustc_interface::queries::Linker>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2}>::{closure#2}::{closure#0}, core::option::Option<rustc_interface::queries::Linker>>::{closure#1}, core::option::Option<rustc_interface::queries::Linker>>::{closure#0}, core::option::Option<rustc_interface::queries::Linker>>
  10: <rustc_middle::ty::context::TyCtxt>::create_global_ctxt::<core::option::Option<rustc_interface::queries::Linker>, rustc_interface::passes::create_and_enter_global_ctxt<core::option::Option<rustc_interface::queries::Linker>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2}>::{closure#2}::{closure#0}>
  11: <rustc_interface::passes::create_and_enter_global_ctxt<core::option::Option<rustc_interface::queries::Linker>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2}>::{closure#2} as core::ops::function::FnOnce<(&rustc_session::session::Session, rustc_middle::ty::context::CurrentGcx, alloc::sync::Arc<rustc_data_structures::jobserver::Proxy>, &std::sync::once_lock::OnceLock<rustc_middle::ty::context::GlobalCtxt>, &rustc_data_structures::sync::worker_local::WorkerLocal<rustc_middle::arena::Arena>, &rustc_data_structures::sync::worker_local::WorkerLocal<rustc_hir::Arena>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2})>>::call_once::{shim:vtable#0}
  12: rustc_interface::passes::create_and_enter_global_ctxt::<core::option::Option<rustc_interface::queries::Linker>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2}>
  13: rustc_interface::interface::run_compiler::<(), rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}
  14: <scoped_tls::ScopedKey<rustc_span::SessionGlobals>>::set::<rustc_interface::util::run_in_thread_with_globals<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<(), rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}::{closure#0}, ()>
  15: rustc_span::create_session_globals_then::<(), rustc_interface::util::run_in_thread_with_globals<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<(), rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}::{closure#0}>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

error: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/miri/issues/new

note: please make sure that you have updated to the latest nightly

note: please attach the file at `/home/folkertdev/rust/rust/src/tools/miri/rustc-ice-2026-01-22T18_30_49-330404.txt` to your bug report

note: rustc 1.95.0-dev running on x86_64-unknown-linux-gnu

note: compiler flags: -Z ui-testing

query stack during panic:
end of query stack

Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:
note: the place in the program where the ICE was triggered
  --> /home/folkertdev/rust/rust/library/core/src/ffi/va_list.rs:224:18
   |
LL |         unsafe { va_end(self) }
   |                  ^^^^^^^^^^^^
   |
   = note: stack backtrace:

Copy link
Member

@RalfJung RalfJung Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generally seems somewhat hacky, using into_raw_parts isn't great.

If you are creating a new allocation on each va_arg call anyway, why do you store the "list index" in the offset? That just poses the risk that someone does add to change where in the list we are. Shouldn't it also work to have just the remaining elements in the va_list_map and have the pointer always point at the "beginning" of the allocation? Then you can define a helper similar to get_ptr_fn that takes a pointer and returns the remaining va_arg list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latest commit the offset is no longer used. That does not solve the issue though: provenance on the pointer is lost when it is written to and then read again from memory.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job x86_64-gnu-miri failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
REPOSITORY                                   TAG       IMAGE ID       CREATED      SIZE
ghcr.io/dependabot/dependabot-updater-core   latest    354d02aa29ac   4 days ago   783MB
=> Removing docker images...
Deleted Images:
untagged: ghcr.io/dependabot/dependabot-updater-core:latest
untagged: ghcr.io/dependabot/dependabot-updater-core@sha256:596da3f22bcbdff2c96fd7126001278022c834c1621c5efa2ad1a7794590636c
deleted: sha256:354d02aa29acf525570c732b6e006ecf138de6d63ca525d552eb4b24880ddc6c
deleted: sha256:8b7af0e426bc2cbeeacfd96b8354d3b80016991520977197e62090e47abaede8
deleted: sha256:cadf11ef1de7fdd5eab563757942353684047f09b212dc99d6ed48e8acf34d62
deleted: sha256:569b0caf9d5285db44ccd2629a3470139eea755be423a33a54d8a24cb3926bfa
deleted: sha256:f9dc5feb048d8f9fd43137e3998f59e9acfbd76c47a4e14984d109654119e282
---
tests/pass/tree_borrows/wildcard/wildcard.rs ... ok
tests/pass/shims/x86/rounding-error.rs ... ok
tests/pass/shims/x86/intrinsics-x86-gfni.rs ... ok

FAILED TEST: tests/pass/c-variadic.rs
command: MIRI_ENV_VAR_TEST="0" MIRI_TEMP="/tmp/miri-uitest-UlvX5q" RUST_BACKTRACE="1" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/miri" "--error-format=json" "--sysroot=/checkout/obj/build/x86_64-unknown-linux-gnu/miri-sysroot" "-Dwarnings" "-Dunused" "-Ainternal_features" "-Zui-testing" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/tmp/miri_ui/0/tests/pass" "tests/pass/c-variadic.rs" "--edition" "2021"

error: test got exit status: 1, but expected 0
 = note: compilation failed, but was expected to succeed

error: no output was expected
Execute `./miri test --bless` to update `tests/pass/c-variadic.stderr` to the actual output
+++ <stderr output>
error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got 0x20868[noalloc] which is a dangling pointer (it has no provenance)
##[error]  --> /checkout/library/core/src/ffi/va_list.rs:224:18
   |
LL |         unsafe { va_end(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<'_> as std::ops::Drop>::drop
               at /checkout/library/core/src/ffi/va_list.rs:224:18: 224:30
           1: std::ptr::drop_in_place::<std::ffi::VaList<'_>> - shim(Some(std::ffi::VaList<'_>))
               at /checkout/library/core/src/ptr/mod.rs:805:1: 807:25
           2: helper
               at tests/pass/c-variadic.rs:9:1: 9:2
           3: variadic
               at tests/pass/c-variadic.rs:13:5: 13:15
---



full stderr:
error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got 0x20868[noalloc] which is a dangling pointer (it has no provenance)
##[error]  --> /checkout/library/core/src/ffi/va_list.rs:224:18
   |
LL |         unsafe { va_end(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<'_> as std::ops::Drop>::drop
               at /checkout/library/core/src/ffi/va_list.rs:224:18: 224:30
           1: std::ptr::drop_in_place::<std::ffi::VaList<'_>> - shim(Some(std::ffi::VaList<'_>))
               at /checkout/library/core/src/ptr/mod.rs:805:1: 807:25
           2: helper
               at tests/pass/c-variadic.rs:9:1: 9:2
           3: variadic
               at tests/pass/c-variadic.rs:13:5: 13:15
---
Location:
   /cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ui_test-0.30.3/src/lib.rs:365

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   1: <color_eyre[c97b998c1a7c9d2]::config::EyreHook>::into_eyre_hook::{closure#0}<unknown>
      at <unknown source file>:<unknown line>
   2: <eyre[619558871d9f979d]::Report>::from_adhoc::<&str><unknown>
      at <unknown source file>:<unknown line>
   3: ui_test[f3d3ddde110bd085]::run_tests_generic::<ui_test[f3d3ddde110bd085]::default_file_filter, ui[a1e834e887d08911]::run_tests::{closure#1}, alloc[bd884804fcb6b85c]::boxed::Box<dyn ui_test[f3d3ddde110bd085]::status_emitter::StatusEmitter>><unknown>
      at <unknown source file>:<unknown line>
   4: ui[a1e834e887d08911]::ui<unknown>
      at <unknown source file>:<unknown line>
   5: ui[a1e834e887d08911]::main<unknown>
      at <unknown source file>:<unknown line>
   6: std[37b30a0f13629a9c]::sys::backtrace::__rust_begin_short_backtrace::<fn() -> core[644e0d92af5c0094]::result::Result<(), eyre[619558871d9f979d]::Report>, core[644e0d92af5c0094]::result::Result<(), eyre[619558871d9f979d]::Report>><unknown>
      at <unknown source file>:<unknown line>
   7: std[37b30a0f13629a9c]::rt::lang_start::<core[644e0d92af5c0094]::result::Result<(), eyre[619558871d9f979d]::Report>>::{closure#0}<unknown>
      at <unknown source file>:<unknown line>
   8: std[37b30a0f13629a9c]::rt::lang_start_internal<unknown>
      at <unknown source file>:<unknown line>
   9: main<unknown>
      at <unknown source file>:<unknown line>
  10: __libc_start_main<unknown>
      at <unknown source file>:<unknown line>
  11: _start<unknown>
      at <unknown source file>:<unknown line>

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
Run with RUST_BACKTRACE=full to include source snippets.
error: test failed, to rerun pass `--test ui`

Caused by:
  process didn't exit successfully: `/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/deps/ui-399d57cbacaa656d` (exit status: 1)
Bootstrap failed while executing `test --stage 2 src/tools/miri src/tools/miri/cargo-miri`
Command `/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo test -Zwarnings --target x86_64-unknown-linux-gnu -Zbinary-dep-depinfo -j 4 -Zroot-dir=/checkout --locked --color=always --profile=release --manifest-path /checkout/src/tools/miri/Cargo.toml -- [workdir=/checkout]` failed with exit code 1
Created at: src/bootstrap/src/core/build_steps/tool.rs:191:21
Executed at: src/bootstrap/src/core/build_steps/test.rs:732:19

Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:47:32
  local time: Fri Jan 23 22:32:20 UTC 2026
  network time: Fri, 23 Jan 2026 22:32:20 GMT
##[error]Process completed with exit code 1.
##[group]Run echo "disk usage:"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. F-c_variadic `#![feature(c_variadic)]` S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants