From ca390f32f63e441b4f45654234d8b1b4c10ab4b5 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 14 Aug 2026 20:19:28 +0300 Subject: [PATCH 01/46] [Priroda] Add debugger-lifecycle regression fixtures Record the current CLI and DAP behavior for interpreter errors and post-exit commands before changing the debugger lifecycle semantics. Temporarily relax the expected process exit status for fixtures whose pre-change behavior lets Miri termination escape the debugger: UB fixtures exit with Miri's error status, and the nonzero-exit fixture exits before returning to the prompt. The implementation commit removes this relaxation after Priroda turns those cases into clean debugger states. --- src/tools/miri/priroda/tests/cli.rs | 19 ++++++++++++++++++- .../tests/ui/continue_finishes_nonzero.rs | 4 ++++ .../tests/ui/continue_finishes_nonzero.stdin | 2 ++ .../tests/ui/continue_finishes_nonzero.stdout | 1 + .../tests/ui/continue_finishes_program.stdin | 2 ++ .../miri/priroda/tests/ui/dap_ub_exception.rs | 8 ++++++++ .../priroda/tests/ui/dap_ub_exception.stdin | 9 +++++++++ .../priroda/tests/ui/dap_ub_exception.stdout | 15 +++++++++++++++ .../tests/ui/dap_ub_exception_continue.rs | 8 ++++++++ .../tests/ui/dap_ub_exception_continue.stdin | 11 +++++++++++ .../tests/ui/dap_ub_exception_continue.stdout | 15 +++++++++++++++ .../priroda/tests/ui/ub_exception_continue.rs | 7 +++++++ .../tests/ui/ub_exception_continue.stderr | 13 +++++++++++++ .../tests/ui/ub_exception_continue.stdin | 2 ++ .../tests/ui/ub_exception_continue.stdout | 1 + .../priroda/tests/ui/ub_exception_step.rs | 7 +++++++ .../priroda/tests/ui/ub_exception_step.stderr | 13 +++++++++++++ .../priroda/tests/ui/ub_exception_step.stdin | 3 +++ .../priroda/tests/ui/ub_exception_step.stdout | 1 + .../priroda/tests/ui/ub_exception_stop.rs | 7 +++++++ .../priroda/tests/ui/ub_exception_stop.stderr | 13 +++++++++++++ .../priroda/tests/ui/ub_exception_stop.stdin | 3 +++ .../priroda/tests/ui/ub_exception_stop.stdout | 1 + 23 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs create mode 100644 src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdin create mode 100644 src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_continue.rs create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_continue.stdin create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_step.rs create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_step.stderr create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_step.stdin create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_step.stdout create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_stop.rs create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_stop.stdin create mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index 2bf7f22bd1d98..82f9e33065966 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -7,6 +7,19 @@ use ui_test::spanned::Spanned; use ui_test::status_emitter::StatusEmitter; use ui_test::{CommandBuilder, Config, default_file_filter, run_tests_generic}; +fn per_file_config(config: &mut Config, file_contents: &Spanned>) { + // `//@ priroda-relax-exit-status` lets a fixture accept any exit code, so + // fixtures that terminate with rustc's error-count-driven nonzero exit can + // live in `tests/ui/` alongside the pass-only suite. + if file_contents + .content + .windows(b"//@ priroda-relax-exit-status".len()) + .any(|w| w == b"//@ priroda-relax-exit-status") + { + config.comment_defaults.base().exit_status = None.into(); + } +} + fn main() -> Result<(), Box> { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let miri_dir = manifest_dir.parent().unwrap(); @@ -53,6 +66,10 @@ fn main() -> Result<(), Box> { config.comment_defaults.base().exit_status = Spanned::dummy(0).into(); config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); + config.custom_comments.insert("priroda-relax-exit-status", |parser, _args, span| { + parser.set_custom_once("priroda-relax-exit-status", (), span); + }); + let mut args = ui_test::Args::test()?; args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); config.with_args(&args); @@ -60,7 +77,7 @@ fn main() -> Result<(), Box> { run_tests_generic( vec![config], default_file_filter, - |_, _| {}, + per_file_config, Box::::from(args.format), )?; diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs new file mode 100644 index 0000000000000..e264ef59841a8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs @@ -0,0 +1,4 @@ +//@ priroda-relax-exit-status +fn main() { + std::process::exit(7); +} diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdin b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdin new file mode 100644 index 0000000000000..a072c2312df66 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdin @@ -0,0 +1,2 @@ +continue +quit diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout new file mode 100644 index 0000000000000..08ddab252f366 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout @@ -0,0 +1 @@ +(priroda) program finished with exit code 7 diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin index 44c5d7d65ead7..8ce5905753dd5 100644 --- a/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin @@ -1 +1,3 @@ continue +continue +quit diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs b/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs new file mode 100644 index 0000000000000..e4644a755de8c --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs @@ -0,0 +1,8 @@ +//@ compile-flags: --dap +//@ priroda-relax-exit-status +#![allow(deref_nullptr)] +fn main() { + unsafe { + *std::ptr::null_mut::() = 1; + } +} diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdin b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdin new file mode 100644 index 0000000000000..ad37d7e1f1742 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdin @@ -0,0 +1,9 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 72 + +{"seq":2,"type":"request","command":"launch","arguments":{"program":""}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 74 + +{"seq":4,"type":"request","command":"continue","arguments":{"threadId":1}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout new file mode 100644 index 0000000000000..5a4f9c8ac3e2b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout @@ -0,0 +1,15 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":false,"message":"memory access failed: attempting to access 1 byte, but got null pointer","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"event","event":"terminated","body":null} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs new file mode 100644 index 0000000000000..e4644a755de8c --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs @@ -0,0 +1,8 @@ +//@ compile-flags: --dap +//@ priroda-relax-exit-status +#![allow(deref_nullptr)] +fn main() { + unsafe { + *std::ptr::null_mut::() = 1; + } +} diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdin b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdin new file mode 100644 index 0000000000000..e92e12721d9e8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdin @@ -0,0 +1,11 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 72 + +{"seq":2,"type":"request","command":"launch","arguments":{"program":""}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 74 + +{"seq":4,"type":"request","command":"continue","arguments":{"threadId":1}}Content-Length: 74 + +{"seq":5,"type":"request","command":"continue","arguments":{"threadId":1}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout new file mode 100644 index 0000000000000..5a4f9c8ac3e2b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout @@ -0,0 +1,15 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":false,"message":"memory access failed: attempting to access 1 byte, but got null pointer","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"event","event":"terminated","body":null} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs b/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs new file mode 100644 index 0000000000000..bfcb5b7312ad8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs @@ -0,0 +1,7 @@ +//@ priroda-relax-exit-status +#![allow(deref_nullptr)] +fn main() { + unsafe { + *std::ptr::null_mut::() = 1; + } +} diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr new file mode 100644 index 0000000000000..84dcd026ab086 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer + --> tests/ui/ub_exception_continue.rs:5:9 + | +5 | *std::ptr::null_mut::() = 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 + diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdin b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdin new file mode 100644 index 0000000000000..153508010cecd --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdin @@ -0,0 +1,2 @@ +continue +continue diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout new file mode 100644 index 0000000000000..d845c0acdfa1b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout @@ -0,0 +1 @@ +(priroda) \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.rs b/src/tools/miri/priroda/tests/ui/ub_exception_step.rs new file mode 100644 index 0000000000000..bfcb5b7312ad8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.rs @@ -0,0 +1,7 @@ +//@ priroda-relax-exit-status +#![allow(deref_nullptr)] +fn main() { + unsafe { + *std::ptr::null_mut::() = 1; + } +} diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr new file mode 100644 index 0000000000000..de296040190dc --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer + --> tests/ui/ub_exception_step.rs:5:9 + | +5 | *std::ptr::null_mut::() = 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 + diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.stdin b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdin new file mode 100644 index 0000000000000..3a45547d4fff4 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdin @@ -0,0 +1,3 @@ +continue +step +quit diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout new file mode 100644 index 0000000000000..d845c0acdfa1b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout @@ -0,0 +1 @@ +(priroda) \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs b/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs new file mode 100644 index 0000000000000..bfcb5b7312ad8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs @@ -0,0 +1,7 @@ +//@ priroda-relax-exit-status +#![allow(deref_nullptr)] +fn main() { + unsafe { + *std::ptr::null_mut::() = 1; + } +} diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr new file mode 100644 index 0000000000000..a0cca5cd6cd70 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer + --> tests/ui/ub_exception_stop.rs:5:9 + | +5 | *std::ptr::null_mut::() = 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 + diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdin b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdin new file mode 100644 index 0000000000000..2393571f36e8a --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdin @@ -0,0 +1,3 @@ +continue +l +quit diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout new file mode 100644 index 0000000000000..d845c0acdfa1b --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout @@ -0,0 +1 @@ +(priroda) \ No newline at end of file From b05e1e92bf7ad7460dd9e6a48513a138e9b7eb7d Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 16 Aug 2026 06:32:37 +0300 Subject: [PATCH 02/46] [Priroda] Keep program exits in debugger state Record normal program termination as debugger state instead of letting the Miri termination escape the session. This lets CLI commands after exit report the saved code and lets the nonzero-exit fixture return through the prompt without relaxing the process status. --- src/tools/miri/priroda/src/debugger.rs | 90 +++++++++++++++---- src/tools/miri/priroda/src/frontend/cli.rs | 23 +++-- src/tools/miri/priroda/src/frontend/dap.rs | 11 +-- src/tools/miri/priroda/src/main.rs | 9 +- .../tests/ui/continue_finishes_nonzero.rs | 1 - .../tests/ui/continue_finishes_nonzero.stdout | 1 + .../tests/ui/continue_finishes_program.stdout | 2 + 7 files changed, 97 insertions(+), 40 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index b2b7c8779709a..2088c16d32cc4 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -3,7 +3,7 @@ use std::ops::Range; use std::path::PathBuf; use miri::Immediate::Uninit; -use miri::*; +use miri::{InterpErrorInfo, InterpErrorKind, TerminationInfo, *}; use rustc_abi::{FIRST_VARIANT, FieldIdx, Size}; use rustc_hir::def::CtorKind; use rustc_middle::mir::interpret::AllocId; @@ -39,6 +39,9 @@ pub(super) struct PrirodaContext<'tcx> { breakpoints: BreakpointTable, pub(super) current_location: Option, last_location: Option, + // FIXME: add restart and other post-exit commands, similar to GDB and + // old Priroda, instead of only replaying the saved exit code. + exit_code: Option, } pub(super) enum StorageProj { @@ -125,13 +128,24 @@ pub(super) enum StepResult { Breakpoint, } +pub(super) enum ExecutionResult { + Stopped(StepResult), + ProgramExited { code: i32 }, +} + fn normalize_path(path: PathBuf) -> PathBuf { path.canonicalize().unwrap_or(path) } impl<'tcx> PrirodaContext<'tcx> { pub(super) fn new(ecx: MiriInterpCx<'tcx>) -> Self { - Self { ecx, breakpoints: HashMap::new(), current_location: None, last_location: None } + Self { + ecx, + breakpoints: HashMap::new(), + current_location: None, + last_location: None, + exit_code: None, + } } pub(super) fn local_path(&self, location: &SourceLocation) -> Option { @@ -152,17 +166,27 @@ impl<'tcx> PrirodaContext<'tcx> { Some((self.local_path(location)?, location.line)) } + fn already_finished(&self) -> Option { + self.exit_code.map(|code| ExecutionResult::ProgramExited { code }) + } + /// Step to the next visible MIR instruction. - fn stepi(&mut self) -> InterpResult<'tcx, StepResult> { + fn stepi(&mut self) -> InterpResult<'tcx, ExecutionResult> { + if let Some(result) = self.already_finished() { + return interp_ok(result); + } self.resume(ResumeMode::MirInstruction) } /// Step until the displayed source file or line changes. - pub(super) fn step(&mut self) -> InterpResult<'tcx, StepResult> { + pub(super) fn step(&mut self) -> InterpResult<'tcx, ExecutionResult> { + if let Some(result) = self.already_finished() { + return interp_ok(result); + } self.resume(ResumeMode::SourceLine(self.current_source_position())) } /// Run until the initial editor-visible stop point. - pub(super) fn stop_at_first_user_location(&mut self) -> InterpResult<'tcx, StepResult> { + pub(super) fn stop_at_first_user_location(&mut self) -> InterpResult<'tcx, ExecutionResult> { self.resume(ResumeMode::FirstUserSourceLocation) } @@ -173,10 +197,17 @@ impl<'tcx> PrirodaContext<'tcx> { } /// Continue execution until reaching a breakpoint or propagating termination. - pub(super) fn continue_execution(&mut self) -> InterpResult<'tcx, StepResult> { + pub(super) fn continue_execution(&mut self) -> InterpResult<'tcx, ExecutionResult> { + if let Some(result) = self.already_finished() { + return interp_ok(result); + } self.resume(ResumeMode::Continue) } + pub(super) fn finish_session(&mut self) -> InterpResult<'tcx, ()> { + interp_ok(()) + } + pub(super) fn set_breakpoint(&mut self, path: PathBuf, line: usize) -> BreakpointSetResult { // FIXME: validate breakpoints here so every frontend gets the same behavior. // Reject empty paths, missing files, directories, and line 0. Decide whether @@ -190,15 +221,35 @@ impl<'tcx> PrirodaContext<'tcx> { } } + fn program_exit(err: &InterpErrorInfo<'tcx>) -> Option { + let InterpErrorKind::MachineStop(info) = err.kind() else { + return None; + }; + // FIXME: Preserve `TerminationInfo::Exit::leak_check` and run Miri's + // leak/thread-leak diagnostics once Priroda grows a proper post-exit + // finalization path. For now, program exit only records the debuggee exit code. + let Some(TerminationInfo::Exit { code, .. }) = info.downcast_ref::() + else { + return None; + }; + Some(*code) + } + /// Advance execution until the selected resume mode reaches a stopping point. - fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx, StepResult> { + fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx, ExecutionResult> { loop { - self.advance()?; + if let Err(err) = self.advance().report_err() { + if let Some(code) = Self::program_exit(&err) { + self.exit_code = Some(code); + return interp_ok(ExecutionResult::ProgramExited { code }); + } + return Err(err).into(); + } // An explicit breakpoint should stop execution even when the current // MIR instruction would normally be hidden during manual stepping. if self.is_at_breakpoint() { - return interp_ok(StepResult::Breakpoint); + return interp_ok(ExecutionResult::Stopped(StepResult::Breakpoint)); } match mode { @@ -208,14 +259,15 @@ impl<'tcx> PrirodaContext<'tcx> { InstructionVisibility::Visible ) => { - return interp_ok(StepResult::Step); + return interp_ok(ExecutionResult::Stopped(StepResult::Step)); } ResumeMode::SourceLine(ref prev_location) => { match (prev_location, &self.current_location) { // We started from an unmapped location; stop once there // is a source position the frontend can display. - (None, Some(_)) => return interp_ok(StepResult::Step), + (None, Some(_)) => + return interp_ok(ExecutionResult::Stopped(StepResult::Step)), (Some((prev_path, prev_line)), Some(current_location)) => { if let Some(current_path) = self.local_path(current_location) { @@ -223,7 +275,7 @@ impl<'tcx> PrirodaContext<'tcx> { // position changes to a different file or line. if *prev_path != current_path || *prev_line != current_location.line { - return interp_ok(StepResult::Step); + return interp_ok(ExecutionResult::Stopped(StepResult::Step)); } } } @@ -235,7 +287,7 @@ impl<'tcx> PrirodaContext<'tcx> { ResumeMode::FirstUserSourceLocation if self.current_location.is_some() && self.has_user_relevant_frame() => { - return interp_ok(StepResult::Step); + return interp_ok(ExecutionResult::Stopped(StepResult::Step)); } ResumeMode::MirInstruction @@ -326,10 +378,9 @@ impl<'tcx> PrirodaContext<'tcx> { command: DebuggerCommand, ) -> InterpResult<'tcx, CommandResult> { match command { - DebuggerCommand::StepI => self.stepi().map(CommandResult::ExecutionStopped), - DebuggerCommand::Step => self.step().map(CommandResult::ExecutionStopped), - DebuggerCommand::Continue => - self.continue_execution().map(CommandResult::ExecutionStopped), + DebuggerCommand::StepI => self.stepi().map(CommandResult::Execution), + DebuggerCommand::Step => self.step().map(CommandResult::Execution), + DebuggerCommand::Continue => self.continue_execution().map(CommandResult::Execution), DebuggerCommand::Breakpoint(path, line) => interp_ok(CommandResult::BreakpointResult(self.set_breakpoint(path, line))), DebuggerCommand::ListLocals => interp_ok(CommandResult::Locals(self.list_locals())), @@ -337,7 +388,8 @@ impl<'tcx> PrirodaContext<'tcx> { interp_ok(CommandResult::SingleLocal(self.get_local(local))), DebuggerCommand::Follow(alloc_id, offset) => self.follow_alloc(alloc_id, offset).map(CommandResult::Memory), - DebuggerCommand::TerminateSession => interp_ok(CommandResult::TerminateSession), + DebuggerCommand::TerminateSession => + self.finish_session().map(|()| CommandResult::TerminateSession), } } @@ -845,7 +897,7 @@ pub(super) enum BreakpointSetResult { } pub(super) enum CommandResult { - ExecutionStopped(StepResult), + Execution(ExecutionResult), BreakpointResult(BreakpointSetResult), Locals(Vec), SingleLocal(Option), diff --git a/src/tools/miri/priroda/src/frontend/cli.rs b/src/tools/miri/priroda/src/frontend/cli.rs index e4e92351a92f6..026f778f27abe 100644 --- a/src/tools/miri/priroda/src/frontend/cli.rs +++ b/src/tools/miri/priroda/src/frontend/cli.rs @@ -6,7 +6,8 @@ use miri::{InterpResult, interp_ok}; use rustc_middle::mir::interpret::AllocId; use crate::debugger::{ - BreakpointSetResult, CommandResult, DebuggerCommand, PrirodaContext, StepResult, + BreakpointSetResult, CommandResult, DebuggerCommand, ExecutionResult, PrirodaContext, + StepResult, }; pub(crate) struct Cli; @@ -46,12 +47,20 @@ impl Cli { session: &PrirodaContext<'tcx>, ) -> InterpResult<'tcx, bool> { match command_res { - CommandResult::ExecutionStopped(result) => { - if matches!(result, StepResult::Breakpoint) { - println!("Hit breakpoint"); - } - Self::print_location(session); - } + CommandResult::Execution(result) => + match result { + ExecutionResult::Stopped(step) => + match step { + StepResult::Step => Self::print_location(session), + StepResult::Breakpoint => { + println!("Hit breakpoint"); + Self::print_location(session); + } + }, + ExecutionResult::ProgramExited { code } => { + println!("program finished with exit code {code}"); + } + }, CommandResult::BreakpointResult(res) => match res { BreakpointSetResult::Added(path, line) => { diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index c2935213a6ea6..386b7436f0f2e 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -13,9 +13,9 @@ use emmy_dap_types::prelude::types::{ StoppedEventReason, Thread, Variable, }; use emmy_dap_types::prelude::{Command, Event, Request, ResponseBody, Server}; -use miri::{InterpErrorInfo, InterpErrorKind, InterpResult, TerminationInfo, bug, interp_ok}; +use miri::{InterpErrorInfo, InterpErrorKind, InterpResult, TerminationInfo, bug}; -use crate::debugger::{LocalDesc, PrirodaContext, StepResult}; +use crate::debugger::{ExecutionResult, LocalDesc, PrirodaContext, StepResult}; // Priroda still exposes one interpreted thread and one selected frame to DAP. // Keep the ids stable so editor follow-up requests can address the stopped state. @@ -77,7 +77,7 @@ impl Dap { eprintln!("priroda dap error: {err:?}"); } - interp_ok(()) + session.finish_session() } } @@ -650,9 +650,10 @@ impl DapSession { Ok(()) } - fn execution_outcome<'tcx>(result: InterpResult<'tcx, StepResult>) -> ExecutionOutcome { + fn execution_outcome<'tcx>(result: InterpResult<'tcx, ExecutionResult>) -> ExecutionOutcome { match result.report_err() { - Ok(step) => ExecutionOutcome::Stopped(step), + Ok(ExecutionResult::Stopped(step)) => ExecutionOutcome::Stopped(step), + Ok(ExecutionResult::ProgramExited { code }) => ExecutionOutcome::Terminated { code }, Err(err) => Self::interp_error_outcome(err), } } diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index f67aab3b3dfea..442e10c366ad7 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -146,14 +146,7 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { Ok(()) => {} Err(err) => if let Some((return_code, _leak_check)) = report_result(&session.ecx, err) { - // FIXME: translate Miri termination into a Priroda execution-state enum so - // the CLI loop can distinguish whole-program exit from individual thread - // completion, run Miri-equivalent leak checks, print the exit code, and - // return to the debugger prompt. - println!("program finished with exit code {return_code}"); - if return_code != 0 { - std::process::exit(return_code); - } + std::process::exit(return_code); }, } diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs index e264ef59841a8..5c60cabc8e0ab 100644 --- a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.rs @@ -1,4 +1,3 @@ -//@ priroda-relax-exit-status fn main() { std::process::exit(7); } diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout index 08ddab252f366..50a003968709f 100644 --- a/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_nonzero.stdout @@ -1 +1,2 @@ (priroda) program finished with exit code 7 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout index d6c4605d6baf3..e8040ca6a1896 100644 --- a/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout +++ b/src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout @@ -1 +1,3 @@ (priroda) program finished with exit code 0 +(priroda) program finished with exit code 0 +(priroda) quitting From 7c889620180c49eeaa2243d1b0548974475c599c Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 16 Aug 2026 10:01:47 +0000 Subject: [PATCH 03/46] add crashtests, remove unused aux files --- tests/crashes/153005.rs | 15 +++++++++++++++ tests/crashes/153362.rs | 6 ++++++ tests/crashes/153375.rs | 15 +++++++++++++++ tests/crashes/153947.rs | 10 ++++++++++ tests/crashes/154296.rs | 12 ++++++++++++ tests/crashes/154779.rs | 4 ++++ tests/crashes/154782.rs | 9 +++++++++ tests/crashes/154871.rs | 7 +++++++ tests/crashes/auxiliary/aux132985.rs | 6 ------ tests/crashes/auxiliary/aux153375.rs | 6 ++++++ .../crashes/auxiliary/overlapping_spans_helper.rs | 15 --------------- 11 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 tests/crashes/153005.rs create mode 100644 tests/crashes/153362.rs create mode 100644 tests/crashes/153375.rs create mode 100644 tests/crashes/153947.rs create mode 100644 tests/crashes/154296.rs create mode 100644 tests/crashes/154779.rs create mode 100644 tests/crashes/154782.rs create mode 100644 tests/crashes/154871.rs delete mode 100644 tests/crashes/auxiliary/aux132985.rs create mode 100644 tests/crashes/auxiliary/aux153375.rs delete mode 100644 tests/crashes/auxiliary/overlapping_spans_helper.rs diff --git a/tests/crashes/153005.rs b/tests/crashes/153005.rs new file mode 100644 index 0000000000000..44b4e029958cd --- /dev/null +++ b/tests/crashes/153005.rs @@ -0,0 +1,15 @@ +//@ known-bug: #153005 +#![feature(non_lifetime_binders)] +#![feature(derive_coerce_pointee)] + +#[derive(core::marker::CoercePointee)] +#[repr(transparent)] +struct _Ptr5<'a, #[pointee] T: ?Sized, X> +where + for V: Sized, +{ + data: &'a T, + x: core::marker::PhantomData, +} + +fn main() {} diff --git a/tests/crashes/153362.rs b/tests/crashes/153362.rs new file mode 100644 index 0000000000000..0a8e4ddae6283 --- /dev/null +++ b/tests/crashes/153362.rs @@ -0,0 +1,6 @@ +//@ known-bug: #153362 +struct ThinDst { + b: unsafe<> (), +} + +const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; diff --git a/tests/crashes/153375.rs b/tests/crashes/153375.rs new file mode 100644 index 0000000000000..46ed4be3b829b --- /dev/null +++ b/tests/crashes/153375.rs @@ -0,0 +1,15 @@ +//@ known-bug: #153375 +//@ aux-build: aux153375.rs +extern crate aux153375; +use aux153375::Request; + +struct Bar<'ws>(&'ws ()); + +impl<'ws> Request for Bar<'ws> { + type A<'a> + = u8 + where + Self: 'a; + + fn f(_: Self::A<'_>) -> impl Sized {} +} diff --git a/tests/crashes/153947.rs b/tests/crashes/153947.rs new file mode 100644 index 0000000000000..39bc8c074cfc0 --- /dev/null +++ b/tests/crashes/153947.rs @@ -0,0 +1,10 @@ +//@ known-bug: #153947 +#![expect(drop_bounds)] +pub struct Thing(T) where [T]: Sized, Self: Drop; +impl Drop for Thing where [T]: Sized, Self: Drop { + fn drop(&mut self) {} +} +impl Drop for Thing where [T]: Sized, Self: Drop { + fn drop(&mut self) {} +} +fn main() {} diff --git a/tests/crashes/154296.rs b/tests/crashes/154296.rs new file mode 100644 index 0000000000000..d904a4d82e426 --- /dev/null +++ b/tests/crashes/154296.rs @@ -0,0 +1,12 @@ +//@ known-bug: #154296 +//@ edition: 2024 +mod m1 { + mod inner { + pub struct S; + } + pub use inner::*; + #[derive(Debug)] + pub struct S; +} +use m1::*; +use S; diff --git a/tests/crashes/154779.rs b/tests/crashes/154779.rs new file mode 100644 index 0000000000000..e6c03e88b0b90 --- /dev/null +++ b/tests/crashes/154779.rs @@ -0,0 +1,4 @@ +//@ known-bug: #154779 +struct Data([[&'static str]; 1]); +const _: &'static Data = &*(&[] as *const Data) ; +fn main() {} diff --git a/tests/crashes/154782.rs b/tests/crashes/154782.rs new file mode 100644 index 0000000000000..d5fdfb84b48ce --- /dev/null +++ b/tests/crashes/154782.rs @@ -0,0 +1,9 @@ +//@ known-bug: #154782 +//@ edition: 2024 +#![feature(pin_ergonomics)] +use core::pin::Pin; +fn test_idempotency(x: Pin<&mut T>) { + || { + x.poll(loop {}); + }; +} diff --git a/tests/crashes/154871.rs b/tests/crashes/154871.rs new file mode 100644 index 0000000000000..c106028623d75 --- /dev/null +++ b/tests/crashes/154871.rs @@ -0,0 +1,7 @@ +//@ known-bug: #154871 +struct Struct { + b: unsafe<> (), +} +fn main() { + std::ptr::null::; +} diff --git a/tests/crashes/auxiliary/aux132985.rs b/tests/crashes/auxiliary/aux132985.rs deleted file mode 100644 index 7ae5567bdc59d..0000000000000 --- a/tests/crashes/auxiliary/aux132985.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(adt_const_params)] - -use std::marker::ConstParamTy; - -#[derive(Eq, PartialEq, ConstParamTy)] -pub struct Foo; diff --git a/tests/crashes/auxiliary/aux153375.rs b/tests/crashes/auxiliary/aux153375.rs new file mode 100644 index 0000000000000..c5f09489181b6 --- /dev/null +++ b/tests/crashes/auxiliary/aux153375.rs @@ -0,0 +1,6 @@ +pub trait Request { + type A<'a> + where + Self: 'a; + fn f(_: Self::A<'_>) -> impl Sized; +} diff --git a/tests/crashes/auxiliary/overlapping_spans_helper.rs b/tests/crashes/auxiliary/overlapping_spans_helper.rs deleted file mode 100644 index e449fcd36c376..0000000000000 --- a/tests/crashes/auxiliary/overlapping_spans_helper.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Auxiliary lib for the issue 147973 regression test with ICEs due to overlapping spans. - -#[macro_export] -macro_rules! identity { - ($x:ident) => { - $x - }; -} - -#[macro_export] -macro_rules! do_loop { - ($x:ident) => { - for $crate::identity!($x) in $x {} - }; -} From 978de22fac43e7c12b9891768af5959422a88aa6 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 16 Aug 2026 06:35:29 +0300 Subject: [PATCH 04/46] [Priroda] Stop at interpreter exceptions Report non-exit interpreter errors as debugger stops instead of terminating the Priroda session. Execution commands may still be issued after an exception stop. They ask Miri to advance again and report the resulting stop, which can be another exception at the same location. --- src/tools/miri/priroda/src/debugger.rs | 13 +++++- src/tools/miri/priroda/src/frontend/cli.rs | 7 +++ src/tools/miri/priroda/src/frontend/dap.rs | 45 ++++++++++++------- src/tools/miri/priroda/tests/cli.rs | 19 +------- .../miri/priroda/tests/ui/dap_ub_exception.rs | 1 - .../priroda/tests/ui/dap_ub_exception.stdout | 4 +- .../tests/ui/dap_ub_exception_continue.rs | 1 - .../tests/ui/dap_ub_exception_continue.stdout | 8 +++- .../priroda/tests/ui/ub_exception_continue.rs | 1 - .../tests/ui/ub_exception_continue.stderr | 13 ------ .../tests/ui/ub_exception_continue.stdout | 6 ++- .../priroda/tests/ui/ub_exception_step.rs | 1 - .../priroda/tests/ui/ub_exception_step.stderr | 13 ------ .../priroda/tests/ui/ub_exception_step.stdout | 6 ++- .../priroda/tests/ui/ub_exception_stop.rs | 1 - .../priroda/tests/ui/ub_exception_stop.stderr | 13 ------ .../priroda/tests/ui/ub_exception_stop.stdout | 6 ++- 17 files changed, 71 insertions(+), 87 deletions(-) delete mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr delete mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_step.stderr delete mode 100644 src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 2088c16d32cc4..70aad2b79b46c 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -126,6 +126,7 @@ enum InstructionVisibility { pub(super) enum StepResult { Step, Breakpoint, + Exception { message: String }, } pub(super) enum ExecutionResult { @@ -177,6 +178,7 @@ impl<'tcx> PrirodaContext<'tcx> { } self.resume(ResumeMode::MirInstruction) } + /// Step until the displayed source file or line changes. pub(super) fn step(&mut self) -> InterpResult<'tcx, ExecutionResult> { if let Some(result) = self.already_finished() { @@ -235,15 +237,24 @@ impl<'tcx> PrirodaContext<'tcx> { Some(*code) } + fn stop_at_exception(&mut self, err: InterpErrorInfo<'tcx>) -> StepResult { + let message = err.kind().to_string(); + self.last_location = self.current_location.take(); + self.current_location = self.resolve_current_location(); + StepResult::Exception { message } + } + /// Advance execution until the selected resume mode reaches a stopping point. fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx, ExecutionResult> { loop { + // Program exits are not debugger exceptions. Preserve all other + // interpreter errors as stopped debugger events. if let Err(err) = self.advance().report_err() { if let Some(code) = Self::program_exit(&err) { self.exit_code = Some(code); return interp_ok(ExecutionResult::ProgramExited { code }); } - return Err(err).into(); + return interp_ok(ExecutionResult::Stopped(self.stop_at_exception(err))); } // An explicit breakpoint should stop execution even when the current diff --git a/src/tools/miri/priroda/src/frontend/cli.rs b/src/tools/miri/priroda/src/frontend/cli.rs index 026f778f27abe..3fdf022cf4864 100644 --- a/src/tools/miri/priroda/src/frontend/cli.rs +++ b/src/tools/miri/priroda/src/frontend/cli.rs @@ -56,6 +56,8 @@ impl Cli { println!("Hit breakpoint"); Self::print_location(session); } + StepResult::Exception { ref message } => + Self::print_exception_stop(message, session), }, ExecutionResult::ProgramExited { code } => { println!("program finished with exit code {code}"); @@ -116,6 +118,11 @@ impl Cli { interp_ok(true) } + fn print_exception_stop<'tcx>(message: &str, session: &PrirodaContext<'tcx>) { + println!("program stopped with error: {message}"); + Self::print_location(session); + } + fn parse_command(&self, input: &str) -> Option { // TODO: look at the Spanned crate for how to easily produce errors in // rustc's style while manually parsing text input. diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 386b7436f0f2e..737141a63b92f 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -337,15 +337,20 @@ impl DapSession { self.require_state(DapState::Launched)?; match Self::execution_outcome(session.stop_at_first_user_location()) { - ExecutionOutcome::Stopped(_) => + ExecutionOutcome::Stopped(result) => { + // A normal startup stop is an entry event, but an interpreter + // error before the first user location is an exception stop. + let stopped = match result { + StepResult::Step => Self::stopped_event_body(StoppedEventReason::Entry), + result => Self::stopped_event_for(result), + }; Ok(HandlerSuccess { response: HandlerResponse::Success(ResponseBody::ConfigurationDone), state: Some(DapState::Stopped), - events: vec![Event::Stopped(Self::stopped_event_body( - StoppedEventReason::Entry, - ))], + events: vec![Event::Stopped(stopped)], outcome: HandlerOutcome::Continue, - }), + }) + } ExecutionOutcome::Terminated { code } => Ok(HandlerSuccess { response: HandlerResponse::Success(ResponseBody::ConfigurationDone), @@ -471,9 +476,7 @@ impl DapSession { Ok(HandlerSuccess { response: HandlerResponse::Success(body), state: Some(DapState::Stopped), - events: vec![Event::Stopped(Self::stopped_event_body(Self::stopped_reason( - result, - )))], + events: vec![Event::Stopped(Self::stopped_event_for(result))], outcome: HandlerOutcome::Continue, }), ExecutionOutcome::Terminated { code } => @@ -511,9 +514,7 @@ impl DapSession { Ok(HandlerSuccess { response: HandlerResponse::Success(body), state: Some(DapState::Stopped), - events: vec![Event::Stopped(Self::stopped_event_body(Self::stopped_reason( - result, - )))], + events: vec![Event::Stopped(Self::stopped_event_for(result))], outcome: HandlerOutcome::Continue, }), ExecutionOutcome::Terminated { code } => @@ -669,22 +670,32 @@ impl DapSession { ExecutionOutcome::Failed(kind.to_string()) } - fn stopped_event_body(reason: StoppedEventReason) -> StoppedEventBody { + fn stopped_event_for(result: StepResult) -> StoppedEventBody { + let (reason, text) = match result { + StepResult::Step => (StoppedEventReason::Step, None), + StepResult::Breakpoint => (StoppedEventReason::Breakpoint, None), + StepResult::Exception { message } => (StoppedEventReason::Exception, Some(message)), + }; StoppedEventBody { reason, description: None, thread_id: Some(THREAD_ID), preserve_focus_hint: None, - text: None, + text, all_threads_stopped: Some(true), hit_breakpoint_ids: None, } } - fn stopped_reason(result: StepResult) -> StoppedEventReason { - match result { - StepResult::Step => StoppedEventReason::Step, - StepResult::Breakpoint => StoppedEventReason::Breakpoint, + fn stopped_event_body(reason: StoppedEventReason) -> StoppedEventBody { + StoppedEventBody { + reason, + description: None, + thread_id: Some(THREAD_ID), + preserve_focus_hint: None, + text: None, + all_threads_stopped: Some(true), + hit_breakpoint_ids: None, } } diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index 82f9e33065966..2bf7f22bd1d98 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -7,19 +7,6 @@ use ui_test::spanned::Spanned; use ui_test::status_emitter::StatusEmitter; use ui_test::{CommandBuilder, Config, default_file_filter, run_tests_generic}; -fn per_file_config(config: &mut Config, file_contents: &Spanned>) { - // `//@ priroda-relax-exit-status` lets a fixture accept any exit code, so - // fixtures that terminate with rustc's error-count-driven nonzero exit can - // live in `tests/ui/` alongside the pass-only suite. - if file_contents - .content - .windows(b"//@ priroda-relax-exit-status".len()) - .any(|w| w == b"//@ priroda-relax-exit-status") - { - config.comment_defaults.base().exit_status = None.into(); - } -} - fn main() -> Result<(), Box> { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let miri_dir = manifest_dir.parent().unwrap(); @@ -66,10 +53,6 @@ fn main() -> Result<(), Box> { config.comment_defaults.base().exit_status = Spanned::dummy(0).into(); config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); - config.custom_comments.insert("priroda-relax-exit-status", |parser, _args, span| { - parser.set_custom_once("priroda-relax-exit-status", (), span); - }); - let mut args = ui_test::Args::test()?; args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); config.with_args(&args); @@ -77,7 +60,7 @@ fn main() -> Result<(), Box> { run_tests_generic( vec![config], default_file_filter, - per_file_config, + |_, _| {}, Box::::from(args.format), )?; diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs b/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs index e4644a755de8c..ec3b9860925eb 100644 --- a/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception.rs @@ -1,5 +1,4 @@ //@ compile-flags: --dap -//@ priroda-relax-exit-status #![allow(deref_nullptr)] fn main() { unsafe { diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout index 5a4f9c8ac3e2b..9db6bc199cf01 100644 --- a/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception.stdout @@ -10,6 +10,6 @@ Content-Length: {CONTENT_LENGTH} {"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":6,"type":"response","request_seq":4,"success":false,"message":"memory access failed: attempting to access 1 byte, but got null pointer","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"continue","body":{"allThreadsContinued":true},"error":null}Content-Length: {CONTENT_LENGTH} -{"seq":7,"type":"event","event":"terminated","body":null} \ No newline at end of file +{"seq":7,"type":"event","event":"stopped","body":{"reason":"exception","description":null,"threadId":1,"preserveFocusHint":null,"text":"memory access failed: attempting to access 1 byte, but got null pointer","allThreadsStopped":true,"hitBreakpointIds":null}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs index e4644a755de8c..ec3b9860925eb 100644 --- a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.rs @@ -1,5 +1,4 @@ //@ compile-flags: --dap -//@ priroda-relax-exit-status #![allow(deref_nullptr)] fn main() { unsafe { diff --git a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout index 5a4f9c8ac3e2b..7dbd6f198f87e 100644 --- a/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_ub_exception_continue.stdout @@ -10,6 +10,10 @@ Content-Length: {CONTENT_LENGTH} {"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":6,"type":"response","request_seq":4,"success":false,"message":"memory access failed: attempting to access 1 byte, but got null pointer","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"continue","body":{"allThreadsContinued":true},"error":null}Content-Length: {CONTENT_LENGTH} -{"seq":7,"type":"event","event":"terminated","body":null} \ No newline at end of file +{"seq":7,"type":"event","event":"stopped","body":{"reason":"exception","description":null,"threadId":1,"preserveFocusHint":null,"text":"memory access failed: attempting to access 1 byte, but got null pointer","allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"response","request_seq":5,"success":true,"command":"continue","body":{"allThreadsContinued":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":9,"type":"event","event":"stopped","body":{"reason":"exception","description":null,"threadId":1,"preserveFocusHint":null,"text":"memory access failed: attempting to access 1 byte, but got null pointer","allThreadsStopped":true,"hitBreakpointIds":null}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs b/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs index bfcb5b7312ad8..148fb9110f8ea 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.rs @@ -1,4 +1,3 @@ -//@ priroda-relax-exit-status #![allow(deref_nullptr)] fn main() { unsafe { diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr deleted file mode 100644 index 84dcd026ab086..0000000000000 --- a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer - --> tests/ui/ub_exception_continue.rs:5:9 - | -5 | *std::ptr::null_mut::() = 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 - diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout index d845c0acdfa1b..cd5908a1540a4 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout +++ b/src/tools/miri/priroda/tests/ui/ub_exception_continue.stdout @@ -1 +1,5 @@ -(priroda) \ No newline at end of file +(priroda) program stopped with error: memory access failed: attempting to access 1 byte, but got null pointer +{MANIFEST_DIR}/tests/ui/ub_exception_continue.rs:4 +(priroda) program stopped with error: memory access failed: attempting to access 1 byte, but got null pointer +{MANIFEST_DIR}/tests/ui/ub_exception_continue.rs:4 +(priroda) stdin closed, stopping diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.rs b/src/tools/miri/priroda/tests/ui/ub_exception_step.rs index bfcb5b7312ad8..148fb9110f8ea 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_step.rs +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.rs @@ -1,4 +1,3 @@ -//@ priroda-relax-exit-status #![allow(deref_nullptr)] fn main() { unsafe { diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr deleted file mode 100644 index de296040190dc..0000000000000 --- a/src/tools/miri/priroda/tests/ui/ub_exception_step.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer - --> tests/ui/ub_exception_step.rs:5:9 - | -5 | *std::ptr::null_mut::() = 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 - diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout index d845c0acdfa1b..b3c54a238847e 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout +++ b/src/tools/miri/priroda/tests/ui/ub_exception_step.stdout @@ -1 +1,5 @@ -(priroda) \ No newline at end of file +(priroda) program stopped with error: memory access failed: attempting to access 1 byte, but got null pointer +{MANIFEST_DIR}/tests/ui/ub_exception_step.rs:4 +(priroda) program stopped with error: memory access failed: attempting to access 1 byte, but got null pointer +{MANIFEST_DIR}/tests/ui/ub_exception_step.rs:4 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs b/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs index bfcb5b7312ad8..148fb9110f8ea 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.rs @@ -1,4 +1,3 @@ -//@ priroda-relax-exit-status #![allow(deref_nullptr)] fn main() { unsafe { diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr deleted file mode 100644 index a0cca5cd6cd70..0000000000000 --- a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got null pointer - --> tests/ui/ub_exception_stop.rs:5:9 - | -5 | *std::ptr::null_mut::() = 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 - diff --git a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout index d845c0acdfa1b..c63037e2e1b55 100644 --- a/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout +++ b/src/tools/miri/priroda/tests/ui/ub_exception_stop.stdout @@ -1 +1,5 @@ -(priroda) \ No newline at end of file +(priroda) program stopped with error: memory access failed: attempting to access 1 byte, but got null pointer +{MANIFEST_DIR}/tests/ui/ub_exception_stop.rs:4 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: , Id: _1, Ty: *mut u8, Value: {0x0 as *mut u8} +(priroda) quitting From 1df3fe0332ad7fab0a59a638ab47b9ce3595b6f8 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 17 Aug 2026 11:22:21 -0400 Subject: [PATCH 05/46] chore: update to cc@1.4.3 With this, we get C dep remap for free when building in rustc bootstrap: See * https://github.com/rust-lang/rust/pull/161049 * https://github.com/rust-lang/cargo/issues/17309 * https://github.com/rust-lang/cc-rs/pull/1794 --- src/tools/miri/Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index ccc524c577dd4..d418b15f2a952 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.62" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -512,9 +512,9 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "foldhash" @@ -1507,9 +1507,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "siphasher" From c53d381d634b5683c9e569265bda9810eb030b7d Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 16 Aug 2026 06:18:56 +0300 Subject: [PATCH 06/46] [Priroda] Add CLI next baseline fixture Record the current CLI behavior before adding the `next` command. --- src/tools/miri/priroda/tests/ui/cli_next_command.rs | 1 + src/tools/miri/priroda/tests/ui/cli_next_command.stdin | 2 ++ src/tools/miri/priroda/tests/ui/cli_next_command.stdout | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_command.rs create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_command.stdin create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_command.stdout diff --git a/src/tools/miri/priroda/tests/ui/cli_next_command.rs b/src/tools/miri/priroda/tests/ui/cli_next_command.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_command.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/cli_next_command.stdin b/src/tools/miri/priroda/tests/ui/cli_next_command.stdin new file mode 100644 index 0000000000000..4e4a3137cbd67 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_command.stdin @@ -0,0 +1,2 @@ +next +quit diff --git a/src/tools/miri/priroda/tests/ui/cli_next_command.stdout b/src/tools/miri/priroda/tests/ui/cli_next_command.stdout new file mode 100644 index 0000000000000..e171009cfbb1a --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_command.stdout @@ -0,0 +1,2 @@ +(priroda) no command +(priroda) quitting From 2dfa7df02fb77e5aa4ceed2d60d24080d09acfc8 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Mon, 17 Aug 2026 22:45:46 +0300 Subject: [PATCH 07/46] [Priroda] Add CLI source next command Add stack-depth based source step-over support and expose it through the CLI `next` command. The CLI fixture now shows `next` being accepted instead of rejected. DAP keeps the baseline behavior until a later commit wires the same helper into the DAP frontend. --- src/tools/miri/priroda/README.md | 3 +- src/tools/miri/priroda/src/debugger.rs | 67 +++++++++++++++++++ src/tools/miri/priroda/src/frontend/cli.rs | 1 + .../priroda/tests/ui/cli_next_command.stdout | 2 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index daab65fc3ad00..bb6ffaca1a0e7 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -152,7 +152,8 @@ RUSTC_BLESS=1 cargo test | Command | Description | |---|---| | Enter, `si`, `stepi` | Execute one Miri interpreter step. | -| `s`, `step` | Step until the displayed source location changes. | +| `s`, `step` | Step to the next displayed source location, entering calls. | +| `n`, `next` | Step over the current displayed source location. | | `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | | `b :`, `break :` | Add a source-location breakpoint. | | `l`, `locals` | List source-level locals in the current frame by name. | diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 88d871a75c95f..8bc93802f9d02 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -106,6 +106,13 @@ enum ResumeMode { /// `None` means the current interpreter position has no source location, so /// the first mapped source location is good enough to report. SourceLine(Option<(PathBuf, usize)>), + /// Step over the source position `start_position`, entered from a stack of + /// depth `start_stack_depth`. + /// + /// Execution keeps going while it is deeper than `start_stack_depth` (i.e. + /// inside a call made from the stepped-over line), and stops once it is back + /// at that depth or shallower and the displayed source position has changed. + StepOver { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, /// Stop at the first mapped source location from a user-relevant frame. /// /// This is the DAP entry-stop primitive: it skips over interpreter startup @@ -180,13 +187,43 @@ impl<'tcx> PrirodaContext<'tcx> { } /// Step until the displayed source file or line changes. + /// + /// This is the CLI source-level step; it shares its stepping semantics with + /// [`Self::step_in_source`]. pub(super) fn step(&mut self) -> InterpResult<'tcx, ExecutionResult> { + self.step_in_source() + } + + /// Step into the next source location, entering any call that is made. + /// + /// This keeps source-line stepping as the step-in behavior while `next` uses + /// [`Self::step_over_source`]. + pub(super) fn step_in_source(&mut self) -> InterpResult<'tcx, ExecutionResult> { if let Some(result) = self.already_finished() { return interp_ok(result); } self.resume(ResumeMode::SourceLine(self.current_source_position())) } + /// Step over the current source position, not stopping inside any call it makes. + /// + /// Records the current source position and stack depth before advancing, + /// then keeps stepping until execution is back at that depth (or shallower) + /// and the displayed source position has changed. + pub(super) fn step_over_source(&mut self) -> InterpResult<'tcx, ExecutionResult> { + if let Some(result) = self.already_finished() { + return interp_ok(result); + } + let start_position = self.current_source_position(); + let start_stack_depth = self.active_thread_stack_depth(); + self.resume(ResumeMode::StepOver { start_position, start_stack_depth }) + } + + /// Number of frames on the active thread's stack. + fn active_thread_stack_depth(&self) -> usize { + self.ecx.active_thread_stack().len() + } + /// Run until the initial editor-visible stop point. pub(super) fn stop_at_first_user_location(&mut self) -> InterpResult<'tcx, ExecutionResult> { self.resume(ResumeMode::FirstUserSourceLocation) @@ -295,6 +332,34 @@ impl<'tcx> PrirodaContext<'tcx> { } } + ResumeMode::StepOver { ref start_position, start_stack_depth } => { + // While deeper than where we started, we are inside a call + // made from the stepped-over line; keep going. + if self.active_thread_stack_depth() > start_stack_depth { + continue; + } + + // Back at (or shallower than) the starting depth: stop once + // the displayed source position has changed. + match (start_position, &self.current_location) { + // We started from an unmapped location; stop once there + // is a source position the frontend can display. + (None, Some(_)) => + return interp_ok(ExecutionResult::Stopped(StepResult::Step)), + (Some((start_path, start_line)), Some(current_location)) => { + // A source step stops when the displayed source + // position changes to a different file or line. + if let Some(current_path) = self.local_path(current_location) + && (*start_path != current_path + || *start_line != current_location.line) + { + return interp_ok(ExecutionResult::Stopped(StepResult::Step)); + } + } + _ => {} + } + } + ResumeMode::FirstUserSourceLocation if self.current_location.is_some() && self.has_user_relevant_frame() => { @@ -391,6 +456,7 @@ impl<'tcx> PrirodaContext<'tcx> { match command { DebuggerCommand::StepI => self.stepi().map(CommandResult::Execution), DebuggerCommand::Step => self.step().map(CommandResult::Execution), + DebuggerCommand::Next => self.step_over_source().map(CommandResult::Execution), DebuggerCommand::Continue => self.continue_execution().map(CommandResult::Execution), DebuggerCommand::Breakpoint(path, line) => interp_ok(CommandResult::BreakpointResult(self.set_breakpoint(path, line))), @@ -888,6 +954,7 @@ impl<'tcx> PrirodaContext<'tcx> { pub(super) enum DebuggerCommand { StepI, Step, + Next, TerminateSession, Continue, Breakpoint(PathBuf, usize), diff --git a/src/tools/miri/priroda/src/frontend/cli.rs b/src/tools/miri/priroda/src/frontend/cli.rs index 3fdf022cf4864..afdd763fc22c9 100644 --- a/src/tools/miri/priroda/src/frontend/cli.rs +++ b/src/tools/miri/priroda/src/frontend/cli.rs @@ -137,6 +137,7 @@ impl Cli { // FIXME: empty line should repats last command user typed not exeute specific command. "" | "si" | "stepi" => Some(DebuggerCommand::StepI), "s" | "step" => Some(DebuggerCommand::Step), + "n" | "next" => Some(DebuggerCommand::Next), "q" | "quit" => Some(DebuggerCommand::TerminateSession), "c" | "continue" => Some(DebuggerCommand::Continue), "b" | "break" => self.parse_breakpoint(args), diff --git a/src/tools/miri/priroda/tests/ui/cli_next_command.stdout b/src/tools/miri/priroda/tests/ui/cli_next_command.stdout index e171009cfbb1a..304623d197977 100644 --- a/src/tools/miri/priroda/tests/ui/cli_next_command.stdout +++ b/src/tools/miri/priroda/tests/ui/cli_next_command.stdout @@ -1,2 +1,2 @@ -(priroda) no command +(priroda) program finished with exit code 0 (priroda) quitting From 1f2b6698502eab0685cace5453a32d1567ba5988 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 00:44:58 +0300 Subject: [PATCH 08/46] [Priroda] Add CLI stepping baseline fixtures Add CLI fixtures for same-line `next` behavior and for the unsupported `out` command before source step-out is implemented. The same-line fixture keeps its physical line layout with `rustfmt::skip` because that layout is the behavior under test. --- .../miri/priroda/tests/ui/cli_next_same_line_call.rs | 10 ++++++++++ .../priroda/tests/ui/cli_next_same_line_call.stdin | 4 ++++ .../priroda/tests/ui/cli_next_same_line_call.stdout | 5 +++++ .../miri/priroda/tests/ui/cli_step_out_command.rs | 1 + .../miri/priroda/tests/ui/cli_step_out_command.stdin | 3 +++ .../miri/priroda/tests/ui/cli_step_out_command.stdout | 3 +++ 6 files changed, 26 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_same_line_call.rs create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdin create mode 100644 src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdout create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_out_command.rs create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_out_command.stdin create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout diff --git a/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.rs b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.rs new file mode 100644 index 0000000000000..d7f26894e3475 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.rs @@ -0,0 +1,10 @@ +// Verifies `next` at a same-line callee/caller location. +// Keep the breakpoint line number in the .stdin file in sync with this file. +#[rustfmt::skip] +fn same_line() { fn callee() {} callee(); let after = 1; let _ = after; } + +fn main() { + same_line(); + let after_same_line = 2; + let _ = after_same_line; +} diff --git a/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdin b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdin new file mode 100644 index 0000000000000..ce815c2355b5f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdin @@ -0,0 +1,4 @@ +break tests/ui/cli_next_same_line_call.rs:4 +continue +next +quit diff --git a/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdout b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdout new file mode 100644 index 0000000000000..f4e52d0b39085 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_next_same_line_call.stdout @@ -0,0 +1,5 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/cli_next_same_line_call.rs:4 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/cli_next_same_line_call.rs:4 +(priroda) {MANIFEST_DIR}/tests/ui/cli_next_same_line_call.rs:7 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/cli_step_out_command.rs b/src/tools/miri/priroda/tests/ui/cli_step_out_command.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_out_command.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdin b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdin new file mode 100644 index 0000000000000..f3eade0065ea7 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdin @@ -0,0 +1,3 @@ +si +out +quit diff --git a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout new file mode 100644 index 0000000000000..6e688344dd157 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout @@ -0,0 +1,3 @@ +(priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 +(priroda) no command +(priroda) quitting From a48ff50914660c6cff0811a403541d8329efc923 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Mon, 17 Aug 2026 22:46:13 +0300 Subject: [PATCH 09/46] [Priroda] Add CLI source step-out command Add source step-out support and expose it through the CLI `out` command. The command runs until execution reaches a source location in a shallower stack frame. --- src/tools/miri/priroda/README.md | 1 + src/tools/miri/priroda/src/debugger.rs | 25 +++++++++++++++++++ src/tools/miri/priroda/src/frontend/cli.rs | 1 + .../tests/ui/cli_step_out_command.stdout | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index bb6ffaca1a0e7..d2bc5be9734a4 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -154,6 +154,7 @@ RUSTC_BLESS=1 cargo test | Enter, `si`, `stepi` | Execute one Miri interpreter step. | | `s`, `step` | Step to the next displayed source location, entering calls. | | `n`, `next` | Step over the current displayed source location. | +| `out`, `stepout` | Run until execution returns to a shallower stack frame. | | `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | | `b :`, `break :` | Add a source-location breakpoint. | | `l`, `locals` | List source-level locals in the current frame by name. | diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 8bc93802f9d02..ddc4a1ead6921 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -113,6 +113,9 @@ enum ResumeMode { /// inside a call made from the stepped-over line), and stops once it is back /// at that depth or shallower and the displayed source position has changed. StepOver { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, + /// Step out of the current frame, stopping once execution returns to a + /// shallower stack depth. + StepOut { start_stack_depth: usize }, /// Stop at the first mapped source location from a user-relevant frame. /// /// This is the DAP entry-stop primitive: it skips over interpreter startup @@ -224,6 +227,18 @@ impl<'tcx> PrirodaContext<'tcx> { self.ecx.active_thread_stack().len() } + /// Step out of the current stack frame. + /// + /// Records the current stack depth and runs until execution reaches a source + /// location in a shallower frame. + pub(super) fn step_out_source(&mut self) -> InterpResult<'tcx, ExecutionResult> { + if let Some(result) = self.already_finished() { + return interp_ok(result); + } + let start_stack_depth = self.active_thread_stack_depth(); + self.resume(ResumeMode::StepOut { start_stack_depth }) + } + /// Run until the initial editor-visible stop point. pub(super) fn stop_at_first_user_location(&mut self) -> InterpResult<'tcx, ExecutionResult> { self.resume(ResumeMode::FirstUserSourceLocation) @@ -360,6 +375,13 @@ impl<'tcx> PrirodaContext<'tcx> { } } + ResumeMode::StepOut { start_stack_depth } + if self.active_thread_stack_depth() < start_stack_depth + && self.current_location.is_some() => + { + return interp_ok(ExecutionResult::Stopped(StepResult::Step)); + } + ResumeMode::FirstUserSourceLocation if self.current_location.is_some() && self.has_user_relevant_frame() => { @@ -368,6 +390,7 @@ impl<'tcx> PrirodaContext<'tcx> { ResumeMode::MirInstruction | ResumeMode::FirstUserSourceLocation + | ResumeMode::StepOut { .. } | ResumeMode::Continue => {} } } @@ -457,6 +480,7 @@ impl<'tcx> PrirodaContext<'tcx> { DebuggerCommand::StepI => self.stepi().map(CommandResult::Execution), DebuggerCommand::Step => self.step().map(CommandResult::Execution), DebuggerCommand::Next => self.step_over_source().map(CommandResult::Execution), + DebuggerCommand::StepOut => self.step_out_source().map(CommandResult::Execution), DebuggerCommand::Continue => self.continue_execution().map(CommandResult::Execution), DebuggerCommand::Breakpoint(path, line) => interp_ok(CommandResult::BreakpointResult(self.set_breakpoint(path, line))), @@ -955,6 +979,7 @@ pub(super) enum DebuggerCommand { StepI, Step, Next, + StepOut, TerminateSession, Continue, Breakpoint(PathBuf, usize), diff --git a/src/tools/miri/priroda/src/frontend/cli.rs b/src/tools/miri/priroda/src/frontend/cli.rs index afdd763fc22c9..a2ac5dc8f71bd 100644 --- a/src/tools/miri/priroda/src/frontend/cli.rs +++ b/src/tools/miri/priroda/src/frontend/cli.rs @@ -138,6 +138,7 @@ impl Cli { "" | "si" | "stepi" => Some(DebuggerCommand::StepI), "s" | "step" => Some(DebuggerCommand::Step), "n" | "next" => Some(DebuggerCommand::Next), + "out" | "stepout" => Some(DebuggerCommand::StepOut), "q" | "quit" => Some(DebuggerCommand::TerminateSession), "c" | "continue" => Some(DebuggerCommand::Continue), "b" | "break" => self.parse_breakpoint(args), diff --git a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout index 6e688344dd157..4ecad1fcef7eb 100644 --- a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout +++ b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout @@ -1,3 +1,3 @@ (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 -(priroda) no command +(priroda) program finished with exit code 0 (priroda) quitting From 1fc2f128fc5d6e65a8de9fc38d4e7f7e8c6c6387 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 16 Aug 2026 06:18:56 +0300 Subject: [PATCH 10/46] [Priroda] Add DAP next baseline fixture Record the current DAP behavior before wiring `next` to source step-over. --- .../miri/priroda/tests/ui/dap_next_at_call.rs | 12 ++++++++++++ .../priroda/tests/ui/dap_next_at_call.stdin | 11 +++++++++++ .../priroda/tests/ui/dap_next_at_call.stdout | 17 +++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap_next_at_call.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_next_at_call.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap_next_at_call.rs b/src/tools/miri/priroda/tests/ui/dap_next_at_call.rs new file mode 100644 index 0000000000000..d071191b60e3f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_next_at_call.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --dap + +fn callee() { + let inner = 1; + let _ = inner; +} + +fn main() { + callee(); + let after = 2; + let _ = after; +} diff --git a/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdin b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdin new file mode 100644 index 0000000000000..d134de9af931e --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdin @@ -0,0 +1,11 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 70 + +{"seq":4,"type":"request","command":"next","arguments":{"threadId":1}}Content-Length: 77 + +{"seq":5,"type":"request","command":"stackTrace","arguments":{"threadId":1}} diff --git a/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout new file mode 100644 index 0000000000000..b65b5512ae31e --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout @@ -0,0 +1,17 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"response","request_seq":5,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"callee","source":{"name":"dap_next_at_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_next_at_call.rs","sourceReference":0},"line":4,"column":9}],"totalFrames":1},"error":null} \ No newline at end of file From 8f41ebf12e3e01cdce054f74f482638e5e0de81b Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Mon, 17 Aug 2026 22:46:41 +0300 Subject: [PATCH 11/46] [Priroda] Wire DAP next to source step-over Route DAP `next` through the source step-over helper and keep `stepIn` on the source step-in path. The fixture now shows DAP `next` stopping after the call instead of inside the callee. --- src/tools/miri/priroda/README.md | 7 ++++--- src/tools/miri/priroda/src/frontend/dap.rs | 20 +++++++++++++++---- .../priroda/tests/ui/dap_next_at_call.stdout | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index d2bc5be9734a4..fcf4a07f5bd06 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -47,9 +47,10 @@ user-relevant source location after `configurationDone`, reports one current stack frame, exposes one flat Locals scope, and maps `list_locals()` into DAP variables with no child expansion. -The `next` and `stepIn` requests are wired to Priroda's existing source-line -step so VS Code can drive one visible step. They are not true DAP step-over or -step-in semantics yet. +DAP supports `stepIn` and `next`. `stepIn` stops at the next displayed source +location and can enter calls, while `next` steps over calls by tracking the +starting stack depth. This is still single-threaded and source-position based, +not the full future thread/frame model. DAP `stepOut` remains unsupported. ### VS Code diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index ca425b05648e4..0bff6db69d9f0 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -56,6 +56,12 @@ enum ExecutionOutcome { Failed(String), } +#[derive(Clone, Copy)] +enum StepKind { + In, + Over, +} + /// Debug Adapter Protocol frontend. pub(crate) struct Dap { pub(crate) port: Option, @@ -192,9 +198,10 @@ impl DapSession { Command::Variables(args) => self.handle_variables(args.variables_reference, session), Command::Continue(args) => self.handle_continue(args.thread_id, session), Command::SetBreakpoints(args) => self.handle_set_breakpoints(args, session), - Command::Next(args) => self.handle_step(ResponseBody::Next, args.thread_id, session), + Command::Next(args) => + self.handle_step(ResponseBody::Next, args.thread_id, session, StepKind::Over), Command::StepIn(args) => - self.handle_step(ResponseBody::StepIn, args.thread_id, session), + self.handle_step(ResponseBody::StepIn, args.thread_id, session, StepKind::In), Command::Disconnect(_) => self.handle_disconnect(), Command::BreakpointLocations(_) | Command::Cancel(_) @@ -461,17 +468,22 @@ impl DapSession { }) } - /// FIXME: distinguish step-over from step-in once Priroda has call-aware stepping. fn handle_step<'tcx>( &self, body: ResponseBody, thread_id: i64, session: &mut PrirodaContext<'tcx>, + step: StepKind, ) -> Result { self.require_stopped()?; Self::require_thread_id(thread_id)?; - match Self::execution_outcome(session.step()) { + let result = match step { + StepKind::In => session.step_in_source(), + StepKind::Over => session.step_over_source(), + }; + + match Self::execution_outcome(result) { ExecutionOutcome::Stopped(result) => Ok(HandlerSuccess { response: HandlerResponse::Success(body), diff --git a/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout index b65b5512ae31e..35cc2f924cf17 100644 --- a/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_next_at_call.stdout @@ -14,4 +14,4 @@ Content-Length: {CONTENT_LENGTH} {"seq":7,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":8,"type":"response","request_seq":5,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"callee","source":{"name":"dap_next_at_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_next_at_call.rs","sourceReference":0},"line":4,"column":9}],"totalFrames":1},"error":null} \ No newline at end of file +{"seq":8,"type":"response","request_seq":5,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_next_at_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_next_at_call.rs","sourceReference":0},"line":10,"column":9}],"totalFrames":1},"error":null} \ No newline at end of file From e9f1e4ce3149187e1521097286a58fba37d7c68f Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 16 Aug 2026 06:18:56 +0300 Subject: [PATCH 12/46] [Priroda] Add DAP stepOut baseline fixture Record the current DAP behavior before supporting `stepOut` requests. --- .../tests/ui/dap_step_out_from_callee.rs | 12 ++++++++++++ .../tests/ui/dap_step_out_from_callee.stdin | 13 +++++++++++++ .../tests/ui/dap_step_out_from_callee.stdout | 19 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.rs b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.rs new file mode 100644 index 0000000000000..d071191b60e3f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --dap + +fn callee() { + let inner = 1; + let _ = inner; +} + +fn main() { + callee(); + let after = 2; + let _ = after; +} diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdin b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdin new file mode 100644 index 0000000000000..8bba6a20352a6 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdin @@ -0,0 +1,13 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 72 + +{"seq":4,"type":"request","command":"stepIn","arguments":{"threadId":1}}Content-Length: 73 + +{"seq":5,"type":"request","command":"stepOut","arguments":{"threadId":1}}Content-Length: 77 + +{"seq":6,"type":"request","command":"stackTrace","arguments":{"threadId":1}} diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout new file mode 100644 index 0000000000000..ec72fddfc4587 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout @@ -0,0 +1,19 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"stepIn","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"response","request_seq":5,"success":false,"message":"unsupported request in Priroda DAP demo mode: stepOut","command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":9,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"callee","source":{"name":"dap_step_out_from_callee.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_callee.rs","sourceReference":0},"line":4,"column":9}],"totalFrames":1},"error":null} \ No newline at end of file From 3ac3fc3207127257d51451eba2b8d2d04d3e3b44 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Mon, 17 Aug 2026 22:47:10 +0300 Subject: [PATCH 13/46] [Priroda] Wire DAP stepOut request Route DAP `stepOut` through the source step-out helper. The fixture now shows `stepOut` returning from the callee to the caller instead of being rejected as unsupported. --- src/tools/miri/priroda/README.md | 9 +++++---- src/tools/miri/priroda/src/frontend/dap.rs | 5 ++++- .../priroda/tests/ui/dap_step_out_from_callee.stdout | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index fcf4a07f5bd06..3e3bf1d7fb000 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -47,10 +47,11 @@ user-relevant source location after `configurationDone`, reports one current stack frame, exposes one flat Locals scope, and maps `list_locals()` into DAP variables with no child expansion. -DAP supports `stepIn` and `next`. `stepIn` stops at the next displayed source -location and can enter calls, while `next` steps over calls by tracking the -starting stack depth. This is still single-threaded and source-position based, -not the full future thread/frame model. DAP `stepOut` remains unsupported. +DAP supports `stepIn`, `next`, and `stepOut`. `stepIn` stops at the next +displayed source location and can enter calls. `next` steps over calls by +tracking the starting stack depth, and `stepOut` runs until execution reaches a +shallower stack frame. This is still single-threaded and source-position based, +not the full future thread/frame model. ### VS Code diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 0bff6db69d9f0..817e47a8b74ba 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -60,6 +60,7 @@ enum ExecutionOutcome { enum StepKind { In, Over, + Out, } /// Debug Adapter Protocol frontend. @@ -202,6 +203,8 @@ impl DapSession { self.handle_step(ResponseBody::Next, args.thread_id, session, StepKind::Over), Command::StepIn(args) => self.handle_step(ResponseBody::StepIn, args.thread_id, session, StepKind::In), + Command::StepOut(args) => + self.handle_step(ResponseBody::StepOut, args.thread_id, session, StepKind::Out), Command::Disconnect(_) => self.handle_disconnect(), Command::BreakpointLocations(_) | Command::Cancel(_) @@ -228,7 +231,6 @@ impl DapSession { | Command::Source(_) | Command::StepBack(_) | Command::StepInTargets(_) - | Command::StepOut(_) | Command::Terminate(_) | Command::TerminateThreads(_) | Command::WriteMemory(_) => self.handle_unsupported_request(&request.command), @@ -481,6 +483,7 @@ impl DapSession { let result = match step { StepKind::In => session.step_in_source(), StepKind::Over => session.step_over_source(), + StepKind::Out => session.step_out_source(), }; match Self::execution_outcome(result) { diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout index ec72fddfc4587..c65cb75e4e58a 100644 --- a/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_callee.stdout @@ -14,6 +14,8 @@ Content-Length: {CONTENT_LENGTH} {"seq":7,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":8,"type":"response","request_seq":5,"success":false,"message":"unsupported request in Priroda DAP demo mode: stepOut","command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":8,"type":"response","request_seq":5,"success":true,"command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} -{"seq":9,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"callee","source":{"name":"dap_step_out_from_callee.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_callee.rs","sourceReference":0},"line":4,"column":9}],"totalFrames":1},"error":null} \ No newline at end of file +{"seq":9,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":10,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_step_out_from_callee.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_callee.rs","sourceReference":0},"line":9,"column":13}],"totalFrames":1},"error":null} \ No newline at end of file From 4b0fc314d468b8a84103dc290b1b3d15743114c9 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 01:37:07 +0300 Subject: [PATCH 14/46] [Priroda] Add source step-over fixtures Add DAP coverage for `next` stepping over a call and CLI coverage for running `next` from a source breakpoint on a call line. The CLI fixture records the old repeated-breakpoint behavior before the following fix changes stepping breakpoint handling. --- .../priroda/tests/ui/cli_step_over_demo.rs | 18 +++++++++++++++++ .../priroda/tests/ui/cli_step_over_demo.stdin | 4 ++++ .../tests/ui/cli_step_over_demo.stdout | 6 ++++++ .../priroda/tests/ui/dap_step_over_demo.rs | 20 +++++++++++++++++++ .../priroda/tests/ui/dap_step_over_demo.stdin | 11 ++++++++++ .../tests/ui/dap_step_over_demo.stdout | 17 ++++++++++++++++ 6 files changed, 76 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_over_demo.rs create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdin create mode 100644 src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_over_demo.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdout diff --git a/src/tools/miri/priroda/tests/ui/cli_step_over_demo.rs b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.rs new file mode 100644 index 0000000000000..52ee4bfe4aa1f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.rs @@ -0,0 +1,18 @@ +fn leaf() { + let inside_leaf = 10; + let _ = inside_leaf; +} + +fn call_leaf() { + leaf(); // Break here, then run `next`. + let after_leaf = 20; + let _ = after_leaf; +} + +#[rustfmt::skip] +fn same_line() { fn callee() {} callee(); let after = 30; let _ = after; } + +fn main() { + call_leaf(); + same_line(); +} diff --git a/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdin b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdin new file mode 100644 index 0000000000000..51698c01f38f1 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdin @@ -0,0 +1,4 @@ +break tests/ui/cli_step_over_demo.rs:7 +continue +next +quit diff --git a/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout new file mode 100644 index 0000000000000..695ca213ec18c --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout @@ -0,0 +1,6 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/dap_step_over_demo.rs b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.rs new file mode 100644 index 0000000000000..1d9ea1b8461bb --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --dap + +fn leaf() { + let inside_leaf = 10; + let _ = inside_leaf; +} + +fn call_leaf() { + leaf(); + let after_leaf = 20; + let _ = after_leaf; +} + +#[rustfmt::skip] +fn same_line() { fn callee() {} callee(); let after = 30; let _ = after; } + +fn main() { + call_leaf(); // DAP `next` starts here. + same_line(); +} diff --git a/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdin b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdin new file mode 100644 index 0000000000000..d134de9af931e --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdin @@ -0,0 +1,11 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 70 + +{"seq":4,"type":"request","command":"next","arguments":{"threadId":1}}Content-Length: 77 + +{"seq":5,"type":"request","command":"stackTrace","arguments":{"threadId":1}} diff --git a/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdout b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdout new file mode 100644 index 0000000000000..27488f1c98232 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_over_demo.stdout @@ -0,0 +1,17 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"response","request_seq":5,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_step_over_demo.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_over_demo.rs","sourceReference":0},"line":19,"column":5}],"totalFrames":1},"error":null} \ No newline at end of file From 3f00a98fcf6452c751d836f12e198d8fcbe1de0c Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 01:52:48 +0300 Subject: [PATCH 15/46] [Priroda] Avoid re-hitting breakpoints while stepping Suppress the source breakpoint a stepping command started from while that command is leaving the line. The CLI breakpoint fixture now shows `next` stopping on the following source line instead of reporting the same breakpoint again. --- src/tools/miri/priroda/src/debugger.rs | 25 +++++++++++++++---- .../tests/ui/cli_step_over_demo.stdout | 3 +-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index ddc4a1ead6921..399b486bd88d6 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -115,7 +115,7 @@ enum ResumeMode { StepOver { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, /// Step out of the current frame, stopping once execution returns to a /// shallower stack depth. - StepOut { start_stack_depth: usize }, + StepOut { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, /// Stop at the first mapped source location from a user-relevant frame. /// /// This is the DAP entry-stop primitive: it skips over interpreter startup @@ -132,6 +132,17 @@ enum InstructionVisibility { Visible, } +impl ResumeMode { + fn skipped_breakpoint(&self) -> Option<&(PathBuf, usize)> { + match self { + ResumeMode::SourceLine(Some(position)) + | ResumeMode::StepOver { start_position: Some(position), .. } + | ResumeMode::StepOut { start_position: Some(position), .. } => Some(position), + _ => None, + } + } +} + /// Describes why execution stopped and returned control to the frontend. pub(super) enum StepResult { Step, @@ -235,8 +246,9 @@ impl<'tcx> PrirodaContext<'tcx> { if let Some(result) = self.already_finished() { return interp_ok(result); } + let start_position = self.current_source_position(); let start_stack_depth = self.active_thread_stack_depth(); - self.resume(ResumeMode::StepOut { start_stack_depth }) + self.resume(ResumeMode::StepOut { start_position, start_stack_depth }) } /// Run until the initial editor-visible stop point. @@ -311,7 +323,7 @@ impl<'tcx> PrirodaContext<'tcx> { // An explicit breakpoint should stop execution even when the current // MIR instruction would normally be hidden during manual stepping. - if self.is_at_breakpoint() { + if self.is_at_breakpoint(mode.skipped_breakpoint()) { return interp_ok(ExecutionResult::Stopped(StepResult::Breakpoint)); } @@ -375,7 +387,7 @@ impl<'tcx> PrirodaContext<'tcx> { } } - ResumeMode::StepOut { start_stack_depth } + ResumeMode::StepOut { start_stack_depth, .. } if self.active_thread_stack_depth() < start_stack_depth && self.current_location.is_some() => { @@ -443,10 +455,13 @@ impl<'tcx> PrirodaContext<'tcx> { } } - fn is_at_breakpoint(&self) -> bool { + fn is_at_breakpoint(&self, skipped_breakpoint: Option<&(PathBuf, usize)>) -> bool { let Some(bp) = self.current_breakpoint() else { return false; }; + if skipped_breakpoint == Some(&bp) { + return false; + } // If the previous interpreter step had the same source position, this // is another MIR location for the breakpoint we just reported. diff --git a/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout index 695ca213ec18c..01d06c06d4d29 100644 --- a/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout +++ b/src/tools/miri/priroda/tests/ui/cli_step_over_demo.stdout @@ -1,6 +1,5 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 -(priroda) Hit breakpoint -{MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:7 +(priroda) {MANIFEST_DIR}/tests/ui/cli_step_over_demo.rs:8 (priroda) quitting From 470da33a7eab287c52c773c9e2bfa222a7985426 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 02:34:26 +0300 Subject: [PATCH 16/46] [Priroda] Add DAP repeated next baseline fixture Record repeated DAP `next` stops around a call followed by a same-line helper. --- .../tests/ui/dap_repeated_next_from_call.rs | 20 ++++++++++++ .../ui/dap_repeated_next_from_call.stdin | 21 +++++++++++++ .../ui/dap_repeated_next_from_call.stdout | 31 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.rs b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.rs new file mode 100644 index 0000000000000..8b3a210f2eca5 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --dap + +fn leaf() { + let inside_leaf = 10; + let _ = inside_leaf; +} + +fn call_leaf() { + leaf(); + let after_leaf = 20; + let _ = after_leaf; +} + +#[rustfmt::skip] +fn same_line() { fn callee() {} callee(); let after = 30; let _ = after; } + +fn main() { + call_leaf(); + same_line(); +} diff --git a/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdin b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdin new file mode 100644 index 0000000000000..2b3c28ad9cef5 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdin @@ -0,0 +1,21 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 76 + +{"seq":4,"type":"request","command":"stackTrace","arguments":{"threadId":1}}Content-Length: 70 + +{"seq":5,"type":"request","command":"next","arguments":{"threadId":1}}Content-Length: 76 + +{"seq":6,"type":"request","command":"stackTrace","arguments":{"threadId":1}}Content-Length: 70 + +{"seq":7,"type":"request","command":"next","arguments":{"threadId":1}}Content-Length: 76 + +{"seq":8,"type":"request","command":"stackTrace","arguments":{"threadId":1}}Content-Length: 70 + +{"seq":9,"type":"request","command":"next","arguments":{"threadId":1}}Content-Length: 77 + +{"seq":10,"type":"request","command":"stackTrace","arguments":{"threadId":1}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout new file mode 100644 index 0000000000000..4ab18d4e169d0 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout @@ -0,0 +1,31 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":18,"column":5}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"response","request_seq":5,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":9,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":19,"column":5}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":10,"type":"response","request_seq":7,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":11,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":12,"type":"response","request_seq":8,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":17,"column":11}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":13,"type":"response","request_seq":9,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":14,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":15,"type":"response","request_seq":10,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":20,"column":2}],"totalFrames":1},"error":null} \ No newline at end of file From a7f61293c85e04d30df68ed5b457f618a88ec172 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 02:35:29 +0300 Subject: [PATCH 17/46] [Priroda] Skip backward source stops during next Keep source `next` moving when a same-frame return span points back to an earlier line in the same file. The repeated DAP `next` fixture now stops after the same-line helper instead of stopping on the `main` function header. --- src/tools/miri/priroda/src/debugger.rs | 8 ++++++++ .../priroda/tests/ui/dap_repeated_next_from_call.stdout | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 399b486bd88d6..9ffac914cee26 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -380,6 +380,14 @@ impl<'tcx> PrirodaContext<'tcx> { && (*start_path != current_path || *start_line != current_location.line) { + // Return spans can point at a function header. Keep walking when + // that would move `next` backwards within the same frame. + if self.active_thread_stack_depth() == start_stack_depth + && *start_path == current_path + && current_location.line < *start_line + { + continue; + } return interp_ok(ExecutionResult::Stopped(StepResult::Step)); } } diff --git a/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout index 4ab18d4e169d0..5e6d0fab18a82 100644 --- a/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_repeated_next_from_call.stdout @@ -22,10 +22,10 @@ Content-Length: {CONTENT_LENGTH} {"seq":11,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":12,"type":"response","request_seq":8,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":17,"column":11}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} +{"seq":12,"type":"response","request_seq":8,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":20,"column":2}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} {"seq":13,"type":"response","request_seq":9,"success":true,"command":"next","error":null}Content-Length: {CONTENT_LENGTH} {"seq":14,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":15,"type":"response","request_seq":10,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_repeated_next_from_call.rs","path":"{MANIFEST_DIR}/tests/ui/dap_repeated_next_from_call.rs","sourceReference":0},"line":20,"column":2}],"totalFrames":1},"error":null} \ No newline at end of file +{"seq":15,"type":"response","request_seq":10,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":">::call_once - shim(fn())","source":{"name":"function.rs","path":"{RUSTC_SYSROOT}/lib/rustlib/src/rust/library/core/src/ops/function.rs","sourceReference":0},"line":250,"column":5}],"totalFrames":1},"error":null} \ No newline at end of file From 03f198424d606d26e2e7eef05e36ee3a94aa6257 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 02:36:52 +0300 Subject: [PATCH 18/46] [Priroda] Add DAP stepOut-from-main fixture Record DAP `stepOut` from the entry user frame before changing step-out to use user frames. The fixture shows the current raw-stack behavior stopping in Rust runtime code. --- .../tests/ui/dap_step_out_from_main.rs | 10 ++++++++++ .../tests/ui/dap_step_out_from_main.stdin | 13 +++++++++++++ .../tests/ui/dap_step_out_from_main.stdout | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_main.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.rs b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.rs new file mode 100644 index 0000000000000..cf0f5204f211e --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --dap + +fn callee() { + let inner = 1; + let _ = inner; +} + +fn main() { + callee(); +} diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdin b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdin new file mode 100644 index 0000000000000..d96fb4ad0c33f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdin @@ -0,0 +1,13 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"}Content-Length: 76 + +{"seq":4,"type":"request","command":"stackTrace","arguments":{"threadId":1}}Content-Length: 73 + +{"seq":5,"type":"request","command":"stepOut","arguments":{"threadId":1}}Content-Length: 76 + +{"seq":6,"type":"request","command":"stackTrace","arguments":{"threadId":1}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout new file mode 100644 index 0000000000000..d94f2e97194b7 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout @@ -0,0 +1,19 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":6,"type":"response","request_seq":4,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_step_out_from_main.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_main.rs","sourceReference":0},"line":9,"column":5}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":7,"type":"response","request_seq":5,"success":true,"command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":8,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} + +{"seq":9,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":">::call_once - shim(fn())","source":{"name":"function.rs","path":"{RUSTC_SYSROOT}/lib/rustlib/src/rust/library/core/src/ops/function.rs","sourceReference":0},"line":250,"column":5}],"totalFrames":1},"error":null} \ No newline at end of file From 77027e8b957e43675b6a2bce9c4cdbfe3524ec70 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 02:37:49 +0300 Subject: [PATCH 19/46] [Priroda] Keep stepOut within user frames Track source step-out using user-relevant frame depth instead of raw interpreter stack depth. DAP stepOut from the entry user frame now returns an error and leaves the selected frame unchanged, while step-out from a callee still returns to the caller. --- src/tools/miri/priroda/src/debugger.rs | 30 ++++++++++++++----- src/tools/miri/priroda/src/frontend/cli.rs | 1 + src/tools/miri/priroda/src/frontend/dap.rs | 24 +++++++++++++++ .../tests/ui/cli_step_out_command.stdout | 2 +- .../tests/ui/dap_step_out_from_main.stdout | 6 ++-- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 9ffac914cee26..2c9d1fd6e0247 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -113,9 +113,9 @@ enum ResumeMode { /// inside a call made from the stepped-over line), and stops once it is back /// at that depth or shallower and the displayed source position has changed. StepOver { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, - /// Step out of the current frame, stopping once execution returns to a - /// shallower stack depth. - StepOut { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, + /// Step out of the current user frame, stopping once execution returns to a + /// shallower user-frame depth. + StepOut { start_position: Option<(PathBuf, usize)>, start_user_frame_depth: usize }, /// Stop at the first mapped source location from a user-relevant frame. /// /// This is the DAP entry-stop primitive: it skips over interpreter startup @@ -153,6 +153,7 @@ pub(super) enum StepResult { pub(super) enum ExecutionResult { Stopped(StepResult), ProgramExited { code: i32 }, + Rejected { message: &'static str }, } fn normalize_path(path: PathBuf) -> PathBuf { @@ -246,9 +247,14 @@ impl<'tcx> PrirodaContext<'tcx> { if let Some(result) = self.already_finished() { return interp_ok(result); } + let start_user_frame_depth = self.active_user_frame_depth(); + if start_user_frame_depth <= 1 { + return interp_ok(ExecutionResult::Rejected { + message: "stepOut is not meaningful in the outermost user frame", + }); + } let start_position = self.current_source_position(); - let start_stack_depth = self.active_thread_stack_depth(); - self.resume(ResumeMode::StepOut { start_position, start_stack_depth }) + self.resume(ResumeMode::StepOut { start_position, start_user_frame_depth }) } /// Run until the initial editor-visible stop point. @@ -395,8 +401,8 @@ impl<'tcx> PrirodaContext<'tcx> { } } - ResumeMode::StepOut { start_stack_depth, .. } - if self.active_thread_stack_depth() < start_stack_depth + ResumeMode::StepOut { start_user_frame_depth, .. } + if self.active_user_frame_depth() < start_user_frame_depth && self.current_location.is_some() => { return interp_ok(ExecutionResult::Stopped(StepResult::Step)); @@ -417,10 +423,18 @@ impl<'tcx> PrirodaContext<'tcx> { } fn has_user_relevant_frame(&self) -> bool { + self.active_user_frame_depth() > 0 + } + + fn active_user_frame_depth(&self) -> usize { // Walk the whole stack, not just the top frame: during interpreter // startup the user's `main` can sit under Miri-internal frames that // have no source span, so checking only `last()` would miss it. - self.ecx.active_thread_stack().iter().any(|frame| frame.extra.user_relevance == u8::MAX) + self.ecx + .active_thread_stack() + .iter() + .filter(|frame| frame.extra.user_relevance == u8::MAX) + .count() } /// Advance Miri by one interpreter-loop transition. diff --git a/src/tools/miri/priroda/src/frontend/cli.rs b/src/tools/miri/priroda/src/frontend/cli.rs index a2ac5dc8f71bd..d1af42d354872 100644 --- a/src/tools/miri/priroda/src/frontend/cli.rs +++ b/src/tools/miri/priroda/src/frontend/cli.rs @@ -62,6 +62,7 @@ impl Cli { ExecutionResult::ProgramExited { code } => { println!("program finished with exit code {code}"); } + ExecutionResult::Rejected { message } => println!("{message}"), }, CommandResult::BreakpointResult(res) => match res { diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 817e47a8b74ba..72a4cc470c8bb 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -53,6 +53,7 @@ enum DapState { enum ExecutionOutcome { Stopped(StepResult), Terminated { code: i32 }, + Rejected(String), Failed(String), } @@ -370,6 +371,13 @@ impl DapSession { ], outcome: HandlerOutcome::Exit, }), + ExecutionOutcome::Rejected(message) => + Ok(HandlerSuccess { + response: HandlerResponse::Error(message), + state: None, + events: Vec::new(), + outcome: HandlerOutcome::Continue, + }), ExecutionOutcome::Failed(message) => Ok(HandlerSuccess { response: HandlerResponse::Error(message), @@ -504,6 +512,13 @@ impl DapSession { ], outcome: HandlerOutcome::Exit, }), + ExecutionOutcome::Rejected(message) => + Ok(HandlerSuccess { + response: HandlerResponse::Error(message), + state: None, + events: Vec::new(), + outcome: HandlerOutcome::Continue, + }), ExecutionOutcome::Failed(message) => Ok(HandlerSuccess { response: HandlerResponse::Error(message), @@ -542,6 +557,13 @@ impl DapSession { ], outcome: HandlerOutcome::Exit, }), + ExecutionOutcome::Rejected(message) => + Ok(HandlerSuccess { + response: HandlerResponse::Error(message), + state: None, + events: Vec::new(), + outcome: HandlerOutcome::Continue, + }), ExecutionOutcome::Failed(message) => Ok(HandlerSuccess { response: HandlerResponse::Error(message), @@ -670,6 +692,8 @@ impl DapSession { match result.report_err() { Ok(ExecutionResult::Stopped(step)) => ExecutionOutcome::Stopped(step), Ok(ExecutionResult::ProgramExited { code }) => ExecutionOutcome::Terminated { code }, + Ok(ExecutionResult::Rejected { message }) => + ExecutionOutcome::Rejected(message.to_string()), Err(err) => Self::interp_error_outcome(err), } } diff --git a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout index 4ecad1fcef7eb..a1d982386e047 100644 --- a/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout +++ b/src/tools/miri/priroda/tests/ui/cli_step_out_command.stdout @@ -1,3 +1,3 @@ (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 -(priroda) program finished with exit code 0 +(priroda) stepOut is not meaningful in the outermost user frame (priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout index d94f2e97194b7..bb8d2ee2354d7 100644 --- a/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_step_out_from_main.stdout @@ -12,8 +12,6 @@ Content-Length: {CONTENT_LENGTH} {"seq":6,"type":"response","request_seq":4,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_step_out_from_main.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_main.rs","sourceReference":0},"line":9,"column":5}],"totalFrames":1},"error":null}Content-Length: {CONTENT_LENGTH} -{"seq":7,"type":"response","request_seq":5,"success":true,"command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":7,"type":"response","request_seq":5,"success":false,"message":"stepOut is not meaningful in the outermost user frame","command":"stepOut","error":null}Content-Length: {CONTENT_LENGTH} -{"seq":8,"type":"event","event":"stopped","body":{"reason":"step","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} - -{"seq":9,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":">::call_once - shim(fn())","source":{"name":"function.rs","path":"{RUSTC_SYSROOT}/lib/rustlib/src/rust/library/core/src/ops/function.rs","sourceReference":0},"line":250,"column":5}],"totalFrames":1},"error":null} \ No newline at end of file +{"seq":8,"type":"response","request_seq":6,"success":true,"command":"stackTrace","body":{"stackFrames":[{"id":1,"name":"main","source":{"name":"dap_step_out_from_main.rs","path":"{MANIFEST_DIR}/tests/ui/dap_step_out_from_main.rs","sourceReference":0},"line":9,"column":5}],"totalFrames":1},"error":null} \ No newline at end of file From 6a3780541c09686654f119620570908f15f5f420 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 18 Aug 2026 02:41:01 +0300 Subject: [PATCH 20/46] [Priroda] Clarify source stepping docs Document that source step-in only enters calls with distinct displayed source positions, and that stepOut operates on user frames. --- src/tools/miri/priroda/README.md | 14 ++++++++------ src/tools/miri/priroda/src/debugger.rs | 12 ++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 3e3bf1d7fb000..25ec34dce64d8 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -48,10 +48,12 @@ stack frame, exposes one flat Locals scope, and maps `list_locals()` into DAP variables with no child expansion. DAP supports `stepIn`, `next`, and `stepOut`. `stepIn` stops at the next -displayed source location and can enter calls. `next` steps over calls by -tracking the starting stack depth, and `stepOut` runs until execution reaches a -shallower stack frame. This is still single-threaded and source-position based, -not the full future thread/frame model. +displayed source location and can enter calls when the callee has a distinct +displayed source position. `next` steps over calls by tracking the starting stack +depth, and `stepOut` runs until execution reaches a shallower user frame. +`stepOut` from the outermost user frame is rejected. This is still +single-threaded and source-position based, not the full future thread/frame +model. ### VS Code @@ -154,9 +156,9 @@ RUSTC_BLESS=1 cargo test | Command | Description | |---|---| | Enter, `si`, `stepi` | Execute one Miri interpreter step. | -| `s`, `step` | Step to the next displayed source location, entering calls. | +| `s`, `step` | Step to the next displayed source location, entering calls with their own displayed position. | | `n`, `next` | Step over the current displayed source location. | -| `out`, `stepout` | Run until execution returns to a shallower stack frame. | +| `out`, `stepout` | Run until execution returns to a shallower user frame. | | `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | | `b :`, `break :` | Add a source-location breakpoint. | | `l`, `locals` | List source-level locals in the current frame by name. | diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index 2c9d1fd6e0247..e77617f62f69c 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -209,10 +209,10 @@ impl<'tcx> PrirodaContext<'tcx> { self.step_in_source() } - /// Step into the next source location, entering any call that is made. + /// Step into the next source location. /// - /// This keeps source-line stepping as the step-in behavior while `next` uses - /// [`Self::step_over_source`]. + /// This can enter calls that have a distinct displayed source position, + /// while `next` uses [`Self::step_over_source`]. pub(super) fn step_in_source(&mut self) -> InterpResult<'tcx, ExecutionResult> { if let Some(result) = self.already_finished() { return interp_ok(result); @@ -239,10 +239,10 @@ impl<'tcx> PrirodaContext<'tcx> { self.ecx.active_thread_stack().len() } - /// Step out of the current stack frame. + /// Step out of the current user frame. /// - /// Records the current stack depth and runs until execution reaches a source - /// location in a shallower frame. + /// Records the current user-frame depth and runs until execution reaches a + /// source location in a shallower user frame. pub(super) fn step_out_source(&mut self) -> InterpResult<'tcx, ExecutionResult> { if let Some(result) = self.already_finished() { return interp_ok(result); From d6ad3625724dca6f9c698f80459afcd605d62e51 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 18 Aug 2026 12:26:29 +0000 Subject: [PATCH 21/46] add crashtests --- tests/crashes/138262.rs | 12 ++++++++++++ tests/crashes/142155.rs | 12 ++++++++++++ tests/crashes/144241.rs | 4 ++++ tests/crashes/149562.rs | 10 ++++++++++ tests/crashes/152414.rs | 6 ++++++ tests/crashes/152416.rs | 17 +++++++++++++++++ tests/crashes/152626.rs | 7 +++++++ tests/crashes/154903.rs | 7 +++++++ tests/crashes/154963.rs | 10 ++++++++++ tests/crashes/155053.rs | 11 +++++++++++ tests/crashes/156101.rs | 4 ++++ tests/crashes/156288.rs | 3 +++ 12 files changed, 103 insertions(+) create mode 100644 tests/crashes/138262.rs create mode 100644 tests/crashes/142155.rs create mode 100644 tests/crashes/144241.rs create mode 100644 tests/crashes/149562.rs create mode 100644 tests/crashes/152414.rs create mode 100644 tests/crashes/152416.rs create mode 100644 tests/crashes/152626.rs create mode 100644 tests/crashes/154903.rs create mode 100644 tests/crashes/154963.rs create mode 100644 tests/crashes/155053.rs create mode 100644 tests/crashes/156101.rs create mode 100644 tests/crashes/156288.rs diff --git a/tests/crashes/138262.rs b/tests/crashes/138262.rs new file mode 100644 index 0000000000000..05cf4bbb858d9 --- /dev/null +++ b/tests/crashes/138262.rs @@ -0,0 +1,12 @@ +//@ known-bug: #138262 +//@ compile-flags: -Zsanitizer=cfi -Ccodegen-units=1 -Clto -Clink-dead-code=true -Cunsafe-allow-abi-mismatch=sanitizer +//@ ignore-backends: gcc +//@ needs-sanitizer-support +fn foo() {} + +core::arch::global_asm!("/* {} */", sym foo::<{ + || {}; + 0 +}>); + +fn main() {} diff --git a/tests/crashes/142155.rs b/tests/crashes/142155.rs new file mode 100644 index 0000000000000..8c0769bf2b586 --- /dev/null +++ b/tests/crashes/142155.rs @@ -0,0 +1,12 @@ +//@ known-bug: #142155 +//@ needs-rustc-debug-assertions +//@ edition: 2021 + +#![warn(tail_expr_drop_order)] +use core::future::Future; + +fn f() -> impl Future> { + async { Some("nope".into()) } +} + +fn main() {} diff --git a/tests/crashes/144241.rs b/tests/crashes/144241.rs new file mode 100644 index 0000000000000..3f91fcc7c6275 --- /dev/null +++ b/tests/crashes/144241.rs @@ -0,0 +1,4 @@ +//@ known-bug: #144241 +fn main() { + |_: dyn ?Sized + !Send| {} +} diff --git a/tests/crashes/149562.rs b/tests/crashes/149562.rs new file mode 100644 index 0000000000000..4d032a0af5c3e --- /dev/null +++ b/tests/crashes/149562.rs @@ -0,0 +1,10 @@ +//@ known-bug: #149562 +//@ needs-rustc-debug-assertions +fn a() -> T +where + T: ?Sized, + T: ?Sized, +{ +} + +fn main() {} diff --git a/tests/crashes/152414.rs b/tests/crashes/152414.rs new file mode 100644 index 0000000000000..226f9e29faad6 --- /dev/null +++ b/tests/crashes/152414.rs @@ -0,0 +1,6 @@ +//@ known-bug: #152414 +//@ needs-rustc-debug-assertions +#![feature(generic_assert)] +fn main() { + assert!(size_of(val, 1) >= 1); +} diff --git a/tests/crashes/152416.rs b/tests/crashes/152416.rs new file mode 100644 index 0000000000000..9ca418cce3628 --- /dev/null +++ b/tests/crashes/152416.rs @@ -0,0 +1,17 @@ +//@ known-bug: #152416 +//@ needs-rustc-debug-assertions +//@ compile-flags: -Zunstable-options + +trait AssetID {} +trait Archive { + fn name(&self); +} +struct NorthlightAssetID; +impl AssetID for NorthlightAssetID {} +fn get() -> Box> { + let x: Box> = todo!(); + x +} +fn main() { + get().name(); +} diff --git a/tests/crashes/152626.rs b/tests/crashes/152626.rs new file mode 100644 index 0000000000000..eafb714c2f5c2 --- /dev/null +++ b/tests/crashes/152626.rs @@ -0,0 +1,7 @@ +//@ known-bug: #152626 +//@ needs-rustc-debug-assertions +struct A>(T); +fn f() -> A<&'static ()> { + todo!() +} +fn main() {} diff --git a/tests/crashes/154903.rs b/tests/crashes/154903.rs new file mode 100644 index 0000000000000..63e80d8f9e251 --- /dev/null +++ b/tests/crashes/154903.rs @@ -0,0 +1,7 @@ +//@ known-bug: #154903 +//@ compile-flags: -Zlint-mir +#![feature(guard_patterns)] + +fn a(((x if true, _) | (_, x)): (i32, i32)) {} + +fn main() {} diff --git a/tests/crashes/154963.rs b/tests/crashes/154963.rs new file mode 100644 index 0000000000000..8fafc29c48342 --- /dev/null +++ b/tests/crashes/154963.rs @@ -0,0 +1,10 @@ +//@ known-bug: #154963 +#![feature(extern_types, negative_impls)] + +unsafe extern "C" { + type ExternType; +} + +impl !Unpin for ExternType {} + +fn main() {} diff --git a/tests/crashes/155053.rs b/tests/crashes/155053.rs new file mode 100644 index 0000000000000..31b9ccaf20540 --- /dev/null +++ b/tests/crashes/155053.rs @@ -0,0 +1,11 @@ +//@ known-bug: #155053 +#![feature(pin_ergonomics)] +#![feature(extern_types)] + +unsafe extern "C" { + type ExternType; +} + +impl Unpin for ExternType {} + +fn main() {} diff --git a/tests/crashes/156101.rs b/tests/crashes/156101.rs new file mode 100644 index 0000000000000..c95361fab2ecc --- /dev/null +++ b/tests/crashes/156101.rs @@ -0,0 +1,4 @@ +//@ known-bug: #156101 +fn main() { + format_args!(concat!("𐏿", "{f:?#}")); +} diff --git a/tests/crashes/156288.rs b/tests/crashes/156288.rs new file mode 100644 index 0000000000000..b745cfe063dda --- /dev/null +++ b/tests/crashes/156288.rs @@ -0,0 +1,3 @@ +//@ known-bug: #156288 +#[warn(rust_2021_incompatible_closure_captures)] +const _: () = |b| move || b; From 60c73107f423265f48962c71790da5cfe90a575a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Aug 2026 14:48:39 +0200 Subject: [PATCH 22/46] Prepare for merging from rust-lang/rust This updates the rust-version file to f7d782a3be46d6bb4b9792fe69a61db389ba1769. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6c163e62d963d..9ff8b0c27d19c 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -67854e511de21d881bb16426996cd4259d44aa2e +f7d782a3be46d6bb4b9792fe69a61db389ba1769 From 0250298b1d315538c51b88d52806b824a73ee10a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Aug 2026 10:58:52 +0200 Subject: [PATCH 23/46] remove no-longer-needed horizontal SSE/AVX ops --- src/tools/miri/src/intrinsics/x86/avx2.rs | 19 +------- src/tools/miri/src/intrinsics/x86/mod.rs | 52 ---------------------- src/tools/miri/src/intrinsics/x86/ssse3.rs | 17 +------ 3 files changed, 3 insertions(+), 85 deletions(-) diff --git a/src/tools/miri/src/intrinsics/x86/avx2.rs b/src/tools/miri/src/intrinsics/x86/avx2.rs index 160bce2dec98b..dc7dbaff9927e 100644 --- a/src/tools/miri/src/intrinsics/x86/avx2.rs +++ b/src/tools/miri/src/intrinsics/x86/avx2.rs @@ -1,9 +1,8 @@ -use rustc_middle::mir; use rustc_span::Symbol; use super::{ - ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, - pmaddwd, pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar, + ShiftOp, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, pmaddwd, pmulhrsw, + psadbw, pshufb, psign, shift_simd_by_scalar, }; use crate::*; @@ -21,20 +20,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx2.").unwrap(); match unprefixed_name { - // Used to implement the _mm256_h{adds,subs}_epi16 functions. - // Horizontally add / subtract with saturation adjacent 16-bit - // integer values in `left` and `right`. - "phadd.sw" | "phsub.sw" => { - let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?; - - let which = match unprefixed_name { - "phadd.sw" => mir::BinOp::Add, - "phsub.sw" => mir::BinOp::Sub, - _ => unreachable!(), - }; - - horizontal_bin_op(this, which, /*saturating*/ true, left, right, dest)?; - } // Used to implement `_mm{,_mask}_{i32,i64}gather_{epi32,epi64,pd,ps}` functions // Gathers elements from `slice` using `offsets * scale` as indices. // When the highest bit of the corresponding element of `mask` is 0, diff --git a/src/tools/miri/src/intrinsics/x86/mod.rs b/src/tools/miri/src/intrinsics/x86/mod.rs index 25361a6435b0a..c0c9354b23a03 100644 --- a/src/tools/miri/src/intrinsics/x86/mod.rs +++ b/src/tools/miri/src/intrinsics/x86/mod.rs @@ -667,58 +667,6 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( interp_ok((num_chunks, items_per_chunk, chunked_op)) } -/// Horizontally performs `which` operation on adjacent values of -/// `left` and `right` SIMD vectors and stores the result in `dest`. -/// "Horizontal" means that the i-th output element is calculated -/// from the elements 2*i and 2*i+1 of the concatenation of `left` and -/// `right`. -/// -/// Each 128-bit chunk is treated independently (i.e., the value for -/// the is i-th 128-bit chunk of `dest` is calculated with the i-th -/// 128-bit chunks of `left` and `right`). -fn horizontal_bin_op<'tcx>( - ecx: &mut crate::MiriInterpCx<'tcx>, - which: mir::BinOp, - saturating: bool, - left: &OpTy<'tcx>, - right: &OpTy<'tcx>, - dest: &MPlaceTy<'tcx>, -) -> InterpResult<'tcx, ()> { - assert_eq!(left.layout, dest.layout); - assert_eq!(right.layout, dest.layout); - - let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?; - let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?; - let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?; - - let middle = items_per_chunk / 2; - for i in 0..num_chunks { - let left = ecx.project_index(&left, i)?; - let right = ecx.project_index(&right, i)?; - let dest = ecx.project_index(&dest, i)?; - - for j in 0..items_per_chunk { - // `j` is the index in `dest` - // `k` is the index of the 2-item chunk in `src` - let (k, src) = if j < middle { (j, &left) } else { (j.strict_sub(middle), &right) }; - // `base_i` is the index of the first item of the 2-item chunk in `src` - let base_i = k.strict_mul(2); - let lhs = ecx.read_immediate(&ecx.project_index(src, base_i)?)?; - let rhs = ecx.read_immediate(&ecx.project_index(src, base_i.strict_add(1))?)?; - - let res = if saturating { - Immediate::from(ecx.saturating_arith(which, &lhs, &rhs)?) - } else { - *ecx.binary_op(which, &lhs, &rhs)? - }; - - ecx.write_immediate(res, &ecx.project_index(&dest, j)?)?; - } - } - - interp_ok(()) -} - /// Conditionally multiplies the packed floating-point elements in /// `left` and `right` using the high 4 bits in `imm`, sums the calculated /// products (up to 4), and conditionally stores the sum in `dest` using diff --git a/src/tools/miri/src/intrinsics/x86/ssse3.rs b/src/tools/miri/src/intrinsics/x86/ssse3.rs index 5b4746e5b1a04..1c88c50e830d2 100644 --- a/src/tools/miri/src/intrinsics/x86/ssse3.rs +++ b/src/tools/miri/src/intrinsics/x86/ssse3.rs @@ -1,7 +1,6 @@ -use rustc_middle::mir; use rustc_span::Symbol; -use super::{horizontal_bin_op, pmaddbw, pmulhrsw, pshufb, psign}; +use super::{pmaddbw, pmulhrsw, pshufb, psign}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -26,20 +25,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pshufb(this, left, right, dest)?; } - // Used to implement the _mm_h{adds,subs}_epi16 functions. - // Horizontally add / subtract with saturation adjacent 16-bit - // integer values in `left` and `right`. - "phadd.sw.128" | "phsub.sw.128" => { - let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?; - - let which = match unprefixed_name { - "phadd.sw.128" => mir::BinOp::Add, - "phsub.sw.128" => mir::BinOp::Sub, - _ => unreachable!(), - }; - - horizontal_bin_op(this, which, /*saturating*/ true, left, right, dest)?; - } // Used to implement the _mm_maddubs_epi16 function. "pmadd.ub.sw.128" => { let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?; From b5db2cbb9d401fddae1b350e139dea79276b485b Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 20 Aug 2026 01:30:58 +0300 Subject: [PATCH 24/46] [Priroda] Merge SourceLine into StepOver Reuse the step-over resume arm for source step-in by treating a usize::MAX start depth as never deeper than the starting point. `step` and `next` now share the same source-position change check. --- src/tools/miri/priroda/src/debugger.rs | 37 +++++--------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index e77617f62f69c..4c4c811c01358 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -101,17 +101,14 @@ impl LocalDesc { enum ResumeMode { /// Stop at the next visible MIR instruction. MirInstruction, - /// Stop at the next source line. - /// - /// `None` means the current interpreter position has no source location, so - /// the first mapped source location is good enough to report. - SourceLine(Option<(PathBuf, usize)>), /// Step over the source position `start_position`, entered from a stack of /// depth `start_stack_depth`. /// /// Execution keeps going while it is deeper than `start_stack_depth` (i.e. /// inside a call made from the stepped-over line), and stops once it is back /// at that depth or shallower and the displayed source position has changed. + /// A `start_stack_depth` of `usize::MAX` means execution is never deeper, + /// turning this into a plain source step that also stops inside called functions. StepOver { start_position: Option<(PathBuf, usize)>, start_stack_depth: usize }, /// Step out of the current user frame, stopping once execution returns to a /// shallower user-frame depth. @@ -135,8 +132,7 @@ enum InstructionVisibility { impl ResumeMode { fn skipped_breakpoint(&self) -> Option<&(PathBuf, usize)> { match self { - ResumeMode::SourceLine(Some(position)) - | ResumeMode::StepOver { start_position: Some(position), .. } + ResumeMode::StepOver { start_position: Some(position), .. } | ResumeMode::StepOut { start_position: Some(position), .. } => Some(position), _ => None, } @@ -217,7 +213,10 @@ impl<'tcx> PrirodaContext<'tcx> { if let Some(result) = self.already_finished() { return interp_ok(result); } - self.resume(ResumeMode::SourceLine(self.current_source_position())) + self.resume(ResumeMode::StepOver { + start_position: self.current_source_position(), + start_stack_depth: usize::MAX, + }) } /// Step over the current source position, not stopping inside any call it makes. @@ -343,28 +342,6 @@ impl<'tcx> PrirodaContext<'tcx> { return interp_ok(ExecutionResult::Stopped(StepResult::Step)); } - ResumeMode::SourceLine(ref prev_location) => { - match (prev_location, &self.current_location) { - // We started from an unmapped location; stop once there - // is a source position the frontend can display. - (None, Some(_)) => - return interp_ok(ExecutionResult::Stopped(StepResult::Step)), - - (Some((prev_path, prev_line)), Some(current_location)) => { - if let Some(current_path) = self.local_path(current_location) { - // A source step stops when the displayed source - // position changes to a different file or line. - if *prev_path != current_path || *prev_line != current_location.line - { - return interp_ok(ExecutionResult::Stopped(StepResult::Step)); - } - } - } - - _ => {} - } - } - ResumeMode::StepOver { ref start_position, start_stack_depth } => { // While deeper than where we started, we are inside a call // made from the stepped-over line; keep going. From 1331f4ec1082e8c9dbb30ebb3ab6c458f9502ac9 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 20 Aug 2026 13:16:07 +0330 Subject: [PATCH 25/46] Add shim for `malloc_usable_size` --- src/tools/miri/src/shims/foreign_items.rs | 33 +++++++++++++++++++ .../tests/fail-dep/libc/malloc_usable_size.rs | 10 ++++++ .../fail-dep/libc/malloc_usable_size.stderr | 13 ++++++++ .../libc/malloc_usable_size_interior.rs | 10 ++++++ .../libc/malloc_usable_size_interior.stderr | 13 ++++++++ .../fail-dep/libc/malloc_usable_size_stack.rs | 10 ++++++ .../libc/malloc_usable_size_stack.stderr | 13 ++++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 20 +++++++++++ 8 files changed, 122 insertions(+) create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size.stderr create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.stderr create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.stderr diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 793a225efab9e..f425da3f0d3a1 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -652,6 +652,39 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } } + "malloc_usable_size" => { + this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; + + let [ptr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> usize), + link_name, + abi, + args, + )?; + let ptr = this.read_pointer(ptr)?; + let size = if this.ptr_is_null(ptr)? { + 0 + } else { + let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + throw_ub_format!( + "`malloc_usable_size` was called on a pointer that does not point to the beginning of its allocation" + ); + } + let Some((alloc_kind, _)) = this.memory.alloc_map().get(alloc_id) else { + throw_ub_format!( + "`malloc_usable_size` was called on a pointer to memory not managed by the C allocator" + ); + }; + if *alloc_kind != MiriMemoryKind::C.into() { + throw_ub_format!( + "`malloc_usable_size` was called on a pointer to {alloc_kind} memory, which is not managed by the C allocator" + ); + } + this.get_alloc_info(alloc_id).size.bytes() + }; + this.write_scalar(Scalar::from_target_usize(size, this), dest)?; + } // C memory handling functions "memcmp" => { diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.rs b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.rs new file mode 100644 index 0000000000000..7d0d24677b822 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.rs @@ -0,0 +1,10 @@ +//@only-target: linux android freebsd + +fn main() { + unsafe { + // A Rust heap allocation is not managed by the C allocator. + let b = Box::new(42); + let p = Box::into_raw(b).cast::(); + libc::malloc_usable_size(p); //~ERROR: not managed by the C allocator + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.stderr new file mode 100644 index 0000000000000..e15f334908a6b --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `malloc_usable_size` was called on a pointer to Rust heap memory, which is not managed by the C allocator + --> tests/fail-dep/libc/malloc_usable_size.rs:LL:CC + | +LL | libc::malloc_usable_size(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.rs b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.rs new file mode 100644 index 0000000000000..cb9b0858833df --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.rs @@ -0,0 +1,10 @@ +//@only-target: linux android freebsd + +fn main() { + unsafe { + // The pointer must point to the beginning of the block. + let p = libc::malloc(1024); + let mid = p.cast::().add(512).cast::(); + libc::malloc_usable_size(mid); //~ERROR: does not point to the beginning + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.stderr new file mode 100644 index 0000000000000..c3a4c167bfd5f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_interior.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `malloc_usable_size` was called on a pointer that does not point to the beginning of its allocation + --> tests/fail-dep/libc/malloc_usable_size_interior.rs:LL:CC + | +LL | libc::malloc_usable_size(mid); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.rs b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.rs new file mode 100644 index 0000000000000..57e1c81c1b702 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.rs @@ -0,0 +1,10 @@ +//@only-target: linux android freebsd + +fn main() { + unsafe { + // A stack variable is not managed by the C allocator. + let mut x = 42; + let p = (&raw mut x).cast::(); + libc::malloc_usable_size(p); //~ERROR: not managed by the C allocator + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.stderr new file mode 100644 index 0000000000000..e9e33015782b9 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_usable_size_stack.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `malloc_usable_size` was called on a pointer to stack variable memory, which is not managed by the C allocator + --> tests/fail-dep/libc/malloc_usable_size_stack.rs:LL:CC + | +LL | libc::malloc_usable_size(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index a64a23aa5a38f..7e7c7e99338cc 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -408,6 +408,24 @@ fn test_strnlen() { } } +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] +fn test_malloc_usable_size() { + unsafe { + // `malloc_usable_size(NULL)` returns 0. + assert_eq!(libc::malloc_usable_size(ptr::null_mut()), 0); + + for size in [1, 2, 5, 16, 123, 1024] { + let p = libc::malloc(size); + if cfg!(miri) { + // Miri returns the exact size, but it doesn't need to. + assert_eq!(libc::malloc_usable_size(p), size); + } + assert!(libc::malloc_usable_size(p) >= size); + libc::free(p); + } + } +} + fn test_wcslen() { fn to_c_wchar_t_str(s: &str) -> Vec { let mut r = Vec::::new(); @@ -444,6 +462,8 @@ fn main() { test_reallocarray(); #[cfg(not(target_os = "windows"))] test_aligned_alloc(); + #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] + test_malloc_usable_size(); test_memcpy(); test_strcpy(); From 279586e4b94d3684352f4743a98d7d32ba64bf4e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 21 Aug 2026 04:30:57 +0000 Subject: [PATCH 26/46] Prepare for merging from rust-lang/rust This updates the rust-version file to 095d9ef41e2360afb04faef48d2fdca76a9a2992. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 9ff8b0c27d19c..79021a65f704a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f7d782a3be46d6bb4b9792fe69a61db389ba1769 +095d9ef41e2360afb04faef48d2fdca76a9a2992 From 4027f6018157376b7d363960dd5320b6cf51aefb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Aug 2026 07:54:22 +0200 Subject: [PATCH 27/46] have triagebot link to LLM policy --- src/tools/miri/triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 727d3cc868742..c4c2008a9afa4 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -17,6 +17,7 @@ allow-unauthenticated = [ [assign] warn_non_default_branch = true contributing_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#pr-review-process" +llm_policy_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#ai-policy" [no-merges] exclude_titles = ["Rustup"] From 3d30691e02a2b87a79ea053487f51b8e553ca400 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Aug 2026 14:25:42 +0200 Subject: [PATCH 28/46] Prepare for merging from rust-lang/rust This updates the rust-version file to c656540d6467dee1381f0cbd882412d6bd1cd5ae. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 79021a65f704a..e01a381d55fd9 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -095d9ef41e2360afb04faef48d2fdca76a9a2992 +c656540d6467dee1381f0cbd882412d6bd1cd5ae From 62714e462e8e30aeaa93fbf4b08232cddaf308bc Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 22 Aug 2026 13:51:55 +0330 Subject: [PATCH 29/46] Relax size and align check of extern statics --- src/tools/miri/src/machine.rs | 6 ++--- .../fail/extern_static/wrong_size_shim.rs | 8 ++++-- .../fail/extern_static/wrong_size_shim.stderr | 9 ++++--- src/tools/miri/tests/pass/extern_static.rs | 27 +++++++++++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 29289df8d823f..361ebef73b357 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1472,8 +1472,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { panic!("extern_statics cannot contain wildcards") }; let info = ecx.get_alloc_info(alloc_id); - if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align { - throw_unsup_format!( + if extern_decl_layout.size > info.size || extern_decl_layout.align.abi > info.align { + throw_ub_format!( "extern static `{link_name}` has been declared as `{krate}::{name}` \ with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ but Miri emulates it via an extern static shim \ @@ -1516,7 +1516,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Validate the allocation matches the declared size and alignment. let alloc_id = static_ptr.provenance.get_alloc_id().unwrap(); let info = ecx.get_alloc_info(alloc_id); - if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align { + if extern_decl_layout.size > info.size || extern_decl_layout.align.abi > info.align { throw_ub_format!( "extern static `{link_name}` has been declared as `{krate}::{name}` \ with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ diff --git a/src/tools/miri/tests/fail/extern_static/wrong_size_shim.rs b/src/tools/miri/tests/fail/extern_static/wrong_size_shim.rs index 56c3ddd351612..4bd2cb530db45 100644 --- a/src/tools/miri/tests/fail/extern_static/wrong_size_shim.rs +++ b/src/tools/miri/tests/fail/extern_static/wrong_size_shim.rs @@ -2,9 +2,13 @@ //@normalize-stderr-test: "[48] bytes" -> "N bytes" extern "C" { - static mut environ: i8; + #[link_name = "environ"] + static mut environ_good: i8; + #[link_name = "environ"] + static mut environ_bad: [i8; 10]; } fn main() { - let _val = unsafe { environ }; //~ ERROR: /with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of [48] bytes and alignment of [48] bytes/ + let _val = unsafe { environ_good }; + let _val = unsafe { environ_bad }; //~ ERROR: /with a size of 10 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of [48] bytes and alignment of [48] bytes/ } diff --git a/src/tools/miri/tests/fail/extern_static/wrong_size_shim.stderr b/src/tools/miri/tests/fail/extern_static/wrong_size_shim.stderr index d3a0f0205ee3b..6eddfe1163bd6 100644 --- a/src/tools/miri/tests/fail/extern_static/wrong_size_shim.stderr +++ b/src/tools/miri/tests/fail/extern_static/wrong_size_shim.stderr @@ -1,10 +1,11 @@ -error: unsupported operation: extern static `environ` has been declared as `wrong_size_shim::environ` with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes +error: Undefined Behavior: extern static `environ` has been declared as `wrong_size_shim::environ_bad` with a size of 10 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes --> tests/fail/extern_static/wrong_size_shim.rs:LL:CC | -LL | let _val = unsafe { environ }; - | ^^^^^^^ unsupported operation occurred here +LL | let _val = unsafe { environ_bad }; + | ^^^^^^^^^^^ Undefined Behavior occurred here | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = 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 diff --git a/src/tools/miri/tests/pass/extern_static.rs b/src/tools/miri/tests/pass/extern_static.rs index 70b8ff304c086..87f776cd92670 100644 --- a/src/tools/miri/tests/pass/extern_static.rs +++ b/src/tools/miri/tests/pass/extern_static.rs @@ -20,6 +20,9 @@ static FOO_U32: u32 = 42; #[no_mangle] static INTERIOR_MUT: SyncUnsafeCell = SyncUnsafeCell::new(42); +#[no_mangle] +static ARRAY: [u32; 5] = [1, 2, 3, 4, 5]; + fn increase_mutable_static_by_original_def(add_val: i32) { unsafe { let new_val = (&raw mut MUTABLE_STATIC).read() + add_val; @@ -60,6 +63,30 @@ fn main() { (&raw mut INTERIOR_MUT_AS_MUTABLE_STATIC).write(7); MUTABLE_STATIC_AS_INTERIOR_MUT.get().write(3); } + + // It's okay for the actual static to be bigger or more aligned than the extern declaration. + extern "C" { + // Actual size is bigger (20 bytes). + #[link_name = "ARRAY"] + static ARRAY_UNKNOWN_SIZE: [u32; 0]; + + // Actual size and alignment is that of u32, not u16. + #[link_name = "FOO_U32"] + static U16_TO_FOO_U32: u16; + } + + unsafe { + let ptr = (&raw const ARRAY_UNKNOWN_SIZE).cast::(); + assert_eq!(ptr.read(), 1); + assert_eq!(ptr.offset(2).read(), 3); + + // We see one half of FOO_U32, depending on endianess. + if cfg!(target_endian = "little") { + assert_eq!(U16_TO_FOO_U32, 42); + } else { + assert_eq!(U16_TO_FOO_U32, 0); + } + } } extern "Rust" { From c50baf5877401fa1fd3c73f4f7ec5d62d86cc331 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Aug 2026 11:12:34 +0200 Subject: [PATCH 30/46] fmt, clippy, and spacing --- src/tools/miri/miri-script/src/commands.rs | 4 ++-- src/tools/miri/miri-script/src/util.rs | 4 ++-- src/tools/miri/src/bin/miri.rs | 4 ++-- src/tools/miri/src/helpers.rs | 2 +- src/tools/miri/tests/ui.rs | 1 - 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 24fe5b66bb0b4..0a372dfbf0c6d 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -55,7 +55,7 @@ impl MiriEnv { .cargo_cmd("cargo-miri", "run", &[]) .arg("--quiet") .arg("--") - .args(&["miri", "setup", "--print-sysroot"]) + .args(["miri", "setup", "--print-sysroot"]) .args(target_flag); if quiet { cmd = cmd.arg("--quiet"); @@ -511,7 +511,7 @@ impl Command { // We invoke the test suite as that has all the logic for running with dependencies. let mut cmd = e .cargo_cmd(".", "test", &features) - .args(&["--test", "ui"]) + .args(["--test", "ui"]) // This does not show anything useful so we always hide it. .arg("--quiet") .arg("--") diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index fd8b1958689df..01743c79f140b 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -178,7 +178,7 @@ impl MiriEnv { // parallelism in `./miri test` as we build Miri and its tests together. let mut cmd = self .cargo_cmd(crate_dir, "build", features) - .args(&["--all-targets"]) + .args(["--all-targets"]) .args(quiet_flag) .args(args); cmd.set_quiet(quiet); @@ -194,7 +194,7 @@ impl MiriEnv { ) -> Result { let cmd = self .cargo_cmd(crate_dir, "build", features) - .args(&["--all-targets", "--message-format=json"]); + .args(["--all-targets", "--message-format=json"]); let output = cmd.output()?; let mut bin = None; for line in output.stdout.lines() { diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index f28bb524775ed..7623422a66e57 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -45,9 +45,9 @@ use rustc_interface::util::DummyCodegenBackend; use rustc_log::tracing::debug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; -use rustc_structures::CrateType; -use rustc_session::config::{ ErrorOutputType, OptLevel}; +use rustc_session::config::{ErrorOutputType, OptLevel}; use rustc_session::{EarlyDiagCtxt, Session}; +use rustc_structures::CrateType; use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 9bf202e8254e8..11f1fa2eb170d 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -12,8 +12,8 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout}; use rustc_middle::ty::{self, FnSigKind, IntTy, Ty, TyCtxt, UintTy}; -use rustc_structures::CrateType; use rustc_span::{Span, Symbol}; +use rustc_structures::CrateType; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::Os; diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index e77c4f20750ed..b2fda8e0c62c9 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -300,7 +300,6 @@ fn run_tests( ) .into(), ); - if let Ok(extra_flags) = env::var("MIRIFLAGS") { for flag in extra_flags.split_whitespace() { config.program.args.push(flag.into()); From 8b32975a38e5fa83bbdd0ccf3e3ec3216e94ffab Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Aug 2026 17:48:08 +0200 Subject: [PATCH 31/46] support a few more basic things on netbsd --- src/tools/miri/ci/ci.sh | 2 +- .../miri/src/shims/unix/foreign_items.rs | 6 +++ src/tools/miri/src/shims/unix/mod.rs | 3 +- .../src/shims/unix/netbsd/foreign_items.rs | 51 +++++++++++++++++++ src/tools/miri/src/shims/unix/netbsd/mod.rs | 1 + .../tests/pass-dep/concurrency/tls_errno.rs | 25 +++++++++ .../miri/tests/pass-dep/libc/libc-misc.rs | 15 ++---- 7 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/tools/miri/src/shims/unix/netbsd/foreign_items.rs create mode 100644 src/tools/miri/src/shims/unix/netbsd/mod.rs create mode 100644 src/tools/miri/tests/pass-dep/concurrency/tls_errno.rs diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 503cc68fbc834..b9f8c10900853 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -168,7 +168,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello libc-env libc-misc ;; armv7-unknown-linux-gnueabihf) # Host diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 2eb8ee0f6105f..9c4c44fece5a6 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -12,6 +12,7 @@ use self::shims::unix::android::foreign_items as android; use self::shims::unix::freebsd::foreign_items as freebsd; use self::shims::unix::linux::foreign_items as linux; use self::shims::unix::macos::foreign_items as macos; +use self::shims::unix::netbsd::foreign_items as netbsd; use self::shims::unix::solarish::foreign_items as solarish; use crate::concurrency::cpu_affinity::CpuAffinityMask; use crate::shims::alloc::EvalContextExt as _; @@ -42,6 +43,7 @@ pub fn is_dyn_sym(name: &str, target_os: &Os) -> bool { Os::Linux => linux::is_dyn_sym(name), Os::MacOs => macos::is_dyn_sym(name), Os::Solaris | Os::Illumos => solarish::is_dyn_sym(name), + Os::NetBsd => netbsd::is_dyn_sym(name), _ => false, }, } @@ -1542,6 +1544,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { solarish::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), + Os::NetBsd => + netbsd::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), _ => interp_ok(EmulateItemResult::NotSupported), }; } diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs index 259bc79b7f9f4..c9423bae958f9 100644 --- a/src/tools/miri/src/shims/unix/mod.rs +++ b/src/tools/miri/src/shims/unix/mod.rs @@ -14,9 +14,10 @@ mod virtual_socket; mod android; mod freebsd; -pub mod linux; +mod linux; mod linux_like; mod macos; +mod netbsd; mod solarish; // All the Unix-specific extension traits diff --git a/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs b/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs new file mode 100644 index 0000000000000..5c7c2300745f2 --- /dev/null +++ b/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs @@ -0,0 +1,51 @@ +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; + +use crate::shims::unix::*; +use crate::*; + +pub fn is_dyn_sym(_name: &str) -> bool { + false +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + match link_name.as_str() { + // Environment + "__unsetenv13" => { + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), + link_name, + abi, + args, + )?; + let result = this.unsetenv(name)?; + this.write_scalar(result, dest)?; + } + + // Miscellaneous + "__errno" => { + let [] = this.check_shim_sig( + shim_sig!(extern "C" fn() -> *mut _), + link_name, + abi, + args, + )?; + let errno_place = this.last_error_place()?; + this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; + } + + _ => return interp_ok(EmulateItemResult::NotSupported), + } + interp_ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/unix/netbsd/mod.rs b/src/tools/miri/src/shims/unix/netbsd/mod.rs new file mode 100644 index 0000000000000..09c6507b24f84 --- /dev/null +++ b/src/tools/miri/src/shims/unix/netbsd/mod.rs @@ -0,0 +1 @@ +pub mod foreign_items; diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_errno.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_errno.rs new file mode 100644 index 0000000000000..6de404c8c0813 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/concurrency/tls_errno.rs @@ -0,0 +1,25 @@ +//@ignore-target: windows # No libc errno on Windows + +/// Tests whether each thread has its own `__errno_location`. +fn main() { + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + use libc::___errno as __errno_location; + #[cfg(target_os = "android")] + use libc::__errno as __errno_location; + #[cfg(target_os = "linux")] + use libc::__errno_location; + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + use libc::__error as __errno_location; + + unsafe { + *__errno_location() = 0xBEEF; + std::thread::spawn(|| { + assert_eq!(*__errno_location(), 0); + *__errno_location() = 0xBAD1DEA; + assert_eq!(*__errno_location(), 0xBAD1DEA); + }) + .join() + .unwrap(); + assert_eq!(*__errno_location(), 0xBEEF); + } +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs index 10d756e05104b..c941e8b82bed1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -3,11 +3,11 @@ use std::mem::transmute; -/// Tests whether each thread has its own `__errno_location`. -fn test_thread_local_errno() { +/// Ensure errno can be written and read. +fn test_errno() { #[cfg(any(target_os = "illumos", target_os = "solaris"))] use libc::___errno as __errno_location; - #[cfg(target_os = "android")] + #[cfg(any(target_os = "android", target_os = "netbsd"))] use libc::__errno as __errno_location; #[cfg(target_os = "linux")] use libc::__errno_location; @@ -16,13 +16,6 @@ fn test_thread_local_errno() { unsafe { *__errno_location() = 0xBEEF; - std::thread::spawn(|| { - assert_eq!(*__errno_location(), 0); - *__errno_location() = 0xBAD1DEA; - assert_eq!(*__errno_location(), 0xBAD1DEA); - }) - .join() - .unwrap(); assert_eq!(*__errno_location(), 0xBEEF); } } @@ -86,7 +79,7 @@ fn test_geteuid() { } fn main() { - test_thread_local_errno(); + test_errno(); test_environ(); test_dlsym(); test_getuid(); From c6b24c00ddd6d66f69ea24ec2d2eba1b651bb7a2 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 22 Aug 2026 13:07:48 -0400 Subject: [PATCH 32/46] Bail out in more tests if we don't detect the required feature at runtime --- .../tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs | 12 +++++++++++- .../pass/shims/x86/intrinsics-x86-vpclmulqdq.rs | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 67b35405ccdf8..82b0d26d4df1b 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -11,10 +11,20 @@ use std::arch::x86_64::*; fn main() { assert!(is_x86_feature_detected!("aes")); - assert!(is_x86_feature_detected!("vaes")); unsafe { test_aes(); + } + + // The tests below require vaes, which is recent enough that contributors may be using CPUs that + // do not support it. But we still want to run this natively if the machine happens to have vaes. + // So we bail out dynamically. + if !is_x86_feature_detected!("vaes") { + println!("warning: skipping vaes tests"); + return; + } + + unsafe { test_vaes(); } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index 5ceaf405f4040..22bd697cfd0a1 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -19,8 +19,15 @@ use std::mem::transmute; fn main() { // Mostly copied from library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs + // These tests require vpclmuldqd, which is recent enough that contributors may be using CPUs that + // do not support it. But we still want to run this natively if the machine happens to have vpclmulqdq. + // So we bail out dynamically. + if !is_x86_feature_detected!("vpclmulqdq") { + println!("warning: skipping vpclmulqdq tests"); + return; + } + assert!(is_x86_feature_detected!("pclmulqdq")); - assert!(is_x86_feature_detected!("vpclmulqdq")); unsafe { test_mm256_clmulepi64_epi128(); From b14b89fd7de491f93517f37968eae7e186bc5b99 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 22 Aug 2026 19:36:22 -0500 Subject: [PATCH 33/46] Bump cfg_aliases to 0.2.2 Resolves FCW in `nix@0.30.1`'s usage of `cfg_aliases`. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d68be636fa92..2749a2cd61897 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,9 +561,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" From 0d74fb7a10379aefbfff1841b43b6835e560a0a7 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Sun, 23 Aug 2026 00:57:14 -0500 Subject: [PATCH 34/46] re-bless `pretty-std` on windows --- tests/debuginfo/pretty-std/lldb_input/windows_gnu.json | 5 ++--- tests/debuginfo/pretty-std/lldb_input/windows_msvc.json | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json index 4b4ffabdc2c95..39ccd60fecfa4 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json @@ -91,7 +91,7 @@ "some": { "type": "core::option::Option", "pretty_print": "Some(8)", - "synthetic": "lldb_lookup.synthetic_lookup", + "synthetic": "lldb_lookup.ClangEncodedEnumProvider", "summary": "lldb_lookup.ClangEncodedEnumSummaryProvider", "children": [ { @@ -104,13 +104,12 @@ "none": { "type": "core::option::Option", "pretty_print": "None", - "synthetic": "lldb_lookup.synthetic_lookup", + "synthetic": "lldb_lookup.ClangEncodedEnumProvider", "summary": "lldb_lookup.ClangEncodedEnumSummaryProvider" }, "os_string": { "type": "std::ffi::os_str::OsString", "pretty_print": "\"IAMA OS string \ud83d\ude03\"", - "synthetic": "lldb_lookup.synthetic_lookup", "summary": "lldb_lookup.StdOsStringSummaryProvider", "children": [ { diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json index 2a67d1cfb60b7..1b070b2ad5106 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json @@ -114,7 +114,6 @@ "os_string": { "type": "std::ffi::os_str::OsString", "pretty_print": "\"IAMA OS string \ud83d\ude03\"", - "synthetic": "lldb_lookup.synthetic_lookup", "summary": "lldb_lookup.StdOsStringSummaryProvider", "children": [ { From c35f47230ae423332c38811f2c1863dcc46b5c27 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Aug 2026 18:25:33 +0200 Subject: [PATCH 35/46] stop using check_shim_sig_lenient for main foreign_items --- src/tools/miri/src/shims/backtrace.rs | 14 +- src/tools/miri/src/shims/foreign_items.rs | 211 ++++++++--- src/tools/miri/src/shims/sig.rs | 40 ++- .../miri/src/shims/unix/foreign_items.rs | 328 +++++++++++++----- .../function_calls/check_arg_count_abort.rs | 2 +- .../check_arg_count_abort.stderr | 2 +- .../check_arg_count_too_few_args.rs | 2 +- .../check_arg_count_too_few_args.stderr | 2 +- .../check_arg_count_too_many_args.rs | 2 +- .../check_arg_count_too_many_args.stderr | 2 +- 10 files changed, 463 insertions(+), 142 deletions(-) diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 1ca814ee7afff..ce441f628d037 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -15,7 +15,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [flags] = + this.check_shim_sig(shim_sig!(extern "Rust" fn(u64) -> usize), link_name, abi, args)?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { @@ -37,7 +38,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; let ptr_layout = this.layout_of(ptr_ty)?; - let [flags, buf] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [flags, buf] = + this.check_shim_sig(shim_sig!(extern "Rust" fn(u64, *_) -> ()), link_name, abi, args)?; let flags = this.read_scalar(flags)?.to_u64()?; let buf_place = this.deref_pointer_as(buf, ptr_layout)?; @@ -191,8 +193,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [ptr, flags, name_ptr, filename_ptr] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, flags, name_ptr, filename_ptr] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*_, u64, *_, *_) -> ()), + link_name, + abi, + args, + )?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index f425da3f0d3a1..4dd4cf56a45b8 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use std::io::Write; use std::path::Path; -use rustc_abi::{Align, CanonAbi, ExternAbi, Size}; +use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE; use rustc_data_structures::either::Either; use rustc_hir::attrs::Linkage; @@ -317,13 +317,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => { // This is a no-op shim that only exists to prevent making the allocator shims // instantly stable. - let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig_nounwind!(extern "Rust" fn() -> ()), + link_name, + abi, + args, + )?; } // Miri-specific extern functions "miri_alloc" => { - let [size, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [size, align] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(usize, usize) -> *mut _), + link_name, + abi, + args, + )?; let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; @@ -339,8 +348,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr, dest)?; } "miri_dealloc" => { - let [ptr, old_size, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, old_size, align] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*mut _, usize, usize) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let old_size = this.read_target_usize(old_size)?; let align = this.read_target_usize(align)?; @@ -353,7 +366,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "miri_track_alloc" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*const _) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( @@ -368,17 +386,27 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "miri_start_unwind" => { - let [payload] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [payload] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*mut _) -> !), + link_name, + abi, + args, + )?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [] = + this.check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), link_name, abi, args)?; this.run_provenance_gc(); } "miri_get_alloc_id" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*const _) -> u64), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( @@ -388,8 +416,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } "miri_print_borrow_state" => { - let [id, show_unnamed] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [id, show_unnamed] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(u64, bool) -> ()), + link_name, + abi, + args, + )?; let id = this.read_scalar(id)?.to_u64()?; let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?; if let Some(id) = std::num::NonZero::new(id).map(AllocId) @@ -403,8 +435,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_pointer_name" => { // This associates a name to a tag. Very useful for debugging, and also makes // tests more strict. - let [ptr, nth_parent, name] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, nth_parent, name] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*const _, u8, &[u8]) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let nth_parent = this.read_scalar(nth_parent)?.to_u8()?; let name = this.read_immediate(name)?; @@ -417,7 +453,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.give_pointer_debug_name(ptr, nth_parent, &name)?; } "miri_static_root" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*const _) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; if offset != Size::ZERO { @@ -428,8 +469,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.static_roots.push(alloc_id); } "miri_host_to_target_path" => { - let [ptr, out, out_size] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, out, out_size] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*_, *_, usize) -> usize), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let out = this.read_pointer(out)?; let out_size = this.read_scalar(out_size)?.to_target_usize(this)?; @@ -445,9 +490,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(if success { 0 } else { needed_size }, dest)?; } "miri_thread_spawn" => { - // FIXME: `check_shim_sig` does not work with function pointers. - let [start_routine, func_arg] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [start_routine, func_arg] = this.check_shim_sig( + // FIXME: The first argument is actually a function pointer. + shim_sig!(extern "Rust" fn(fn(..) -> _, *_) -> usize), + link_name, + abi, + args, + )?; let start_routine = this.read_pointer(start_routine)?; let func_arg = this.read_immediate(func_arg)?; @@ -486,7 +535,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Hint that a loop is spinning indefinitely. "miri_spin_loop" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [] = + this.check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), link_name, abi, args)?; // Try to run another thread to maximize the chance of finding actual bugs. this.yield_active_thread(); @@ -509,10 +559,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_resolve_frame_names" => { this.handle_miri_resolve_frame_names(abi, link_name, args)?; } - // Writes some bytes to the interpreter's stdout/stderr. See the - // README for details. + // Writes some bytes to the interpreter's stdout/stderr. See the README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { - let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [msg] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(&[u8]) -> ()), + link_name, + abi, + args, + )?; let msg = this.read_immediate(msg)?; let msg = this.read_byte_slice(&msg)?; // Note: we're ignoring errors writing to host stdout/stderr. @@ -526,8 +580,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_promise_symbolic_alignment" => { use rustc_abi::AlignFromBytesError; - let [ptr, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, align] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(*const _, usize) -> ()), + link_name, + abi, + args, + )?; + let ptr = this.read_pointer(ptr)?; let align = this.read_target_usize(align)?; if !align.is_power_of_two() { @@ -567,8 +626,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // GenMC mode: Assume statements block the current thread when their condition is false. "miri_genmc_assume" => { - let [condition] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [condition] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(bool) -> ()), + link_name, + abi, + args, + )?; + if this.machine.data_race.as_genmc_ref().is_some() { this.handle_genmc_verifier_assume(condition)?; } else { @@ -579,7 +643,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Aborting the process. "exit" => { // FIXME: This does not have a direct test (#3179). - let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [code] = + this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?; let code = this.read_scalar(code)?.to_i32()?; if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { // If there is no error, execution should continue (on a different thread). @@ -594,7 +659,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "abort" => { // FIXME: This does not have a direct test (#3179). - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> ()), link_name, abi, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() )); @@ -602,7 +668,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Standard C allocation "malloc" => { - let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [size] = this.check_shim_sig( + shim_sig!(extern "C" fn(usize) -> *mut _), + link_name, + abi, + args, + )?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { let res = this.malloc(size, AllocInit::Uninit)?; @@ -616,8 +687,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "calloc" => { - let [items, elem_size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [items, elem_size] = this.check_shim_sig( + shim_sig!(extern "C" fn(usize, usize) -> *mut _), + link_name, + abi, + args, + )?; let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { @@ -632,13 +707,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "free" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; this.free(ptr)?; } "realloc" => { - let [old_ptr, new_size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [old_ptr, new_size] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), + link_name, + abi, + args, + )?; let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_target_usize(new_size)?; if new_size <= this.max_size_of_val().bytes() { @@ -688,8 +772,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // C memory handling functions "memcmp" => { - let [left, right, n] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [left, right, n] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _, usize) -> i32), + link_name, + abi, + args, + )?; let left = this.read_pointer(left)?; let right = this.read_pointer(right)?; let n = Size::from_bytes(this.read_target_usize(n)?); @@ -772,7 +860,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "strlen" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> usize), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_c_str(ptr)?.len(); @@ -782,7 +875,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "strnlen" => { - let [ptr, num] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr, num] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, usize) -> usize), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let num = this.read_target_usize(num)?; @@ -795,7 +893,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_target_usize(idx, this), dest)?; } "wcslen" => { - let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> usize), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_wchar_t_str(ptr)?.len(); @@ -805,8 +908,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "memcpy" => { - let [ptr_dest, ptr_src, n] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src, n] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, *const _, usize) -> *mut _), + link_name, + abi, + args, + )?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; let n = this.read_target_usize(n)?; @@ -820,8 +927,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } "strcpy" => { - let [ptr_dest, ptr_src] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, *const _) -> *mut _), + link_name, + abi, + args, + )?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; @@ -836,8 +947,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } "memset" => { - let [ptr_dest, val, n] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, val, n] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, i32, usize) -> *mut _), + link_name, + abi, + args, + )?; let ptr_dest = this.read_pointer(ptr_dest)?; let val = this.read_scalar(val)?.to_i32()?; let n = this.read_target_usize(n)?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index d40e9039f2b60..a781b3d2a3691 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -12,6 +12,7 @@ pub struct ShimSig<'tcx, const ARGS: usize> { pub abi: ExternAbi, pub args: [Ty<'tcx>; ARGS], pub ret: Ty<'tcx>, + pub nounwind: bool, } /// Construct a `ShimSig` with convenient syntax: @@ -32,6 +33,20 @@ macro_rules! shim_sig { abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), args: shim_sig_args_sep!(this, [$($args)*]), ret: shim_sig_arg!(this, $($ret)*), + nounwind: false, + } + }; +} + +/// Same as `shim_sig!` but promises that this function will not unwind, even if the ABI allows it. +#[macro_export] +macro_rules! shim_sig_nounwind { + (extern $abi:literal fn($($args:tt)*) -> $($ret:tt)*) => { + |this| $crate::shims::sig::ShimSig { + abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), + args: shim_sig_args_sep!(this, [$($args)*]), + ret: shim_sig_arg!(this, $($ret)*), + nounwind: true, } }; } @@ -121,6 +136,9 @@ macro_rules! shim_sig_arg { ($this:ident, ()) => { $this.tcx.types.unit }; + ($this:ident, !) => { + $this.tcx.types.never + }; ($this:ident, bool) => { $this.tcx.types.bool }; @@ -130,6 +148,22 @@ macro_rules! shim_sig_arg { ($this:ident, *mut _) => { $this.machine.layouts.mut_raw_ptr.ty }; + ($this:ident, *_) => { + // Mutability does not matter for ABI. + $this.machine.layouts.mut_raw_ptr.ty + }; + ($this:ident, fn(..) -> _) => { + // We currently treat fn ptrs as ABI-compatible with data ptrs so we can just use a raw ptr. + $this.machine.layouts.const_raw_ptr.ty + }; + ($this:ident, &[$($ty:tt)*]) => { + rustc_middle::ty::Ty::new_ref( + *$this.tcx, + $this.tcx.lifetimes.re_erased, + rustc_middle::ty::Ty::new_slice(*$this.tcx, shim_sig_arg!($this, $($ty)*)), + rustc_middle::mir::Mutability::Not, + ) + }; ($this:ident, winapi::$ty:ident) => { $this.windows_ty_layout(stringify!($ty)).ty }; @@ -145,6 +179,7 @@ macro_rules! shim_sig_arg { fn check_shim_abi<'tcx>( this: &MiriInterpCx<'tcx>, callee_abi: &FnAbi<'tcx, Ty<'tcx>>, + callee_nounwind: bool, caller_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> InterpResult<'tcx> { if callee_abi.conv != caller_abi.conv { @@ -154,7 +189,8 @@ fn check_shim_abi<'tcx>( caller = caller_abi.conv, ); } - if callee_abi.can_unwind && !caller_abi.can_unwind { + // FIXME: is this needed? Or is it enough to just check this if/when an actual unwind happens? + if callee_abi.can_unwind && !callee_nounwind && !caller_abi.can_unwind { throw_ub_format!( "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", ); @@ -280,7 +316,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; // Check everything. - check_shim_abi(this, callee_fn_abi, caller_fn_abi)?; + check_shim_abi(this, callee_fn_abi, shim_sig.nounwind, caller_fn_abi)?; this.check_shim_symbol_clash(link_name)?; // Return arguments. diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 9c4c44fece5a6..e2e8a633e5e00 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -426,17 +426,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "fstat" => { - let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _) -> i32), + link_name, + abi, + args, + )?; let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } "lstat" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> i32), + link_name, + abi, + args, + )?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } "stat" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> i32), + link_name, + abi, + args, + )?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } @@ -514,7 +529,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "readdir" => { - let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> *mut _), + link_name, + abi, + args, + )?; this.readdir(dirp, dest)?; } "lseek" => { @@ -873,8 +893,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "posix_memalign" => { - let [memptr, align, size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [memptr, align, size] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, usize) -> i32), + link_name, + abi, + args, + )?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } @@ -925,8 +949,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; - let [ptr, nmemb, size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr, nmemb, size] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, usize) -> *mut _), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let nmemb = this.read_target_usize(nmemb)?; let size = this.read_target_usize(size)?; @@ -949,16 +977,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "aligned_alloc" => { // This is a C11 function, we assume all Unixes have it. // (MSVC explicitly does not support this.) - let [align, size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [align, size] = this.check_shim_sig( + shim_sig!(extern "C" fn(usize, usize) -> *mut _), + link_name, + abi, + args, + )?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } // Dynamic symbol loading "dlsym" => { - let [handle, symbol] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [handle, symbol] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, *const _) -> *mut _), + link_name, + abi, + args, + )?; this.read_target_usize(handle)?; let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; @@ -977,7 +1013,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "pthread_key_create" => { - let [key, dtor] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [key, dtor] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, fn(..) -> _) -> i32), + link_name, + abi, + args, + )?; let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?; let dtor = this.read_pointer(dtor)?; @@ -1009,7 +1050,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_key_delete" => { // FIXME: This does not have a direct test (#3179). - let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pthread_key_t) -> i32), + link_name, + abi, + args, + )?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) @@ -1017,16 +1063,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_getspecific" => { // FIXME: This does not have a direct test (#3179). - let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pthread_key_t) -> *mut _), + link_name, + abi, + args, + )?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - // FIXME: This does not have a direct test (#3179). - let [key, new_ptr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [key, new_ptr] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pthread_key_t, *_) -> i32), + link_name, + abi, + args, + )?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -1038,161 +1092,229 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "pthread_mutexattr_init" => { - let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_mutexattr_init(attr)?; this.write_null(dest)?; } "pthread_mutexattr_settype" => { - let [attr, kind] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr, kind] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, i32) -> i32), + link_name, + abi, + args, + )?; let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(result, dest)?; } "pthread_mutexattr_destroy" => { - let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_mutexattr_destroy(attr)?; this.write_null(dest)?; } "pthread_mutex_init" => { - let [mutex, attr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [mutex, attr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_mutex_init(mutex, attr)?; this.write_null(dest)?; } "pthread_mutex_lock" => { - let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [mutex] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_mutex_lock(mutex, dest)?; } "pthread_mutex_trylock" => { - let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [mutex] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_unlock" => { - let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [mutex] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_destroy" => { - let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [mutex] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_mutex_destroy(mutex)?; this.write_int(0, dest)?; } "pthread_rwlock_rdlock" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_rwlock_rdlock(rwlock, dest)?; } "pthread_rwlock_tryrdlock" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_wrlock" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_rwlock_wrlock(rwlock, dest)?; } "pthread_rwlock_trywrlock" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_unlock" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_rwlock_unlock(rwlock)?; this.write_null(dest)?; } "pthread_rwlock_destroy" => { - let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_rwlock_destroy(rwlock)?; this.write_null(dest)?; } "pthread_condattr_init" => { - let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_condattr_init(attr)?; this.write_null(dest)?; } "pthread_condattr_setclock" => { - let [attr, clock_id] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, i32) -> i32), + link_name, + abi, + args, + )?; let result = this.pthread_condattr_setclock(attr, clock_id)?; this.write_scalar(result, dest)?; } "pthread_condattr_getclock" => { - let [attr, clock_id] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_condattr_getclock(attr, clock_id)?; this.write_null(dest)?; } "pthread_condattr_destroy" => { - let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [attr] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_condattr_destroy(attr)?; this.write_null(dest)?; } "pthread_cond_init" => { - let [cond, attr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond, attr] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_cond_init(cond, attr)?; this.write_null(dest)?; } "pthread_cond_signal" => { - let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_cond_signal(cond)?; this.write_null(dest)?; } "pthread_cond_broadcast" => { - let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_cond_broadcast(cond)?; this.write_null(dest)?; } "pthread_cond_wait" => { - let [cond, mutex] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_cond_wait(cond, mutex, dest)?; } "pthread_cond_timedwait" => { - let [cond, mutex, abstime] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex, abstime] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_cond_timedwait( cond, mutex, abstime, dest, /* macos_relative_np */ false, )?; } "pthread_cond_destroy" => { - let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [cond] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; this.pthread_cond_destroy(cond)?; this.write_null(dest)?; } // Threading "pthread_create" => { - let [thread, attr, start, arg] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [thread, attr, start, arg] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_, fn(..) -> _, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_create(thread, attr, start, arg)?; this.write_null(dest)?; } "pthread_join" => { - let [thread, retval] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [thread, retval] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pthread_t, *_) -> i32), + link_name, + abi, + args, + )?; this.pthread_join(thread, retval, dest)?; } "pthread_detach" => { - let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pthread_t) -> i32), + link_name, + abi, + args, + )?; let res = this.pthread_detach(thread)?; this.write_scalar(res, dest)?; } "pthread_self" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "C" fn() -> libc::pthread_t), + link_name, + abi, + args, + )?; let res = this.pthread_self()?; this.write_scalar(res, dest)?; } "sched_yield" => { // FIXME: This does not have a direct test (#3179). - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; this.sched_yield()?; this.write_null(dest)?; } "nanosleep" => { - let [duration, rem] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [duration, rem] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, *_) -> i32), + link_name, + abi, + args, + )?; let result = this.nanosleep(duration, rem)?; this.write_scalar(result, dest)?; } @@ -1203,8 +1325,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; - let [clock_id, flags, req, rem] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [clock_id, flags, req, rem] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::clockid_t, i32, *_, *_) -> i32), + link_name, + abi, + args, + )?; let result = this.clock_nanosleep(clock_id, flags, req, rem)?; this.write_scalar(result, dest)?; } @@ -1212,8 +1338,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; - let [pid, cpusetsize, mask] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pid_t, usize, *_) -> i32), + link_name, + abi, + args, + )?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1265,8 +1395,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; - let [pid, cpusetsize, mask] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::pid_t, usize, *_) -> i32), + link_name, + abi, + args, + )?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1322,20 +1456,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "isatty" => { - let [fd] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), + link_name, + abi, + args, + )?; let result = this.isatty(fd)?; this.write_scalar(result, dest)?; } "pthread_atfork" => { // FIXME: This does not have a direct test (#3179). - let [prepare, parent, child] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [prepare, parent, child] = this.check_shim_sig( + shim_sig!(extern "C" fn(fn(..) -> _, fn(..) -> _, fn(..) -> _) -> i32), + link_name, + abi, + args, + )?; this.read_pointer(prepare)?; this.read_pointer(parent)?; this.read_pointer(child)?; // We do not support forking, so there is nothing to do here. this.write_null(dest)?; } + "strerror_r" => { + let [errnum, buf, buflen] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, usize) -> i32), + link_name, + abi, + args, + )?; + let result = this.strerror_r(errnum, buf, buflen)?; + this.write_scalar(result, dest)?; + } "getentropy" => { // This function is non-standard but exists with the same signature and behavior on // Linux, macOS, FreeBSD and Solaris/Illumos. @@ -1344,8 +1497,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; - let [buf, bufsize] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [buf, bufsize] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, usize) -> i32), + link_name, + abi, + args, + )?; let buf = this.read_pointer(buf)?; let bufsize = this.read_target_usize(bufsize)?; @@ -1361,14 +1518,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } } - - "strerror_r" => { - let [errnum, buf, buflen] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let result = this.strerror_r(errnum, buf, buflen)?; - this.write_scalar(result, dest)?; - } - "getrandom" => { // This function is non-standard but exists with the same signature and behavior on // Linux, FreeBSD and Solaris/Illumos. @@ -1377,8 +1526,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; - let [ptr, len, flags] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr, len, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, usize, u32) -> isize), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; let _flags = this.read_scalar(flags)?.to_i32()?; @@ -1391,7 +1544,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // same behavior (eg never fails) on FreeBSD and Solaris/Illumos. this.check_target_os(&[Os::FreeBsd, Os::Illumos, Os::Solaris], link_name)?; - let [ptr, len] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [ptr, len] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_, usize) -> ()), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -1416,12 +1574,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // This function looks and behaves exactly like miri_start_unwind. - let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [payload] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> u32), link_name, abi, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" | "geteuid" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "C" fn() -> libc::uid_t), + link_name, + abi, + args, + )?; // For now, just pretend we always have this fixed UID. this.write_int(UID, dest)?; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs index db7bd223bd45a..54d49e059bf6d 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs @@ -5,6 +5,6 @@ fn main() { unsafe { abort(1); - //~^ ERROR: Undefined Behavior: incorrect number of arguments for `abort`: got 1, expected 0 + //~^ ERROR: expected 0 arguments, found 1 arguments } } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr index 5b4703ca16605..75efe0af99238 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: incorrect number of arguments for `abort`: got 1, expected 0 +error: Undefined Behavior: ABI mismatch: expected 0 arguments, found 1 arguments --> tests/fail/function_calls/check_arg_count_abort.rs:LL:CC | LL | abort(1); diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs index ecdda9e509d4e..6e51b3ed89036 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs @@ -6,6 +6,6 @@ fn main() { } unsafe { - let _ = malloc(); //~ ERROR: Undefined Behavior: incorrect number of arguments for `malloc`: got 0, expected 1 + let _ = malloc(); //~ ERROR: expected 1 arguments, found 0 arguments }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr index 5f81145d26afd..bd51eee6ee013 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: incorrect number of arguments for `malloc`: got 0, expected 1 +error: Undefined Behavior: ABI mismatch: expected 1 arguments, found 0 arguments --> tests/fail/function_calls/check_arg_count_too_few_args.rs:LL:CC | LL | let _ = malloc(); diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs index 1d3fec0fe32f8..537538c1c4b04 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs @@ -6,6 +6,6 @@ fn main() { } unsafe { - let _ = malloc(1, 2); //~ ERROR: Undefined Behavior: incorrect number of arguments for `malloc`: got 2, expected 1 + let _ = malloc(1, 2); //~ ERROR: expected 1 arguments, found 2 arguments }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr index 3ed4aaacb8c40..87d7bf7083d48 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: incorrect number of arguments for `malloc`: got 2, expected 1 +error: Undefined Behavior: ABI mismatch: expected 1 arguments, found 2 arguments --> tests/fail/function_calls/check_arg_count_too_many_args.rs:LL:CC | LL | let _ = malloc(1, 2); From b4f7e4ede89961b785df877b04f4c4d946ca0be2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Aug 2026 10:50:51 +0200 Subject: [PATCH 36/46] we don't need the pointer's mutability for the ABI check --- src/tools/miri/src/shims/foreign_items.rs | 50 ++--- src/tools/miri/src/shims/sig.rs | 12 +- .../src/shims/unix/android/foreign_items.rs | 4 +- .../miri/src/shims/unix/foreign_items.rs | 200 +++++++----------- .../src/shims/unix/freebsd/foreign_items.rs | 2 +- .../src/shims/unix/linux/foreign_items.rs | 4 +- .../src/shims/unix/netbsd/foreign_items.rs | 16 +- .../src/shims/unix/solarish/foreign_items.rs | 8 +- .../miri/src/shims/windows/foreign_items.rs | 130 ++++++------ 9 files changed, 182 insertions(+), 244 deletions(-) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 4dd4cf56a45b8..df87fb5322982 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -328,7 +328,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miri-specific extern functions "miri_alloc" => { let [size, align] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(usize, usize) -> *mut _), + shim_sig!(extern "Rust" fn(usize, usize) -> *_), link_name, abi, args, @@ -349,7 +349,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_dealloc" => { let [ptr, old_size, align] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*mut _, usize, usize) -> ()), + shim_sig!(extern "Rust" fn(*_, usize, usize) -> ()), link_name, abi, args, @@ -367,7 +367,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_track_alloc" => { let [ptr] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*const _) -> ()), + shim_sig!(extern "Rust" fn(*_) -> ()), link_name, abi, args, @@ -387,7 +387,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_start_unwind" => { let [payload] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*mut _) -> !), + shim_sig!(extern "Rust" fn(*_) -> !), link_name, abi, args, @@ -402,7 +402,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_get_alloc_id" => { let [ptr] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*const _) -> u64), + shim_sig!(extern "Rust" fn(*_) -> u64), link_name, abi, args, @@ -436,7 +436,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This associates a name to a tag. Very useful for debugging, and also makes // tests more strict. let [ptr, nth_parent, name] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*const _, u8, &[u8]) -> ()), + shim_sig!(extern "Rust" fn(*_, u8, &[u8]) -> ()), link_name, abi, args, @@ -454,7 +454,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_static_root" => { let [ptr] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*const _) -> ()), + shim_sig!(extern "Rust" fn(*_) -> ()), link_name, abi, args, @@ -581,7 +581,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { use rustc_abi::AlignFromBytesError; let [ptr, align] = this.check_shim_sig( - shim_sig!(extern "Rust" fn(*const _, usize) -> ()), + shim_sig!(extern "Rust" fn(*_, usize) -> ()), link_name, abi, args, @@ -669,7 +669,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Standard C allocation "malloc" => { let [size] = this.check_shim_sig( - shim_sig!(extern "C" fn(usize) -> *mut _), + shim_sig!(extern "C" fn(usize) -> *_), link_name, abi, args, @@ -688,7 +688,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "calloc" => { let [items, elem_size] = this.check_shim_sig( - shim_sig!(extern "C" fn(usize, usize) -> *mut _), + shim_sig!(extern "C" fn(usize, usize) -> *_), link_name, abi, args, @@ -707,18 +707,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "free" => { - let [ptr] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> ()), - link_name, - abi, - args, - )?; + let [ptr] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), link_name, abi, args)?; let ptr = this.read_pointer(ptr)?; this.free(ptr)?; } "realloc" => { let [old_ptr, new_size] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), + shim_sig!(extern "C" fn(*_, usize) -> *_), link_name, abi, args, @@ -740,7 +736,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; let [ptr] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> usize), + shim_sig!(extern "C" fn(*_) -> usize), link_name, abi, args, @@ -773,7 +769,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // C memory handling functions "memcmp" => { let [left, right, n] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _, usize) -> i32), + shim_sig!(extern "C" fn(*_, *_, usize) -> i32), link_name, abi, args, @@ -806,7 +802,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "memchr" => { let [ptr, val, num] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, i32, usize) -> *const _), + shim_sig!(extern "C" fn(*_, i32, usize) -> *_), link_name, abi, args, @@ -835,7 +831,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?; let [ptr, val, num] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, i32, usize) -> *const _), + shim_sig!(extern "C" fn(*_, i32, usize) -> *_), link_name, abi, args, @@ -861,7 +857,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strlen" => { let [ptr] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> usize), + shim_sig!(extern "C" fn(*_) -> usize), link_name, abi, args, @@ -876,7 +872,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strnlen" => { let [ptr, num] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, usize) -> usize), + shim_sig!(extern "C" fn(*_, usize) -> usize), link_name, abi, args, @@ -894,7 +890,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "wcslen" => { let [ptr] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> usize), + shim_sig!(extern "C" fn(*_) -> usize), link_name, abi, args, @@ -909,7 +905,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "memcpy" => { let [ptr_dest, ptr_src, n] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, *const _, usize) -> *mut _), + shim_sig!(extern "C" fn(*_, *_, usize) -> *_), link_name, abi, args, @@ -928,7 +924,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strcpy" => { let [ptr_dest, ptr_src] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, *const _) -> *mut _), + shim_sig!(extern "C" fn(*_, *_) -> *_), link_name, abi, args, @@ -948,7 +944,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "memset" => { let [ptr_dest, val, n] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, i32, usize) -> *mut _), + shim_sig!(extern "C" fn(*_, i32, usize) -> *_), link_name, abi, args, diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index a781b3d2a3691..e2e5cbfced8e6 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -23,7 +23,7 @@ pub struct ShimSig<'tcx, const ARGS: usize> { /// The following types are supported: /// - primitive integer types /// - `()` -/// - (thin) raw pointers, written `*const _` and `*mut _` since the pointee type is irrelevant +/// - (thin) raw pointers, written `*_` since the mutability and pointee type are irrelevant /// - `$crate::$mod::...::$ty` for a type from the given crate (most commonly that is `libc`) /// - `winapi::$ty` for a type from `std::sys::pal::windows::c` #[macro_export] @@ -58,9 +58,9 @@ macro_rules! shim_sig_nounwind { /// # Examples /// /// ```ignore -/// shim_sig_args_sep!(this, [*const _, i32, libc::off64_t]); +/// shim_sig_args_sep!(this, [*_, i32, libc::off64_t]); /// // expands to: -/// [shim_sig_arg!(*const _), shim_sig_arg!(i32), shim_sig_arg!(libc::off64_t)]; +/// [shim_sig_arg!(*_), shim_sig_arg!(i32), shim_sig_arg!(libc::off64_t)]; /// ``` #[macro_export] macro_rules! shim_sig_args_sep { @@ -142,12 +142,6 @@ macro_rules! shim_sig_arg { ($this:ident, bool) => { $this.tcx.types.bool }; - ($this:ident, *const _) => { - $this.machine.layouts.const_raw_ptr.ty - }; - ($this:ident, *mut _) => { - $this.machine.layouts.mut_raw_ptr.ty - }; ($this:ident, *_) => { // Mutability does not matter for ABI. $this.machine.layouts.mut_raw_ptr.ty diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 999750a9e00a9..c86bfeb2c8ac8 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -30,7 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pread64" => { // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize), link_name, abi, args, @@ -44,7 +44,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pwrite64" => { // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize), link_name, abi, args, diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index e2e8a633e5e00..0920774a4e5fd 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -131,28 +131,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment related shims "getenv" => { - let [name] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> *mut _), - link_name, - abi, - args, - )?; + let [name] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), link_name, abi, args)?; let result = this.getenv(name)?; this.write_pointer(result, dest)?; } "unsetenv" => { - let [name] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> i32), - link_name, - abi, - args, - )?; + let [name] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.unsetenv(name)?; this.write_scalar(result, dest)?; } "setenv" => { let [name, value, overwrite] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _, i32) -> i32), + shim_sig!(extern "C" fn(*_, *_, i32) -> i32), link_name, abi, args, @@ -164,7 +156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getcwd" => { // FIXME: This does not have a direct test (#3179). let [buf, size] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), + shim_sig!(extern "C" fn(*_, usize) -> *_), link_name, abi, args, @@ -174,7 +166,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "gethostname" => { let [name, len] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize) -> i32), + shim_sig!(extern "C" fn(*_, usize) -> i32), link_name, abi, args, @@ -184,12 +176,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "chdir" => { // FIXME: This does not have a direct test (#3179). - let [path] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> i32), - link_name, - abi, - args, - )?; + let [path] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.chdir(path)?; this.write_scalar(result, dest)?; } @@ -210,12 +198,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; - let [uname] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> i32), - link_name, - abi, - args, - )?; + let [uname] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.uname(uname, None)?; this.write_scalar(result, dest)?; } @@ -232,7 +216,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File descriptors "read" => { let [fd, buf, count] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, usize) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize) -> isize), link_name, abi, args, @@ -244,7 +228,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "write" => { let [fd, buf, n] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, usize) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize) -> isize), link_name, abi, args, @@ -257,7 +241,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "readv" => { let [fd, iov, iovcnt] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + shim_sig!(extern "C" fn(i32, *_, i32) -> isize), link_name, abi, args, @@ -266,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "writev" => { let [fd, iov, iovcnt] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + shim_sig!(extern "C" fn(i32, *_, i32) -> isize), link_name, abi, args, @@ -275,7 +259,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pread" => { let [fd, buf, count, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off_t) -> isize), link_name, abi, args, @@ -288,7 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pwrite" => { let [fd, buf, n, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off_t) -> isize), link_name, abi, args, @@ -302,7 +286,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "preadv" => { let [fd, iov, iovcnt, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, i32, libc::off_t) -> isize), link_name, abi, args, @@ -311,7 +295,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pwritev" => { let [fd, iov, iovcnt, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, i32, libc::off_t) -> isize), link_name, abi, args, @@ -395,19 +379,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "unlink" => { // FIXME: This does not have a direct test (#3179). - let [path] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> i32), - link_name, - abi, - args, - )?; + let [path] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.unlink(path)?; this.write_scalar(result, dest)?; } "symlink" => { // FIXME: This does not have a direct test (#3179). let [target, linkpath] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _) -> i32), + shim_sig!(extern "C" fn(*_, *_) -> i32), link_name, abi, args, @@ -417,7 +397,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "linkat" => { let [oldfd, oldpath, newfd, newpath, flags] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, i32, *const _, i32) -> i32), + shim_sig!(extern "C" fn(i32, *_, i32, *_, i32) -> i32), link_name, abi, args, @@ -427,7 +407,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "fstat" => { let [fd, buf] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, *_) -> i32), link_name, abi, args, @@ -437,7 +417,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "lstat" => { let [path, buf] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *mut _) -> i32), + shim_sig!(extern "C" fn(*_, *_) -> i32), link_name, abi, args, @@ -447,7 +427,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "stat" => { let [path, buf] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *mut _) -> i32), + shim_sig!(extern "C" fn(*_, *_) -> i32), link_name, abi, args, @@ -457,7 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "chmod" => { let [path, mode] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32), + shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32), link_name, abi, args, @@ -478,7 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "rename" => { // FIXME: This does not have a direct test (#3179). let [oldpath, newpath] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _) -> i32), + shim_sig!(extern "C" fn(*_, *_) -> i32), link_name, abi, args, @@ -489,7 +469,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "mkdir" => { // FIXME: This does not have a direct test (#3179). let [path, mode] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32), + shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32), link_name, abi, args, @@ -499,42 +479,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "rmdir" => { // FIXME: This does not have a direct test (#3179). - let [path] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> i32), - link_name, - abi, - args, - )?; + let [path] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.rmdir(path)?; this.write_scalar(result, dest)?; } "opendir" => { - let [name] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> *mut _), - link_name, - abi, - args, - )?; + let [name] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), link_name, abi, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "closedir" => { - let [dirp] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> i32), - link_name, - abi, - args, - )?; + let [dirp] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.closedir(dirp)?; this.write_scalar(result, dest)?; } "readdir" => { - let [dirp] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> *mut _), - link_name, - abi, - args, - )?; + let [dirp] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), link_name, abi, args)?; this.readdir(dirp, dest)?; } "lseek" => { @@ -586,7 +550,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "futimens" => { let [fd, times] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _) -> i32), + shim_sig!(extern "C" fn(i32, *_) -> i32), link_name, abi, args, @@ -596,7 +560,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "readlink" => { let [pathname, buf, bufsize] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *mut _, usize) -> isize), + shim_sig!(extern "C" fn(*_, *_, usize) -> isize), link_name, abi, args, @@ -645,7 +609,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "realpath" => { let [path, resolved_path] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), + shim_sig!(extern "C" fn(*_, *_) -> *_), link_name, abi, args, @@ -654,12 +618,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "mkstemp" => { - let [template] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> i32), - link_name, - abi, - args, - )?; + let [template] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.mkstemp(template)?; this.write_scalar(result, dest)?; } @@ -667,7 +627,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Poll "poll" => { let [fds, nfds, timeout] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, libc::nfds_t, i32) -> i32), + shim_sig!(extern "C" fn(*_, libc::nfds_t, i32) -> i32), link_name, abi, args, @@ -678,7 +638,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Sockets and pipes "socketpair" => { let [domain, type_, protocol, sv] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, i32, i32, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, i32, i32, *_) -> i32), link_name, abi, args, @@ -687,12 +647,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "pipe" => { - let [pipefd] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> i32), - link_name, - abi, - args, - )?; + let [pipefd] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.pipe2(pipefd, /*flags*/ None)?; this.write_scalar(result, dest)?; } @@ -704,7 +660,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; let [pipefd, flags] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, i32) -> i32), + shim_sig!(extern "C" fn(*_, i32) -> i32), link_name, abi, args, @@ -726,7 +682,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "bind" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, libc::socklen_t) -> i32), + shim_sig!(extern "C" fn(i32, *_, libc::socklen_t) -> i32), link_name, abi, args, @@ -746,7 +702,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "accept" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, *_, *_) -> i32), link_name, abi, args, @@ -755,7 +711,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "accept4" => { let [socket, address, address_len, flags] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, *mut _, i32) -> i32), + shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32), link_name, abi, args, @@ -764,7 +720,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "connect" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, libc::socklen_t) -> i32), + shim_sig!(extern "C" fn(i32, *_, libc::socklen_t) -> i32), link_name, abi, args, @@ -773,7 +729,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "send" => { let [socket, buffer, length, flags] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, libc::size_t, i32) -> libc::ssize_t), + shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t), link_name, abi, args, @@ -782,7 +738,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "recv" => { let [socket, buffer, length, flags] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, libc::size_t, i32) -> libc::ssize_t), + shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t), link_name, abi, args, @@ -791,7 +747,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "setsockopt" => { let [socket, level, option_name, option_value, option_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, i32, i32, *const _, libc::socklen_t) -> i32), + shim_sig!(extern "C" fn(i32, i32, i32, *_, libc::socklen_t) -> i32), link_name, abi, args, @@ -802,7 +758,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "getsockopt" => { let [socket, level, option_name, option_value, option_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, i32, i32, *mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, i32, i32, *_, *_) -> i32), link_name, abi, args, @@ -813,7 +769,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "getsockname" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, *_, *_) -> i32), link_name, abi, args, @@ -823,7 +779,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "getpeername" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, *_, *_) -> i32), link_name, abi, args, @@ -842,7 +798,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "getaddrinfo" => { let [node, service, hints, res] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _, *const _, *mut _) -> i32), + shim_sig!(extern "C" fn(*_, *_, *_, *_) -> i32), link_name, abi, args, @@ -851,19 +807,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "freeaddrinfo" => { - let [res] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> ()), - link_name, - abi, - args, - )?; + let [res] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), link_name, abi, args)?; this.freeaddrinfo(res)?; } // Time "gettimeofday" => { let [tv, tz] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(*_, *_) -> i32), link_name, abi, args, @@ -873,7 +825,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "localtime_r" => { let [timep, result_op] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), + shim_sig!(extern "C" fn(*_, *_) -> *_), link_name, abi, args, @@ -883,7 +835,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "clock_gettime" => { let [clk_id, tp] = this.check_shim_sig( - shim_sig!(extern "C" fn(libc::clockid_t, *mut _) -> i32), + shim_sig!(extern "C" fn(libc::clockid_t, *_) -> i32), link_name, abi, args, @@ -894,7 +846,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "posix_memalign" => { let [memptr, align, size] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize, usize) -> i32), + shim_sig!(extern "C" fn(*_, usize, usize) -> i32), link_name, abi, args, @@ -905,7 +857,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "mmap" => { let [addr, length, prot, flags, fd, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _), + shim_sig!(extern "C" fn(*_, usize, i32, i32, i32, libc::off_t) -> *_), link_name, abi, args, @@ -916,7 +868,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "munmap" => { let [addr, length] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize) -> i32), + shim_sig!(extern "C" fn(*_, usize) -> i32), link_name, abi, args, @@ -926,7 +878,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mprotect" => { let [addr, length, prot] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + shim_sig!(extern "C" fn(*_, usize, i32) -> i32), link_name, abi, args, @@ -936,7 +888,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "madvise" => { let [addr, length, advice] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + shim_sig!(extern "C" fn(*_, usize, i32) -> i32), link_name, abi, args, @@ -950,7 +902,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?; let [ptr, nmemb, size] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, usize, usize) -> *mut _), + shim_sig!(extern "C" fn(*_, usize, usize) -> *_), link_name, abi, args, @@ -978,7 +930,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This is a C11 function, we assume all Unixes have it. // (MSVC explicitly does not support this.) let [align, size] = this.check_shim_sig( - shim_sig!(extern "C" fn(usize, usize) -> *mut _), + shim_sig!(extern "C" fn(usize, usize) -> *_), link_name, abi, args, @@ -990,7 +942,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Dynamic symbol loading "dlsym" => { let [handle, symbol] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _, *const _) -> *mut _), + shim_sig!(extern "C" fn(*_, *_) -> *_), link_name, abi, args, @@ -1064,7 +1016,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_getspecific" => { // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( - shim_sig!(extern "C" fn(libc::pthread_key_t) -> *mut _), + shim_sig!(extern "C" fn(libc::pthread_key_t) -> *_), link_name, abi, args, @@ -1574,8 +1526,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // This function looks and behaves exactly like miri_start_unwind. - let [payload] = - this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> u32), link_name, abi, args)?; + let [payload] = this.check_shim_sig( + shim_sig!(extern "C" fn(*_) -> unwind::_Unwind_Reason_Code), + link_name, + abi, + args, + )?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 1cc87050e59d8..77f68bf5a9e00 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -174,7 +174,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/sys/sys/utsname.h#L64 // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/lib/libc/gen/uname.c#L44 let [size, uname] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, *_) -> i32), link_name, abi, args, diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index b905bdcd68974..50c2e36bb1b6f 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -48,7 +48,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pread64" => { // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize), link_name, abi, args, @@ -62,7 +62,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pwrite64" => { // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), + shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize), link_name, abi, args, diff --git a/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs b/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs index 5c7c2300745f2..c265cef273a57 100644 --- a/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/netbsd/foreign_items.rs @@ -22,24 +22,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment "__unsetenv13" => { - let [name] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _) -> i32), - link_name, - abi, - args, - )?; + let [name] = + this.check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), link_name, abi, args)?; let result = this.unsetenv(name)?; this.write_scalar(result, dest)?; } // Miscellaneous "__errno" => { - let [] = this.check_shim_sig( - shim_sig!(extern "C" fn() -> *mut _), - link_name, - abi, - args, - )?; + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> *_), link_name, abi, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index 37b665ceebd1f..aa600c81aa845 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__xnet_bind" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, libc::socklen_t) -> i32), + shim_sig!(extern "C" fn(i32, *_, libc::socklen_t) -> i32), link_name, abi, args, @@ -134,7 +134,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__xnet_connect" => { let [socket, address, address_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, *const _, libc::socklen_t) -> i32), + shim_sig!(extern "C" fn(i32, *_, libc::socklen_t) -> i32), link_name, abi, args, @@ -143,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__xnet_getaddrinfo" => { let [node, service, hints, res] = this.check_shim_sig( - shim_sig!(extern "C" fn(*const _, *const _, *const _, *mut _) -> i32), + shim_sig!(extern "C" fn(*_, *_, *_, *_) -> i32), link_name, abi, args, @@ -153,7 +153,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__xnet_getsockopt" => { let [socket, level, option_name, option_value, option_len] = this.check_shim_sig( - shim_sig!(extern "C" fn(i32, i32, i32, *mut _, *mut _) -> i32), + shim_sig!(extern "C" fn(i32, i32, i32, *_, *_) -> i32), link_name, abi, args, diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index efef55f5cf91b..d435bac532ed3 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -150,7 +150,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetEnvironmentVariableW" => { // FIXME: This does not have a direct test (#3179). let [name, buf, size] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _, *mut _, u32) -> u32), + shim_sig!(extern "system" fn(*_, *_, u32) -> u32), link_name, abi, args, @@ -161,7 +161,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SetEnvironmentVariableW" => { // FIXME: This does not have a direct test (#3179). let [name, value] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _, *const _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, *_) -> winapi::BOOL), link_name, abi, args, @@ -172,7 +172,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetEnvironmentStringsW" => { // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig( - shim_sig!(extern "system" fn() -> *mut _), + shim_sig!(extern "system" fn() -> *_), link_name, abi, args, @@ -183,7 +183,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "FreeEnvironmentStringsW" => { // FIXME: This does not have a direct test (#3179). let [env_block] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, @@ -194,7 +194,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetCurrentDirectoryW" => { // FIXME: This does not have a direct test (#3179). let [size, buf] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32, *mut _) -> u32), + shim_sig!(extern "system" fn(u32, *_) -> u32), link_name, abi, args, @@ -205,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SetCurrentDirectoryW" => { // FIXME: This does not have a direct test (#3179). let [path] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, @@ -216,7 +216,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetUserProfileDirectoryW" => { // FIXME: This does not have a direct test (#3179). let [token, buf, size] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *mut _, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, *_, *_) -> winapi::BOOL), link_name, abi, args, @@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetTempPathW" => { // FIXME: This does not have a direct test (#3179). let [bufferlength, buffer] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32, *mut _) -> u32), + shim_sig!(extern "system" fn(u32, *_) -> u32), link_name, abi, args, @@ -264,13 +264,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { extern "system" fn( winapi::HANDLE, winapi::HANDLE, - *mut _, - *mut _, - *mut _, - *mut _, + *_, + *_, + *_, + *_, u32, - *mut _, - *mut _, + *_, + *_, ) -> i32 ), link_name, @@ -306,13 +306,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { extern "system" fn( winapi::HANDLE, winapi::HANDLE, - *mut _, - *mut _, - *mut _, - *mut _, + *_, + *_, + *_, + *_, u32, - *mut _, - *mut _, + *_, + *_, ) -> i32 ), link_name, @@ -335,7 +335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetFullPathNameW" => { // FIXME: This does not have a direct test (#3179). let [filename, size, buffer, filepart] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _, u32, *mut _, *mut _) -> u32), + shim_sig!(extern "system" fn(*_, u32, *_, *_) -> u32), link_name, abi, args, @@ -379,10 +379,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ] = this.check_shim_sig( shim_sig!( extern "system" fn( - *const _, + *_, u32, u32, - *mut _, + *_, u32, u32, winapi::HANDLE, @@ -405,7 +405,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetFileInformationByHandle" => { let [handle, info] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> winapi::BOOL), link_name, abi, args, @@ -419,7 +419,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { extern "system" fn( winapi::HANDLE, winapi::FILE_INFO_BY_HANDLE_CLASS, - *mut _, + *_, u32, ) -> winapi::BOOL ), @@ -442,7 +442,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "DeleteFileW" => { let [file_name] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, @@ -453,7 +453,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SetFilePointerEx" => { let [file, distance_to_move, new_file_pointer, move_method] = this.check_shim_sig( // i64 is actually a LARGE_INTEGER union of {u32, i32} and {i64} - shim_sig!(extern "system" fn(winapi::HANDLE, i64, *mut _, u32) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, i64, *_, u32) -> winapi::BOOL), link_name, abi, args, @@ -464,7 +464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "MoveFileExW" => { let [existing_name, new_name, flags] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _, *const _, u32) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, *_, u32) -> winapi::BOOL), link_name, abi, args, @@ -477,7 +477,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "HeapAlloc" => { // FIXME: This does not have a direct test (#3179). let [handle, flags, size] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, u32, usize) -> *mut _), + shim_sig!(extern "system" fn(winapi::HANDLE, u32, usize) -> *_), link_name, abi, args, @@ -505,7 +505,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "HeapFree" => { // FIXME: This does not have a direct test (#3179). let [handle, flags, ptr] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, u32, *_) -> winapi::BOOL), link_name, abi, args, @@ -523,7 +523,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "HeapReAlloc" => { // FIXME: This does not have a direct test (#3179). let [handle, flags, old_ptr, size] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _, usize) -> *mut _), + shim_sig!(extern "system" fn(winapi::HANDLE, u32, *_, usize) -> *_), link_name, abi, args, @@ -614,7 +614,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: This does not have a direct test (#3179). // Also called from `page_size` crate. let [system_info] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> ()), + shim_sig!(extern "system" fn(*_) -> ()), link_name, abi, args, @@ -654,7 +654,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "TlsGetValue" => { // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32) -> *mut _), + shim_sig!(extern "system" fn(u32) -> *_), link_name, abi, args, @@ -667,7 +667,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "TlsSetValue" => { // FIXME: This does not have a direct test (#3179). let [key, new_ptr] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(u32, *_) -> winapi::BOOL), link_name, abi, args, @@ -723,7 +723,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "FlsGetValue" => { // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32) -> *mut _), + shim_sig!(extern "system" fn(u32) -> *_), link_name, abi, args, @@ -736,7 +736,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "FlsSetValue" => { // FIXME: This does not have a direct test (#3179). let [key, new_ptr] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(u32, *_) -> winapi::BOOL), link_name, abi, args, @@ -787,7 +787,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetCommandLineW" => { // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig( - shim_sig!(extern "system" fn() -> *mut _), + shim_sig!(extern "system" fn() -> *_), link_name, abi, args, @@ -802,7 +802,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => { // FIXME: This does not have a direct test (#3179). let [filetime] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> ()), + shim_sig!(extern "system" fn(*_) -> ()), link_name, abi, args, @@ -812,7 +812,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "QueryPerformanceCounter" => { // FIXME: This does not have a direct test (#3179). let [performance_count] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, @@ -823,7 +823,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "QueryPerformanceFrequency" => { // FIXME: This does not have a direct test (#3179). let [frequency] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, @@ -845,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "CreateWaitableTimerExW" => { // FIXME: This does not have a direct test (#3179). let [attributes, name, flags, access] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _, *const _, u32, u32) -> winapi::HANDLE), + shim_sig!(extern "system" fn(*_, *_, u32, u32) -> winapi::HANDLE), link_name, abi, args, @@ -863,7 +863,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "InitOnceBeginInitialize" => { let [ptr, flags, pending, context] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _, u32, *mut _, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, u32, *_, *_) -> winapi::BOOL), link_name, abi, args, @@ -872,7 +872,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "InitOnceComplete" => { let [ptr, flags, context] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _, u32, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, u32, *_) -> winapi::BOOL), link_name, abi, args, @@ -884,7 +884,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: This does not have a direct test (#3179). let [ptr_op, compare_op, size_op, timeout_op] = this.check_shim_sig( // First pointer is volatile - shim_sig!(extern "system" fn(*mut _, *mut _, usize, u32) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, *_, usize, u32) -> winapi::BOOL), link_name, abi, args, @@ -895,7 +895,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "WakeByAddressSingle" => { // FIXME: This does not have a direct test (#3179). let [ptr_op] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> ()), + shim_sig!(extern "system" fn(*_) -> ()), link_name, abi, args, @@ -906,7 +906,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "WakeByAddressAll" => { // FIXME: This does not have a direct test (#3179). let [ptr_op] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> ()), + shim_sig!(extern "system" fn(*_) -> ()), link_name, abi, args, @@ -919,7 +919,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetProcAddress" => { // FIXME: This does not have a direct test (#3179). let [module, proc_name] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HMODULE, *const _) -> winapi::FARPROC), + shim_sig!(extern "system" fn(winapi::HMODULE, *_) -> winapi::FARPROC), link_name, abi, args, @@ -941,12 +941,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [security, stacksize, start, arg, flags, thread] = this.check_shim_sig( shim_sig!( extern "system" fn( - *mut _, + *_, usize, - *mut _, - *mut _, + *_, + *_, u32, - *mut _, + *_, ) -> winapi::HANDLE ), link_name, @@ -997,7 +997,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "SetThreadDescription" => { let [handle, name] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *const _) -> i32), + shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> i32), link_name, abi, args, @@ -1017,7 +1017,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetThreadDescription" => { let [handle, name_ptr] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> i32), + shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> i32), link_name, abi, args, @@ -1086,7 +1086,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This is really 'RtlGenRandom'. let [ptr, len] = this.check_shim_sig( // Returns winapi::BOOLEAN, which is a byte - shim_sig!(extern "system" fn(*mut _, u32) -> u8), + shim_sig!(extern "system" fn(*_, u32) -> u8), link_name, abi, args, @@ -1100,7 +1100,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: This does not have a direct test (#3179). // used by `std` let [ptr, len] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _, usize) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_, usize) -> winapi::BOOL), link_name, abi, args, @@ -1113,7 +1113,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "BCryptGenRandom" => { // used by getrandom 0.2 let [algorithm, ptr, len, flags] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _, *mut _, u32, u32) -> i32), + shim_sig!(extern "system" fn(*_, *_, u32, u32) -> i32), link_name, abi, args, @@ -1153,7 +1153,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: This does not have a direct test (#3179). // `term` needs this, so we fake it. let [console, buffer_info] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> winapi::BOOL), link_name, abi, args, @@ -1184,7 +1184,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { winapi::HANDLE, winapi::HANDLE, winapi::HANDLE, - *mut _, + *_, u32, winapi::BOOL, u32, @@ -1220,7 +1220,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetModuleFileNameW" => { // FIXME: This does not have a direct test (#3179). let [handle, filename, size] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HMODULE, *mut _, u32) -> u32), + shim_sig!(extern "system" fn(winapi::HMODULE, *_, u32) -> u32), link_name, abi, args, @@ -1261,7 +1261,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [flags, module, message_id, language_id, buffer, size, arguments] = this .check_shim_sig( shim_sig!( - extern "system" fn(u32, *const _, u32, u32, *mut _, u32, *mut _) -> u32 + extern "system" fn(u32, *_, u32, u32, *_, u32, *_) -> u32 ), link_name, abi, @@ -1311,7 +1311,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // This function looks and behaves exactly like miri_start_unwind. let [payload] = this.check_shim_sig( - shim_sig!(extern "C" fn(*mut _) -> unwind::libunwind::_Unwind_Reason_Code), + shim_sig!(extern "C" fn(*_) -> unwind::_Unwind_Reason_Code), link_name, abi, args, @@ -1335,7 +1335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetModuleHandleA" if this.frame_in_std() => { let [_module_name] = this.check_shim_sig( - shim_sig!(extern "system" fn(*const _) -> winapi::HMODULE), + shim_sig!(extern "system" fn(*_) -> winapi::HMODULE), link_name, abi, args, @@ -1355,7 +1355,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetConsoleMode" if this.frame_in_std() => { let [console, mode] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> winapi::BOOL), link_name, abi, args, @@ -1377,7 +1377,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "AddVectoredExceptionHandler" if this.frame_in_std() => { let [_first, _handler] = this.check_shim_sig( - shim_sig!(extern "system" fn(u32, *mut _) -> *mut _), + shim_sig!(extern "system" fn(u32, *_) -> *_), link_name, abi, args, @@ -1387,7 +1387,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "SetThreadStackGuarantee" if this.frame_in_std() => { let [_stack_size_in_bytes] = this.check_shim_sig( - shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + shim_sig!(extern "system" fn(*_) -> winapi::BOOL), link_name, abi, args, From cd12a2814a45249fca673286ced08a2513d4382c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Aug 2026 11:07:38 +0200 Subject: [PATCH 37/46] deprecate the old 'lenient' check_shim_sig --- src/tools/miri/src/shims/alloc.rs | 6 +- src/tools/miri/src/shims/backtrace.rs | 2 +- src/tools/miri/src/shims/math.rs | 16 ++-- src/tools/miri/src/shims/sig.rs | 3 +- .../src/shims/unix/android/foreign_items.rs | 13 ++-- .../miri/src/shims/unix/foreign_items.rs | 15 ++-- .../src/shims/unix/freebsd/foreign_items.rs | 25 +++--- .../src/shims/unix/linux/foreign_items.rs | 35 +++++---- .../src/shims/unix/macos/foreign_items.rs | 77 +++++++++++-------- .../src/shims/unix/solarish/foreign_items.rs | 29 +++---- 10 files changed, 121 insertions(+), 100 deletions(-) diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index b4d53c36d19b3..3874c00187fdc 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match method { SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => { let [size, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::Rust, link_name, args)?; let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; @@ -145,7 +145,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } SpecialAllocatorMethod::Dealloc => { let [ptr, old_size, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let old_size = this.read_target_usize(old_size)?; let align = this.read_target_usize(align)?; @@ -159,7 +159,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } SpecialAllocatorMethod::Realloc => { let [ptr, old_size, align, new_size] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let old_size = this.read_target_usize(old_size)?; let align = this.read_target_usize(align)?; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index ce441f628d037..7b66ce563f646 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [ptr, flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, flags] = this.check_shim_sig_deprecated(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs index 593e4883cc08a..1a3228425af9a 100644 --- a/src/tools/miri/src/shims/math.rs +++ b/src/tools/miri/src/shims/math.rs @@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erff" | "erfcf" => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_deprecated(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { @@ -81,7 +81,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2f" | "fdimf" => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_deprecated(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; @@ -125,7 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erf" | "erfc" => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_deprecated(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { @@ -168,7 +168,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2" | "fdim" => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_deprecated(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; @@ -199,7 +199,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "ldexp" | "scalbn" => { - let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let [x, exp] = this.check_shim_sig_deprecated(abi, CanonAbi::C , link_name, args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(x)?.to_f64()?; let exp = this.read_scalar(exp)?.to_i32()?; @@ -209,7 +209,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgammaf_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f32()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; @@ -228,7 +229,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgamma_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f64()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index e2e5cbfced8e6..221c2e03c665c 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -253,7 +253,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(()) } - fn check_shim_sig_lenient<'a, const N: usize>( + /// 'Lenient' signature check. Deprecated; use `check_shim_sig` instead. + fn check_shim_sig_deprecated<'a, const N: usize>( &mut self, abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: CanonAbi, diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index c86bfeb2c8ac8..37f789362cc81 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -84,36 +84,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { let [epfd, op, fd, event] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Miscellaneous "__errno" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "gettid" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 0920774a4e5fd..0bb154a549832 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -1550,7 +1550,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These shims are enabled only when the caller is in the standard library. "pthread_attr_getguardsize" if this.frame_in_std() => { let [_attr, guard_size] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let guard_size_layout = this.machine.layouts.usize; let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?; this.write_scalar( @@ -1563,11 +1563,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => { - let [_] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [_] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "pthread_attr_setstacksize" if this.frame_in_std() => { - let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } @@ -1575,7 +1575,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. // Hence we can mostly ignore the input `attr_place`. let [attr_place, addr_place, size_place] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?; @@ -1595,18 +1595,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "signal" | "sigaltstack" if this.frame_in_std() => { - let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "sigaction" if this.frame_in_std() => { - let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [_, _, _] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; let uid = this.read_scalar(uid)?.to_u32()?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 77f68bf5a9e00..5a8df974a5bbb 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -25,7 +25,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { let [thread, name] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let max_len = u64::MAX; // FreeBSD does not seem to have a limit. let res = match this.pthread_setname_np( this.read_scalar(thread)?, @@ -41,7 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_getname_np" => { let [thread, name, len] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value, // but always adds a null terminator (except for zero-sized buffers). // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144 @@ -59,7 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getthreadid_np" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -67,7 +67,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "cpuset_getaffinity" => { // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically. let [level, which, id, set_size, mask] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let level = this.read_scalar(level)?.to_i32()?; let which = this.read_scalar(which)?.to_i32()?; @@ -139,33 +139,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "_umtx_op" => { let [obj, op, val, uaddr, uaddr2] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?; } // File related shims "stat@FBSD_1.0" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat@FBSD_1.0" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat@FBSD_1.0" => { - let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir@FBSD_1.0" => { - let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.readdir(dirp, dest)?; } // Miscellaneous "__error" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } @@ -187,7 +190,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These shims are enabled only when the caller is in the standard library. "pthread_attr_get_np" if this.frame_in_std() => { let [_thread, _attr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 50c2e36bb1b6f..61d32c599680f 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -152,40 +152,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "readdir64" => { - let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.readdir(dirp, dest)?; } "sync_file_range" => { let [fd, offset, nbytes, flags] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(result, dest)?; } "statx" => { let [dirfd, pathname, flags, mask, statxbuf] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; this.write_scalar(result, dest)?; } // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { let [epfd, op, fd, event] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } @@ -193,7 +194,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { let [thread, name] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let res = match this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, @@ -209,7 +210,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_getname_np" => { let [thread, name, len] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of glibc, the length of the output buffer must // be not shorter than TASK_COMM_LEN. @@ -232,7 +233,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "gettid" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -246,7 +247,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "mmap64" => { let [addr, length, prot, flags, fd, offset] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_i64()?; let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?; this.write_scalar(ptr, dest)?; @@ -259,22 +260,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__xpg_strerror_r" => { let [errnum, buf, buflen] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } "__errno_location" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "__libc_current_sigrtmin" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMIN, dest)?; } "__libc_current_sigrtmax" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMAX, dest)?; } @@ -283,14 +284,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These shims are enabled only when the caller is in the standard library. "pthread_getattr_np" if this.frame_in_std() => { let [_thread, _attr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "gnu_get_libc_version" if this.frame_in_std() && this.tcx.sess.target.env == rustc_target::spec::Env::Gnu => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // We have to be at least version 2.26 so that std does not call `res_init`. // This returns a C string, so we have to add a null terminator. let version = "2.26\0"; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 9254031a8a4d1..3d6694137b9e4 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -35,45 +35,48 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // errno "__error" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } // File related shims "close$NOCANCEL" => { - let [fd] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [fd] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let result = this.close(fd)?; this.write_scalar(result, dest)?; } "stat$INODE64" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat$INODE64" => { - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat$INODE64" => { - let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } "opendir$INODE64" => { - let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir$INODE64" => { - let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.readdir(dirp, dest)?; } "realpath$DARWIN_EXTSN" => { let [path, resolved_path] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } @@ -81,7 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Environment related shims "_NSGetEnviron" => { // FIXME: This does not have a direct test (#3179). - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let environ = this.machine.env_vars.unix().environ(); this.write_pointer(environ, dest)?; } @@ -89,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Random data generation "CCRandomGenerateBytes" => { let [bytes, count] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let bytes = this.read_pointer(bytes)?; let count = this.read_target_usize(count)?; let success = this.eval_libc_i32("kCCSuccess"); @@ -99,21 +102,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "mach_absolute_time" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.mach_absolute_time()?; this.write_scalar(result, dest)?; } "mach_timebase_info" => { // FIXME: This does not have a direct test (#3179). - let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [info] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.mach_timebase_info(info)?; this.write_scalar(result, dest)?; } "mach_wait_until" => { // FIXME: This does not have a direct test (#3179). - let [deadline] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [deadline] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.mach_wait_until(deadline)?; this.write_scalar(result, dest)?; } @@ -121,18 +125,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Access to command-line arguments "_NSGetArgc" => { // FIXME: This does not have a direct test (#3179). - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { // FIXME: This does not have a direct test (#3179). - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?; } "_NSGetExecutablePath" => { // FIXME: This does not have a direct test (#3179). let [buf, bufsize] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`_NSGetExecutablePath`")?; let buf_ptr = this.read_pointer(buf)?; @@ -158,7 +162,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "_tlv_atexit" => { let [dtor, data] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let dtor = this.read_pointer(dtor)?; let dtor = this.get_ptr_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?; @@ -174,14 +178,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "pthread_get_stackaddr_np" => { // FIXME: This does not have a direct test (#3179). - let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { // FIXME: This does not have a direct test (#3179). - let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size()); this.write_scalar(stack_size, dest)?; @@ -189,7 +193,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { - let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // The real implementation has logic in two places: // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200, @@ -217,7 +221,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_getname_np" => { let [thread, name, len] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of macOS, a truncated name (due to a too small buffer) @@ -242,7 +246,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_threadid_np" => { let [thread, tid_ptr] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let res = this.apple_pthread_threadid_np(thread, tid_ptr)?; this.write_scalar(res, dest)?; } @@ -250,7 +254,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "os_sync_wait_on_address" => { let [addr_op, value_op, size_op, flags_op] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -262,7 +266,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_deadline" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -274,7 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_timeout" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -286,42 +290,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wake_by_address_any" => { let [addr_op, size_op, flags_op] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ false, dest, )?; } "os_sync_wake_by_address_all" => { let [addr_op, size_op, flags_op] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ true, dest, )?; } "os_unfair_lock_lock" => { - let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_lock(lock_op)?; } "os_unfair_lock_trylock" => { - let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_trylock(lock_op, dest)?; } "os_unfair_lock_unlock" => { - let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_unlock(lock_op)?; } "os_unfair_lock_assert_owner" => { - let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_owner(lock_op)?; } "os_unfair_lock_assert_not_owner" => { - let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_not_owner(lock_op)?; } "pthread_cond_timedwait_relative_np" => { let [cond, mutex, reltime] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_timedwait( cond, mutex, reltime, dest, /* macos_relative_np */ true, )?; @@ -331,7 +340,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These shims are enabled only when the caller is in the standard library. "confstr" => { let [_key, _buf, _buflen] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // We just pretend that no configuration key exists, and return EINVAL. this.set_last_error(LibcError("EINVAL"))?; this.write_null(dest)?; diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index aa600c81aa845..2b885aedecc11 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -28,26 +28,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // epoll, eventfd (NOT available on Solaris!) "epoll_create1" => { this.assert_target_os(Os::Illumos, "epoll_create1"); - let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { this.assert_target_os(Os::Illumos, "epoll_ctl"); let [epfd, op, fd, event] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { this.assert_target_os(Os::Illumos, "epoll_wait"); let [epfd, events, maxevents, timeout] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { this.assert_target_os(Os::Illumos, "eventfd"); - let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } @@ -55,7 +56,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { let [thread, name] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // THREAD_NAME_MAX allows a thread name of 31+1 length // https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613 let max_len = 32; @@ -74,7 +75,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_getname_np" => { let [thread, name, len] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // See https://illumos.org/man/3C/pthread_getname_np for the error codes. let res = match this.pthread_getname_np( this.read_scalar(thread)?, @@ -92,13 +93,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "stat" => { // FIXME: This does not have a direct test (#3179). - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" => { // FIXME: This does not have a direct test (#3179). - let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } @@ -106,7 +109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Sockets and pipes "__xnet_socketpair" => { let [domain, type_, protocol, sv] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } @@ -165,14 +168,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "___errno" => { - let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "stack_getbounds" => { // FIXME: This does not have a direct test (#3179). - let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [stack] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; this.write_int_fields_named( @@ -192,7 +195,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pset_info" => { // FIXME: This does not have a direct test (#3179). let [pset, tpe, cpus, list] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; // We do not need to handle the current process cpu mask, available_parallelism // implementation pass null anyway. We only care for the number of // cpus. @@ -221,7 +224,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__sysconf_xpg7" => { - let [val] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [val] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.sysconf(val)?; this.write_scalar(result, dest)?; } From 6a024d4fc65b92012947bb2370e274627eb4603b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Aug 2026 11:27:38 +0200 Subject: [PATCH 38/46] better error message for ABI mismatches --- src/tools/miri/src/shims/sig.rs | 17 ++++++++++------- .../tests/fail/function_calls/check_arg_abi.rs | 2 +- .../fail/function_calls/check_arg_abi.stderr | 2 +- .../function_calls/check_arg_count_abort.rs | 2 +- .../function_calls/check_arg_count_abort.stderr | 2 +- .../check_arg_count_too_few_args.rs | 2 +- .../check_arg_count_too_few_args.stderr | 2 +- .../check_arg_count_too_many_args.rs | 2 +- .../check_arg_count_too_many_args.stderr | 2 +- .../shims/vararg_caller_signature_mismatch.rs | 2 +- .../vararg_caller_signature_mismatch.stderr | 2 +- 11 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 221c2e03c665c..6bf1873d1d670 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -172,13 +172,14 @@ macro_rules! shim_sig_arg { /// Helper function to compare two ABIs. fn check_shim_abi<'tcx>( this: &MiriInterpCx<'tcx>, + link_name: Symbol, callee_abi: &FnAbi<'tcx, Ty<'tcx>>, callee_nounwind: bool, caller_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> InterpResult<'tcx> { if callee_abi.conv != caller_abi.conv { throw_ub_format!( - r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#, + r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#, callee = callee_abi.conv, caller = caller_abi.conv, ); @@ -186,25 +187,27 @@ fn check_shim_abi<'tcx>( // FIXME: is this needed? Or is it enough to just check this if/when an actual unwind happens? if callee_abi.can_unwind && !callee_nounwind && !caller_abi.can_unwind { throw_ub_format!( - "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", + "ABI mismatch: callee may unwind, but caller asumes that no unwinding will occur", ); } if caller_abi.c_variadic && !callee_abi.c_variadic { throw_ub_format!( - "ABI mismatch: calling a non-variadic function with a variadic caller-side signature" + "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a variadic signature" ); } if !caller_abi.c_variadic && callee_abi.c_variadic { throw_ub_format!( - "ABI mismatch: calling a variadic function with a non-variadic caller-side signature" + "ABI mismatch: `{link_name}` is a variadic function, but the caller is using a non-variadic signature" ); } if callee_abi.fixed_count != caller_abi.fixed_count { throw_ub_format!( - "ABI mismatch: expected {} arguments, found {} arguments ", + "ABI mismatch: calling `{link_name}` which takes {} argument{}, but {} argument{} given", callee_abi.fixed_count, - caller_abi.fixed_count + if callee_abi.fixed_count == 1 { "" } else { "s" }, + caller_abi.fixed_count, + if caller_abi.fixed_count == 1 { " was" } else { "s were" }, ); } @@ -311,7 +314,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; // Check everything. - check_shim_abi(this, callee_fn_abi, shim_sig.nounwind, caller_fn_abi)?; + check_shim_abi(this, link_name, callee_fn_abi, shim_sig.nounwind, caller_fn_abi)?; this.check_shim_symbol_clash(link_name)?; // Return arguments. diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs index e36b516887962..ab0cb4d84251c 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs @@ -6,6 +6,6 @@ fn main() { } unsafe { - let _ = malloc(0); //~ ERROR: calling a function with calling convention "C" using caller calling convention "Rust" + let _ = malloc(0); //~ ERROR: has calling convention "C", but the caller is using calling convention "Rust" }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr index 84a3c75538944..88060595ce90d 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with calling convention "C" using caller calling convention "Rust" +error: Undefined Behavior: ABI mismatch: `malloc` has calling convention "C", but the caller is using calling convention "Rust" --> tests/fail/function_calls/check_arg_abi.rs:LL:CC | LL | let _ = malloc(0); diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs index 54d49e059bf6d..5e7d9a687b871 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.rs @@ -5,6 +5,6 @@ fn main() { unsafe { abort(1); - //~^ ERROR: expected 0 arguments, found 1 arguments + //~^ ERROR: takes 0 arguments, but 1 argument was given } } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr index 75efe0af99238..94f5ff1b64be8 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_abort.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: ABI mismatch: expected 0 arguments, found 1 arguments +error: Undefined Behavior: ABI mismatch: calling `abort` which takes 0 arguments, but 1 argument was given --> tests/fail/function_calls/check_arg_count_abort.rs:LL:CC | LL | abort(1); diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs index 6e51b3ed89036..2e2b1e019479c 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs @@ -6,6 +6,6 @@ fn main() { } unsafe { - let _ = malloc(); //~ ERROR: expected 1 arguments, found 0 arguments + let _ = malloc(); //~ ERROR: takes 1 argument, but 0 arguments were given }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr index bd51eee6ee013..56e75a8ac55ac 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: ABI mismatch: expected 1 arguments, found 0 arguments +error: Undefined Behavior: ABI mismatch: calling `malloc` which takes 1 argument, but 0 arguments were given --> tests/fail/function_calls/check_arg_count_too_few_args.rs:LL:CC | LL | let _ = malloc(); diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs index 537538c1c4b04..2334f524dbca0 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs @@ -6,6 +6,6 @@ fn main() { } unsafe { - let _ = malloc(1, 2); //~ ERROR: expected 1 arguments, found 2 arguments + let _ = malloc(1, 2); //~ ERROR: takes 1 argument, but 2 arguments were given }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr index 87d7bf7083d48..a586fafe1eb1b 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: ABI mismatch: expected 1 arguments, found 2 arguments +error: Undefined Behavior: ABI mismatch: calling `malloc` which takes 1 argument, but 2 arguments were given --> tests/fail/function_calls/check_arg_count_too_many_args.rs:LL:CC | LL | let _ = malloc(1, 2); diff --git a/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.rs b/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.rs index ac6e221fcd8d3..1dc36706e284b 100644 --- a/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.rs @@ -9,6 +9,6 @@ extern "C" { fn main() { let mut fds = [-1, -1]; let res = unsafe { pipe(fds.as_mut_ptr()) }; - //~^ ERROR: ABI mismatch: calling a non-variadic function with a variadic caller-side signature + //~^ ERROR: is a non-variadic function, but the caller is using a variadic signature assert_eq!(res, 0); } diff --git a/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.stderr b/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.stderr index 12c5a21909ab8..0405fe0ef7501 100644 --- a/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.stderr +++ b/src/tools/miri/tests/fail/shims/vararg_caller_signature_mismatch.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: ABI mismatch: calling a non-variadic function with a variadic caller-side signature +error: Undefined Behavior: ABI mismatch: `pipe` is a non-variadic function, but the caller is using a variadic signature --> tests/fail/shims/vararg_caller_signature_mismatch.rs:LL:CC | LL | let res = unsafe { pipe(fds.as_mut_ptr()) }; From 615e6d188eff6cc35ff0685de96c16de8866fef7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Aug 2026 13:12:27 +0200 Subject: [PATCH 39/46] update eyre --- Cargo.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d68be636fa92..a8f8a8c45b9bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1400,10 +1400,11 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.12" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +checksum = "c08309dbcc659c5549a24ddb9b27027640641b282ef5768267c7e675558986a3" dependencies = [ + "autocfg", "indenter", "once_cell", ] From 33100e44a3d99c1da0f396df6dda6114ed9c9f0b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 31 Jul 2026 21:50:05 +0200 Subject: [PATCH 40/46] target_featurs: avx2 and probably all of sse is incompatible with soft-float ABI --- compiler/rustc_codegen_ssa/src/diagnostics.rs | 6 +++ .../rustc_codegen_ssa/src/target_features.rs | 13 +++++- compiler/rustc_lint_defs/src/builtin.rs | 40 +++++++++++++++++++ compiler/rustc_target/src/target_features.rs | 8 +++- ...rget-feature-attribute-fcw.aarch64.stderr} | 20 +++++----- ...compatible-target-feature-attribute-fcw.rs | 18 ++++++--- ...target-feature-attribute-fcw.x86_64.stderr | 31 ++++++++++++++ 7 files changed, 117 insertions(+), 19 deletions(-) rename tests/ui/target-feature/{abi-incompatible-target-feature-attribute-fcw.stderr => abi-incompatible-target-feature-attribute-fcw.aarch64.stderr} (60%) create mode 100644 tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.x86_64.stderr diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index 2b77eb2cf24fb..078822af09561 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -1193,6 +1193,12 @@ pub(crate) struct XcrunSdkPathWarning { #[diag("enabling the `neon` target feature on the current target is unsound due to ABI issues")] pub(crate) struct Aarch64SoftfloatNeon; +#[derive(Diagnostic)] +#[diag( + "enabling the `sse` target feature on the current target is unsupported due to LLVM backend issues" +)] +pub(crate) struct X86SoftfloatSse; + #[derive(Diagnostic)] #[diag("ignoring feature with missing prefix in `-Ctarget-feature`: `{$feature}`")] #[note("features must begin with a `+` to enable or `-` to disable it")] diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 2150cbd17aec8..96ded8f6a2002 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -3,7 +3,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::attrs::InstructionSetAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_lint_defs::builtin::AARCH64_SOFTFLOAT_NEON; +use rustc_lint_defs::builtin::{AARCH64_SOFTFLOAT_NEON, X86_SOFTFLOAT_SSE}; use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -99,6 +99,8 @@ pub(crate) fn from_target_feature_attr( if abi_feature_constraints.incompatible.contains(&name.as_str()) { // For "neon" specifically, we emit an FCW instead of a hard error. // See . + // Similar for "sse" on x86. + // See . if tcx.sess.target.arch == Arch::AArch64 && name.as_str() == "neon" { tcx.emit_node_span_lint( AARCH64_SOFTFLOAT_NEON, @@ -106,6 +108,15 @@ pub(crate) fn from_target_feature_attr( feature_span, diagnostics::Aarch64SoftfloatNeon, ); + } else if matches!(tcx.sess.target.arch, Arch::X86 | Arch::X86_64) + && name.as_str() == "sse" + { + tcx.emit_node_span_lint( + X86_SOFTFLOAT_SSE, + tcx.local_def_id_to_hir_id(did), + feature_span, + diagnostics::X86SoftfloatSse, + ); } else { tcx.dcx().emit_err(diagnostics::InternalOnlyTargetFeatureAttr { span: feature_span, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5ee3c5a741bdc..8c8d37ce355fb 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -157,6 +157,7 @@ pub mod hardwired { USELESS_DEPRECATED, VARARGS_WITHOUT_PATTERN, WARNINGS, + X86_SOFTFLOAT_SSE, // tidy-alphabetical-end ] } @@ -5375,6 +5376,45 @@ declare_lint! { }; } +declare_lint! { + /// The `x86_softfloat_sse` lint detects usage of `#[target_feature(enable = "sse")]` or target + /// features that imply SSE on softfloat x86 and x86-64 targets. Enabling this target feature + /// in a soft-float configuration is not supported by LLVM and can lead to crashes. + /// + /// ### Example + /// + /// ```rust,ignore (needs x86_64-unknown-none) + /// #[target_feature(enable = "avx")] + /// fn with_avx() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: enabling the `sse` target feature on the current target is unsupported due to LLVM backend issues + /// --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:18 + /// | + /// | #[target_feature(enable = "avx")] + /// | ^^^^^^^^^^^^^^^ + /// | + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #117938 + /// ``` + /// + /// ### Explanation + /// + /// LLVM does not support combining the `soft-float` target feature (which is implicitly enabled + /// on these targets) with `sse`. This can lead to crashes of the backend. To prevent that, + /// Rust is turning that combination into an error. + pub X86_SOFTFLOAT_SSE, + Warn, + "detects code that could be affected by LLVM backend issues on x86 softfloat targets", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #117938), + report_in_deps: true, + }; +} + declare_lint! { /// The `tail_call_track_caller` lint detects usage of `become` attempting to tail call /// a function marked with `#[track_caller]`. diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index f1a78ba2ed5bf..f282ff6792195 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1293,7 +1293,9 @@ impl Target { // `x87` and all other FPU features so those do not matter. // Note that this one requirement is the entire implementation of the ABI! // LLVM handles the rest. - FeatureConstraints { required: &["soft-float"], incompatible: &[] } + // We mark "sse" as incompatible since LLVM likes to crash when both + // "soft-float" and "sse" are enabled. + FeatureConstraints { required: &["soft-float"], incompatible: &["sse"] } } _ => unreachable!(), } @@ -1314,7 +1316,9 @@ impl Target { // `x87` and all other FPU features so those do not matter. // Note that this one requirement is the entire implementation of the ABI! // LLVM handles the rest. - FeatureConstraints { required: &["soft-float"], incompatible: &[] } + // We mark "sse" as incompatible since LLVM likes to crash when both + // "soft-float" and "sse" are enabled. + FeatureConstraints { required: &["soft-float"], incompatible: &["sse"] } } _ => unreachable!(), } diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.aarch64.stderr similarity index 60% rename from tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr rename to tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.aarch64.stderr index 9595d1aba477f..72828f701d66f 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.aarch64.stderr @@ -1,31 +1,31 @@ error: enabling the `neon` target feature on the current target is unsound due to ABI issues - --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:13:18 + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:16:36 | -LL | #[target_feature(enable = "neon")] - | ^^^^^^^^^^^^^^^ +LL | #[cfg_attr(aarch64, target_feature(enable = "neon"))] + | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #134375 note: the lint level is defined here - --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:8:9 + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:9 | -LL | #![deny(aarch64_softfloat_neon)] +LL | #![deny(aarch64_softfloat_neon, x86_softfloat_sse)] | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error Future incompatibility report: Future breakage diagnostic: error: enabling the `neon` target feature on the current target is unsound due to ABI issues - --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:13:18 + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:16:36 | -LL | #[target_feature(enable = "neon")] - | ^^^^^^^^^^^^^^^ +LL | #[cfg_attr(aarch64, target_feature(enable = "neon"))] + | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #134375 note: the lint level is defined here - --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:8:9 + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:9 | -LL | #![deny(aarch64_softfloat_neon)] +LL | #![deny(aarch64_softfloat_neon, x86_softfloat_sse)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs index dba9e2366d9e9..937c8d93ae940 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs @@ -1,16 +1,22 @@ //@ compile-flags: --crate-type=lib -//@ compile-flags: --target=aarch64-unknown-none-softfloat -//@ needs-llvm-components: aarch64 +//@ revisions: aarch64 x86_64 +//@[aarch64] compile-flags: --target=aarch64-unknown-none-softfloat +//@[aarch64] needs-llvm-components: aarch64 +//@[x86_64] compile-flags: --target=x86_64-unknown-none +//@[x86_64] needs-llvm-components: x86 //@ add-minicore //@ ignore-backends: gcc #![feature(no_core)] #![no_core] -#![deny(aarch64_softfloat_neon)] +#![deny(aarch64_softfloat_neon, x86_softfloat_sse)] extern crate minicore; use minicore::*; -#[target_feature(enable = "neon")] -//~^ERROR: enabling the `neon` target feature on the current target is unsound -//~|WARN: previously accepted +#[cfg_attr(aarch64, target_feature(enable = "neon"))] +//[aarch64]~^ERROR: enabling the `neon` target feature on the current target is unsound +//[aarch64]~|WARN: previously accepted +#[cfg_attr(x86_64, target_feature(enable = "avx"))] +//[x86_64]~^ERROR: enabling the `sse` target feature on the current target is unsupported +//[x86_64]~|WARN: previously accepted pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.x86_64.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.x86_64.stderr new file mode 100644 index 0000000000000..52f577b02bae7 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.x86_64.stderr @@ -0,0 +1,31 @@ +error: enabling the `sse` target feature on the current target is unsupported due to LLVM backend issues + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:19:35 + | +LL | #[cfg_attr(x86_64, target_feature(enable = "avx"))] + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #117938 +note: the lint level is defined here + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:33 + | +LL | #![deny(aarch64_softfloat_neon, x86_softfloat_sse)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +Future incompatibility report: Future breakage diagnostic: +error: enabling the `sse` target feature on the current target is unsupported due to LLVM backend issues + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:19:35 + | +LL | #[cfg_attr(x86_64, target_feature(enable = "avx"))] + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #117938 +note: the lint level is defined here + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:33 + | +LL | #![deny(aarch64_softfloat_neon, x86_softfloat_sse)] + | ^^^^^^^^^^^^^^^^^ + From a6aa52312457bdf0d6e58df6a270e36560364385 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 5 Aug 2026 09:08:52 +0200 Subject: [PATCH 41/46] make these lints deny-by-default --- compiler/rustc_lint_defs/src/builtin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 8c8d37ce355fb..d7339d1b60269 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5368,7 +5368,7 @@ declare_lint! { /// on this target due to this issue, but the problem was not known at the time of /// stabilization. pub AARCH64_SOFTFLOAT_NEON, - Warn, + Deny, "detects code that could be affected by ABI issues on aarch64 softfloat targets", @future_incompatible = FutureIncompatibleInfo { reason: fcw!(FutureReleaseError #134375), @@ -5407,7 +5407,7 @@ declare_lint! { /// on these targets) with `sse`. This can lead to crashes of the backend. To prevent that, /// Rust is turning that combination into an error. pub X86_SOFTFLOAT_SSE, - Warn, + Deny, "detects code that could be affected by LLVM backend issues on x86 softfloat targets", @future_incompatible = FutureIncompatibleInfo { reason: fcw!(FutureReleaseError #117938), From 68c66e8518ac8cd26331f90a97d3e371ea9890f1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 23 Aug 2026 14:02:43 +0200 Subject: [PATCH 42/46] silence new warning in stdarch --- library/portable-simd/crates/core_simd/src/swizzle_dyn.rs | 4 ++++ library/stdarch/crates/core_arch/src/aarch64/mod.rs | 7 ------- library/stdarch/crates/core_arch/src/arm_shared/mod.rs | 6 ------ library/stdarch/crates/core_arch/src/mod.rs | 3 +++ 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index f0253c284e35f..18327e6ba4c9f 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -1,3 +1,7 @@ +// Allow these FCW: anyone soundly using the intrinsics has to enable +// the target feature, and that will generate a warning for them. +#![allow(aarch64_softfloat_neon, x86_softfloat_sse)] + use crate::simd::Simd; use core::mem; diff --git a/library/stdarch/crates/core_arch/src/aarch64/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/mod.rs index 1f07f024721dc..34fa5c817918a 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/mod.rs @@ -6,13 +6,6 @@ //! [arm_ref]: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf //! [arm_dat]: https://developer.arm.com/technologies/neon/intrinsics -#![cfg_attr( - all(target_arch = "aarch64", target_abi = "softfloat"), - // Just allow the warning: anyone soundly using the intrinsics has to enable - // the target feature, and that will generate a warning for them. - allow(aarch64_softfloat_neon) -)] - mod mte; #[unstable(feature = "stdarch_aarch64_mte", issue = "129010")] pub use self::mte::*; diff --git a/library/stdarch/crates/core_arch/src/arm_shared/mod.rs b/library/stdarch/crates/core_arch/src/arm_shared/mod.rs index 8074648a28a28..072743a69fc7e 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/mod.rs @@ -47,12 +47,6 @@ //! //! - [ACLE Q2 2018](https://developer.arm.com/docs/101028/latest) -#![cfg_attr( - all(target_arch = "aarch64", target_abi = "softfloat"), - // Just allow the warning: anyone soundly using the intrinsics has to enable - // the target feature, and that will generate a warning for them. - allow(aarch64_softfloat_neon) -)] // Only for 'neon' submodule #![allow(non_camel_case_types)] diff --git a/library/stdarch/crates/core_arch/src/mod.rs b/library/stdarch/crates/core_arch/src/mod.rs index 2483d07b230f9..ab787590a274f 100644 --- a/library/stdarch/crates/core_arch/src/mod.rs +++ b/library/stdarch/crates/core_arch/src/mod.rs @@ -1,6 +1,9 @@ //! `core_arch` #![allow(unknown_lints, unnecessary_transmutes)] +// Allow these FCW: anyone soundly using the intrinsics has to enable +// the target feature, and that will generate a warning for them. +#![allow(aarch64_softfloat_neon, x86_softfloat_sse)] #[macro_use] mod macros; From 1867b66630ff2a10019217252229e7fb41178a90 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 23 Aug 2026 14:39:13 +0200 Subject: [PATCH 43/46] Add test for attribute in use tree --- tests/ui/use/attr-in-use-tree.rs | 23 +++++++++++++++++++++++ tests/ui/use/attr-in-use-tree.stderr | 8 ++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/ui/use/attr-in-use-tree.rs create mode 100644 tests/ui/use/attr-in-use-tree.stderr diff --git a/tests/ui/use/attr-in-use-tree.rs b/tests/ui/use/attr-in-use-tree.rs new file mode 100644 index 0000000000000..afe9844d69dc0 --- /dev/null +++ b/tests/ui/use/attr-in-use-tree.rs @@ -0,0 +1,23 @@ +#![allow(unused_imports)] + +use foo::{ + #[cfg(true)] + //~^ ERROR expected identifier, found `#` + bar, + #[cfg(false)] + baz, +}; + +// Make sure we handle reserved symbols (leading `::` is `sym::PathRoot`). +use ::foo::{ + #[cfg(false)] + qux, +}; + +mod foo { + pub(crate) mod bar {} + pub(crate) mod baz {} + pub(crate) mod qux {} +} + +fn main() {} diff --git a/tests/ui/use/attr-in-use-tree.stderr b/tests/ui/use/attr-in-use-tree.stderr new file mode 100644 index 0000000000000..79478f28667ac --- /dev/null +++ b/tests/ui/use/attr-in-use-tree.stderr @@ -0,0 +1,8 @@ +error: expected identifier, found `#` + --> $DIR/attr-in-use-tree.rs:4:5 + | +LL | #[cfg(true)] + | ^ expected identifier + +error: aborting due to 1 previous error + From e6e1f048cb3065309935105032f409144d9dc8ea Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Fri, 10 Apr 2026 14:10:02 +0000 Subject: [PATCH 44/46] Recover on attribute in use tree --- compiler/rustc_parse/src/diagnostics.rs | 21 +++++ compiler/rustc_parse/src/parser/attr.rs | 14 +-- compiler/rustc_parse/src/parser/item.rs | 111 ++++++++++++++++++++++-- compiler/rustc_span/src/source_map.rs | 10 ++- tests/ui/use/attr-in-use-tree.fixed | 33 +++++++ tests/ui/use/attr-in-use-tree.rs | 6 +- tests/ui/use/attr-in-use-tree.stderr | 53 ++++++++++- 7 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 tests/ui/use/attr-in-use-tree.fixed diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 7897239d248ea..397baa0ae4ddb 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -1118,6 +1118,27 @@ pub(crate) struct ArrayBracketsInsteadOfBracesSugg { pub right: Span, } +#[derive(Diagnostic)] +#[diag("attributes are not allowed inside imports")] +pub(crate) struct AttrInUseTree { + #[primary_span] + pub attr_span: Span, + #[subdiagnostic] + pub sub: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("move the import to its own item", style = "verbose")] +pub(crate) struct AttrInUseTreeSugg { + #[suggestion_part(code = "{code}")] + pub use_lo: Span, + #[suggestion_part(code = "")] + pub attr_span: Span, + #[suggestion_part(code = "")] + pub tree_span: Span, + pub code: String, +} + #[derive(Diagnostic)] #[diag("`match` arm body without braces")] pub(crate) struct MatchArmBodyWithoutBraces { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index fae58c29954d0..0f49e3c02873d 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -310,11 +310,15 @@ impl<'a> Parser<'a> { /// Parses an inner part of an attribute (the path and following tokens). /// The tokens must be either a delimited token stream, or empty token stream, /// or the "legacy" key-value form. - /// PATH `(` TOKEN_STREAM `)` - /// PATH `[` TOKEN_STREAM `]` - /// PATH `{` TOKEN_STREAM `}` - /// PATH - /// PATH `=` UNSUFFIXED_LIT + /// + /// ```text + /// PATH `(` TOKEN_STREAM `)` + /// PATH `[` TOKEN_STREAM `]` + /// PATH `{` TOKEN_STREAM `}` + /// PATH + /// PATH `=` UNSUFFIXED_LIT + /// ``` + /// /// The delimiters or `=` are still put into the resulting token stream. pub fn parse_attr_item( &mut self, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1306f1fcfb1ce..9de1491030b39 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -434,7 +434,8 @@ impl<'a> Parser<'a> { } fn parse_use_item(&mut self) -> PResult<'a, ItemKind> { - let tree = self.parse_use_tree()?; + let use_token_span = self.prev_token.span; + let tree = self.parse_use_tree(use_token_span, None)?; if let Err(mut e) = self.expect_semi() { match tree.kind { UseTreeKind::Glob(_) => { @@ -1317,7 +1318,11 @@ impl<'a> Parser<'a> { /// PATH `::` `{` USE_TREE_LIST `}` | /// PATH [`as` IDENT] /// ``` - fn parse_use_tree(&mut self) -> PResult<'a, UseTree> { + fn parse_use_tree<'b>( + &mut self, + use_token_span: Span, + use_path: Option<&'b UsePathList<'b>>, + ) -> PResult<'a, UseTree> { let lo = self.token.span; let mut prefix = ast::Path { segments: ThinVec::new(), span: lo.shrink_to_lo() }; @@ -1331,13 +1336,14 @@ impl<'a> Parser<'a> { .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } - self.parse_use_tree_glob_or_nested()? + self.parse_use_tree_glob_or_nested(use_token_span, use_path)? } else { // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` prefix = self.parse_path(PathStyle::Mod)?; if self.eat_path_sep() { - self.parse_use_tree_glob_or_nested()? + let use_path = UsePathList { elements: &prefix.segments, prev: use_path }; + self.parse_use_tree_glob_or_nested(use_token_span, Some(&use_path))? } else { // Recover from using a colon as path separator. while self.eat_noexpect(&token::Colon) { @@ -1358,13 +1364,17 @@ impl<'a> Parser<'a> { } /// Parses `*` or `{...}`. - fn parse_use_tree_glob_or_nested(&mut self) -> PResult<'a, UseTreeKind> { + fn parse_use_tree_glob_or_nested<'b>( + &mut self, + use_token_span: Span, + use_path: Option<&'b UsePathList<'b>>, + ) -> PResult<'a, UseTreeKind> { Ok(if self.eat(exp!(Star)) { UseTreeKind::Glob(self.prev_token.span) } else { let lo = self.token.span; UseTreeKind::Nested { - items: self.parse_use_tree_list()?, + items: self.parse_use_tree_list(use_token_span, use_path)?, span: lo.to(self.prev_token.span), } }) @@ -1375,14 +1385,93 @@ impl<'a> Parser<'a> { /// ```text /// USE_TREE_LIST = ∅ | (USE_TREE `,`)* USE_TREE [`,`] /// ``` - fn parse_use_tree_list(&mut self) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { + fn parse_use_tree_list<'b>( + &mut self, + use_token_span: Span, + prefix: Option<&'b UsePathList<'b>>, + ) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| { p.recover_vcs_conflict_marker(); - Ok((p.parse_use_tree()?, DUMMY_NODE_ID)) + let mut attr_span = None; + let attrs = p.parse_outer_attributes()?; + if !attrs.is_empty() { + let raw_attrs = attrs.take_for_recovery(&p.psess); + attr_span = + Some(raw_attrs.first().unwrap().span.to(raw_attrs.last().unwrap().span)); + } + let use_tree = p.parse_use_tree(use_token_span, prefix)?; + if let Some(attr_span) = attr_span { + p.emit_error_attr_in_use_tree(use_token_span, prefix, use_tree.span(), attr_span); + } + + Ok((use_tree, DUMMY_NODE_ID)) }) .map(|(r, _)| r) } + fn emit_error_attr_in_use_tree<'b>( + &self, + use_token_span: Span, + prefix: Option<&'b UsePathList<'b>>, + use_tree_span: Span, + attr_span: Span, + ) { + { + let mut prefix = prefix; + let Ok(attr) = self.psess.source_map().span_to_snippet(attr_span) else { + return; + }; + + let prefix = { + let mut tmp = Vec::new(); + while let Some(prefix_) = prefix { + tmp.push(prefix_.elements); + prefix = prefix_.prev; + } + tmp.reverse(); + tmp.iter().flat_map(|segments| segments.iter()).collect::>() + }; + + let prefix = + prefix + .iter() + .map(|segment| { + if segment.ident.name == kw::PathRoot { "" } else { segment.ident.as_str() } + }) + .collect::>() + .join("::"); + + let mut comma_reached = false; + let Ok(tree_span) = self.psess.source_map().span_extend_while(use_tree_span, |c| { + if comma_reached { + return false; + } + comma_reached = c == ','; + c.is_whitespace() || comma_reached + }) else { + return; + }; + + let Ok(use_tree) = self.psess.source_map().span_to_snippet(use_tree_span) else { + return; + }; + + // FIXME: duplicate the attributes that are at the root of the initial use-item. + let code = format!("{attr}\nuse {prefix}::{use_tree};\n"); + + let err = crate::diagnostics::AttrInUseTree { + attr_span, + sub: Some(crate::diagnostics::AttrInUseTreeSugg { + use_lo: use_token_span.shrink_to_lo(), + attr_span, + tree_span, + code, + }), + }; + self.dcx().emit_err(err); + } + } + fn parse_rename(&mut self) -> PResult<'a, Option> { if self.eat_keyword(exp!(As)) { self.parse_ident_or_underscore().map(Some) @@ -2737,7 +2826,13 @@ impl<'a> Parser<'a> { } } } + enum IsMacroRulesItem { Yes { has_bang: bool }, No, } + +struct UsePathList<'a> { + elements: &'a [ast::PathSegment], + prev: Option<&'a Self>, +} diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 80d1bae71ae89..f394765ab9f8e 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -596,9 +596,13 @@ impl SourceMap { /// Extracts the source surrounding the given `Span` using the `extract_source` function. The /// extract function takes three arguments: a string slice containing the source, an index in /// the slice for the beginning of the span and an index in the slice for the end of the span. - pub fn span_to_source(&self, sp: Span, extract_source: F) -> Result + pub fn span_to_source( + &self, + sp: Span, + mut extract_source: F, + ) -> Result where - F: Fn(&str, usize, usize) -> Result, + F: FnMut(&str, usize, usize) -> Result, { let local_begin = self.lookup_byte_offset(sp.lo()); let local_end = self.lookup_byte_offset(sp.hi()); @@ -753,7 +757,7 @@ impl SourceMap { pub fn span_extend_while( &self, span: Span, - f: impl Fn(char) -> bool, + mut f: impl FnMut(char) -> bool, ) -> Result { self.span_to_source(span, |s, _start, end| { let n = s[end..].char_indices().find(|&(_, c)| !f(c)).map_or(s.len() - end, |(i, _)| i); diff --git a/tests/ui/use/attr-in-use-tree.fixed b/tests/ui/use/attr-in-use-tree.fixed new file mode 100644 index 0000000000000..49d3ce7ed8cae --- /dev/null +++ b/tests/ui/use/attr-in-use-tree.fixed @@ -0,0 +1,33 @@ +//@ run-rustfix + +#![allow(unused_imports)] + +#[cfg(true)] +use foo::bar; +#[cfg(false)] +use foo::baz; +use foo::{ + + //~^ ERROR attributes are not allowed inside imports + + + //~^ ERROR attributes are not allowed inside imports + +}; + +// Make sure we handle reserved symbols (leading `::` is `sym::PathRoot`). +#[cfg(false)] +use ::foo::qux; +use ::foo::{ + + //~^ ERROR attributes are not allowed inside imports + +}; + +mod foo { + pub(crate) mod bar {} + pub(crate) mod baz {} + pub(crate) mod qux {} +} + +fn main() {} diff --git a/tests/ui/use/attr-in-use-tree.rs b/tests/ui/use/attr-in-use-tree.rs index afe9844d69dc0..20a21d89e2721 100644 --- a/tests/ui/use/attr-in-use-tree.rs +++ b/tests/ui/use/attr-in-use-tree.rs @@ -1,16 +1,20 @@ +//@ run-rustfix + #![allow(unused_imports)] use foo::{ #[cfg(true)] - //~^ ERROR expected identifier, found `#` + //~^ ERROR attributes are not allowed inside imports bar, #[cfg(false)] + //~^ ERROR attributes are not allowed inside imports baz, }; // Make sure we handle reserved symbols (leading `::` is `sym::PathRoot`). use ::foo::{ #[cfg(false)] + //~^ ERROR attributes are not allowed inside imports qux, }; diff --git a/tests/ui/use/attr-in-use-tree.stderr b/tests/ui/use/attr-in-use-tree.stderr index 79478f28667ac..e8c76a90635ac 100644 --- a/tests/ui/use/attr-in-use-tree.stderr +++ b/tests/ui/use/attr-in-use-tree.stderr @@ -1,8 +1,53 @@ -error: expected identifier, found `#` - --> $DIR/attr-in-use-tree.rs:4:5 +error: attributes are not allowed inside imports + --> $DIR/attr-in-use-tree.rs:6:5 | LL | #[cfg(true)] - | ^ expected identifier + | ^^^^^^^^^^^^ + | +help: move the import to its own item + | +LL + #[cfg(true)] +LL + use foo::bar; +LL | use foo::{ +LL ~ +LL | +LL ~ + | + +error: attributes are not allowed inside imports + --> $DIR/attr-in-use-tree.rs:9:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | +help: move the import to its own item + | +LL + #[cfg(false)] +LL + use foo::baz; +LL | use foo::{ +LL | #[cfg(true)] +LL | +LL | bar, +LL ~ +LL | +LL ~ + | + +error: attributes are not allowed inside imports + --> $DIR/attr-in-use-tree.rs:16:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + | +help: move the import to its own item + | +LL + #[cfg(false)] +LL + use ::foo::qux; +LL | use ::foo::{ +LL ~ +LL | +LL ~ + | -error: aborting due to 1 previous error +error: aborting due to 3 previous errors From 1333ad790ae46e4c9893d88fd4bcd26d7712fc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 23 Aug 2026 14:38:37 +0200 Subject: [PATCH 45/46] Small tweaks to parse error recovery code --- compiler/rustc_parse/src/parser/item.rs | 90 +++++++++++-------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9de1491030b39..44ce647568d00 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1392,6 +1392,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| { p.recover_vcs_conflict_marker(); + let mut attr_span = None; let attrs = p.parse_outer_attributes()?; if !attrs.is_empty() { @@ -1399,7 +1400,9 @@ impl<'a> Parser<'a> { attr_span = Some(raw_attrs.first().unwrap().span.to(raw_attrs.last().unwrap().span)); } + let use_tree = p.parse_use_tree(use_token_span, prefix)?; + if let Some(attr_span) = attr_span { p.emit_error_attr_in_use_tree(use_token_span, prefix, use_tree.span(), attr_span); } @@ -1409,67 +1412,56 @@ impl<'a> Parser<'a> { .map(|(r, _)| r) } - fn emit_error_attr_in_use_tree<'b>( + fn emit_error_attr_in_use_tree( &self, use_token_span: Span, - prefix: Option<&'b UsePathList<'b>>, + mut prefix: Option<&UsePathList<'_>>, use_tree_span: Span, attr_span: Span, ) { - { - let mut prefix = prefix; - let Ok(attr) = self.psess.source_map().span_to_snippet(attr_span) else { - return; - }; + let Ok(attr) = self.psess.source_map().span_to_snippet(attr_span) else { return }; - let prefix = { - let mut tmp = Vec::new(); - while let Some(prefix_) = prefix { - tmp.push(prefix_.elements); - prefix = prefix_.prev; - } - tmp.reverse(); - tmp.iter().flat_map(|segments| segments.iter()).collect::>() - }; + let prefix: Vec<_> = { + let mut tmp = Vec::new(); + while let Some(prefix_) = prefix { + tmp.push(prefix_.elements); + prefix = prefix_.prev; + } + tmp.reverse(); + tmp.into_iter().flatten().collect() + }; - let prefix = - prefix - .iter() - .map(|segment| { - if segment.ident.name == kw::PathRoot { "" } else { segment.ident.as_str() } - }) - .collect::>() - .join("::"); + let prefix: String = prefix + .iter() + .map(|seg| if seg.ident.name == kw::PathRoot { "" } else { seg.ident.as_str() }) + .intersperse("::") + .collect(); - let mut comma_reached = false; - let Ok(tree_span) = self.psess.source_map().span_extend_while(use_tree_span, |c| { - if comma_reached { - return false; - } - comma_reached = c == ','; - c.is_whitespace() || comma_reached - }) else { - return; - }; + let mut comma_reached = false; + let Ok(tree_span) = self.psess.source_map().span_extend_while(use_tree_span, |c| { + if comma_reached { + return false; + } + comma_reached = c == ','; + c.is_whitespace() || comma_reached + }) else { + return; + }; - let Ok(use_tree) = self.psess.source_map().span_to_snippet(use_tree_span) else { - return; - }; + let Ok(use_tree) = self.psess.source_map().span_to_snippet(use_tree_span) else { return }; - // FIXME: duplicate the attributes that are at the root of the initial use-item. - let code = format!("{attr}\nuse {prefix}::{use_tree};\n"); + // FIXME: duplicate the attributes that are at the root of the initial use-item. + let code = format!("{attr}\nuse {prefix}::{use_tree};\n"); - let err = crate::diagnostics::AttrInUseTree { + self.dcx().emit_err(crate::diagnostics::AttrInUseTree { + attr_span, + sub: Some(crate::diagnostics::AttrInUseTreeSugg { + use_lo: use_token_span.shrink_to_lo(), attr_span, - sub: Some(crate::diagnostics::AttrInUseTreeSugg { - use_lo: use_token_span.shrink_to_lo(), - attr_span, - tree_span, - code, - }), - }; - self.dcx().emit_err(err); - } + tree_span, + code, + }), + }); } fn parse_rename(&mut self) -> PResult<'a, Option> { From 1bb161062bd877839c8bdb74e510e2f5031dfc5a Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 23 Aug 2026 20:58:16 +0800 Subject: [PATCH 46/46] [Bootstrap] Use full exact CI `llvm-config` executable path Otherwise, this will cause `rustc_llvm` build script to consider the `llvm-config` executable missing, causing cargo build cache invalidation. --- src/bootstrap/src/core/build_steps/llvm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 94c886649109f..e29e112b5ff39 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -285,7 +285,7 @@ fn try_download_ci_llvm(builder: &Builder<'_>, target: TargetSelection) -> Optio Some(DownloadedLlvm { output: LlvmOutput { - host_llvm_config: ci_llvm.join("bin").join("llvm-config"), + host_llvm_config: ci_llvm.join("bin").join(exe("llvm-config", builder.host_target)), link_shared, llvm_root_dir: ci_llvm, kind: LlvmKind::DownloadedFromCi,