From f51fb9178e8d3648a908136d132bbfd1811481fd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 21 Sep 2025 23:39:59 +0200 Subject: [PATCH 01/19] kcfi: only reify trait methods when dyn-compatible --- compiler/rustc_middle/src/ty/instance.rs | 4 +- .../sanitizer/kcfi/fn-ptr-reify-shim.rs | 74 +++++++++++++++++++ .../sanitizer/kcfi/naked-function.rs | 7 +- tests/ui/sanitizer/kcfi-c-variadic.rs | 20 +++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs create mode 100644 tests/ui/sanitizer/kcfi-c-variadic.rs diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 34ead91b4f6de..c27d47fcc0d8d 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -618,11 +618,11 @@ impl<'tcx> Instance<'tcx> { // be directly reified because it's closure-like. The reify can handle the // unresolved instance. resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } - // Reify `Trait::method` implementations - // FIXME(maurer) only reify it if it is a vtable-safe function + // Reify `Trait::method` implementations if the trait is dyn-compatible. } else if let Some(assoc) = tcx.opt_associated_item(def_id) && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = assoc.container + && tcx.is_dyn_compatible(assoc.container_id(tcx)) { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. diff --git a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs new file mode 100644 index 0000000000000..604b4c8c2f8a5 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs @@ -0,0 +1,74 @@ +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +// A `ReifyShim` should only be created when the trait is dyn-compatible. + +extern crate minicore; +use minicore::*; + +trait DynCompatible { + fn dyn_name(&self) -> &'static str; + + fn dyn_name_default(&self) -> &'static str { + let _ = self; + "dyn_default" + } +} + +// Not dyn-compatible because the `Self: Sized` bound is missing. +trait NotDynCompatible { + fn not_dyn_name() -> &'static str; + + fn not_dyn_name_default() -> &'static str { + "not_dyn_default" + } +} + +struct S; + +impl DynCompatible for S { + fn dyn_name(&self) -> &'static str { + "dyn_compatible" + } +} + +impl NotDynCompatible for S { + fn not_dyn_name() -> &'static str { + "not_dyn_compatible" + } +} + +#[no_mangle] +pub fn main() { + let s = S; + + // `DynCompatible` is indeed dyn-compatible. + let _: &dyn DynCompatible = &s; + + // CHECK: call ::dyn_name{{.*}}reify.shim.fnptr + let dyn_name = S::dyn_name as fn(&S) -> &str; + let _unused = dyn_name(&s); + + // CHECK: call fn_ptr_reify_shim::DynCompatible::dyn_name_default{{.*}}reify.shim.fnptr + let dyn_name_default = S::dyn_name_default as fn(&S) -> &str; + let _unused = dyn_name_default(&s); + + // Check using $ (end-of-line) that these calls do not contain `reify.shim.fnptr`. + + // CHECK: call ::not_dyn_name{{$}} + let not_dyn_name = S::not_dyn_name as fn() -> &'static str; + let _unused = not_dyn_name(); + + // CHECK: call fn_ptr_reify_shim::NotDynCompatible::not_dyn_name_default{{$}} + let not_dyn_name_default = S::not_dyn_name_default as fn() -> &'static str; + let _unused = not_dyn_name_default(); +} diff --git a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs index 2c8cdc919b85d..31f59ee01decb 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs @@ -15,8 +15,9 @@ use minicore::*; struct Thing; trait MyTrait { + // NOTE: this test assumes that this trait is dyn-compatible. #[unsafe(naked)] - extern "C" fn my_naked_function() { + extern "C" fn my_naked_function(&self) { // the real function is defined // CHECK: .globl // CHECK-SAME: my_naked_function @@ -34,13 +35,13 @@ impl MyTrait for Thing {} #[unsafe(no_mangle)] pub fn main() { // Trick the compiler into generating an indirect call. - const F: extern "C" fn() = Thing::my_naked_function; + const F: extern "C" fn(&Thing) = Thing::my_naked_function; // main calls the shim function // CHECK: call void // CHECK-SAME: my_naked_function // CHECK-SAME: reify.shim.fnptr - (F)(); + (F)(&Thing); } // CHECK: declare !kcfi_type diff --git a/tests/ui/sanitizer/kcfi-c-variadic.rs b/tests/ui/sanitizer/kcfi-c-variadic.rs new file mode 100644 index 0000000000000..45d00a4524ebf --- /dev/null +++ b/tests/ui/sanitizer/kcfi-c-variadic.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-kcfi +//@ no-prefer-dynamic +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer +//@ ignore-backends: gcc +//@ run-pass + +#![feature(c_variadic)] + +trait Trait { + unsafe extern "C" fn foo(x: i32, y: i32, mut ap: ...) -> i32 { + x + y + ap.arg::() + ap.arg::() + } +} + +impl Trait for i32 {} + +fn main() { + let f = i32::foo as unsafe extern "C" fn(i32, i32, ...) -> i32; + assert_eq!(unsafe { f(1, 2, 3, 4) }, 1 + 2 + 3 + 4); +} From 07d41a7cd7f3ef8ca1e9b70768975695d3536272 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 13 Jul 2025 20:38:12 +0200 Subject: [PATCH 02/19] Correctly handle `--no-run` rustdoc test option --- src/librustdoc/doctest.rs | 2 +- src/librustdoc/doctest/runner.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 9499258f983a1..a1e917df75ab9 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -358,7 +358,7 @@ pub(crate) fn run_tests( ); for (doctest, scraped_test) in &doctests { - tests_runner.add_test(doctest, scraped_test, &target_str); + tests_runner.add_test(doctest, scraped_test, &target_str, rustdoc_options); } let (duration, ret) = tests_runner.run_merged_tests( rustdoc_test_options, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index fcfa424968e48..5493d56456872 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -39,6 +39,7 @@ impl DocTestRunner { doctest: &DocTestBuilder, scraped_test: &ScrapedDocTest, target_str: &str, + opts: &RustdocOptions, ) { let ignore = match scraped_test.langstr.ignore { Ignore::All => true, @@ -62,6 +63,7 @@ impl DocTestRunner { self.nb_tests, &mut self.output, &mut self.output_merged_tests, + opts, ), )); self.supports_color &= doctest.supports_color; @@ -223,6 +225,7 @@ fn generate_mergeable_doctest( id: usize, output: &mut String, output_merged_tests: &mut String, + opts: &RustdocOptions, ) -> String { let test_id = format!("__doctest_{id}"); @@ -256,7 +259,7 @@ fn main() {returns_result} {{ ) .unwrap(); } - let not_running = ignore || scraped_test.langstr.no_run; + let not_running = ignore || scraped_test.no_run(opts); writeln!( output_merged_tests, " @@ -270,7 +273,7 @@ test::StaticTestFn( test_name = scraped_test.name, file = scraped_test.path(), line = scraped_test.line, - no_run = scraped_test.langstr.no_run, + no_run = scraped_test.no_run(opts), should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic, // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply // don't give it the function to run. From 796c4efe44a57ddbaf071937e26f1279e8595302 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 7 Jul 2025 11:53:18 +0200 Subject: [PATCH 03/19] Correctly handle `should_panic` doctest attribute --- src/librustdoc/doctest.rs | 4 ++-- src/librustdoc/doctest/runner.rs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index a1e917df75ab9..df457536b70e8 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -836,7 +836,7 @@ fn run_test( match result { Err(e) => return (duration, Err(TestFailure::ExecutionError(e))), Ok(out) => { - if langstr.should_panic && out.status.success() { + if langstr.should_panic && out.status.code() != Some(101) { return (duration, Err(TestFailure::UnexpectedRunPass)); } else if !langstr.should_panic && !out.status.success() { return (duration, Err(TestFailure::ExecutionFailure(out))); @@ -1146,7 +1146,7 @@ fn doctest_run_fn( eprint!("Test compiled successfully, but it's marked `compile_fail`."); } TestFailure::UnexpectedRunPass => { - eprint!("Test executable succeeded, but it's marked `should_panic`."); + eprint!("Test didn't panic, but it's marked `should_panic`."); } TestFailure::MissingErrorCodes(codes) => { eprint!("Some expected error codes were not found: {codes:?}"); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 5493d56456872..d02b32faa319e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -136,13 +136,20 @@ mod __doctest_mod {{ }} #[allow(unused)] - pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{ + pub fn doctest_runner(bin: &std::path::Path, test_nb: usize, should_panic: bool) -> ExitCode {{ let out = std::process::Command::new(bin) .env(self::RUN_OPTION, test_nb.to_string()) .args(std::env::args().skip(1).collect::>()) .output() .expect(\"failed to run command\"); - if !out.status.success() {{ + if should_panic {{ + if out.status.code() != Some(101) {{ + eprintln!(\"Test didn't panic, but it's marked `should_panic`.\"); + ExitCode::FAILURE + }} else {{ + ExitCode::SUCCESS + }} + }} else if !out.status.success() {{ if let Some(code) = out.status.code() {{ eprintln!(\"Test executable failed (exit status: {{code}}).\"); }} else {{ @@ -265,7 +272,7 @@ fn main() {returns_result} {{ " mod {test_id} {{ pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( -{test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic}, +{test_name:?}, {ignore}, {file:?}, {line}, {no_run}, false, test::StaticTestFn( || {{{runner}}}, )); @@ -274,7 +281,6 @@ test::StaticTestFn( file = scraped_test.path(), line = scraped_test.line, no_run = scraped_test.no_run(opts), - should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic, // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply // don't give it the function to run. runner = if not_running { @@ -283,11 +289,12 @@ test::StaticTestFn( format!( " if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ - test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id})) + test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}, {should_panic})) }} else {{ test::assert_test_result(doctest_bundle::{test_id}::__main_fn()) }} ", + should_panic = scraped_test.langstr.should_panic, ) }, ) From 11b7070577e52c1d26b35021b8a07475e794e93c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Jul 2025 21:42:19 +0200 Subject: [PATCH 04/19] Add regression test for #143009 --- tests/run-make/rustdoc-should-panic/rmake.rs | 36 ++++++++++++++++++++ tests/run-make/rustdoc-should-panic/test.rs | 14 ++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/run-make/rustdoc-should-panic/rmake.rs create mode 100644 tests/run-make/rustdoc-should-panic/test.rs diff --git a/tests/run-make/rustdoc-should-panic/rmake.rs b/tests/run-make/rustdoc-should-panic/rmake.rs new file mode 100644 index 0000000000000..100a62f5db313 --- /dev/null +++ b/tests/run-make/rustdoc-should-panic/rmake.rs @@ -0,0 +1,36 @@ +// Ensure that `should_panic` doctests only succeed if the test actually panicked. +// Regression test for . + +//@ needs-target-std + +use run_make_support::rustdoc; + +fn check_output(output: String, edition: &str) { + let should_contain = &[ + "test test.rs - bad_exit_code (line 1) ... FAILED", + "test test.rs - did_not_panic (line 6) ... FAILED", + "test test.rs - did_panic (line 11) ... ok", + "---- test.rs - bad_exit_code (line 1) stdout ---- +Test executable failed (exit status: 1).", + "---- test.rs - did_not_panic (line 6) stdout ---- +Test didn't panic, but it's marked `should_panic`.", + "test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out;", + ]; + for text in should_contain { + assert!( + output.contains(text), + "output doesn't contains (edition: {edition}) {:?}\nfull output: {output}", + text + ); + } +} + +fn main() { + check_output(rustdoc().input("test.rs").arg("--test").run_fail().stdout_utf8(), "2015"); + + // Same check with the merged doctest feature (enabled with the 2024 edition). + check_output( + rustdoc().input("test.rs").arg("--test").edition("2024").run_fail().stdout_utf8(), + "2024", + ); +} diff --git a/tests/run-make/rustdoc-should-panic/test.rs b/tests/run-make/rustdoc-should-panic/test.rs new file mode 100644 index 0000000000000..1eea8e1e1958c --- /dev/null +++ b/tests/run-make/rustdoc-should-panic/test.rs @@ -0,0 +1,14 @@ +/// ``` +/// std::process::exit(1); +/// ``` +fn bad_exit_code() {} + +/// ```should_panic +/// std::process::exit(1); +/// ``` +fn did_not_panic() {} + +/// ```should_panic +/// panic!("yeay"); +/// ``` +fn did_panic() {} From 21a4d9dda7b2234d4454c2413956f96d8884cd65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 7 Jul 2025 13:35:02 +0200 Subject: [PATCH 05/19] Update std doctests --- library/std/src/error.rs | 16 ++++++++++++---- .../failed-doctest-should-panic-2021.stdout | 2 +- .../doctest/failed-doctest-should-panic.stdout | 5 +++-- tests/rustdoc-ui/doctest/wrong-ast-2024.stdout | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index def5f984c88e4..09bfc83ebca6c 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -123,7 +123,7 @@ use crate::fmt::{self, Write}; /// the `Debug` output means `Report` is an ideal starting place for formatting errors returned /// from `main`. /// -/// ```should_panic +/// ``` /// #![feature(error_reporter)] /// use std::error::Report; /// # use std::error::Error; @@ -154,10 +154,14 @@ use crate::fmt::{self, Write}; /// # Err(SuperError { source: SuperErrorSideKick }) /// # } /// -/// fn main() -> Result<(), Report> { +/// fn run() -> Result<(), Report> { /// get_super_error()?; /// Ok(()) /// } +/// +/// fn main() { +/// assert!(run().is_err()); +/// } /// ``` /// /// This example produces the following output: @@ -170,7 +174,7 @@ use crate::fmt::{self, Write}; /// output format. If you want to make sure your `Report`s are pretty printed and include backtrace /// you will need to manually convert and enable those flags. /// -/// ```should_panic +/// ``` /// #![feature(error_reporter)] /// use std::error::Report; /// # use std::error::Error; @@ -201,12 +205,16 @@ use crate::fmt::{self, Write}; /// # Err(SuperError { source: SuperErrorSideKick }) /// # } /// -/// fn main() -> Result<(), Report> { +/// fn run() -> Result<(), Report> { /// get_super_error() /// .map_err(Report::from) /// .map_err(|r| r.pretty(true).show_backtrace(true))?; /// Ok(()) /// } +/// +/// fn main() { +/// assert!(run().is_err()); +/// } /// ``` /// /// This example produces the following output: diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout index 9f4d60e6f4de5..f8413756e3d6d 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout @@ -5,7 +5,7 @@ test $DIR/failed-doctest-should-panic-2021.rs - Foo (line 10) ... FAILED failures: ---- $DIR/failed-doctest-should-panic-2021.rs - Foo (line 10) stdout ---- -Test executable succeeded, but it's marked `should_panic`. +Test didn't panic, but it's marked `should_panic`. failures: $DIR/failed-doctest-should-panic-2021.rs - Foo (line 10) diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index 9047fe0dcdd93..61099e6424ae7 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -1,11 +1,12 @@ running 1 test -test $DIR/failed-doctest-should-panic.rs - Foo (line 12) - should panic ... FAILED +test $DIR/failed-doctest-should-panic.rs - Foo (line 12) ... FAILED failures: ---- $DIR/failed-doctest-should-panic.rs - Foo (line 12) stdout ---- -note: test did not panic as expected at $DIR/failed-doctest-should-panic.rs:12:0 +Test didn't panic, but it's marked `should_panic`. + failures: $DIR/failed-doctest-should-panic.rs - Foo (line 12) diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout index 13567b41e51f5..27f9a0157a6cc 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -1,6 +1,6 @@ running 1 test -test $DIR/wrong-ast-2024.rs - three (line 20) - should panic ... ok +test $DIR/wrong-ast-2024.rs - three (line 20) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME From 5f2ae4fb078569486b3a285b3bc5378e3bf7c9fb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 13 Jul 2025 20:49:29 +0200 Subject: [PATCH 06/19] Add regression test for #143858 --- tests/run-make/rustdoc-should-panic/rmake.rs | 2 +- .../doctest/no-run.edition2021.stdout | 12 +++++ .../doctest/no-run.edition2024.stdout | 18 ++++++++ tests/rustdoc-ui/doctest/no-run.rs | 44 +++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-ui/doctest/no-run.edition2021.stdout create mode 100644 tests/rustdoc-ui/doctest/no-run.edition2024.stdout create mode 100644 tests/rustdoc-ui/doctest/no-run.rs diff --git a/tests/run-make/rustdoc-should-panic/rmake.rs b/tests/run-make/rustdoc-should-panic/rmake.rs index 100a62f5db313..ffe3c9ef6eb60 100644 --- a/tests/run-make/rustdoc-should-panic/rmake.rs +++ b/tests/run-make/rustdoc-should-panic/rmake.rs @@ -1,7 +1,7 @@ // Ensure that `should_panic` doctests only succeed if the test actually panicked. // Regression test for . -//@ needs-target-std +//@ ignore-cross-compile use run_make_support::rustdoc; diff --git a/tests/rustdoc-ui/doctest/no-run.edition2021.stdout b/tests/rustdoc-ui/doctest/no-run.edition2021.stdout new file mode 100644 index 0000000000000..937cd76bfb462 --- /dev/null +++ b/tests/rustdoc-ui/doctest/no-run.edition2021.stdout @@ -0,0 +1,12 @@ + +running 7 tests +test $DIR/no-run.rs - f (line 14) - compile ... ok +test $DIR/no-run.rs - f (line 17) - compile ... ok +test $DIR/no-run.rs - f (line 20) ... ignored +test $DIR/no-run.rs - f (line 23) - compile ... ok +test $DIR/no-run.rs - f (line 29) - compile fail ... ok +test $DIR/no-run.rs - f (line 34) - compile ... ok +test $DIR/no-run.rs - f (line 38) - compile ... ok + +test result: ok. 6 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/no-run.edition2024.stdout b/tests/rustdoc-ui/doctest/no-run.edition2024.stdout new file mode 100644 index 0000000000000..921e059979b1f --- /dev/null +++ b/tests/rustdoc-ui/doctest/no-run.edition2024.stdout @@ -0,0 +1,18 @@ + +running 5 tests +test $DIR/no-run.rs - f (line 14) - compile ... ok +test $DIR/no-run.rs - f (line 17) - compile ... ok +test $DIR/no-run.rs - f (line 23) - compile ... ok +test $DIR/no-run.rs - f (line 34) - compile ... ok +test $DIR/no-run.rs - f (line 38) - compile ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 2 tests +test $DIR/no-run.rs - f (line 20) ... ignored +test $DIR/no-run.rs - f (line 29) - compile fail ... ok + +test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/no-run.rs b/tests/rustdoc-ui/doctest/no-run.rs new file mode 100644 index 0000000000000..78198badd43b5 --- /dev/null +++ b/tests/rustdoc-ui/doctest/no-run.rs @@ -0,0 +1,44 @@ +// This test ensures that the `--no-run` flag works the same between normal and merged doctests. +// Regression test for . + +//@ check-pass +//@ revisions: edition2021 edition2024 +//@ [edition2021]edition:2021 +//@ [edition2024]edition:2024 +//@ compile-flags:-Z unstable-options --test --no-run --test-args=--test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" + +/// ``` +/// let a = true; +/// ``` +/// ```should_panic +/// panic!() +/// ``` +/// ```ignore (incomplete-code) +/// fn foo() { +/// ``` +/// ```no_run +/// loop { +/// println!("Hello, world"); +/// } +/// ``` +/// fails to compile +/// ```compile_fail +/// let x = 5; +/// x += 2; // shouldn't compile! +/// ``` +/// Ok the test does not run +/// ``` +/// panic!() +/// ``` +/// Ok the test does not run +/// ```should_panic +/// loop { +/// println!("Hello, world"); +/// panic!() +/// } +/// ``` +pub fn f() {} From b001ba67dda247037b052cfa76b154d2df3908ab Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 1 Aug 2025 11:28:17 +0200 Subject: [PATCH 07/19] Add FIXME comments to use `test::ERROR_EXIT_CODE` once public and fix typo --- src/librustdoc/doctest.rs | 1 + src/librustdoc/doctest/runner.rs | 1 + tests/run-make/rustdoc-should-panic/rmake.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index df457536b70e8..51972f8b149c7 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -836,6 +836,7 @@ fn run_test( match result { Err(e) => return (duration, Err(TestFailure::ExecutionError(e))), Ok(out) => { + // FIXME: use test::ERROR_EXIT_CODE once public if langstr.should_panic && out.status.code() != Some(101) { return (duration, Err(TestFailure::UnexpectedRunPass)); } else if !langstr.should_panic && !out.status.success() { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index d02b32faa319e..d241d44441d53 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -143,6 +143,7 @@ mod __doctest_mod {{ .output() .expect(\"failed to run command\"); if should_panic {{ + // FIXME: use test::ERROR_EXIT_CODE once public if out.status.code() != Some(101) {{ eprintln!(\"Test didn't panic, but it's marked `should_panic`.\"); ExitCode::FAILURE diff --git a/tests/run-make/rustdoc-should-panic/rmake.rs b/tests/run-make/rustdoc-should-panic/rmake.rs index ffe3c9ef6eb60..4d6e2c98ae771 100644 --- a/tests/run-make/rustdoc-should-panic/rmake.rs +++ b/tests/run-make/rustdoc-should-panic/rmake.rs @@ -19,7 +19,7 @@ Test didn't panic, but it's marked `should_panic`.", for text in should_contain { assert!( output.contains(text), - "output doesn't contains (edition: {edition}) {:?}\nfull output: {output}", + "output (edition: {edition}) doesn't contain {:?}\nfull output: {output}", text ); } From 030b66441986be7b84d355d5377cb68783b4b564 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 4 Oct 2025 13:39:31 +0200 Subject: [PATCH 08/19] Use libtest `ERROR_EXIT_CODE` constant --- src/librustdoc/doctest.rs | 3 +-- src/librustdoc/doctest/runner.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 51972f8b149c7..0eb698ca544fe 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -836,8 +836,7 @@ fn run_test( match result { Err(e) => return (duration, Err(TestFailure::ExecutionError(e))), Ok(out) => { - // FIXME: use test::ERROR_EXIT_CODE once public - if langstr.should_panic && out.status.code() != Some(101) { + if langstr.should_panic && out.status.code() != Some(test::ERROR_EXIT_CODE) { return (duration, Err(TestFailure::UnexpectedRunPass)); } else if !langstr.should_panic && !out.status.success() { return (duration, Err(TestFailure::ExecutionFailure(out))); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index d241d44441d53..66ffb8aae23e6 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -143,8 +143,7 @@ mod __doctest_mod {{ .output() .expect(\"failed to run command\"); if should_panic {{ - // FIXME: use test::ERROR_EXIT_CODE once public - if out.status.code() != Some(101) {{ + if out.status.code() != Some(test::ERROR_EXIT_CODE) {{ eprintln!(\"Test didn't panic, but it's marked `should_panic`.\"); ExitCode::FAILURE }} else {{ From 7e1c00dbce512b260c396b4425527b00891168a7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 17 Sep 2025 17:06:14 +0000 Subject: [PATCH 09/19] Prevent downstream impl DerefMut for Pin --- library/core/src/pin.rs | 59 ++++++++++++++++++- ...y.run2-{closure#0}.Inline.panic-abort.diff | 56 ++++++++++-------- ....run2-{closure#0}.Inline.panic-unwind.diff | 56 ++++++++++-------- tests/ui/deref/pin-impl-deref.rs | 4 +- tests/ui/deref/pin-impl-deref.stderr | 22 ++++--- .../pin-unsound-issue-85099-derefmut.rs | 4 +- .../pin-unsound-issue-85099-derefmut.stderr | 14 +++++ 7 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 535830f2e749f..d7e348b71e1d0 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1689,9 +1689,66 @@ impl const Deref for Pin { } } +mod helper { + /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. + /// + /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in + /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + #[repr(transparent)] + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[allow(missing_debug_implementations)] + pub struct Pin { + pointer: Ptr, + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const trait DerefMut { + type Target: ?Sized; + fn deref_mut(&mut self) -> &mut Self::Target; + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const DerefMut for Pin + where + Ptr::Target: crate::marker::Unpin, + { + type Target = Ptr::Target; + + #[inline(always)] + fn deref_mut(&mut self) -> &mut Ptr::Target { + &mut self.pointer + } + } +} + #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -impl> const DerefMut for Pin { +#[cfg(not(doc))] +impl const DerefMut for Pin +where + Ptr: [const] Deref, + helper::Pin: [const] helper::DerefMut, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Ptr::Target { + // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // `&mut self.pointer` which is safe because `Target: Unpin`. + helper::DerefMut::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::Pin) + }) + } +} + +#[stable(feature = "pin", since = "1.33.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +#[cfg(doc)] +impl const DerefMut for Pin +where + Ptr: [const] DerefMut, + Ptr::Target: Unpin, +{ fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 0acb33febe52b..226a16d6eec39 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,27 +63,25 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _46: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _48: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { + } + } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _47: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _49: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _48: isize; -+ let mut _49: !; -+ scope 23 { ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _50: isize; ++ let mut _51: !; ++ scope 21 { + } + } + } @@ -217,18 +215,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_44); -+ StorageLive(_49); ++ StorageLive(_46); ++ StorageLive(_51); + StorageLive(_41); + StorageLive(_42); -+ _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_47); -+ _47 = Option::<()>::None; -+ _42 = copy ((*_44).0: std::option::Option<()>); -+ ((*_44).0: std::option::Option<()>) = copy _47; ++ _47 = &raw mut _19; ++ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); -+ StorageLive(_48); -+ _48 = discriminant(_42); -+ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _44 = copy ((*_46).0: &mut std::future::Ready<()>); ++ StorageLive(_49); ++ _49 = Option::<()>::None; ++ _42 = copy ((*_44).0: std::option::Option<()>); ++ ((*_44).0: std::option::Option<()>) = copy _49; ++ StorageDead(_49); ++ StorageLive(_50); ++ _50 = discriminant(_42); ++ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -291,16 +294,17 @@ + } + + bb11: { -+ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { + _41 = move ((_42 as Some).0: ()); -+ StorageDead(_48); ++ StorageDead(_50); + StorageDead(_42); + _18 = Poll::<()>::Ready(move _41); + StorageDead(_41); -+ StorageDead(_49); ++ StorageDead(_51); ++ StorageDead(_46); + StorageDead(_44); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 98ee46c29b1be..770b6e471ff8b 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,27 +65,25 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _48: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _50: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { + } + } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _49: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _51: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _50: isize; -+ let mut _51: !; -+ scope 23 { ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _52: isize; ++ let mut _53: !; ++ scope 21 { + } + } + } @@ -234,18 +232,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_46); -+ StorageLive(_51); ++ StorageLive(_48); ++ StorageLive(_53); + StorageLive(_43); + StorageLive(_44); -+ _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_49); -+ _49 = Option::<()>::None; -+ _44 = copy ((*_46).0: std::option::Option<()>); -+ ((*_46).0: std::option::Option<()>) = copy _49; ++ _49 = &raw mut _19; ++ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); -+ StorageLive(_50); -+ _50 = discriminant(_44); -+ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _46 = copy ((*_48).0: &mut std::future::Ready<()>); ++ StorageLive(_51); ++ _51 = Option::<()>::None; ++ _44 = copy ((*_46).0: std::option::Option<()>); ++ ((*_46).0: std::option::Option<()>) = copy _51; ++ StorageDead(_51); ++ StorageLive(_52); ++ _52 = discriminant(_44); ++ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -332,16 +335,17 @@ + } + + bb16: { -+ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { + _43 = move ((_44 as Some).0: ()); -+ StorageDead(_50); ++ StorageDead(_52); + StorageDead(_44); + _18 = Poll::<()>::Ready(move _43); + StorageDead(_43); -+ StorageDead(_51); ++ StorageDead(_53); ++ StorageDead(_48); + StorageDead(_46); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/ui/deref/pin-impl-deref.rs b/tests/ui/deref/pin-impl-deref.rs index b1dc8dea3f248..ccd8d0dfc72ae 100644 --- a/tests/ui/deref/pin-impl-deref.rs +++ b/tests/ui/deref/pin-impl-deref.rs @@ -22,7 +22,7 @@ impl MyPinType { fn impl_deref_mut(_: impl DerefMut) {} fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) { impl_deref_mut(r_unpin) - //~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied + //~^ ERROR: the trait bound `&MyUnpinType: DerefMut` is not satisfied } fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { impl_deref_mut(r_unpin) @@ -30,7 +30,7 @@ fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { fn pin_impl_ref(r_pin: Pin<&MyPinType>) { impl_deref_mut(r_pin) //~^ ERROR: `PhantomPinned` cannot be unpinned - //~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied + //~| ERROR: the trait bound `&MyPinType: DerefMut` is not satisfied } fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) { impl_deref_mut(r_pin) diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 106654641a117..918ff7ca91218 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -1,40 +1,36 @@ -error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyUnpinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:24:20 | LL | impl_deref_mut(r_unpin) - | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>` + | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `&MyUnpinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` + = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_unpin) - | ++++ -error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyPinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:31:20 | LL | impl_deref_mut(r_pin) - | -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>` + | -------------- ^^^^^ the trait `DerefMut` is not implemented for `&MyPinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_pin) - | ++++ error[E0277]: `PhantomPinned` cannot be unpinned --> $DIR/pin-impl-deref.rs:31:20 @@ -51,6 +47,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -73,6 +70,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs index f3ece563f5403..e8c3bbba1e458 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs @@ -1,5 +1,4 @@ -//@ check-pass -//@ known-bug: #85099 +//@ check-fail // Should fail. Can coerce `Pin` into `Pin` where // `T: Deref` and `U: Deref`, using the @@ -43,6 +42,7 @@ impl<'a, Fut: Future> SomeTrait<'a, Fut> for Fut { } impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { +//~^ ERROR: conflicting implementations of trait `DerefMut` fn deref_mut<'c>( self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>, ) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) { diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr new file mode 100644 index 0000000000000..cc56c77809eaa --- /dev/null +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `DerefMut` for type `Pin<&dyn SomeTrait<'_, _>>` + --> $DIR/pin-unsound-issue-85099-derefmut.rs:44:1 + | +LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl DerefMut for Pin + where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. From cd44cd0f63a7e67dcfc3618aa7bf6e2e70643561 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 25 Sep 2025 08:39:35 +0000 Subject: [PATCH 10/19] Improve docs for module --- library/core/src/pin.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d7e348b71e1d0..fdbeaea464194 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1692,8 +1692,22 @@ impl const Deref for Pin { mod helper { /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. /// - /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in - /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + /// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires + /// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this + /// helper module, downstream crates are able to write `impl DerefMut for Pin` as + /// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is + /// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not + /// implement today. + /// + /// However, this is problematic. Downstream crates could implement `DerefMut` for + /// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for + /// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the + /// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in + /// the future. Because of this, downstream crates can no longer provide an implementation of + /// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the + /// orphan rules, the stdlib could introduce without a breaking change in a future release. + /// + /// See for the issue this fixes. #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] From f65250b3dd0eada1ab25ad4f766048f2ebe24623 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 29 Sep 2025 09:34:18 +0000 Subject: [PATCH 11/19] Document workaround in docs --- library/core/src/pin.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index fdbeaea464194..23950acd97610 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1755,13 +1755,21 @@ where } } +/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference +/// to a pinned value. +/// +/// For soundness reasons, implementations of `DerefMut` for `Pin` are rejected even when `T` is +/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations +/// would normally be possible.) +/// +/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(doc)] impl const DerefMut for Pin where Ptr: [const] DerefMut, - Ptr::Target: Unpin, + ::Target: Unpin, { fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) From 76dcb39c240382a7d62cc8ee475e0c3866ad8025 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 1 Oct 2025 11:14:08 +0000 Subject: [PATCH 12/19] Adjust error messages --- compiler/rustc_span/src/symbol.rs | 1 + .../src/error_reporting/traits/suggestions.rs | 18 ++++++++++++++++++ library/core/src/pin.rs | 15 ++++++++------- ...dy.run2-{closure#0}.Inline.panic-abort.diff | 6 +++--- ...y.run2-{closure#0}.Inline.panic-unwind.diff | 6 +++--- tests/ui/deref/pin-impl-deref.stderr | 4 ---- .../pin-unsound-issue-85099-derefmut.stderr | 4 ++-- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc37..7f1ca96b2a75b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -309,6 +309,7 @@ symbols! { PathBuf, Pending, PinCoerceUnsized, + PinDerefMutHelper, Pointer, Poll, ProcMacro, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 37e622102e70f..5665229a4cbe6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3476,6 +3476,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // can do about it. As far as they are concerned, `?` is compiler magic. return; } + if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) { + let parent_predicate = + self.resolve_vars_if_possible(data.derived.parent_trait_pred); + + // Skip PinDerefMutHelper in suggestions, but still show downstream suggestions. + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.derived.parent_code, + obligated_types, + seen_requirements, + ) + }); + return; + } let self_ty_str = tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path()); let trait_name = tcx.short_string( diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 23950acd97610..81c2dabf0d1d8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1711,20 +1711,21 @@ mod helper { #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] - pub struct Pin { + pub struct PinHelper { pointer: Ptr, } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - pub const trait DerefMut { + #[rustc_diagnostic_item = "PinDerefMutHelper"] + pub const trait PinDerefMutHelper { type Target: ?Sized; fn deref_mut(&mut self) -> &mut Self::Target; } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - impl const DerefMut for Pin + impl const PinDerefMutHelper for PinHelper where Ptr::Target: crate::marker::Unpin, { @@ -1743,14 +1744,14 @@ mod helper { impl const DerefMut for Pin where Ptr: [const] Deref, - helper::Pin: [const] helper::DerefMut, + helper::PinHelper: [const] helper::PinDerefMutHelper, { #[inline] fn deref_mut(&mut self) -> &mut Ptr::Target { - // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // SAFETY: Pin and PinHelper have the same layout, so this is equivalent to // `&mut self.pointer` which is safe because `Target: Unpin`. - helper::DerefMut::deref_mut(unsafe { - &mut *(self as *mut Pin as *mut helper::Pin) + helper::PinDerefMutHelper::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::PinHelper) }) } } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 226a16d6eec39..0203ff52d63f8 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,9 +63,9 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _46: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _48: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -221,7 +221,7 @@ + StorageLive(_42); + StorageLive(_47); + _47 = &raw mut _19; -+ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _46 = copy _47 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); + _44 = copy ((*_46).0: &mut std::future::Ready<()>); + StorageLive(_49); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 770b6e471ff8b..6c8cad3e992e7 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,9 +65,9 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _48: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _50: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -238,7 +238,7 @@ + StorageLive(_44); + StorageLive(_49); + _49 = &raw mut _19; -+ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _48 = copy _49 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); + _46 = copy ((*_48).0: &mut std::future::Ready<()>); + StorageLive(_51); diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 918ff7ca91218..4143d66f42723 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -7,7 +7,6 @@ LL | impl_deref_mut(r_unpin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` - = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -24,7 +23,6 @@ LL | impl_deref_mut(r_pin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -47,7 +45,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -70,7 +67,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr index cc56c77809eaa..2bcd92b76a09d 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -6,8 +6,8 @@ LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { | = note: conflicting implementation in crate `core`: - impl DerefMut for Pin - where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; - = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + where as pin::helper::PinDerefMutHelper>::Target == as Deref>::Target, Ptr: Deref, pin::helper::PinHelper: pin::helper::PinDerefMutHelper, pin::helper::PinHelper: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::PinDerefMutHelper` for type `std::pin::helper::PinHelper<&dyn SomeTrait<'_, _>>` in future versions error: aborting due to 1 previous error From 560d4505591c160f0cfbfcc7653f281e8325cc0b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 5 Oct 2025 20:43:05 +0200 Subject: [PATCH 13/19] Correctly handle `should_panic` on targets not supporting it --- src/librustdoc/doctest.rs | 16 ++++++++++++++++ src/librustdoc/doctest/runner.rs | 11 ++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 0eb698ca544fe..c3db6b712b316 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -803,6 +803,22 @@ fn run_test( let duration = instant.elapsed(); if doctest.no_run { return (duration, Ok(())); + } else if doctest.langstr.should_panic + // Equivalent of: + // + // ``` + // (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm")) + // && !cfg!(target_os = "emscripten") + // ``` + && let TargetTuple::TargetTuple(ref s) = rustdoc_options.target + && let mut iter = s.split('-') + && let Some(arch) = iter.next() + && iter.next().is_some() + && let os = iter.next() + && (arch.starts_with("wasm") || os == Some("zkvm")) && os != Some("emscripten") + { + // We cannot correctly handle `should_panic` in some wasm targets so we exit early. + return (duration, Ok(())); } // Run the code! diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 66ffb8aae23e6..d7fd586ebac1c 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -129,6 +129,9 @@ mod __doctest_mod {{ pub static BINARY_PATH: OnceLock = OnceLock::new(); pub const RUN_OPTION: &str = \"RUSTDOC_DOCTEST_RUN_NB_TEST\"; + pub const SHOULD_PANIC_DISABLED: bool = ( + cfg!(target_family = \"wasm\") || cfg!(target_os = \"zkvm\") + ) && !cfg!(target_os = \"emscripten\"); #[allow(unused)] pub fn doctest_path() -> Option<&'static PathBuf> {{ @@ -266,13 +269,14 @@ fn main() {returns_result} {{ ) .unwrap(); } + let should_panic = scraped_test.langstr.should_panic; let not_running = ignore || scraped_test.no_run(opts); writeln!( output_merged_tests, " mod {test_id} {{ pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( -{test_name:?}, {ignore}, {file:?}, {line}, {no_run}, false, +{test_name:?}, {ignore} || ({should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED), {file:?}, {line}, {no_run}, false, test::StaticTestFn( || {{{runner}}}, )); @@ -288,13 +292,14 @@ test::StaticTestFn( } else { format!( " -if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ +if {should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED {{ + test::assert_test_result(Ok::<(), String>(())) +}} else if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}, {should_panic})) }} else {{ test::assert_test_result(doctest_bundle::{test_id}::__main_fn()) }} ", - should_panic = scraped_test.langstr.should_panic, ) }, ) From 525ed4cc5cfc9a8230deccf92edd4a41fbebc8ee Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 7 Oct 2025 17:26:01 +1100 Subject: [PATCH 14/19] Read the whole test file before parsing directives Few tests are larger than a handful of kilobytes, and nowadays we scan the whole file for directives anyway, so there's little reason not to just read the whole thing up-front. This avoids having to deal with I/O within `iter_directives`, which should make it easier to overhaul directive processing. --- src/tools/compiletest/src/directives.rs | 37 +++++++------------ src/tools/compiletest/src/directives/tests.rs | 36 ++++++++---------- src/tools/compiletest/src/lib.rs | 5 ++- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index dab6850e0d62a..34ecbc2d59f10 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1,9 +1,6 @@ use std::collections::HashSet; -use std::env; -use std::fs::File; -use std::io::BufReader; -use std::io::prelude::*; use std::process::Command; +use std::{env, fs}; use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; @@ -54,18 +51,19 @@ pub struct EarlyProps { impl EarlyProps { pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { - let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops"); - Self::from_reader(config, testfile, file) + let file_contents = + fs::read_to_string(testfile).expect("read test file to parse earlyprops"); + Self::from_file_contents(config, testfile, &file_contents) } - pub fn from_reader(config: &Config, testfile: &Utf8Path, rdr: R) -> Self { + pub fn from_file_contents(config: &Config, testfile: &Utf8Path, file_contents: &str) -> Self { let mut props = EarlyProps::default(); let mut poisoned = false; iter_directives( config.mode, &mut poisoned, testfile, - rdr, + file_contents, // (dummy comment to force args into vertical layout) &mut |ref ln: DirectiveLine<'_>| { parse_and_update_aux(config, ln, testfile, &mut props.aux); @@ -362,7 +360,7 @@ impl TestProps { fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) { let mut has_edition = false; if !testfile.is_dir() { - let file = File::open(testfile.as_std_path()).unwrap(); + let file_contents = fs::read_to_string(testfile).unwrap(); let mut poisoned = false; @@ -370,7 +368,7 @@ impl TestProps { config.mode, &mut poisoned, testfile, - file, + &file_contents, &mut |ref ln: DirectiveLine<'_>| { if !ln.applies_to_test_revision(test_revision) { return; @@ -859,7 +857,7 @@ fn iter_directives( mode: TestMode, poisoned: &mut bool, testfile: &Utf8Path, - rdr: impl Read, + file_contents: &str, it: &mut dyn FnMut(DirectiveLine<'_>), ) { if testfile.is_dir() { @@ -886,16 +884,7 @@ fn iter_directives( } } - let mut rdr = BufReader::with_capacity(1024, rdr); - let mut ln = String::new(); - let mut line_number = 0; - - loop { - line_number += 1; - ln.clear(); - if rdr.read_line(&mut ln).unwrap() == 0 { - break; - } + for (line_number, ln) in (1..).zip(file_contents.lines()) { let ln = ln.trim(); let Some(directive_line) = line_directive(line_number, ln) else { @@ -1359,13 +1348,13 @@ where Some((min, max)) } -pub(crate) fn make_test_description( +pub(crate) fn make_test_description( config: &Config, cache: &DirectivesCache, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, test_revision: Option<&str>, poisoned: &mut bool, ) -> CollectedTestDesc { @@ -1380,7 +1369,7 @@ pub(crate) fn make_test_description( config.mode, &mut local_poisoned, path, - src, + file_contents, &mut |ref ln @ DirectiveLine { line_number, .. }| { if !ln.applies_to_test_revision(test_revision) { return; diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 95dd46532ba88..77080c7469371 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1,5 +1,3 @@ -use std::io::Read; - use camino::Utf8Path; use semver::Version; @@ -10,12 +8,12 @@ use crate::directives::{ }; use crate::executor::{CollectedTestDesc, ShouldPanic}; -fn make_test_description( +fn make_test_description( config: &Config, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, revision: Option<&str>, ) -> CollectedTestDesc { let cache = DirectivesCache::load(config); @@ -26,7 +24,7 @@ fn make_test_description( name, path, filterable_path, - src, + file_contents, revision, &mut poisoned, ); @@ -226,14 +224,13 @@ fn cfg() -> ConfigBuilder { } fn parse_rs(config: &Config, contents: &str) -> EarlyProps { - let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes) + EarlyProps::from_file_contents(config, Utf8Path::new("a.rs"), contents) } fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None); + let d = make_test_description(&config, tn, p, p, contents, None); d.ignore } @@ -243,9 +240,9 @@ fn should_fail() { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None); + let d = make_test_description(&config, tn.clone(), p, p, "", None); assert_eq!(d.should_panic, ShouldPanic::No); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None); + let d = make_test_description(&config, tn, p, p, "//@ should-fail", None); assert_eq!(d.should_panic, ShouldPanic::Yes); } @@ -778,9 +775,8 @@ fn threads_support() { } } -fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) { - let rdr = std::io::Cursor::new(&buf); - iter_directives(TestMode::Ui, poisoned, path, rdr, &mut |_| {}); +fn run_path(poisoned: &mut bool, path: &Utf8Path, file_contents: &str) { + iter_directives(TestMode::Ui, poisoned, path, file_contents, &mut |_| {}); } #[test] @@ -789,7 +785,7 @@ fn test_unknown_directive_check() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/unknown_directive.rs"), + include_str!("./test-auxillary/unknown_directive.rs"), ); assert!(poisoned); } @@ -800,7 +796,7 @@ fn test_known_directive_check_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/known_directive.rs"), + include_str!("./test-auxillary/known_directive.rs"), ); assert!(!poisoned); } @@ -811,7 +807,7 @@ fn test_error_annotation_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/error_annotation.rs"), + include_str!("./test-auxillary/error_annotation.rs"), ); assert!(!poisoned); } @@ -822,7 +818,7 @@ fn test_non_rs_unknown_directive_not_checked() { run_path( &mut poisoned, Utf8Path::new("a.Makefile"), - include_bytes!("./test-auxillary/not_rs.Makefile"), + include_str!("./test-auxillary/not_rs.Makefile"), ); assert!(!poisoned); } @@ -830,21 +826,21 @@ fn test_non_rs_unknown_directive_not_checked() { #[test] fn test_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm"); assert!(poisoned); } #[test] fn test_trailing_directive_with_comment() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm with comment"); assert!(poisoned); } #[test] fn test_not_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ revisions: incremental"); assert!(!poisoned); } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2d759279f34bc..ac1a8226112a9 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -892,7 +892,8 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te // `CollectedTest` that can be handed over to the test executor. collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. - let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); + let file_contents = + fs::read_to_string(&test_path).expect("read test file to parse ignores"); let (test_name, filterable_path) = make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. @@ -904,7 +905,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te test_name, &test_path, &filterable_path, - src_file, + &file_contents, revision, &mut collector.poisoned, ); From a3ba8d8a9de05a04cbe4a1fa0161bbbe6a389864 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 21:47:02 -0400 Subject: [PATCH 15/19] Use globals instead of metadata, since metadata isn't emitted in debug builds --- .../src/builder/autodiff.rs | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 4a749642265df..c4ad8e4d075f7 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -13,7 +13,7 @@ use crate::builder::{Builder, PlaceRef, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; use crate::llvm; -use crate::llvm::{Metadata, TRUE, Type}; +use crate::llvm::{TRUE, Type}; use crate::value::Value; pub(crate) fn adjust_activity_to_abi<'tcx>( @@ -159,32 +159,36 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( let mut outer_pos: usize = 0; let mut activity_pos = 0; - let enzyme_const = cx.create_metadata(b"enzyme_const"); - let enzyme_out = cx.create_metadata(b"enzyme_out"); - let enzyme_dup = cx.create_metadata(b"enzyme_dup"); - let enzyme_dupv = cx.create_metadata(b"enzyme_dupv"); - let enzyme_dupnoneed = cx.create_metadata(b"enzyme_dupnoneed"); - let enzyme_dupnoneedv = cx.create_metadata(b"enzyme_dupnoneedv"); + // We used to use llvm's metadata to instruct enzyme how to differentiate a function. + // In debug mode we would use incremental compilation which caused the metadata to be + // dropped. This is prevented by now using named globals, which are also understood + // by Enzyme. + let global_const = cx.declare_global("enzyme_const", cx.type_ptr()); + let global_out = cx.declare_global("enzyme_out", cx.type_ptr()); + let global_dup = cx.declare_global("enzyme_dup", cx.type_ptr()); + let global_dupv = cx.declare_global("enzyme_dupv", cx.type_ptr()); + let global_dupnoneed = cx.declare_global("enzyme_dupnoneed", cx.type_ptr()); + let global_dupnoneedv = cx.declare_global("enzyme_dupnoneedv", cx.type_ptr()); while activity_pos < inputs.len() { let diff_activity = inputs[activity_pos as usize]; // Duplicated arguments received a shadow argument, into which enzyme will write the // gradient. - let (activity, duplicated): (&Metadata, bool) = match diff_activity { + let (activity, duplicated): (&llvm::Value, bool) = match diff_activity { DiffActivity::None => panic!("not a valid input activity"), - DiffActivity::Const => (enzyme_const, false), - DiffActivity::Active => (enzyme_out, false), - DiffActivity::ActiveOnly => (enzyme_out, false), - DiffActivity::Dual => (enzyme_dup, true), - DiffActivity::Dualv => (enzyme_dupv, true), - DiffActivity::DualOnly => (enzyme_dupnoneed, true), - DiffActivity::DualvOnly => (enzyme_dupnoneedv, true), - DiffActivity::Duplicated => (enzyme_dup, true), - DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true), - DiffActivity::FakeActivitySize(_) => (enzyme_const, false), + DiffActivity::Const => (global_const, false), + DiffActivity::Active => (global_out, false), + DiffActivity::ActiveOnly => (global_out, false), + DiffActivity::Dual => (global_dup, true), + DiffActivity::Dualv => (global_dupv, true), + DiffActivity::DualOnly => (global_dupnoneed, true), + DiffActivity::DualvOnly => (global_dupnoneedv, true), + DiffActivity::Duplicated => (global_dup, true), + DiffActivity::DuplicatedOnly => (global_dupnoneed, true), + DiffActivity::FakeActivitySize(_) => (global_const, false), }; let outer_arg = outer_args[outer_pos]; - args.push(cx.get_metadata_value(activity)); + args.push(activity); if matches!(diff_activity, DiffActivity::Dualv) { let next_outer_arg = outer_args[outer_pos + 1]; let elem_bytes_size: u64 = match inputs[activity_pos + 1] { @@ -244,7 +248,7 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>( assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer); args.push(next_outer_arg2); } - args.push(cx.get_metadata_value(enzyme_const)); + args.push(global_const); args.push(next_outer_arg); outer_pos += 2 + 2 * iterations; activity_pos += 2; @@ -353,13 +357,13 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( let mut args = Vec::with_capacity(num_args as usize + 1); args.push(fn_to_diff); - let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return"); + let global_primal_ret = cx.declare_global("enzyme_primal_return", cx.type_ptr()); if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { - args.push(cx.get_metadata_value(enzyme_primal_ret)); + args.push(global_primal_ret); } if attrs.width > 1 { - let enzyme_width = cx.create_metadata(b"enzyme_width"); - args.push(cx.get_metadata_value(enzyme_width)); + let global_width = cx.declare_global("enzyme_width", cx.type_ptr()); + args.push(global_width); args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } From 5f9ee17bbade08017c1ad70376ac1c0375a84315 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 22:45:09 -0400 Subject: [PATCH 16/19] add incremental/debug test for autodiff --- tests/ui/autodiff/incremental.rs | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/ui/autodiff/incremental.rs diff --git a/tests/ui/autodiff/incremental.rs b/tests/ui/autodiff/incremental.rs new file mode 100644 index 0000000000000..a79059deaa778 --- /dev/null +++ b/tests/ui/autodiff/incremental.rs @@ -0,0 +1,41 @@ +//@ revisions: DEBUG RELEASE +//@[RELEASE] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat +//@[DEBUG] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=0 -Clto=fat -C debuginfo=2 +//@ needs-enzyme +//@ incremental +//@ no-prefer-dynamic +//@ build-pass +#![crate_type = "bin"] +#![feature(autodiff)] + +// We used to use llvm's metadata to instruct enzyme how to differentiate a function. +// In debug mode we would use incremental compilation which caused the metadata to be +// dropped. We now use globals instead and add this test to verify that incremental +// keeps working. Also testing debug mode while at it. + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(bar, Duplicated, Duplicated)] +pub fn foo(r: &[f64; 10], res: &mut f64) { + let mut output = [0.0; 10]; + output[0] = r[0]; + output[1] = r[1] * r[2]; + output[2] = r[4] * r[5]; + output[3] = r[2] * r[6]; + output[4] = r[1] * r[7]; + output[5] = r[2] * r[8]; + output[6] = r[1] * r[9]; + output[7] = r[5] * r[6]; + output[8] = r[5] * r[7]; + output[9] = r[4] * r[8]; + *res = output.iter().sum(); +} +fn main() { + let inputs = Box::new([3.1; 10]); + let mut d_inputs = Box::new([0.0; 10]); + let mut res = Box::new(0.0); + let mut d_res = Box::new(1.0); + + bar(&inputs, &mut d_inputs, &mut res, &mut d_res); + dbg!(&d_inputs); +} From 6ce845a7776a34ce7cee7eb607a6146c2fcc9ce7 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 23:10:25 -0400 Subject: [PATCH 17/19] update ui test since the new frontend is a bit more lenient --- tests/ui/autodiff/autodiff_illegal.rs | 23 ------------- tests/ui/autodiff/autodiff_illegal.stderr | 39 +++-------------------- 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index a53b6d5e58981..6bb384cc87e8d 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -110,15 +110,6 @@ fn f14(x: f32) -> Foo { type MyFloat = f32; -// We would like to support type alias to f32/f64 in argument type in the future, -// but that requires us to implement our checks at a later stage -// like THIR which has type information available. -#[autodiff_reverse(df15, Active, Active)] -fn f15(x: MyFloat) -> f32 { - //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] - unimplemented!() -} - // We would like to support type alias to f32/f64 in return type in the future #[autodiff_reverse(df16, Active, Active)] fn f16(x: f32) -> MyFloat { @@ -136,13 +127,6 @@ fn f17(x: f64) -> F64Trans { unimplemented!() } -// We would like to support `#[repr(transparent)]` f32/f64 wrapper in argument type in the future -#[autodiff_reverse(df18, Active, Active)] -fn f18(x: F64Trans) -> f64 { - //~^^ ERROR failed to resolve: use of undeclared type `F64Trans` [E0433] - unimplemented!() -} - // Invalid return activity #[autodiff_forward(df19, Dual, Active)] fn f19(x: f32) -> f32 { @@ -163,11 +147,4 @@ fn f21(x: f32) -> f32 { unimplemented!() } -struct DoesNotImplDefault; -#[autodiff_forward(df22, Dual)] -pub fn f22() -> DoesNotImplDefault { - //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - unimplemented!() -} - fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index ad6f10af46780..848ae1155e9c4 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -107,53 +107,24 @@ LL | #[autodiff_reverse(df13, Reverse)] | ^^^^^^^ error: invalid return activity Active in Forward Mode - --> $DIR/autodiff_illegal.rs:147:1 + --> $DIR/autodiff_illegal.rs:131:1 | LL | #[autodiff_forward(df19, Dual, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Dual in Reverse Mode - --> $DIR/autodiff_illegal.rs:153:1 + --> $DIR/autodiff_illegal.rs:137:1 | LL | #[autodiff_reverse(df20, Active, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Duplicated in Reverse Mode - --> $DIR/autodiff_illegal.rs:160:1 + --> $DIR/autodiff_illegal.rs:144:1 | LL | #[autodiff_reverse(df21, Active, Duplicated)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type `MyFloat` - --> $DIR/autodiff_illegal.rs:116:1 - | -LL | #[autodiff_reverse(df15, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` - -error[E0433]: failed to resolve: use of undeclared type `F64Trans` - --> $DIR/autodiff_illegal.rs:140:1 - | -LL | #[autodiff_reverse(df18, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` - -error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - --> $DIR/autodiff_illegal.rs:167:1 - | -LL | struct DoesNotImplDefault; - | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` -LL | #[autodiff_forward(df22, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `DoesNotImplDefault: Default` - which is required by `(DoesNotImplDefault, DoesNotImplDefault): Default` -help: consider annotating `DoesNotImplDefault` with `#[derive(Default)]` - | -LL + #[derive(Default)] -LL | struct DoesNotImplDefault; - | - -error: aborting due to 21 previous errors +error: aborting due to 18 previous errors -Some errors have detailed explanations: E0428, E0433, E0599, E0658. +Some errors have detailed explanations: E0428, E0658. For more information about an error, try `rustc --explain E0428`. From 0fe466cc25975a5d531a546ec7cce2f38f3e0d6a Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Tue, 7 Oct 2025 14:47:39 +0700 Subject: [PATCH 18/19] Fix doc comment --- compiler/rustc_middle/src/thir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f1d19800a7891..eed27345b747b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -839,7 +839,7 @@ pub enum PatKind<'tcx> { /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. /// * raw pointers derived from integers, other raw pointers will have already resulted in an - // error. + /// error. /// * `String`, if `string_deref_patterns` is enabled. Constant { value: ty::Value<'tcx>, From 43f7eaa0ba4cbd2537260fe223ce1e7883c405fc Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 6 Oct 2025 11:38:09 +0100 Subject: [PATCH 19/19] Fix; correct placement of type inference error for method calls --- compiler/rustc_hir_typeck/src/method/probe.rs | 21 ++++++++++++++----- ...eref-ambiguity-becomes-nonambiguous.stderr | 2 +- ...tion-with-leaking-placeholders.next.stderr | 2 +- .../hidden-type-is-opaque-2.default.stderr | 4 ++-- .../hidden-type-is-opaque-2.next.stderr | 4 ++-- ...ncompat-call-after-qualified-path-0.stderr | 2 +- ...ncompat-call-after-qualified-path-1.stderr | 2 +- tests/ui/issues/issue-20261.stderr | 4 ++-- tests/ui/issues/issue-2151.stderr | 2 +- .../branches3.stderr | 8 +++---- .../call_method_unknown_pointee.stderr | 18 +++++++--------- .../call_method_unknown_referent.stderr | 8 +++---- .../ui/proc-macro/quote/not-repeatable.stderr | 4 ++-- ...opy-inference-side-effects-are-lazy.stderr | 2 +- .../issue-42234-unknown-receiver-type.stderr | 4 +--- .../closures_in_branches.stderr | 4 ++-- .../regression-issue-81317.stderr | 2 +- tests/ui/typeck/issue-13853.stderr | 4 ++-- 18 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 12f80a197b1b8..45b87cee957c9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -7,9 +7,8 @@ use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def::DefKind; +use rustc_hir::{self as hir, ExprKind, HirId, Node}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; @@ -486,13 +485,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { ty::Infer(ty::TyVar(_)) => { + // We want to get the variable name that the method + // is being called on. If it is a method call. + let err_span = match (mode, self.tcx.hir_node(scope_expr_id)) { + ( + Mode::MethodCall, + Node::Expr(hir::Expr { + kind: ExprKind::MethodCall(_, recv, ..), + .. + }), + ) => recv.span, + _ => span, + }; + let raw_ptr_call = bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types(); - // FIXME: Ideally we'd use the span of the self-expr here, - // not of the method path. + let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, - span, + err_span, ty.into(), TypeAnnotationNeeded::E0282, !raw_ptr_call, diff --git a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr index 19c3c64181985..bad799b2550b9 100644 --- a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr +++ b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr @@ -5,7 +5,7 @@ LL | let var_fn = Value::wrap(); | ^^^^^^ ... LL | let _ = var_fn.clone(); - | ----- type must be known at this point + | ------ type must be known at this point | help: consider giving `var_fn` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr index 4bb9047b3035d..3d667f12371ab 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr @@ -5,7 +5,7 @@ LL | needs_foo(|x| { | ^ ... LL | x.to_string(); - | --------- type must be known at this point + | - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr index cb383b2db3893..dca0a7b0a1a9f 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr index cb383b2db3893..dca0a7b0a1a9f 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr index ba1c81c4518a7..10056bdf3d4f4 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-0.rs:21:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr index 93bba3625b540..632a9b99f84ef 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-1.rs:25:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/issues/issue-20261.stderr b/tests/ui/issues/issue-20261.stderr index 6738708ca225d..c5348abb3c501 100644 --- a/tests/ui/issues/issue-20261.stderr +++ b/tests/ui/issues/issue-20261.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/issue-20261.rs:4:11 + --> $DIR/issue-20261.rs:4:9 | LL | i.clone(); - | ^^^^^ cannot infer type + | ^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2151.stderr b/tests/ui/issues/issue-2151.stderr index 59fef42eb5e8b..b130f162414d0 100644 --- a/tests/ui/issues/issue-2151.stderr +++ b/tests/ui/issues/issue-2151.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x = panic!(); | ^ LL | x.clone(); - | ----- type must be known at this point + | - type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index 539673bc343ce..117d189867bd7 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:9:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:18:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -24,7 +24,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:26:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -35,7 +35,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:33:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index 9d0f38cf6b5c1..e20c6f8e8a17c 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -1,11 +1,10 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:10:41 + --> $DIR/call_method_unknown_pointee.rs:10:23 | LL | let _a: i32 = (ptr as *const _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*const _` --> $DIR/call_method_unknown_pointee.rs:12:13 @@ -22,13 +21,12 @@ LL | let b: *const _ = ptr as *const _; | ++++++++++ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:21:39 + --> $DIR/call_method_unknown_pointee.rs:21:23 | LL | let _a: i32 = (ptr as *mut _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*mut _` --> $DIR/call_method_unknown_pointee.rs:23:13 diff --git a/tests/ui/methods/call_method_unknown_referent.stderr b/tests/ui/methods/call_method_unknown_referent.stderr index 5d6974a00c695..35c7d9caf3efa 100644 --- a/tests/ui/methods/call_method_unknown_referent.stderr +++ b/tests/ui/methods/call_method_unknown_referent.stderr @@ -1,14 +1,14 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:20:31 + --> $DIR/call_method_unknown_referent.rs:20:19 | LL | let _a: i32 = (ptr as &_).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^ cannot infer type error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:26:37 + --> $DIR/call_method_unknown_referent.rs:26:14 | LL | let _b = (rc as std::rc::Rc<_>).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error[E0599]: no method named `read` found for struct `SmartPtr` in the current scope --> $DIR/call_method_unknown_referent.rs:46:35 diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index ff31799abb007..5943111efd585 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -21,10 +21,10 @@ note: the traits `Iterator` and `ToTokens` must be implemented --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0282]: type annotations needed - --> $DIR/not-repeatable.rs:11:13 + --> $DIR/not-repeatable.rs:11:25 | LL | let _ = quote! { $($ip)* }; - | ^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr index b8a8f927542f0..c98b9bb38fdb0 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -5,7 +5,7 @@ LL | let x = [Foo(PhantomData); 2]; | ^ LL | LL | extract(x).max(2); - | --- type must be known at this point + | ---------- type must be known at this point | help: consider giving `x` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.stderr index 71ac4f53b3f4f..10308ec07da5a 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ------------------------------------ type must be known at this point + | ---------- type must be known at this point | help: consider specifying the generic argument | @@ -16,8 +16,6 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` -LL | .to_string() - | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 559bc57d90638..849ffd214f07d 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/type-inference/regression-issue-81317.stderr b/tests/ui/type-inference/regression-issue-81317.stderr index a070b50e31175..fcd3fca06e18b 100644 --- a/tests/ui/type-inference/regression-issue-81317.stderr +++ b/tests/ui/type-inference/regression-issue-81317.stderr @@ -5,7 +5,7 @@ LL | let iv = S ^ index.into(); | ^^ LL | LL | &iv.to_bytes_be(); - | ----------- type must be known at this point + | -- type must be known at this point | help: consider giving `iv` an explicit type | diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 9b8698d6ed2c0..4a39b404770d0 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -18,10 +18,10 @@ LL | for node in graph.iter() { | ^^^^ method not found in `&G` error[E0282]: type annotations needed - --> $DIR/issue-13853.rs:28:14 + --> $DIR/issue-13853.rs:28:9 | LL | node.zomg(); - | ^^^^ cannot infer type + | ^^^^ cannot infer type error[E0308]: mismatched types --> $DIR/issue-13853.rs:37:13