From c00e493750293ef5ddf3294dd503dd7bb317a069 Mon Sep 17 00:00:00 2001 From: Yukang Date: Tue, 7 Jul 2026 18:43:40 +0800 Subject: [PATCH 01/51] Preserve visibility in nested macro import suggestions --- .../rustc_resolve/src/diagnostics/impls.rs | 13 ++- tests/ui/imports/issue-99695-b.fixed | 4 +- tests/ui/imports/issue-99695-b.stderr | 4 +- .../ui/imports/issue-99695.edition_2015.fixed | 4 +- .../imports/issue-99695.edition_2015.stderr | 4 +- .../ui/imports/issue-99695.edition_2018.fixed | 4 +- .../imports/issue-99695.edition_2018.stderr | 4 +- ...ested-import-visibility-issue-141134.fixed | 79 ++++++++++++++++++ ...t-nested-import-visibility-issue-141134.rs | 73 +++++++++++++++++ ...sted-import-visibility-issue-141134.stderr | 81 +++++++++++++++++++ 10 files changed, 254 insertions(+), 16 deletions(-) create mode 100644 tests/ui/imports/macro-export-nested-import-visibility-issue-141134.fixed create mode 100644 tests/ui/imports/macro-export-nested-import-visibility-issue-141134.rs create mode 100644 tests/ui/imports/macro-export-nested-import-visibility-issue-141134.stderr diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ce41dd86a48be..24e679a6323c1 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -3572,10 +3572,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } else { // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); + if let Ok(vis) = source_map.span_to_snippet(import.vis_span) + && let Some(indentation) = source_map.indentation_before(import.use_span) + { + let vis = if vis.trim().is_empty() { String::new() } else { format!("{vis} ") }; + corrections.push(( + import.use_span.shrink_to_lo(), + format!("{vis}use {module_name}::{import_snippet};\n{indentation}"), + )); + } } } diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed index ae63b0c4627c5..2804be27bbb2a 100644 --- a/tests/ui/imports/issue-99695-b.fixed +++ b/tests/ui/imports/issue-99695-b.fixed @@ -11,8 +11,8 @@ mod m { pub struct other_item; } - use crate::nu; -pub use self::p::{other_item as _}; + pub use crate::nu; + pub use self::p::{other_item as _}; //~^ ERROR unresolved import `self::p::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate } diff --git a/tests/ui/imports/issue-99695-b.stderr b/tests/ui/imports/issue-99695-b.stderr index ad752d5c45a7e..5352c26a06ae1 100644 --- a/tests/ui/imports/issue-99695-b.stderr +++ b/tests/ui/imports/issue-99695-b.stderr @@ -7,8 +7,8 @@ LL | pub use self::p::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use crate::nu; -LL ~ pub use self::p::{other_item as _}; +LL ~ pub use crate::nu; +LL ~ pub use self::p::{other_item as _}; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-99695.edition_2015.fixed b/tests/ui/imports/issue-99695.edition_2015.fixed index 798acfd587467..58018baffd7b9 100644 --- a/tests/ui/imports/issue-99695.edition_2015.fixed +++ b/tests/ui/imports/issue-99695.edition_2015.fixed @@ -12,8 +12,8 @@ mod m { pub struct other_item; - use crate::nu; -pub use self::{other_item as _}; + pub use crate::nu; + pub use self::{other_item as _}; //~^ ERROR unresolved import `self::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate } diff --git a/tests/ui/imports/issue-99695.edition_2015.stderr b/tests/ui/imports/issue-99695.edition_2015.stderr index 4ef8e6426fb29..ad24b406c34ca 100644 --- a/tests/ui/imports/issue-99695.edition_2015.stderr +++ b/tests/ui/imports/issue-99695.edition_2015.stderr @@ -7,8 +7,8 @@ LL | pub use self::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use crate::nu; -LL ~ pub use self::{other_item as _}; +LL ~ pub use crate::nu; +LL ~ pub use self::{other_item as _}; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-99695.edition_2018.fixed b/tests/ui/imports/issue-99695.edition_2018.fixed index 798acfd587467..58018baffd7b9 100644 --- a/tests/ui/imports/issue-99695.edition_2018.fixed +++ b/tests/ui/imports/issue-99695.edition_2018.fixed @@ -12,8 +12,8 @@ mod m { pub struct other_item; - use crate::nu; -pub use self::{other_item as _}; + pub use crate::nu; + pub use self::{other_item as _}; //~^ ERROR unresolved import `self::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate } diff --git a/tests/ui/imports/issue-99695.edition_2018.stderr b/tests/ui/imports/issue-99695.edition_2018.stderr index 4ef8e6426fb29..ad24b406c34ca 100644 --- a/tests/ui/imports/issue-99695.edition_2018.stderr +++ b/tests/ui/imports/issue-99695.edition_2018.stderr @@ -7,8 +7,8 @@ LL | pub use self::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use crate::nu; -LL ~ pub use self::{other_item as _}; +LL ~ pub use crate::nu; +LL ~ pub use self::{other_item as _}; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.fixed b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.fixed new file mode 100644 index 0000000000000..0311defd9e83f --- /dev/null +++ b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.fixed @@ -0,0 +1,79 @@ +//@ run-rustfix +//@ edition: 2021 + +// Nested macro import suggestions should preserve the original `use` visibility. + +#![allow(unused)] + +mod a { + #[macro_export] + macro_rules! m_pub { + () => {}; + } + #[macro_export] + macro_rules! m_crate { + () => {}; + } + #[macro_export] + macro_rules! m_in { + () => {}; + } + #[macro_export] + macro_rules! m_super { + () => {}; + } + #[macro_export] + macro_rules! m_self { + () => {}; + } + #[macro_export] + macro_rules! m_private { + () => {}; + } + + pub struct S; + + mod b0 { + pub use crate::m_pub; + pub use super::{S as PubS}; + //~^ ERROR unresolved import `super::m_pub` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b1 { + pub(crate) use crate::m_crate; + pub(crate) use super::{S as CrateS}; + //~^ ERROR unresolved import `super::m_crate` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b2 { + pub(in crate::a) use crate::m_in; + pub(in crate::a) use super::{S as InS}; + //~^ ERROR unresolved import `super::m_in` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b3 { + pub(super) use crate::m_super; + pub(super) use super::{S as SuperS}; + //~^ ERROR unresolved import `super::m_super` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b4 { + pub(self) use crate::m_self; + pub(self) use super::{S as SelfS}; + //~^ ERROR unresolved import `super::m_self` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b5 { + use crate::m_private; + use super::{S as PrivateS}; + //~^ ERROR unresolved import `super::m_private` + //~| HELP a macro with this name exists at the root of the crate + } +} + +fn main() {} diff --git a/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.rs b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.rs new file mode 100644 index 0000000000000..5be2ba6ff85df --- /dev/null +++ b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.rs @@ -0,0 +1,73 @@ +//@ run-rustfix +//@ edition: 2021 + +// Nested macro import suggestions should preserve the original `use` visibility. + +#![allow(unused)] + +mod a { + #[macro_export] + macro_rules! m_pub { + () => {}; + } + #[macro_export] + macro_rules! m_crate { + () => {}; + } + #[macro_export] + macro_rules! m_in { + () => {}; + } + #[macro_export] + macro_rules! m_super { + () => {}; + } + #[macro_export] + macro_rules! m_self { + () => {}; + } + #[macro_export] + macro_rules! m_private { + () => {}; + } + + pub struct S; + + mod b0 { + pub use super::{m_pub, S as PubS}; + //~^ ERROR unresolved import `super::m_pub` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b1 { + pub(crate) use super::{m_crate, S as CrateS}; + //~^ ERROR unresolved import `super::m_crate` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b2 { + pub(in crate::a) use super::{m_in, S as InS}; + //~^ ERROR unresolved import `super::m_in` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b3 { + pub(super) use super::{m_super, S as SuperS}; + //~^ ERROR unresolved import `super::m_super` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b4 { + pub(self) use super::{m_self, S as SelfS}; + //~^ ERROR unresolved import `super::m_self` + //~| HELP a macro with this name exists at the root of the crate + } + + mod b5 { + use super::{m_private, S as PrivateS}; + //~^ ERROR unresolved import `super::m_private` + //~| HELP a macro with this name exists at the root of the crate + } +} + +fn main() {} diff --git a/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.stderr b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.stderr new file mode 100644 index 0000000000000..f1cf7e6aa56a0 --- /dev/null +++ b/tests/ui/imports/macro-export-nested-import-visibility-issue-141134.stderr @@ -0,0 +1,81 @@ +error[E0432]: unresolved import `super::m_pub` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:37:25 + | +LL | pub use super::{m_pub, S as PubS}; + | ^^^^^ no `m_pub` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ pub use crate::m_pub; +LL ~ pub use super::{S as PubS}; + | + +error[E0432]: unresolved import `super::m_crate` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:43:32 + | +LL | pub(crate) use super::{m_crate, S as CrateS}; + | ^^^^^^^ no `m_crate` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ pub(crate) use crate::m_crate; +LL ~ pub(crate) use super::{S as CrateS}; + | + +error[E0432]: unresolved import `super::m_in` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:49:38 + | +LL | pub(in crate::a) use super::{m_in, S as InS}; + | ^^^^ no `m_in` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ pub(in crate::a) use crate::m_in; +LL ~ pub(in crate::a) use super::{S as InS}; + | + +error[E0432]: unresolved import `super::m_super` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:55:32 + | +LL | pub(super) use super::{m_super, S as SuperS}; + | ^^^^^^^ no `m_super` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ pub(super) use crate::m_super; +LL ~ pub(super) use super::{S as SuperS}; + | + +error[E0432]: unresolved import `super::m_self` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:61:31 + | +LL | pub(self) use super::{m_self, S as SelfS}; + | ^^^^^^ no `m_self` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ pub(self) use crate::m_self; +LL ~ pub(self) use super::{S as SelfS}; + | + +error[E0432]: unresolved import `super::m_private` + --> $DIR/macro-export-nested-import-visibility-issue-141134.rs:67:21 + | +LL | use super::{m_private, S as PrivateS}; + | ^^^^^^^^^ no `m_private` in `a` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ use crate::m_private; +LL ~ use super::{S as PrivateS}; + | + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0432`. From e19a83471adc19de02ca1c9d8981723d8c3c5cf1 Mon Sep 17 00:00:00 2001 From: lapla Date: Sat, 18 Jul 2026 11:55:55 +0900 Subject: [PATCH 02/51] Don't ICE when evaluating ValTrees that contain error constants --- compiler/rustc_middle/src/mir/consts.rs | 4 +++ .../mgca/type_const-adt-expr-missing-field.rs | 14 ++++++++++ .../type_const-adt-expr-missing-field.stderr | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs create mode 100644 tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 54e64b37245c3..28216c83e8a01 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -320,6 +320,10 @@ impl<'tcx> Const<'tcx> { ) -> Result { match self { Const::Ty(_, c) => { + if let Err(e) = c.error_reported() { + return Err(ReportedErrorInfo::non_const_eval_error(e).into()); + } + // FIXME(generic_const_exprs): We shouldn't encounter placeholders here // and could change this to ICE when encountering them instead. if c.has_non_region_param() || c.has_non_region_placeholders() { diff --git a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs new file mode 100644 index 0000000000000..05be2097e124f --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs @@ -0,0 +1,14 @@ +// Regression test for https://github.com/rust-lang/rust/issues/154632 + +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] +#![feature(macroless_generic_const_args)] +#![feature(generic_const_items)] + +type const ADD1: usize = const { N + 1 }; +//~^ ERROR: unconstrained generic constant +type const AliasFnUnused: ADD1 = ADD1::<{ Some:: {} }>; +//~^ ERROR: cannot find type `ADD1` in this scope [E0573] +//~| ERROR: struct expression with missing field initialiser for `0` + +fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr new file mode 100644 index 0000000000000..bc4821f20cf24 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr @@ -0,0 +1,28 @@ +error[E0573]: cannot find type `ADD1` in this scope + --> $DIR/type_const-adt-expr-missing-field.rs:10:27 + | +LL | type const AliasFnUnused: ADD1 = ADD1::<{ Some:: {} }>; + | ^^^^ not found in this scope + | + = note: a constant named `ADD1` exists in another namespace + +error: unconstrained generic constant + --> $DIR/type_const-adt-expr-missing-field.rs:8:1 + | +LL | type const ADD1: usize = const { N + 1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | type const ADD1: usize where [(); const { N + 1 }]: = const { N + 1 }; + | ++++++++++++++++++++++++++++ + +error: struct expression with missing field initialiser for `0` + --> $DIR/type_const-adt-expr-missing-field.rs:10:43 + | +LL | type const AliasFnUnused: ADD1 = ADD1::<{ Some:: {} }>; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0573`. From db8797ee48c23bb2877b271532623f28c0f3b0ec Mon Sep 17 00:00:00 2001 From: lapla Date: Sun, 9 Aug 2026 19:16:19 +0900 Subject: [PATCH 03/51] Move test/crashes/150969 to UI now that it no longer ICEs --- tests/crashes/150969.rs | 7 ---- .../mgca/none-as-usize-const-arg.rs | 14 +++++++ .../mgca/none-as-usize-const-arg.stderr | 40 +++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) delete mode 100644 tests/crashes/150969.rs create mode 100644 tests/ui/const-generics/mgca/none-as-usize-const-arg.rs create mode 100644 tests/ui/const-generics/mgca/none-as-usize-const-arg.stderr diff --git a/tests/crashes/150969.rs b/tests/crashes/150969.rs deleted file mode 100644 index 5f0e09becf36f..0000000000000 --- a/tests/crashes/150969.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: #150969 -#![feature(generic_const_exprs)] -#![feature(min_generic_const_args)] - -fn pass_enum { - pass_enum::<{core::direct_const_arg!(None)}> -} diff --git a/tests/ui/const-generics/mgca/none-as-usize-const-arg.rs b/tests/ui/const-generics/mgca/none-as-usize-const-arg.rs new file mode 100644 index 0000000000000..c21f1d0014ad5 --- /dev/null +++ b/tests/ui/const-generics/mgca/none-as-usize-const-arg.rs @@ -0,0 +1,14 @@ +// Regression test for https://github.com/rust-lang/rust/issues/150969 + +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] + +fn pass_enum { + //~^ ERROR: missing parameters for function definition + //~| ERROR: defaults for generic parameters are not allowed here + //~| ERROR: overly complex generic constant + pass_enum::<{ core::direct_const_arg!(None) }> + //~^ ERROR: missing generics for enum `Option` [E0107] +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/none-as-usize-const-arg.stderr b/tests/ui/const-generics/mgca/none-as-usize-const-arg.stderr new file mode 100644 index 0000000000000..dbd1ac607e88c --- /dev/null +++ b/tests/ui/const-generics/mgca/none-as-usize-const-arg.stderr @@ -0,0 +1,40 @@ +error: missing parameters for function definition + --> $DIR/none-as-usize-const-arg.rs:6:59 + | +LL | fn pass_enum { + | ^ + | +help: add a parameter list + | +LL | fn pass_enum() { + | ++ + +error: defaults for generic parameters are not allowed here + --> $DIR/none-as-usize-const-arg.rs:6:30 + | +LL | fn pass_enum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0107]: missing generics for enum `Option` + --> $DIR/none-as-usize-const-arg.rs:10:43 + | +LL | pass_enum::<{ core::direct_const_arg!(None) }> + | ^^^^ expected 1 generic argument + | +help: add missing generic argument + | +LL | pass_enum::<{ core::direct_const_arg!(None) }> + | +++ + +error: overly complex generic constant + --> $DIR/none-as-usize-const-arg.rs:6:47 + | +LL | fn pass_enum { + | ^^^^^^^^^^^ const blocks are not supported in generic constants + | + = help: consider moving this anonymous constant into a `const` function + = note: this operation may be supported in the future + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0107`. From 07683989587570cff17c939062afb606ac289ddb Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 18:07:41 +0700 Subject: [PATCH 04/51] Fix async `#[track_caller]` feature gate tests Previously, these tests were testing the wrong feature gate, and had unnecessary type errors. --- .../track-caller/async-block.afn.stderr | 12 ++-- .../track-caller/async-block.cls.stderr | 42 +++++++++++++ .../track-caller/async-block.nofeat.stderr | 12 ++-- .../async-await/track-caller/async-block.rs | 27 ++++---- .../async-closure-gate.afn.stderr | 61 +++++-------------- .../async-closure-gate.cls.stderr | 24 ++++++++ .../async-closure-gate.nofeat.stderr | 61 +++++-------------- .../track-caller/async-closure-gate.rs | 54 ++++++++-------- 8 files changed, 150 insertions(+), 143 deletions(-) create mode 100644 tests/ui/async-await/track-caller/async-block.cls.stderr create mode 100644 tests/ui/async-await/track-caller/async-closure-gate.cls.stderr diff --git a/tests/ui/async-await/track-caller/async-block.afn.stderr b/tests/ui/async-await/track-caller/async-block.afn.stderr index b6a7481a4d119..89ebe2df60609 100644 --- a/tests/ui/async-await/track-caller/async-block.afn.stderr +++ b/tests/ui/async-await/track-caller/async-block.afn.stderr @@ -1,7 +1,7 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:8:13 + --> $DIR/async-block.rs:11:13 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -9,9 +9,9 @@ LL | let _ = #[track_caller] async { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:15:13 + --> $DIR/async-block.rs:19:13 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -19,9 +19,9 @@ LL | let _ = #[track_caller] async { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:23:17 + --> $DIR/async-block.rs:28:17 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information diff --git a/tests/ui/async-await/track-caller/async-block.cls.stderr b/tests/ui/async-await/track-caller/async-block.cls.stderr new file mode 100644 index 0000000000000..0e9dbc4afa05f --- /dev/null +++ b/tests/ui/async-await/track-caller/async-block.cls.stderr @@ -0,0 +1,42 @@ +error: `#[track_caller]` on async functions is a no-op + --> $DIR/async-block.rs:16:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn foo() { +LL | | let _ = #[track_caller] +LL | | +LL | | async {}; +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +note: the lint level is defined here + --> $DIR/async-block.rs:6:9 + | +LL | #![deny(ungated_async_fn_track_caller)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[track_caller]` on async functions is a no-op + --> $DIR/async-block.rs:24:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn foo2() { +LL | | let _ = async { +LL | | let _ = #[track_caller] +... | +LL | | }; +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + diff --git a/tests/ui/async-await/track-caller/async-block.nofeat.stderr b/tests/ui/async-await/track-caller/async-block.nofeat.stderr index b6a7481a4d119..89ebe2df60609 100644 --- a/tests/ui/async-await/track-caller/async-block.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-block.nofeat.stderr @@ -1,7 +1,7 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:8:13 + --> $DIR/async-block.rs:11:13 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -9,9 +9,9 @@ LL | let _ = #[track_caller] async { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:15:13 + --> $DIR/async-block.rs:19:13 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -19,9 +19,9 @@ LL | let _ = #[track_caller] async { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:23:17 + --> $DIR/async-block.rs:28:17 | -LL | let _ = #[track_caller] async { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information diff --git a/tests/ui/async-await/track-caller/async-block.rs b/tests/ui/async-await/track-caller/async-block.rs index 900d5ef25504d..f56921c017d12 100644 --- a/tests/ui/async-await/track-caller/async-block.rs +++ b/tests/ui/async-await/track-caller/async-block.rs @@ -1,27 +1,32 @@ //@ edition:2021 -//@ revisions: afn nofeat +//@ revisions: afn cls afn_cls nofeat +//@[afn_cls] check-pass #![feature(stmt_expr_attributes)] -#![cfg_attr(afn, feature(async_fn_track_caller))] +#![deny(ungated_async_fn_track_caller)] +#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] fn main() { - let _ = #[track_caller] async { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + async {}; } #[track_caller] +//[cls]~^ ERROR `#[track_caller]` on async functions is a no-op async fn foo() { - let _ = #[track_caller] async { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + async {}; } #[track_caller] +//[cls]~^ ERROR `#[track_caller]` on async functions is a no-op async fn foo2() { let _ = async { - let _ = #[track_caller] async { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + async {}; }; } diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr index 6887a904211ec..fb774290bde4e 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -1,7 +1,7 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:8:13 + --> $DIR/async-closure-gate.rs:11:13 | -LL | let _ = #[track_caller] async || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -9,9 +9,9 @@ LL | let _ = #[track_caller] async || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:15:13 + --> $DIR/async-closure-gate.rs:19:13 | -LL | let _ = #[track_caller] async || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -19,9 +19,9 @@ LL | let _ = #[track_caller] async || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:21:13 + --> $DIR/async-closure-gate.rs:25:13 | -LL | let _ = #[track_caller] || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -29,9 +29,9 @@ LL | let _ = #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:29:17 + --> $DIR/async-closure-gate.rs:32:17 | -LL | let _ = #[track_caller] || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -39,9 +39,9 @@ LL | let _ = #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:37:9 + --> $DIR/async-closure-gate.rs:40:9 | -LL | #[track_caller] || { +LL | #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -49,48 +49,15 @@ LL | #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:47:13 + --> $DIR/async-closure-gate.rs:49:13 | -LL | #[track_caller] || { +LL | #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0308]: mismatched types - --> $DIR/async-closure-gate.rs:27:5 - | -LL | fn foo3() { - | - help: try adding a return type: `-> impl Future` -LL | / async { -LL | | -LL | | let _ = #[track_caller] || { -... | -LL | | } - | |_____^ expected `()`, found `async` block - | - = note: expected unit type `()` - found `async` block `{async block@$DIR/async-closure-gate.rs:27:5: 27:10}` - -error[E0308]: mismatched types - --> $DIR/async-closure-gate.rs:44:5 - | -LL | fn foo5() { - | - help: try adding a return type: `-> impl Future` -LL | / async { -LL | | -LL | | let _ = || { -LL | | #[track_caller] || { -... | -LL | | }; -LL | | } - | |_____^ expected `()`, found `async` block - | - = note: expected unit type `()` - found `async` block `{async block@$DIR/async-closure-gate.rs:44:5: 44:10}` - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0308, E0658. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr new file mode 100644 index 0000000000000..5fe7a2fb7d36a --- /dev/null +++ b/tests/ui/async-await/track-caller/async-closure-gate.cls.stderr @@ -0,0 +1,24 @@ +error: `#[track_caller]` on async functions is a no-op + --> $DIR/async-closure-gate.rs:16:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn foo() { +LL | | let _ = #[track_caller] +LL | | +LL | | async || {}; +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +note: the lint level is defined here + --> $DIR/async-closure-gate.rs:6:9 + | +LL | #![deny(ungated_async_fn_track_caller)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr index 6887a904211ec..fb774290bde4e 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -1,7 +1,7 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:8:13 + --> $DIR/async-closure-gate.rs:11:13 | -LL | let _ = #[track_caller] async || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -9,9 +9,9 @@ LL | let _ = #[track_caller] async || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:15:13 + --> $DIR/async-closure-gate.rs:19:13 | -LL | let _ = #[track_caller] async || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -19,9 +19,9 @@ LL | let _ = #[track_caller] async || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:21:13 + --> $DIR/async-closure-gate.rs:25:13 | -LL | let _ = #[track_caller] || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -29,9 +29,9 @@ LL | let _ = #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:29:17 + --> $DIR/async-closure-gate.rs:32:17 | -LL | let _ = #[track_caller] || { +LL | let _ = #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -39,9 +39,9 @@ LL | let _ = #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:37:9 + --> $DIR/async-closure-gate.rs:40:9 | -LL | #[track_caller] || { +LL | #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information @@ -49,48 +49,15 @@ LL | #[track_caller] || { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:47:13 + --> $DIR/async-closure-gate.rs:49:13 | -LL | #[track_caller] || { +LL | #[track_caller] | ^^^^^^^^^^^^^^^ | = note: see issue #87417 for more information = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0308]: mismatched types - --> $DIR/async-closure-gate.rs:27:5 - | -LL | fn foo3() { - | - help: try adding a return type: `-> impl Future` -LL | / async { -LL | | -LL | | let _ = #[track_caller] || { -... | -LL | | } - | |_____^ expected `()`, found `async` block - | - = note: expected unit type `()` - found `async` block `{async block@$DIR/async-closure-gate.rs:27:5: 27:10}` - -error[E0308]: mismatched types - --> $DIR/async-closure-gate.rs:44:5 - | -LL | fn foo5() { - | - help: try adding a return type: `-> impl Future` -LL | / async { -LL | | -LL | | let _ = || { -LL | | #[track_caller] || { -... | -LL | | }; -LL | | } - | |_____^ expected `()`, found `async` block - | - = note: expected unit type `()` - found `async` block `{async block@$DIR/async-closure-gate.rs:44:5: 44:10}` - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0308, E0658. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.rs b/tests/ui/async-await/track-caller/async-closure-gate.rs index e72ce2afa45fd..13d3a5787ca9d 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.rs +++ b/tests/ui/async-await/track-caller/async-closure-gate.rs @@ -1,52 +1,54 @@ //@ edition:2021 -//@ revisions: afn nofeat +//@ revisions: afn cls afn_cls nofeat +//@[afn_cls] check-pass #![feature(stmt_expr_attributes)] -#![cfg_attr(afn, feature(async_fn_track_caller))] +#![deny(ungated_async_fn_track_caller)] +#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] fn main() { - let _ = #[track_caller] async || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + async || {}; } #[track_caller] +//[cls]~^ ERROR `#[track_caller]` on async functions is a no-op async fn foo() { - let _ = #[track_caller] async || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + async || {}; } async fn foo2() { - let _ = #[track_caller] || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + || {}; } fn foo3() { - async { - //~^ ERROR mismatched types - let _ = #[track_caller] || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; - } + let _ = async { + let _ = #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + || {}; + }; } async fn foo4() { let _ = || { - #[track_caller] || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + || {}; }; } fn foo5() { - async { - //~^ ERROR mismatched types + let _ = async { let _ = || { - #[track_caller] || { - //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] - }; + #[track_caller] + //[nofeat,afn]~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + || {}; }; - } + }; } From 88fb17e4a4a6e768f2476bcf8f2682915d07e8bc Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 18:17:31 +0700 Subject: [PATCH 05/51] Add a test revision to panic-track-caller.rs --- .../panic-track-caller.cls.stderr | 6 +- .../panic-track-caller.nofeat.stderr | 6 +- .../track-caller/panic-track-caller.rs | 55 ++++++++++--------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 464cbfba2acfe..762e4add96e41 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -3,7 +3,7 @@ warning: `#[track_caller]` on async functions is a no-op | LL | #[track_caller] | ^^^^^^^^^^^^^^^ -... +LL | LL | / async fn bar_track_caller() { LL | | panic!() LL | | } @@ -15,11 +15,11 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:67:5 + --> $DIR/panic-track-caller.rs:66:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ -... +LL | LL | / async fn bar_assoc() { LL | | panic!(); LL | | } diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 464cbfba2acfe..762e4add96e41 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -3,7 +3,7 @@ warning: `#[track_caller]` on async functions is a no-op | LL | #[track_caller] | ^^^^^^^^^^^^^^^ -... +LL | LL | / async fn bar_track_caller() { LL | | panic!() LL | | } @@ -15,11 +15,11 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:67:5 + --> $DIR/panic-track-caller.rs:66:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ -... +LL | LL | / async fn bar_assoc() { LL | | panic!(); LL | | } diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index bd12bf11d6c84..7e6a913fa78c7 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,11 +1,11 @@ //@ run-pass //@ edition:2021 -//@ revisions: afn cls nofeat +//@ revisions: afn cls afn_cls nofeat //@ needs-unwind // gate-test-async_fn_track_caller #![feature(stmt_expr_attributes)] -#![cfg_attr(afn, feature(async_fn_track_caller))] -#![cfg_attr(cls, feature(closure_track_caller))] +#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] #![allow(unused)] use std::future::Future; @@ -51,8 +51,7 @@ async fn foo() { } #[track_caller] -//[cls]~^ WARN `#[track_caller]` on async functions is a no-op -//[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op +//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_track_caller() { panic!() } @@ -65,8 +64,7 @@ struct Foo; impl Foo { #[track_caller] - //[cls]~^ WARN `#[track_caller]` on async functions is a no-op - //[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op + //[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op async fn bar_assoc() { panic!(); } @@ -76,21 +74,23 @@ async fn foo_assoc() { Foo::bar_assoc().await } -// Since compilation is expected to fail for this fn when using -// `nofeat`, we test that separately in `async-closure-gate.rs` -#[cfg(cls)] +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] async fn foo_closure() { - let c = #[track_caller] async || { + let c = #[track_caller] + async || { panic!(); }; c().await } -// Since compilation is expected to fail for this fn when using -// `nofeat`, we test that separately in `async-block.rs` -#[cfg(cls)] +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] async fn foo_block() { - let a = #[track_caller] async { + let a = #[track_caller] + async { panic!(); }; a.await @@ -113,22 +113,27 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { } fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 46 -); + assert_eq!(panicked_at(|| block_on(foo())), 46); - #[cfg(afn)] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 61); + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 56); - #[cfg(afn)] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 69); + // FIXME(closure_track_caller): if closure_track_caller is enabled, but + // async_fn_track_caller is disabled, then #[track_caller] on async closures + // silently do nothing. Either it should function, or we should emit a warning. + // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 84); + assert_eq!(panicked_at(|| block_on(foo_closure())), 83); + #[cfg(afn_cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 85); - #[cfg(cls)] + #[cfg(any(cls, afn_cls))] assert_eq!(panicked_at(|| block_on(foo_block())), 96); } From 95ded567ed9e32a769339ee92b1576dcd532836e Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 18:27:40 +0700 Subject: [PATCH 06/51] In panic-track-caller.rs, split call and await into different lines. This is done so that we can test whether the location used is at the function call site or the await site. --- .../panic-track-caller.cls.stderr | 4 +-- .../panic-track-caller.nofeat.stderr | 4 +-- .../track-caller/panic-track-caller.rs | 35 +++++++++++-------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 762e4add96e41..2721918972b2a 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:53:1 + --> $DIR/panic-track-caller.rs:54:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:66:5 + --> $DIR/panic-track-caller.rs:68:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 762e4add96e41..2721918972b2a 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:53:1 + --> $DIR/panic-track-caller.rs:54:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:66:5 + --> $DIR/panic-track-caller.rs:68:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 7e6a913fa78c7..15fff0d281694 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -47,7 +47,8 @@ async fn bar() { } async fn foo() { - bar().await + let future = bar(); + future.await; } #[track_caller] @@ -57,7 +58,8 @@ async fn bar_track_caller() { } async fn foo_track_caller() { - bar_track_caller().await + let future = bar_track_caller(); + future.await; } struct Foo; @@ -71,29 +73,31 @@ impl Foo { } async fn foo_assoc() { - Foo::bar_assoc().await + let future = Foo::bar_assoc(); + future.await; } // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` #[cfg(any(cls, afn_cls))] async fn foo_closure() { - let c = #[track_caller] + let closure = #[track_caller] async || { panic!(); }; - c().await + let future = closure(); + future.await; } // Since compilation is expected to fail for this fn when `closure_track_caller` // is disabled, we test that separately in `async-closure-gate.rs` #[cfg(any(cls, afn_cls))] async fn foo_block() { - let a = #[track_caller] + let future = #[track_caller] async { panic!(); }; - a.await + future.await; } fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { @@ -112,28 +116,31 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { x } +// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function +// uses the location where the future is awaited. +// The correct behavior as per T-lang is to use the location where the function is called. fn main() { assert_eq!(panicked_at(|| block_on(foo())), 46); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 56); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 77); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 69); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 83); + assert_eq!(panicked_at(|| block_on(foo_closure())), 86); #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 85); + assert_eq!(panicked_at(|| block_on(foo_closure())), 89); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 96); + assert_eq!(panicked_at(|| block_on(foo_block())), 100); } From 40b84d357bbe33b41ca8fc5150769fea9868092d Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 18:33:49 +0700 Subject: [PATCH 07/51] Add manual polling test to panic-track-caller.rs --- .../panic-track-caller.cls.stderr | 17 ++++++++++++++- .../panic-track-caller.nofeat.stderr | 17 ++++++++++++++- .../track-caller/panic-track-caller.rs | 21 ++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 2721918972b2a..7b116387169c2 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -29,5 +29,20 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 2 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:103:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 3 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 2721918972b2a..7b116387169c2 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -29,5 +29,20 @@ LL | | } = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 2 warnings emitted +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:103:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | +LL | / async fn bar_manual_poll() { +LL | | panic!(); +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: 3 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 15fff0d281694..41835977f8d6c 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -100,6 +100,20 @@ async fn foo_block() { future.await; } +#[track_caller] +//[cls,nofeat]~^ WARN `#[track_caller]` on async functions is a no-op +async fn bar_manual_poll() { + panic!(); +} + +fn foo_manual_poll() { + let future = bar_manual_poll(); + let future = std::pin::pin!(future); + let mut cx = std::task::Context::from_waker(std::task::Waker::noop()); + let res = future.poll(&mut cx); + assert_eq!(res, std::task::Poll::Ready(())); +} + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { let loc = Arc::new(Mutex::new(None)); @@ -117,7 +131,7 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { } // FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function -// uses the location where the future is awaited. +// uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { assert_eq!(panicked_at(|| block_on(foo())), 46); @@ -143,4 +157,9 @@ fn main() { #[cfg(any(cls, afn_cls))] assert_eq!(panicked_at(|| block_on(foo_block())), 100); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| foo_manual_poll()), 113); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| foo_manual_poll()), 106); } From 785e7096639aafe2658a1805bb6023a86b9079fa Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 18:55:00 +0700 Subject: [PATCH 08/51] Also test async track_caller in miri --- .../tests/pass/async-panic-track-caller.rs | 168 ++++++++++++++++++ .../panic-track-caller.cls.stderr | 6 +- .../panic-track-caller.nofeat.stderr | 6 +- .../track-caller/panic-track-caller.rs | 23 +-- 4 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 src/tools/miri/tests/pass/async-panic-track-caller.rs diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs new file mode 100644 index 0000000000000..8cb43f8fef9ce --- /dev/null +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -0,0 +1,168 @@ +// This test is duplicated (with changes) at +// tests/ui/async-await/track-caller/panic-track-caller.rs + +//@ edition:2021 +//@ revisions: afn cls afn_cls nofeat +// +// +// Padding comment so that the line numbers are the same as panic-track-caller.rs +#![feature(stmt_expr_attributes)] +#![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] +#![cfg_attr(any(cls, afn_cls), feature(closure_track_caller))] +#![allow(unused)] + +use std::future::Future; +use std::panic; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Wake}; +use std::thread::{self, Thread}; + +/// A waker that wakes up the current thread when called. +struct ThreadWaker(Thread); + +impl Wake for ThreadWaker { + fn wake(self: Arc) { + self.0.unpark(); + } +} + +/// Run a future to completion on the current thread. +fn block_on(fut: impl Future) -> T { + // Pin the future so it can be polled. + let mut fut = Box::pin(fut); + + // Create a new context to be passed to the future. + let t = thread::current(); + let waker = Arc::new(ThreadWaker(t)).into(); + let mut cx = Context::from_waker(&waker); + + // Run the future to completion. + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(res) => return res, + Poll::Pending => thread::park(), + } + } +} + +async fn bar() { + panic!() +} + +async fn foo() { + let future = bar(); + future.await; +} + +#[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] +#[track_caller] +async fn bar_track_caller() { + panic!() +} + +async fn foo_track_caller() { + let future = bar_track_caller(); + future.await; +} + +struct Foo; + +impl Foo { + #[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] + #[track_caller] + async fn bar_assoc() { + panic!(); + } +} + +async fn foo_assoc() { + let future = Foo::bar_assoc(); + future.await; +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] +async fn foo_closure() { + let closure = #[track_caller] + async || { + panic!(); + }; + let future = closure(); + future.await; +} + +// Since compilation is expected to fail for this fn when `closure_track_caller` +// is disabled, we test that separately in `async-closure-gate.rs` +#[cfg(any(cls, afn_cls))] +async fn foo_block() { + let future = #[track_caller] + async { + panic!(); + }; + future.await; +} + +#[cfg_attr(any(cls, nofeat), expect(ungated_async_fn_track_caller))] +#[track_caller] +async fn bar_manual_poll() { + panic!(); +} + +fn foo_manual_poll() { + let future = bar_manual_poll(); + let future = std::pin::pin!(future); + let mut cx = std::task::Context::from_waker(std::task::Waker::noop()); + let res = future.poll(&mut cx); + assert_eq!(res, std::task::Poll::Ready(())); +} + +fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { + let loc = Arc::new(Mutex::new(None)); + + let hook = panic::take_hook(); + { + let loc = loc.clone(); + panic::set_hook(Box::new(move |info| { + *loc.lock().unwrap() = info.location().map(|loc| loc.line()) + })); + } + panic::catch_unwind(f).unwrap_err(); + panic::set_hook(hook); + let x = loc.lock().unwrap().unwrap(); + x +} + +// FIXME(async_fn_track_caller): Currently, #[track_caller] on an async function +// uses the location where the future is awaited or polled. +// The correct behavior as per T-lang is to use the location where the function is called. +fn main() { + assert_eq!(panicked_at(|| block_on(foo())), 49); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 65); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 80); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); + + // FIXME(closure_track_caller): if closure_track_caller is enabled, but + // async_fn_track_caller is disabled, then #[track_caller] on async closures + // silently do nothing. Either it should function, or we should emit a warning. + // See #161961 + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 89); + #[cfg(afn_cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 92); + + #[cfg(any(cls, afn_cls))] + assert_eq!(panicked_at(|| block_on(foo_block())), 103); + + #[cfg(any(afn, afn_cls))] + assert_eq!(panicked_at(|| foo_manual_poll()), 116); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| foo_manual_poll()), 109); +} diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 7b116387169c2..7890592095b15 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:54:1 + --> $DIR/panic-track-caller.rs:57:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:68:5 + --> $DIR/panic-track-caller.rs:71:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:103:1 + --> $DIR/panic-track-caller.rs:106:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 7b116387169c2..7890592095b15 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:54:1 + --> $DIR/panic-track-caller.rs:57:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:68:5 + --> $DIR/panic-track-caller.rs:71:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:103:1 + --> $DIR/panic-track-caller.rs:106:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 41835977f8d6c..d0e09a396217f 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,3 +1,6 @@ +// This test is duplicated (with changes) at +// src/tools/miri/tests/pass/async-panic-track-caller.rs + //@ run-pass //@ edition:2021 //@ revisions: afn cls afn_cls nofeat @@ -134,32 +137,32 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { // uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 46); + assert_eq!(panicked_at(|| block_on(foo())), 49); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 65); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 77); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 80); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 86); - #[cfg(afn_cls)] assert_eq!(panicked_at(|| block_on(foo_closure())), 89); + #[cfg(afn_cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 92); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 100); + assert_eq!(panicked_at(|| block_on(foo_block())), 103); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 113); + assert_eq!(panicked_at(|| foo_manual_poll()), 116); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 106); + assert_eq!(panicked_at(|| foo_manual_poll()), 109); } From e9bdbfd470c0d0272ff060c1949296f17809378d Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 29 Aug 2026 21:52:23 +0700 Subject: [PATCH 09/51] Exclude gcc from panic-track-caller test --- .../tests/pass/async-panic-track-caller.rs | 22 ++++++++++--------- .../panic-track-caller.cls.stderr | 6 ++--- .../panic-track-caller.nofeat.stderr | 6 ++--- .../track-caller/panic-track-caller.rs | 22 ++++++++++--------- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 8cb43f8fef9ce..46ab0984dbdec 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -5,6 +5,8 @@ //@ revisions: afn cls afn_cls nofeat // // +// +// // Padding comment so that the line numbers are the same as panic-track-caller.rs #![feature(stmt_expr_attributes)] #![cfg_attr(any(afn, afn_cls), feature(async_fn_track_caller))] @@ -137,32 +139,32 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { // uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 49); + assert_eq!(panicked_at(|| block_on(foo())), 51); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 65); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 80); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 89); + assert_eq!(panicked_at(|| block_on(foo_closure())), 91); #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 92); + assert_eq!(panicked_at(|| block_on(foo_closure())), 94); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 103); + assert_eq!(panicked_at(|| block_on(foo_block())), 105); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 116); + assert_eq!(panicked_at(|| foo_manual_poll()), 118); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 109); + assert_eq!(panicked_at(|| foo_manual_poll()), 111); } diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr index 7890592095b15..5611e53f50a40 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:57:1 + --> $DIR/panic-track-caller.rs:59:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:71:5 + --> $DIR/panic-track-caller.rs:73:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:106:1 + --> $DIR/panic-track-caller.rs:108:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 7890592095b15..5611e53f50a40 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,5 +1,5 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:57:1 + --> $DIR/panic-track-caller.rs:59:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | | } = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:71:5 + --> $DIR/panic-track-caller.rs:73:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | | } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:106:1 + --> $DIR/panic-track-caller.rs:108:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index d0e09a396217f..294150500170d 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,6 +1,8 @@ // This test is duplicated (with changes) at // src/tools/miri/tests/pass/async-panic-track-caller.rs +// FIXME: catch_unwind is broken in gcc. Will be fixed in the next rustc_codegen_gcc sync. +//@ ignore-backends: gcc //@ run-pass //@ edition:2021 //@ revisions: afn cls afn_cls nofeat @@ -137,32 +139,32 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { // uses the location where the future is awaited or polled. // The correct behavior as per T-lang is to use the location where the function is called. fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 49); + assert_eq!(panicked_at(|| block_on(foo())), 51); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 65); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 67); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 60); + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 62); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 80); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 82); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 74); + assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); // FIXME(closure_track_caller): if closure_track_caller is enabled, but // async_fn_track_caller is disabled, then #[track_caller] on async closures // silently do nothing. Either it should function, or we should emit a warning. // See #161961 #[cfg(cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 89); + assert_eq!(panicked_at(|| block_on(foo_closure())), 91); #[cfg(afn_cls)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 92); + assert_eq!(panicked_at(|| block_on(foo_closure())), 94); #[cfg(any(cls, afn_cls))] - assert_eq!(panicked_at(|| block_on(foo_block())), 103); + assert_eq!(panicked_at(|| block_on(foo_block())), 105); #[cfg(any(afn, afn_cls))] - assert_eq!(panicked_at(|| foo_manual_poll()), 116); + assert_eq!(panicked_at(|| foo_manual_poll()), 118); #[cfg(any(cls, nofeat))] - assert_eq!(panicked_at(|| foo_manual_poll()), 109); + assert_eq!(panicked_at(|| foo_manual_poll()), 111); } From d00696d3ecbef19d9680514032231380ff396193 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Sun, 30 Aug 2026 13:26:19 +0700 Subject: [PATCH 10/51] Add run-native to async-panic-track-caller.rs Co-authored-by: Ralf Jung --- src/tools/miri/tests/pass/async-panic-track-caller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/async-panic-track-caller.rs b/src/tools/miri/tests/pass/async-panic-track-caller.rs index 46ab0984dbdec..062e07cffc8b9 100644 --- a/src/tools/miri/tests/pass/async-panic-track-caller.rs +++ b/src/tools/miri/tests/pass/async-panic-track-caller.rs @@ -3,7 +3,7 @@ //@ edition:2021 //@ revisions: afn cls afn_cls nofeat -// +//@ run-native // // // From 1c4440205607cd18ccfb30abe065bf0f62b5dd18 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 31 Aug 2026 14:46:11 +1000 Subject: [PATCH 11/51] Preliminary cleanup in coverage codegen - Rename `generate_*` functions to `emit_*`, since they emit LLVM globals - Consistently use qualified paths in `counter_for_term` - Remove an unnecessary Clone from `llvm_cov::Regions` --- .../src/coverageinfo/llvm_cov.rs | 2 +- .../src/coverageinfo/mapgen.rs | 14 ++++++------- .../src/coverageinfo/mapgen/covfun.rs | 20 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index a58202834cfa5..93d0b09578315 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -64,7 +64,7 @@ pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef]) -> Vec, pub(crate) expansion_regions: Vec, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 18818fd1a56c0..e9543ebeecf99 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -74,7 +74,7 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) { unused::prepare_covfun_records_for_unused_functions(cx, &mut covfun_records); } - // If there are no covfun records for this CGU, don't generate a covmap record. + // If there are no covfun records for this CGU, don't emit a covmap record. // Emitting a covmap record without any covfun records causes `llvm-cov` to // fail when generating coverage reports, and if there are no covfun records // then the covmap record isn't useful anyway. @@ -89,13 +89,13 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) { GlobalFileTable::build(tcx, covfun_records.iter().flat_map(|c| c.all_source_files())); for covfun in &covfun_records { - covfun::generate_covfun_record(cx, &global_file_table, covfun) + covfun::emit_covfun_record(cx, &global_file_table, covfun); } - // Generate the coverage map header, which contains the filenames used by + // Emit the coverage map header, which contains the filenames used by // this CGU's coverage mappings, and store it in a well-known global. // (This is skipped if we returned early due to having no covfun records.) - generate_covmap_record(cx, covmap_version, &global_file_table.filenames_buffer); + emit_covmap_record(cx, covmap_version, &global_file_table.filenames_buffer); } /// Maps "global" (per-CGU) file ID numbers to their underlying source file paths. @@ -218,10 +218,10 @@ impl VirtualFileMapping { } } -/// Generates the contents of the covmap record for this CGU, which mostly -/// consists of a header and a list of filenames. The record is then stored +/// Generates and emits the covmap record for this CGU, which mostly +/// consists of a header and a list of filenames. The record is emitted /// as a global variable in the `__llvm_covmap` section. -fn generate_covmap_record<'ll>( +fn emit_covmap_record<'ll>( cx: &mut CodegenCx<'ll, '_>, version: CovmapVersion, filenames_buffer: &[u8], diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 28985448fd1c1..b83570911c3a0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -77,15 +77,15 @@ pub(crate) fn prepare_covfun_record<'tcx>( Some(covfun) } -pub(crate) fn counter_for_term(term: CovTerm) -> ffi::Counter { - use ffi::Counter; +fn counter_for_term(term: CovTerm) -> ffi::Counter { match term { - CovTerm::Zero => Counter::ZERO, - CovTerm::Counter(id) => { - Counter { kind: ffi::CounterKind::CounterValueReference, id: CounterId::as_u32(id) } - } + CovTerm::Zero => ffi::Counter::ZERO, + CovTerm::Counter(id) => ffi::Counter { + kind: ffi::CounterKind::CounterValueReference, + id: CounterId::as_u32(id), + }, CovTerm::Expression(id) => { - Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) } + ffi::Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) } } } } @@ -174,10 +174,10 @@ fn fill_region_tables<'tcx>( } } -/// Generates the contents of the covfun record for this function, which -/// contains the function's coverage mapping data. The record is then stored +/// Generates and emits the covfun record for this function, which +/// contains the function's coverage mapping data. The record is emitted /// as a global variable in the `__llvm_covfun` section. -pub(crate) fn generate_covfun_record<'tcx>( +pub(crate) fn emit_covfun_record<'tcx>( cx: &mut CodegenCx<'_, 'tcx>, global_file_table: &GlobalFileTable, covfun: &CovfunRecord<'tcx>, From be8a828aa9b2532c8d726adcafdab72831db9dcf Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 29 Aug 2026 17:15:24 +1000 Subject: [PATCH 12/51] Resolve spans to file-coordinates in a separate step One of the key tasks in coverage codegen is to take the source-code spans that were recorded during MIR instrumentation, and resolve them to physical coordinates in their respective files. In rare cases this resolution can fail, which leads to the awkward possibility that a function might lose _all_ of its mappings for a particular file/expansion. If that happens, we need to avoid emitting a covfun file section containing no regions, because doing so would trigger errors in LLVM. The existing code does handle this edge case, but in a way that won't generalise to multiple files/expansions. Having an explicit intermediate resolution step will make it easier to add support for expansion regions in the future. --- .../src/coverageinfo/mapgen.rs | 30 --- .../src/coverageinfo/mapgen/covfun.rs | 175 ++++++++++++------ 2 files changed, 118 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index e9543ebeecf99..3dac4b3e0167f 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,11 +1,9 @@ use std::assert_matches; -use std::sync::Arc; use itertools::Itertools; use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::FxIndexMap; -use rustc_index::IndexVec; use rustc_middle::ty::TyCtxt; use rustc_span::{FileName, RemapPathScopeComponents, SourceFile, StableSourceFileId}; use tracing::debug; @@ -190,34 +188,6 @@ rustc_index::newtype_index! { struct LocalFileId {} } -/// Holds a mapping from "local" (per-function) file IDs to their corresponding -/// source files. -#[derive(Debug, Default)] -struct VirtualFileMapping { - local_file_table: IndexVec>, -} - -impl VirtualFileMapping { - fn push_file(&mut self, source_file: &Arc) -> LocalFileId { - self.local_file_table.push(Arc::clone(source_file)) - } - - /// Resolves all of the filenames in this local file mapping to a list of - /// global file IDs in its CGU, for inclusion in this function's - /// `__llvm_covfun` record. - /// - /// The global file IDs are returned as `u32` to make FFI easier. - fn resolve_all(&self, global_file_table: &GlobalFileTable) -> Option> { - self.local_file_table - .iter() - .map(|file| try { - let id = global_file_table.get_existing_id(file)?; - GlobalFileId::as_u32(id) - }) - .collect::>>() - } -} - /// Generates and emits the covmap record for this CGU, which mostly /// consists of a header and a list of filenames. The record is emitted /// as a global variable in the `__llvm_covmap` section. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index b83570911c3a0..d01ed8765302e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -5,13 +5,15 @@ //! [^win]: On Windows the section name is `.lcovfun`. use std::ffi::CString; +use std::iter; use std::sync::Arc; use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods}; +use rustc_index::IndexVec; use rustc_middle::mir::coverage::{ - BasicCoverageBlock, CounterId, CovTerm, CoverageCodegenInfo, CoverageMirInfo, Expression, - ExpressionId, Mapping, MappingKind, Op, + BasicCoverageBlock, CounterId, CovTerm, CoverageCodegenInfo, Expression, ExpressionId, Mapping, + MappingKind, Op, }; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::{SourceFile, Span}; @@ -19,7 +21,7 @@ use rustc_target::spec::HasTargetSpec; use tracing::debug; use crate::common::CodegenCx; -use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, spans}; +use crate::coverageinfo::mapgen::{GlobalFileTable, LocalFileId, spans}; use crate::coverageinfo::{ffi, llvm_cov}; use crate::llvm; @@ -34,16 +36,15 @@ pub(crate) struct CovfunRecord<'tcx> { source_hash: u64, is_used: bool, - virtual_file_mapping: VirtualFileMapping, expressions: Vec, - regions: llvm_cov::Regions, + mappings: ResolvedMappings, } impl<'tcx> CovfunRecord<'tcx> { /// Iterator that yields all source files referred to by this function's /// coverage mappings. Used to build the global file table for the CGU. pub(crate) fn all_source_files(&self) -> impl Iterator { - self.virtual_file_mapping.local_file_table.iter().map(Arc::as_ref) + self.mappings.all_source_files() } } @@ -56,24 +57,17 @@ pub(crate) fn prepare_covfun_record<'tcx>( let cg_info = tcx.coverage_codegen_info(instance.def)?; let expressions = prepare_expressions(cg_info); + let mappings = prepare_resolved_mappings(tcx, cg_info, is_used, &mir_info.mappings)?; - let mut covfun = CovfunRecord { + let covfun = CovfunRecord { _instance: instance, mangled_function_name: tcx.symbol_name(instance).name, source_hash: if is_used { mir_info.function_source_hash } else { 0 }, is_used, - virtual_file_mapping: VirtualFileMapping::default(), expressions, - regions: llvm_cov::Regions::default(), + mappings, }; - fill_region_tables(tcx, mir_info, cg_info, &mut covfun); - - if covfun.regions.has_no_regions() { - debug!(?covfun, "function has no mappings to embed; skipping"); - return None; - } - Some(covfun) } @@ -110,16 +104,60 @@ fn prepare_expressions(cg_info: &CoverageCodegenInfo) -> Vec>() } -/// Populates the mapping region tables in the current function's covfun record. -fn fill_region_tables<'tcx>( +/// Intermediate representation of coverage mappings, after all mapping spans +/// have been resolved to file coordinates (or discarded), but before producing +/// a final [`llvm_cov::Regions`]. +/// +/// Having a separate resolution step makes it easier to handle edge cases +/// where a function (or someday an expansion) manages to lose all of its spans, +/// without accidentally emitting invalid covfun records containing empty files. +#[derive(Debug)] +struct ResolvedMappings { + /// Source file for all of the [`spans::Coords`] in these mappings. + source_file: Arc, + + code_mappings: Vec, + branch_mappings: Vec, +} + +impl ResolvedMappings { + fn ensure_nonempty(self) -> Option { + let ResolvedMappings { source_file: _, code_mappings, branch_mappings } = &self; + if code_mappings.is_empty() && branch_mappings.is_empty() { None } else { Some(self) } + } + + fn all_source_files(&self) -> impl Iterator { + // FIXME(Zalathar): When expansion regions are supported, this also needs to yield + // any source files used by descendant expansions. + let ResolvedMappings { source_file, code_mappings: _, branch_mappings: _ } = self; + iter::once(source_file.as_ref()) + } +} + +/// Resolved from [`MappingKind::Code`], and the precursor to [`ffi::CodeRegion`]. +#[derive(Debug)] +struct CodeMapping { + coords: spans::Coords, + counter: ffi::Counter, +} + +/// Resolved from [`MappingKind::Branch`], and the precursor to [`ffi::BranchRegion`]. +#[derive(Debug)] +struct BranchMapping { + coords: spans::Coords, + true_counter: ffi::Counter, + false_counter: ffi::Counter, +} + +fn prepare_resolved_mappings<'tcx>( tcx: TyCtxt<'tcx>, - mir_info: &'tcx CoverageMirInfo, cg_info: &'tcx CoverageCodegenInfo, - covfun: &mut CovfunRecord<'tcx>, -) { + is_used: bool, + mappings: &[Mapping], +) -> Option { // If this function is unused, replace all counters with zero. let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter { - let term = if covfun.is_used { + let term = if is_used { cg_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term") } else { CovTerm::Zero @@ -130,14 +168,9 @@ fn fill_region_tables<'tcx>( // Currently a function's mappings must all be in the same file, so use the // first mapping's span to determine the file. let source_map = tcx.sess.source_map(); - let Some(first_span) = (try { mir_info.mappings.first()?.span }) else { - debug_assert!(false, "function has no mappings: {covfun:?}"); - return; - }; + let first_span = mappings.first()?.span; let source_file = source_map.lookup_source_file(first_span.lo()); - let local_file_id = covfun.virtual_file_mapping.push_file(&source_file); - // In rare cases, _all_ of a function's spans are discarded, and coverage // codegen needs to handle that gracefully to avoid #133606. // It's hard for tests to trigger this organically, so instead we set @@ -147,30 +180,57 @@ fn fill_region_tables<'tcx>( if discard_all { None } else { spans::make_coords(source_map, &source_file, span) } }; + let mut code_mappings = vec![]; + let mut branch_mappings = vec![]; + + for &Mapping { ref kind, span } in mappings { + let Some(coords) = make_coords(span) else { continue }; + match *kind { + MappingKind::Code { bcb } => { + code_mappings.push(CodeMapping { coords, counter: counter_for_bcb(bcb) }) + } + MappingKind::Branch { true_bcb, false_bcb } => branch_mappings.push(BranchMapping { + coords, + true_counter: counter_for_bcb(true_bcb), + false_counter: counter_for_bcb(false_bcb), + }), + } + } + + ResolvedMappings { source_file, code_mappings, branch_mappings }.ensure_nonempty() +} + +/// Populates the mapping region tables for the current function's covfun record. +fn fill_region_tables( + global_file_table: &GlobalFileTable, + mappings: &ResolvedMappings, + virtual_file_mapping: &mut IndexVec, + regions: &mut llvm_cov::Regions, +) { + let ResolvedMappings { source_file, code_mappings, branch_mappings } = mappings; + let Some(global_file_id) = global_file_table.get_existing_id(source_file) else { + debug_assert!(false, "couldn't find an existing global-file-id for {source_file:?}"); + return; + }; + let llvm_cov::Regions { code_regions, expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions branch_regions, - } = &mut covfun.regions; + } = regions; - // For each counter/region pair in this function+file, convert it to a - // form suitable for FFI. - for &Mapping { ref kind, span } in &mir_info.mappings { - let Some(coords) = make_coords(span) else { continue }; + // The global file IDs are stored as `u32` to make FFI easier. + // FIXME(Zalathar): Consider giving `newtype_index!` a safe transmute to `&[u32]`. + let local_file_id = virtual_file_mapping.push(global_file_id.as_u32()); + + for &CodeMapping { coords, counter } in code_mappings { let cov_span = coords.make_coverage_span(local_file_id); + code_regions.push(ffi::CodeRegion { cov_span, counter }); + } - match *kind { - MappingKind::Code { bcb } => { - code_regions.push(ffi::CodeRegion { cov_span, counter: counter_for_bcb(bcb) }); - } - MappingKind::Branch { true_bcb, false_bcb } => { - branch_regions.push(ffi::BranchRegion { - cov_span, - true_counter: counter_for_bcb(true_bcb), - false_counter: counter_for_bcb(false_bcb), - }); - } - } + for &BranchMapping { coords, true_counter, false_counter } in branch_mappings { + let cov_span = coords.make_coverage_span(local_file_id); + branch_regions.push(ffi::BranchRegion { cov_span, true_counter, false_counter }); } } @@ -187,24 +247,25 @@ pub(crate) fn emit_covfun_record<'tcx>( mangled_function_name, source_hash, is_used, - ref virtual_file_mapping, ref expressions, - ref regions, + ref mappings, } = covfun; - let Some(local_file_table) = virtual_file_mapping.resolve_all(global_file_table) else { - debug_assert!( - false, - "all local files should be present in the global file table: \ - global_file_table = {global_file_table:?}, \ - virtual_file_mapping = {virtual_file_mapping:?}" - ); + let mut regions = llvm_cov::Regions::default(); + let mut virtual_file_mapping = IndexVec::new(); + fill_region_tables(global_file_table, mappings, &mut virtual_file_mapping, &mut regions); + + if regions.has_no_regions() { + debug_assert!(false, "mappings should have produced at least one region: {mappings:#?}"); return; - }; + } // Encode the function's coverage mappings into a buffer. - let coverage_mapping_buffer = - llvm_cov::write_function_mappings_to_buffer(&local_file_table, expressions, regions); + let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer( + &virtual_file_mapping.raw, + expressions, + ®ions, + ); // A covfun record consists of four target-endian integers, followed by the // encoded mapping data in bytes. Note that the length field is 32 bits. From 41258df4130f7c8eaab4d3177d156b1661587456 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 30 Aug 2026 16:19:50 +0200 Subject: [PATCH 13/51] Move more `rustdoc-html` tests using `--test` into the right folder --- .../doctest}/async-move-doctest.rs | 2 + .../doctest/async-move-doctest.stdout | 6 +++ .../doctest}/comment-in-doctest.rs | 2 + .../doctest/comment-in-doctest.stdout | 6 +++ .../doctest}/demo-allocator-54478.rs | 7 +++- .../doctest/demo-allocator-54478.stdout | 6 +++ .../doctest}/doc-cfg-target-feature.rs | 3 +- .../doctest/doc-cfg-target-feature.stdout | 39 +++++++++++++++++++ .../doctest}/doc-test-attr-18199.rs | 5 ++- .../doctest/doc-test-attr-18199.stdout | 6 +++ .../doctest}/edition-doctest.rs | 4 +- .../rustdoc-ui/doctest/edition-doctest.stdout | 7 ++++ .../doctest}/edition-flag.rs | 2 + tests/rustdoc-ui/doctest/edition-flag.stdout | 6 +++ .../doctest}/force-target-feature.rs | 5 ++- .../doctest/force-target-feature.stdout | 27 +++++++++++++ .../doctest}/ice-type-error-19181.rs | 3 ++ .../doctest/ice-type-error-19181.stdout | 5 +++ .../doctest}/no-run-still-checks-lints.rs | 3 +- .../doctest/no-run-still-checks-lints.stdout | 29 ++++++++++++++ .../doctest}/process-termination.rs | 4 +- .../doctest/process-termination.stdout | 8 ++++ .../doctest}/sanitizer-option.rs | 4 +- .../doctest/test-option-check-2.rs} | 5 ++- .../doctest/test-option-check-2.stdout | 8 ++++ .../doctest/test-option-check.rs} | 2 + .../doctest/test-option-check.stdout | 6 +++ .../lints/renamed-lint-still-applies.rs | 10 ----- 28 files changed, 200 insertions(+), 20 deletions(-) rename tests/{rustdoc-html/async => rustdoc-ui/doctest}/async-move-doctest.rs (77%) create mode 100644 tests/rustdoc-ui/doctest/async-move-doctest.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/comment-in-doctest.rs (89%) create mode 100644 tests/rustdoc-ui/doctest/comment-in-doctest.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/demo-allocator-54478.rs (93%) create mode 100644 tests/rustdoc-ui/doctest/demo-allocator-54478.stdout rename tests/{rustdoc-html/doc-cfg => rustdoc-ui/doctest}/doc-cfg-target-feature.rs (78%) create mode 100644 tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/doc-test-attr-18199.rs (74%) create mode 100644 tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/edition-doctest.rs (87%) create mode 100644 tests/rustdoc-ui/doctest/edition-doctest.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/edition-flag.rs (63%) create mode 100644 tests/rustdoc-ui/doctest/edition-flag.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/force-target-feature.rs (64%) create mode 100644 tests/rustdoc-ui/doctest/force-target-feature.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/ice-type-error-19181.rs (65%) create mode 100644 tests/rustdoc-ui/doctest/ice-type-error-19181.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/no-run-still-checks-lints.rs (55%) create mode 100644 tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/process-termination.rs (80%) create mode 100644 tests/rustdoc-ui/doctest/process-termination.stdout rename tests/{rustdoc-html => rustdoc-ui/doctest}/sanitizer-option.rs (86%) rename tests/{rustdoc-html/test_option_check/test.rs => rustdoc-ui/doctest/test-option-check-2.rs} (54%) create mode 100644 tests/rustdoc-ui/doctest/test-option-check-2.stdout rename tests/{rustdoc-html/test_option_check/bar.rs => rustdoc-ui/doctest/test-option-check.rs} (65%) create mode 100644 tests/rustdoc-ui/doctest/test-option-check.stdout delete mode 100644 tests/rustdoc-ui/lints/renamed-lint-still-applies.rs diff --git a/tests/rustdoc-html/async/async-move-doctest.rs b/tests/rustdoc-ui/doctest/async-move-doctest.rs similarity index 77% rename from tests/rustdoc-html/async/async-move-doctest.rs rename to tests/rustdoc-ui/doctest/async-move-doctest.rs index e18ec353533df..f491a9a04f851 100644 --- a/tests/rustdoc-html/async/async-move-doctest.rs +++ b/tests/rustdoc-ui/doctest/async-move-doctest.rs @@ -1,5 +1,7 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ edition:2018 +//@ check-pass // Prior to setting the default edition for the doctest pre-parser, // this doctest would fail due to a fatal parsing error. diff --git a/tests/rustdoc-ui/doctest/async-move-doctest.stdout b/tests/rustdoc-ui/doctest/async-move-doctest.stdout new file mode 100644 index 0000000000000..4790438d4602f --- /dev/null +++ b/tests/rustdoc-ui/doctest/async-move-doctest.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/async-move-doctest.rs - (line 10) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/comment-in-doctest.rs b/tests/rustdoc-ui/doctest/comment-in-doctest.rs similarity index 89% rename from tests/rustdoc-html/comment-in-doctest.rs rename to tests/rustdoc-ui/doctest/comment-in-doctest.rs index e580aa2bb72c6..2caec5db9c920 100644 --- a/tests/rustdoc-html/comment-in-doctest.rs +++ b/tests/rustdoc-ui/doctest/comment-in-doctest.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // comments, both doc comments and regular ones, used to trick rustdoc's doctest parser into // thinking that everything after it was part of the regular program. combined with the librustc_ast diff --git a/tests/rustdoc-ui/doctest/comment-in-doctest.stdout b/tests/rustdoc-ui/doctest/comment-in-doctest.stdout new file mode 100644 index 0000000000000..5cb97c53f37fd --- /dev/null +++ b/tests/rustdoc-ui/doctest/comment-in-doctest.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/comment-in-doctest.rs - (line 12) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/demo-allocator-54478.rs b/tests/rustdoc-ui/doctest/demo-allocator-54478.rs similarity index 93% rename from tests/rustdoc-html/demo-allocator-54478.rs rename to tests/rustdoc-ui/doctest/demo-allocator-54478.rs index 80acfc0ff58a1..073d83e11120e 100644 --- a/tests/rustdoc-html/demo-allocator-54478.rs +++ b/tests/rustdoc-ui/doctest/demo-allocator-54478.rs @@ -1,4 +1,9 @@ // https://github.com/rust-lang/rust/issues/54478 + +//@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + #![crate_name="foo"] // Issue #54478: regression test showing that we can demonstrate @@ -15,8 +20,6 @@ // decided to change `rustdoc` to behave more like the compiler's // default setting, by leaving off `-C prefer-dynamic`. -//@ compile-flags:--test - //! This is a doc comment //! //! ```rust diff --git a/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout b/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout new file mode 100644 index 0000000000000..f32d9a5b7d932 --- /dev/null +++ b/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/demo-allocator-54478.rs - (line 25) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs similarity index 78% rename from tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs rename to tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs index b66e86e36af8b..99a133a6829c5 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs @@ -1,6 +1,7 @@ //@ only-x86_64 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ compile-flags:--test -//@ should-fail +//@ failure-status: 101 // #49723: rustdoc didn't add target features when extracting or running doctests diff --git a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout new file mode 100644 index 0000000000000..d71b1032e60ec --- /dev/null +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout @@ -0,0 +1,39 @@ + +running 1 test +test $DIR/doc-cfg-target-feature.rs - foo (line 14) ... FAILED + +failures: + +---- $DIR/doc-cfg-target-feature.rs - foo (line 14) stdout ---- +warning: the feature `cfg_target_feature` has been stable since 1.27.0 and no longer requires an attribute to enable + --> $DIR/doc-cfg-target-feature.rs:14:12 + | +LL | #![feature(cfg_target_feature)] + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(stable_features)]` on by default + +warning: 1 warning emitted + +Test executable failed (exit status: 101). + +stderr: + +thread 'main' ($TID) panicked at $DIR/doc-cfg-target-feature.rs:7:1: +assertion failed: false +stack backtrace: + 0: __rustc::rust_begin_unwind + 1: core::panicking::panic_fmt + 2: core::panicking::panic + 3: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_doc_cfg_target_feature_rs_14_0 + 4: rust_out::main + 5: >::call_once +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + + + +failures: + $DIR/doc-cfg-target-feature.rs - foo (line 14) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doc-test-attr-18199.rs b/tests/rustdoc-ui/doctest/doc-test-attr-18199.rs similarity index 74% rename from tests/rustdoc-html/doc-test-attr-18199.rs rename to tests/rustdoc-ui/doctest/doc-test-attr-18199.rs index 64016e32eeeb1..8350f244fccac 100644 --- a/tests/rustdoc-html/doc-test-attr-18199.rs +++ b/tests/rustdoc-ui/doctest/doc-test-attr-18199.rs @@ -1,6 +1,9 @@ -//@ compile-flags:--test // https://github.com/rust-lang/rust/issues/18199 +//@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + #![doc(test(attr(feature(staged_api))))] /// ``` diff --git a/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout b/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout new file mode 100644 index 0000000000000..a182a3b911af6 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doc-test-attr-18199.rs - foo (line 9) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/edition-doctest.rs b/tests/rustdoc-ui/doctest/edition-doctest.rs similarity index 87% rename from tests/rustdoc-html/edition-doctest.rs rename to tests/rustdoc-ui/doctest/edition-doctest.rs index f43c074f806bd..066475dae7bf0 100644 --- a/tests/rustdoc-html/edition-doctest.rs +++ b/tests/rustdoc-ui/doctest/edition-doctest.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// ```rust,edition2018 /// #![feature(try_blocks)] diff --git a/tests/rustdoc-ui/doctest/edition-doctest.stdout b/tests/rustdoc-ui/doctest/edition-doctest.stdout new file mode 100644 index 0000000000000..40d0df0575a76 --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-doctest.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/edition-doctest.rs - foo (line 24) - compile fail ... ok +test $DIR/edition-doctest.rs - foo (line 5) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/edition-flag.rs b/tests/rustdoc-ui/doctest/edition-flag.rs similarity index 63% rename from tests/rustdoc-html/edition-flag.rs rename to tests/rustdoc-ui/doctest/edition-flag.rs index c57c8d50b2357..51235634dbf4a 100644 --- a/tests/rustdoc-html/edition-flag.rs +++ b/tests/rustdoc-ui/doctest/edition-flag.rs @@ -1,5 +1,7 @@ //@ compile-flags:--test //@ edition:2018 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// ```rust /// fn main() { diff --git a/tests/rustdoc-ui/doctest/edition-flag.stdout b/tests/rustdoc-ui/doctest/edition-flag.stdout new file mode 100644 index 0000000000000..4833a6dcf9adf --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-flag.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/edition-flag.rs - main (line 6) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/force-target-feature.rs b/tests/rustdoc-ui/doctest/force-target-feature.rs similarity index 64% rename from tests/rustdoc-html/force-target-feature.rs rename to tests/rustdoc-ui/doctest/force-target-feature.rs index fa71bbeea2747..c3f9798147074 100644 --- a/tests/rustdoc-html/force-target-feature.rs +++ b/tests/rustdoc-ui/doctest/force-target-feature.rs @@ -1,6 +1,9 @@ //@ only-x86_64 //@ compile-flags:--test -C target-feature=+avx -//@ should-fail +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ failure-status: 101 + +#![feature(doc_cfg)] /// (written on a spider's web) Some Struct /// diff --git a/tests/rustdoc-ui/doctest/force-target-feature.stdout b/tests/rustdoc-ui/doctest/force-target-feature.stdout new file mode 100644 index 0000000000000..861a742075623 --- /dev/null +++ b/tests/rustdoc-ui/doctest/force-target-feature.stdout @@ -0,0 +1,27 @@ + +running 1 test +test $DIR/force-target-feature.rs - SomeStruct (line 10) ... FAILED + +failures: + +---- $DIR/force-target-feature.rs - SomeStruct (line 10) stdout ---- +Test executable failed (exit status: 101). + +stderr: + +thread 'main' ($TID) panicked at $DIR/force-target-feature.rs:3:1: +oh no +stack backtrace: + 0: std::panicking::begin_panic::<&str> + 1: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_force_target_feature_rs_10_0 + 2: rust_out::main + 3: >::call_once +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + + + +failures: + $DIR/force-target-feature.rs - SomeStruct (line 10) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/ice-type-error-19181.rs b/tests/rustdoc-ui/doctest/ice-type-error-19181.rs similarity index 65% rename from tests/rustdoc-html/ice-type-error-19181.rs rename to tests/rustdoc-ui/doctest/ice-type-error-19181.rs index 02c6404762222..accb9e2cab1f4 100644 --- a/tests/rustdoc-html/ice-type-error-19181.rs +++ b/tests/rustdoc-ui/doctest/ice-type-error-19181.rs @@ -1,4 +1,7 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + // https://github.com/rust-lang/rust/issues/19181 // rustdoc should not panic when target crate has compilation errors diff --git a/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout b/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout new file mode 100644 index 0000000000000..7326c0a25a069 --- /dev/null +++ b/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/no-run-still-checks-lints.rs b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs similarity index 55% rename from tests/rustdoc-html/no-run-still-checks-lints.rs rename to tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs index 73e311b72d5e5..cae6331f4723d 100644 --- a/tests/rustdoc-html/no-run-still-checks-lints.rs +++ b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs @@ -1,5 +1,6 @@ //@ compile-flags:--test -//@ should-fail +//@ failure-status: 101 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" #![doc(test(attr(deny(warnings))))] diff --git a/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout new file mode 100644 index 0000000000000..86d1b4d3094b6 --- /dev/null +++ b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout @@ -0,0 +1,29 @@ + +running 1 test +test $DIR/no-run-still-checks-lints.rs - foo (line 7) - compile ... FAILED + +failures: + +---- $DIR/no-run-still-checks-lints.rs - foo (line 7) stdout ---- +error: unused variable: `a` + --> $DIR/no-run-still-checks-lints.rs:8:5 + | +LL | let a = 3; + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: the lint level is defined here + --> $DIR/no-run-still-checks-lints.rs:6:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]` + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/no-run-still-checks-lints.rs - foo (line 7) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/process-termination.rs b/tests/rustdoc-ui/doctest/process-termination.rs similarity index 80% rename from tests/rustdoc-html/process-termination.rs rename to tests/rustdoc-ui/doctest/process-termination.rs index 73a86e57424a2..02ac594b3f0d4 100644 --- a/tests/rustdoc-html/process-termination.rs +++ b/tests/rustdoc-ui/doctest/process-termination.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// A check of using various process termination strategies /// diff --git a/tests/rustdoc-ui/doctest/process-termination.stdout b/tests/rustdoc-ui/doctest/process-termination.stdout new file mode 100644 index 0000000000000..3e15b9a5df80a --- /dev/null +++ b/tests/rustdoc-ui/doctest/process-termination.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/process-termination.rs - check_process_termination (line 16) ... ok +test $DIR/process-termination.rs - check_process_termination (line 22) ... ok +test $DIR/process-termination.rs - check_process_termination (line 9) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/sanitizer-option.rs b/tests/rustdoc-ui/doctest/sanitizer-option.rs similarity index 86% rename from tests/rustdoc-html/sanitizer-option.rs rename to tests/rustdoc-ui/doctest/sanitizer-option.rs index 7b0038138f09f..5f29f1b8bac7e 100644 --- a/tests/rustdoc-html/sanitizer-option.rs +++ b/tests/rustdoc-ui/doctest/sanitizer-option.rs @@ -1,7 +1,9 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address //@ compile-flags: --test -Z sanitizer=address -C unsafe-allow-abi-mismatch=sanitizer -// +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + // #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern // function that is provided by the sanitizer runtime, if flag is not passed // correctly, then linking will fail. diff --git a/tests/rustdoc-html/test_option_check/test.rs b/tests/rustdoc-ui/doctest/test-option-check-2.rs similarity index 54% rename from tests/rustdoc-html/test_option_check/test.rs rename to tests/rustdoc-ui/doctest/test-option-check-2.rs index af7a5827690f0..2e74da1eca794 100644 --- a/tests/rustdoc-html/test_option_check/test.rs +++ b/tests/rustdoc-ui/doctest/test-option-check-2.rs @@ -1,6 +1,9 @@ -//@ compile-flags: --test +//@ compile-flags: --test --test-args=--test-threads=1 //@ check-test-line-numbers-match +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass +#[path = "test-option-check.rs"] pub mod bar; /// This is a Foo; diff --git a/tests/rustdoc-ui/doctest/test-option-check-2.stdout b/tests/rustdoc-ui/doctest/test-option-check-2.stdout new file mode 100644 index 0000000000000..ab2db4938dfab --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-option-check-2.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/test-option-check-2.rs - Bar (line 18) ... ok +test $DIR/test-option-check-2.rs - Foo (line 11) ... ok +test $DIR/test-option-check.rs - bar::foooo (line 8) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/test_option_check/bar.rs b/tests/rustdoc-ui/doctest/test-option-check.rs similarity index 65% rename from tests/rustdoc-html/test_option_check/bar.rs rename to tests/rustdoc-ui/doctest/test-option-check.rs index 7c2309a79d4b9..e5d3350e3f981 100644 --- a/tests/rustdoc-html/test_option_check/bar.rs +++ b/tests/rustdoc-ui/doctest/test-option-check.rs @@ -1,5 +1,7 @@ //@ compile-flags: --test //@ check-test-line-numbers-match +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// This looks like another awesome test! /// diff --git a/tests/rustdoc-ui/doctest/test-option-check.stdout b/tests/rustdoc-ui/doctest/test-option-check.stdout new file mode 100644 index 0000000000000..38f949612a47a --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-option-check.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/test-option-check.rs - foooo (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs b/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs deleted file mode 100644 index a4d3a4b497117..0000000000000 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs +++ /dev/null @@ -1,10 +0,0 @@ -// compile-args: --crate-type lib -#![deny(broken_intra_doc_links)] -//~^ WARNING renamed to `rustdoc::broken_intra_doc_links` -//! [x] -//~^ ERROR unresolved link - -#![deny(rustdoc::non_autolinks)] -//~^ WARNING renamed to `rustdoc::bare_urls` -//! http://example.com -//~^ ERROR not a hyperlink From 614d9ea42ce84b46371e653f882496877ce277b4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 30 Aug 2026 16:24:44 +0200 Subject: [PATCH 14/51] Fix invalid `compile-args` ui tests argument --- .../lints/renamed-lint-still-applies.stderr | 12 ++++++------ tests/ui/lint/forbid-error-capped.rs | 1 - tests/ui/lint/forbid-error-capped.stderr | 4 ++-- tests/ui/mir/issue-71793-inline-args-storage.rs | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr index 88807dfb495d0..f4428ff6e5983 100644 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr @@ -1,5 +1,5 @@ warning: lint `broken_intra_doc_links` has been renamed to `rustdoc::broken_intra_doc_links` - --> $DIR/renamed-lint-still-applies.rs:2:9 + --> $DIR/renamed-lint-still-applies.rs:3:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links` @@ -7,33 +7,33 @@ LL | #![deny(broken_intra_doc_links)] = note: `#[warn(renamed_and_removed_lints)]` on by default warning: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls` - --> $DIR/renamed-lint-still-applies.rs:7:9 + --> $DIR/renamed-lint-still-applies.rs:8:9 | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` error: unresolved link to `x` - --> $DIR/renamed-lint-still-applies.rs:4:6 + --> $DIR/renamed-lint-still-applies.rs:5:6 | LL | //! [x] | ^ no item named `x` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:2:9 + --> $DIR/renamed-lint-still-applies.rs:3:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ error: this URL is not a hyperlink - --> $DIR/renamed-lint-still-applies.rs:9:5 + --> $DIR/renamed-lint-still-applies.rs:10:5 | LL | //! http://example.com | ^^^^^^^^^^^^^^^^^^ | = note: bare URLs are not automatically turned into clickable links note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:7:9 + --> $DIR/renamed-lint-still-applies.rs:8:9 | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/forbid-error-capped.rs b/tests/ui/lint/forbid-error-capped.rs index e458ddf90746e..bfa72beac5828 100644 --- a/tests/ui/lint/forbid-error-capped.rs +++ b/tests/ui/lint/forbid-error-capped.rs @@ -1,5 +1,4 @@ //@ check-pass -// compile-args: --cap-lints=warn -Fwarnings // This checks that the forbid attribute checking is ignored when the forbidden // lint is capped. diff --git a/tests/ui/lint/forbid-error-capped.stderr b/tests/ui/lint/forbid-error-capped.stderr index 479e7b9412d57..3de8c2fe0ce61 100644 --- a/tests/ui/lint/forbid-error-capped.stderr +++ b/tests/ui/lint/forbid-error-capped.stderr @@ -1,5 +1,5 @@ warning: allow(unused) incompatible with previous forbid - --> $DIR/forbid-error-capped.rs:8:10 + --> $DIR/forbid-error-capped.rs:7:10 | LL | #![forbid(warnings)] | -------- `forbid` level set here @@ -14,7 +14,7 @@ warning: 1 warning emitted Future incompatibility report: Future breakage diagnostic: warning: allow(unused) incompatible with previous forbid - --> $DIR/forbid-error-capped.rs:8:10 + --> $DIR/forbid-error-capped.rs:7:10 | LL | #![forbid(warnings)] | -------- `forbid` level set here diff --git a/tests/ui/mir/issue-71793-inline-args-storage.rs b/tests/ui/mir/issue-71793-inline-args-storage.rs index 0ed4d4723731e..38ce28a035346 100644 --- a/tests/ui/mir/issue-71793-inline-args-storage.rs +++ b/tests/ui/mir/issue-71793-inline-args-storage.rs @@ -1,10 +1,10 @@ // Verifies that inliner emits StorageLive & StorageDead when introducing // temporaries for arguments, so that they don't become part of the coroutine. // Regression test for #71793. -// + //@ check-pass //@ edition:2018 -// compile-args: -Zmir-opt-level=3 +//@ compile-flags: -Zmir-opt-level=3 #![crate_type = "lib"] From f92efc873b925b3a25038ff526b9b5cdad5b4230 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Tue, 1 Sep 2026 02:19:18 +0900 Subject: [PATCH 15/51] docs(num): clarify conditions under which error occurs --- library/core/src/convert/num.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 64e429b2f2591..c0d8fd670d7ad 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -336,8 +336,7 @@ macro_rules! impl_try_from_unbounded { type Error = TryFromIntError; /// Tries to create the target number type from a source - /// number type. This returns an error if the source value - /// is outside of the range of the target type. + /// number type. This never returns an error. #[inline] fn try_from(value: $source) -> Result { Ok(value as Self) @@ -356,7 +355,7 @@ macro_rules! impl_try_from_lower_bounded { /// Tries to create the target number type from a source /// number type. This returns an error if the source value - /// is outside of the range of the target type. + #[doc = concat!("is less than [`", stringify!($target), "::MIN`].")] #[inline] fn try_from(u: $source) -> Result { if u >= 0 { @@ -379,7 +378,7 @@ macro_rules! impl_try_from_upper_bounded { /// Tries to create the target number type from a source /// number type. This returns an error if the source value - /// is outside of the range of the target type. + #[doc = concat!("is greater than [`", stringify!($target), "::MAX`].")] #[inline] fn try_from(u: $source) -> Result { if u > (Self::MAX as $source) { From 7a38c0c984f6d05be47cda885b66bc48b3b23ae3 Mon Sep 17 00:00:00 2001 From: Yukang Date: Tue, 1 Sep 2026 14:00:23 +0800 Subject: [PATCH 16/51] Add regression test for for-loop temporary scope --- ...es-for-iterator-temp-scope-issue-160741.rs | 22 +++++++++++++++++++ ...or-iterator-temp-scope-issue-160741.stderr | 19 ++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.rs create mode 100644 tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.stderr diff --git a/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.rs b/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.rs new file mode 100644 index 0000000000000..6c85b8e50c5e3 --- /dev/null +++ b/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.rs @@ -0,0 +1,22 @@ +//! A block around a Rust 2024 `for` iterator expression may shorten the lifetime of temporaries. + +//@ edition: 2024 +//@ check-pass + +#![warn(unused_braces)] + +use std::sync::{Arc, Mutex}; + +struct State { + values: Vec, + total: u32, +} + +fn main() { + let data = Arc::new(Mutex::new(State { values: vec![1, 2, 3], total: 0 })); + + for value in { data.lock().unwrap().values.clone() } { + //~^ WARN unnecessary braces around `for` iterator expression + data.lock().unwrap().total += value; + } +} diff --git a/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.stderr b/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.stderr new file mode 100644 index 0000000000000..6658184c9a5bd --- /dev/null +++ b/tests/ui/lint/unused-braces-for-iterator-temp-scope-issue-160741.stderr @@ -0,0 +1,19 @@ +warning: unnecessary braces around `for` iterator expression + --> $DIR/unused-braces-for-iterator-temp-scope-issue-160741.rs:18:18 + | +LL | for value in { data.lock().unwrap().values.clone() } { + | ^^ ^^ + | +note: the lint level is defined here + --> $DIR/unused-braces-for-iterator-temp-scope-issue-160741.rs:6:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ +help: remove these braces + | +LL - for value in { data.lock().unwrap().values.clone() } { +LL + for value in data.lock().unwrap().values.clone() { + | + +warning: 1 warning emitted + From 16b206a7ae3a672df2441fe5657252e2484d8911 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 23 Aug 2026 14:32:16 +0600 Subject: [PATCH 17/51] suggest calling a fn item used as the iterator of a `for` loop `suggest_fn_call` only fired when the failing obligation came from `ObligationCauseCode::FunctionArg`, so a fn item or closure used as the iterator of a `for` loop got no structured suggestion to call it. the iterator of a `for` loop is passed to `IntoIterator::into_iter`, so the failing `Iterator` goal is a derived obligation and the cause span carries the loop desugaring, which makes `can_be_used_for_suggestions` return false. carry the `HirId` of the iterator expression in `ObligationCauseCode::ForLoopIterator` and gate the suggestion on the span of that expression instead. https://github.com/rust-lang/rust/issues/161564 --- compiler/rustc_hir_typeck/src/expr.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 27 ++++++-- ...est-calling-fn-in-for-loop-issue-161564.rs | 37 ++++++++++ ...calling-fn-in-for-loop-issue-161564.stderr | 68 +++++++++++++++++++ 5 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs create mode 100644 tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6ebf382083f25..8e3be44caf959 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -555,7 +555,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { LangItem::IntoIterIntoIter | LangItem::IteratorNext if expr.span.is_desugaring(DesugaringKind::ForLoop) => { - Some(ObligationCauseCode::ForLoopIterator) + Some(ObligationCauseCode::ForLoopIterator(arg.hir_id)) } LangItem::TryTraitFromOutput if expr.span.is_desugaring(DesugaringKind::TryBlock) => diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 9736aabf29009..4dfc8d7c9705d 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -374,7 +374,7 @@ pub enum ObligationCauseCode<'tcx> { AwaitableExpr(HirId), - ForLoopIterator, + ForLoopIterator(HirId), QuestionMark, 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 7921250a85a9e..a7f05d756af0f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1119,12 +1119,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .collect::>() .join(", "); - if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() - && obligation.cause.span.can_be_used_for_suggestions() - { + let callee_hir_id = match obligation.cause.code() { + ObligationCauseCode::FunctionArg { arg_hir_id, .. } + if obligation.cause.span.can_be_used_for_suggestions() => + { + Some(*arg_hir_id) + } + // The iterator of a `for` loop is passed to `IntoIterator::into_iter`, so the failing + // `Iterator` goal is a derived obligation and `cause.span` carries the loop's + // desugaring context. The expression is still the user's, which its own span attests. + code => match code.peel_derives() { + ObligationCauseCode::ForLoopIterator(iter_hir_id) + if self.tcx.hir_span(*iter_hir_id).can_be_used_for_suggestions() => + { + Some(*iter_hir_id) + } + _ => None, + }, + }; + + if let Some(callee_hir_id) = callee_hir_id { let span = obligation.cause.span; - let arg_expr = match self.tcx.hir_node(*arg_hir_id) { + let arg_expr = match self.tcx.hir_node(callee_hir_id) { hir::Node::Expr(expr) => Some(expr), _ => None, }; @@ -3779,7 +3796,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(..) | ObligationCauseCode::AwaitableExpr(_) - | ObligationCauseCode::ForLoopIterator + | ObligationCauseCode::ForLoopIterator(_) | ObligationCauseCode::QuestionMark | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse diff --git a/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs new file mode 100644 index 0000000000000..5d50346b5b6fe --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs @@ -0,0 +1,37 @@ +//@ edition: 2021 + +//! Check that a fn item used as the iterator of a `for` loop is suggested to be called, the way +//! it already is when it is passed as a function argument. + +struct S; + +trait T { + fn assoc_in_trait() -> std::vec::IntoIter; +} + +impl T for S { + fn assoc_in_trait() -> std::vec::IntoIter { + vec![1u8].into_iter() + } +} + +impl S { + fn inherent_assoc() -> impl Iterator { + [1u8].into_iter() + } +} + +fn free_fn() -> impl Iterator { + [1u8].into_iter() +} + +fn main() { + for _ in S::inherent_assoc {} //~ ERROR [E0277] + for _ in ::assoc_in_trait {} //~ ERROR [E0277] + for _ in free_fn {} //~ ERROR [E0277] + + let closure = || vec![1u8].into_iter(); + for _ in closure {} //~ ERROR [E0277] + + for _ in || vec![1u8].into_iter() {} //~ ERROR [E0277] +} diff --git a/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr new file mode 100644 index 0000000000000..e2edaebb65c9b --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr @@ -0,0 +1,68 @@ +error[E0277]: `fn() -> impl Iterator {S::inherent_assoc}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:29:14 + | +LL | for _ in S::inherent_assoc {} + | ^^^^^^^^^^^^^^^^^ `fn() -> impl Iterator {S::inherent_assoc}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator {S::inherent_assoc}` + = note: required for `fn() -> impl Iterator {S::inherent_assoc}` to implement `IntoIterator` +help: use parentheses to call this associated function + | +LL | for _ in S::inherent_assoc() {} + | ++ + +error[E0277]: `fn() -> std::vec::IntoIter {::assoc_in_trait}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:30:14 + | +LL | for _ in ::assoc_in_trait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn() -> std::vec::IntoIter {::assoc_in_trait}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> std::vec::IntoIter {::assoc_in_trait}` + = note: required for `fn() -> std::vec::IntoIter {::assoc_in_trait}` to implement `IntoIterator` +help: use parentheses to call this associated function + | +LL | for _ in ::assoc_in_trait() {} + | ++ + +error[E0277]: `fn() -> impl Iterator {free_fn}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:31:14 + | +LL | for _ in free_fn {} + | ^^^^^^^ `fn() -> impl Iterator {free_fn}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator {free_fn}` + = note: required for `fn() -> impl Iterator {free_fn}` to implement `IntoIterator` +help: use parentheses to call this function + | +LL | for _ in free_fn() {} + | ++ + +error[E0277]: `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:34:14 + | +LL | for _ in closure {} + | ^^^^^^^ `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` is not an iterator + | + = help: the trait `Iterator` is not implemented for closure `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` + = note: required for `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` to implement `IntoIterator` +help: use parentheses to call this closure + | +LL | for _ in closure() {} + | ++ + +error[E0277]: `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14 + | +LL | for _ in || vec![1u8].into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` is not an iterator + | + = help: the trait `Iterator` is not implemented for closure `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` + = note: required for `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` to implement `IntoIterator` +help: use parentheses to call this closure + | +LL | for _ in (|| vec![1u8].into_iter())() {} + | + +++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. From a97dc3135416f40b49ac345074643e3805e1624f Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Tue, 1 Sep 2026 13:17:42 +0600 Subject: [PATCH 18/51] suggest calling a function item that is being dereferenced dereferencing an uncalled function only said the function type cannot be dereferenced. it now suggests the call, gated on the return type actually being dereferenceable. https://github.com/rust-lang/rust/issues/161564#issuecomment-5383101040 --- compiler/rustc_hir_typeck/src/expr.rs | 10 +++ ...uggest-calling-fn-in-deref-issue-161564.rs | 73 ++++++++++++++++++ ...st-calling-fn-in-deref-issue-161564.stderr | 75 +++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs create mode 100644 tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 8e3be44caf959..548177a150b9a 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -430,6 +430,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } + // The operand may be an uncalled function, in which case it is its return type + // the user meant to dereference. Only suggest the call when that return type is + // itself dereferenceable, mirroring the checks `lookup_derefing` just failed. + self.suggest_fn_call(&mut err, oprnd, oprnd_t, |output| { + output.builtin_deref(true).is_some() + || self.tcx.lang_items().deref_trait().is_some_and(|deref_trait| { + self.type_implements_trait(deref_trait, [output], self.param_env) + .may_apply() + }) + }); Ty::new_error(tcx, err.emit()) }), hir::UnOp::Not => { diff --git a/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs new file mode 100644 index 0000000000000..6f1bf4e4589b8 --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs @@ -0,0 +1,73 @@ +// Dereferencing an uncalled function item should suggest calling it, rather than +// only complaining that the function's own type cannot be dereferenced. + +pub fn ret_ref() -> &'static usize { + &const { 12 } +} + +pub fn ret_val() -> usize { + 12 +} + +pub fn with_args(_: u8) -> &'static usize { + &const { 12 } +} + +pub fn ret_box() -> Box { + Box::new(12) +} + +struct S; + +impl S { + fn assoc() -> &'static usize { + &const { 12 } + } +} + +pub fn fn_item() { + let _a = *ret_ref; + //~^ ERROR type `fn() -> &'static usize {ret_ref}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn takes_args() { + let _a = *with_args; + //~^ ERROR type `fn(u8) -> &'static usize {with_args}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn assoc_fn() { + let _a = *S::assoc; + //~^ ERROR type `fn() -> &'static usize {S::assoc}` cannot be dereferenced + //~| HELP use parentheses to call this associated function +} + +pub fn overloaded_deref() { + let _a = *ret_box; + //~^ ERROR type `fn() -> Box {ret_box}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn fn_pointer() { + let f: fn() -> &'static usize = ret_ref; + let _a = *f; + //~^ ERROR type `fn() -> &'static usize` cannot be dereferenced + //~| HELP use parentheses to call this function pointer +} + +pub fn closure() { + let c = || &const { 12usize }; + let _a = *c; + //~^ ERROR cannot be dereferenced + //~| HELP use parentheses to call this closure +} + +// Negative case: calling this one still would not produce something dereferenceable, +// so no suggestion should be offered. +pub fn not_derefable_when_called() { + let _a = *ret_val; + //~^ ERROR type `fn() -> usize {ret_val}` cannot be dereferenced +} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr new file mode 100644 index 0000000000000..a119a368450c3 --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr @@ -0,0 +1,75 @@ +error[E0614]: type `fn() -> &'static usize {ret_ref}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:29:14 + | +LL | let _a = *ret_ref; + | ^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *ret_ref(); + | ++ + +error[E0614]: type `fn(u8) -> &'static usize {with_args}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:35:14 + | +LL | let _a = *with_args; + | ^^^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *with_args(/* u8 */); + | ++++++++++ + +error[E0614]: type `fn() -> &'static usize {S::assoc}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:41:14 + | +LL | let _a = *S::assoc; + | ^^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this associated function + | +LL | let _a = *S::assoc(); + | ++ + +error[E0614]: type `fn() -> Box {ret_box}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:47:14 + | +LL | let _a = *ret_box; + | ^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *ret_box(); + | ++ + +error[E0614]: type `fn() -> &'static usize` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:54:14 + | +LL | let _a = *f; + | ^^ can't be dereferenced + | +help: use parentheses to call this function pointer + | +LL | let _a = *f(); + | ++ + +error[E0614]: type `{closure@$DIR/suggest-calling-fn-in-deref-issue-161564.rs:60:13: 60:15}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:61:14 + | +LL | let _a = *c; + | ^^ can't be dereferenced + | +help: use parentheses to call this closure + | +LL | let _a = *c(); + | ++ + +error[E0614]: type `fn() -> usize {ret_val}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:69:14 + | +LL | let _a = *ret_val; + | ^^^^^^^^ can't be dereferenced + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0614`. From cb5e3df331e1684fd591c95658c033aed368175d Mon Sep 17 00:00:00 2001 From: Sang-Woo Kim Date: Tue, 1 Sep 2026 08:43:32 +0000 Subject: [PATCH 19/51] std: don't reference `libc::O_NOFOLLOW` on VxWorks in `set_perm_nofollow` VxWorks' libc defines no `O_NOFOLLOW`, so building std for x86_64-wrs-vxworks stopped compiling once `set_perm_nofollow` was consolidated into `sys/fs/unix.rs` without a vxworks guard. VxWorks also has no way to express a no-follow permission change: its `fchmodat` rejects `AT_SYMLINK_NOFOLLOW` with `ENOTSUP`. Return `Unsupported`, matching the existing Android stub. --- library/std/src/sys/fs/unix.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 1045a7b7e2f56..acd2c7f5dfbd1 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1884,6 +1884,13 @@ pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) } +#[cfg(target_os = "vxworks")] +pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { + // VxWorks has no `O_NOFOLLOW`, and its `fchmodat` rejects + // `AT_SYMLINK_NOFOLLOW` with `ENOTSUP`, so a no-follow chmod is unsupported. + Err(crate::io::ErrorKind::Unsupported.into()) +} + #[cfg(target_os = "android")] pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { // Currently Android seems to be having inconsistent behavior with fchmodat @@ -1896,7 +1903,7 @@ pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { Err(crate::io::ErrorKind::Unsupported.into()) } -#[cfg(not(target_os = "android"))] +#[cfg(not(any(target_os = "android", target_os = "vxworks")))] pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { #[inline] /// Helper function for fallback open with `O_NOFOLLOW` + `fchmod` behavior From 5ba810f06513b45387f1ed249a49b3ed75c8f47f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Sep 2026 14:18:42 +0200 Subject: [PATCH 20/51] Normalize more rustdoc-ui doctest output --- tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs | 1 + tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout | 10 +++++----- tests/rustdoc-ui/doctest/force-target-feature.rs | 1 + tests/rustdoc-ui/doctest/force-target-feature.stdout | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs index 99a133a6829c5..a55b31d61e3d0 100644 --- a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs @@ -1,5 +1,6 @@ //@ only-x86_64 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "rust_out::main::.+" -> "rust_out::main::$$PATH" //@ compile-flags:--test //@ failure-status: 101 diff --git a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout index d71b1032e60ec..6526e9898dd2f 100644 --- a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/doc-cfg-target-feature.rs - foo (line 14) ... FAILED +test $DIR/doc-cfg-target-feature.rs - foo (line 15) ... FAILED failures: ----- $DIR/doc-cfg-target-feature.rs - foo (line 14) stdout ---- +---- $DIR/doc-cfg-target-feature.rs - foo (line 15) stdout ---- warning: the feature `cfg_target_feature` has been stable since 1.27.0 and no longer requires an attribute to enable - --> $DIR/doc-cfg-target-feature.rs:14:12 + --> $DIR/doc-cfg-target-feature.rs:15:12 | LL | #![feature(cfg_target_feature)] | ^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ stack backtrace: 0: __rustc::rust_begin_unwind 1: core::panicking::panic_fmt 2: core::panicking::panic - 3: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_doc_cfg_target_feature_rs_14_0 + 3: rust_out::main::$PATH 4: rust_out::main 5: >::call_once note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. @@ -33,7 +33,7 @@ note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose bac failures: - $DIR/doc-cfg-target-feature.rs - foo (line 14) + $DIR/doc-cfg-target-feature.rs - foo (line 15) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/force-target-feature.rs b/tests/rustdoc-ui/doctest/force-target-feature.rs index c3f9798147074..f39e7cf3a9094 100644 --- a/tests/rustdoc-ui/doctest/force-target-feature.rs +++ b/tests/rustdoc-ui/doctest/force-target-feature.rs @@ -1,6 +1,7 @@ //@ only-x86_64 //@ compile-flags:--test -C target-feature=+avx //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "rust_out::main::.+" -> "rust_out::main::$$PATH" //@ failure-status: 101 #![feature(doc_cfg)] diff --git a/tests/rustdoc-ui/doctest/force-target-feature.stdout b/tests/rustdoc-ui/doctest/force-target-feature.stdout index 861a742075623..fb898dd14b2c9 100644 --- a/tests/rustdoc-ui/doctest/force-target-feature.stdout +++ b/tests/rustdoc-ui/doctest/force-target-feature.stdout @@ -1,10 +1,10 @@ running 1 test -test $DIR/force-target-feature.rs - SomeStruct (line 10) ... FAILED +test $DIR/force-target-feature.rs - SomeStruct (line 11) ... FAILED failures: ----- $DIR/force-target-feature.rs - SomeStruct (line 10) stdout ---- +---- $DIR/force-target-feature.rs - SomeStruct (line 11) stdout ---- Test executable failed (exit status: 101). stderr: @@ -13,7 +13,7 @@ thread 'main' ($TID) panicked at $DIR/force-target-feature.rs:3:1: oh no stack backtrace: 0: std::panicking::begin_panic::<&str> - 1: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_force_target_feature_rs_10_0 + 1: rust_out::main::$PATH 2: rust_out::main 3: >::call_once note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. @@ -21,7 +21,7 @@ note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose bac failures: - $DIR/force-target-feature.rs - SomeStruct (line 10) + $DIR/force-target-feature.rs - SomeStruct (line 11) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME From 7394cc66c3994fc944228e166a66ab9e9d4a1692 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 1 Sep 2026 16:31:12 +0200 Subject: [PATCH 21/51] std: improve safety documentation in UNIX stack overflow code --- .../std/src/sys/pal/unix/stack_overflow.rs | 331 +++++++++++------- 1 file changed, 213 insertions(+), 118 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 5679b55e0d486..5604e3e6dbf42 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -1,4 +1,5 @@ #![cfg_attr(test, allow(dead_code))] +#![forbid(unsafe_op_in_unsafe_fn)] pub use self::imp::init; use self::imp::{drop_handler, make_handler}; @@ -73,31 +74,33 @@ mod imp { use crate::sys::pal::unix::conf; use crate::{io, mem, ptr}; - // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages - // (unmapped pages) at the end of every thread's stack, so if a thread ends - // up running into the guard page it'll trigger this handler. We want to - // detect these cases and print out a helpful error saying that the stack - // has overflowed. All other signals, however, should go back to what they - // were originally supposed to do. - // - // This handler currently exists purely to print an informative message - // whenever a thread overflows its stack. We then abort to exit and - // indicate a crash, but to avoid a misleading SIGSEGV that might lead - // users to believe that unsafe code has accessed an invalid pointer; the - // SIGSEGV encountered when overflowing the stack is expected and - // well-defined. - // - // If this is not a stack overflow, the handler un-registers itself and - // then returns (to allow the original signal to be delivered again). - // Returning from this kind of signal handler is technically not defined - // to work when reading the POSIX spec strictly, but in practice it turns - // out many large systems and all implementations allow returning from a - // signal handler to work. For a more detailed explanation see the - // comments on #26458. - /// SIGSEGV/SIGBUS entry point + /// Signal handler for the SIGSEGV and SIGBUS handlers. + /// + /// We've got guard pages (unmapped pages) at the end of every thread's + /// stack, so if a thread ends up running into the guard page it'll trigger + /// this handler. We want to detect these cases and print out a helpful error + /// saying that the stack has overflowed. All other signals, however, should + /// go back to what they were originally supposed to do. + /// + /// This handler currently exists purely to print an informative message + /// whenever a thread overflows its stack. We then abort to exit and + /// indicate a crash, but to avoid a misleading SIGSEGV that might lead + /// users to believe that unsafe code has accessed an invalid pointer; the + /// SIGSEGV encountered when overflowing the stack is expected and + /// well-defined. + /// + /// If this is not a stack overflow, the handler un-registers itself and + /// then returns (to allow the original signal to be delivered again). + /// Returning from this kind of signal handler is technically not defined + /// to work when reading the POSIX spec strictly, but in practice it turns + /// out many large systems and all implementations allow returning from a + /// signal handler to work. For a more detailed explanation see the + /// comments on #26458. + /// /// # Safety - /// Rust doesn't call this, it *gets called*. - #[forbid(unsafe_op_in_unsafe_fn)] + /// Rust doesn't call this, it *gets called* by the kernel, which we expect + /// to provide valid parameters. Apart from that, this function does not + /// have any other preconditions. unsafe extern "C" fn signal_handler( signum: libc::c_int, info: *mut libc::siginfo_t, @@ -153,11 +156,14 @@ mod imp { static NEED_ALTSTACK: Atomic = AtomicBool::new(false); /// # Safety - /// Must be called only once - #[forbid(unsafe_op_in_unsafe_fn)] + /// Must be called only once, on the main thread, during program startup. pub unsafe fn init() { PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed); + // SAFETY: + // This is only called on the main thread, and since it is still early + // in the programs lifetime there is (almost) certainly enough stack + // space left to install the guard page. let mut guard_page_range = unsafe { install_main_guard() }; // Even for panic=immediate-abort, installing the guard pages is important for soundness. @@ -167,17 +173,20 @@ mod imp { return; } - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + // SAFETY: C structures are always zero-initializable. let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { // SAFETY: just fetches the current signal handler into action unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; - // Configure our signal handler if one is not already set. + // We assume that overriding the signal handler is always safe, + // which might conflict with certain libraries that rely on a + // specific signal behaviour. To prevent problems, we only + // override the handler if it has not been set yet. if action.sa_sigaction == SIG_DFL { if !NEED_ALTSTACK.load(Ordering::Relaxed) { // haven't set up our sigaltstack yet NEED_ALTSTACK.store(true, Ordering::Release); - let handler = unsafe { make_handler(true) }; + let handler = make_handler(true); MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); @@ -190,13 +199,15 @@ mod imp { action.sa_sigaction = signal_handler as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void) as sighandler_t; - // SAFETY: only overriding signals if the default is set + // SAFETY: + // `&action` describes a valid `sigaction` and `signal_handler` + // is safe to use as a signal handler for `SIGSEGV` and `SIGBUS`. unsafe { sigaction(signal, &action, ptr::null_mut()) }; } } } - unsafe fn get_stack() -> libc::stack_t { + fn get_stack() -> libc::stack_t { // OpenBSD requires this flag for stack mapping // otherwise the said mapping will fail as a no-op on most systems // and has a different meaning on FreeBSD @@ -218,51 +229,62 @@ mod imp { let sigstack_size = sigstack_size(); let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stackp = mmap64( - ptr::null_mut(), - sigstack_size + page_size, - PROT_READ | PROT_WRITE, - flags, - -1, - 0, - ); + // SAFETY: this does not unmap any existing pages. + let stackp = unsafe { + mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0) + }; if stackp == MAP_FAILED { panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error()); } - let guard_result = libc::mprotect(stackp, page_size, PROT_NONE); + // SAFETY: this only affects the memory we just allocated. + let guard_result = unsafe { libc::mprotect(stackp, page_size, PROT_NONE) }; if guard_result != 0 { panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error()); } - let stackp = stackp.add(page_size); + // SAFETY: + // The region was allocated with a larger size than `page_size`, so this + // addition is within bounds. + let stackp = unsafe { stackp.add(page_size) }; libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } - /// # Safety - /// Mutates the alternate signal stack - #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn make_handler(main_thread: bool) -> Handler { + pub fn make_handler(main_thread: bool) -> Handler { if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } if !main_thread { - if let Some(guard_page_range) = unsafe { current_guard() } { + if let Some(guard_page_range) = current_guard() { set_current_info(guard_page_range); } } - // SAFETY: assuming stack_t is zero-initializable + // Load the current alternate signal stack to see if we need to install + // our own. + // + // SAFETY: C structures are always zero-initializable. let mut stack = unsafe { mem::zeroed() }; - // SAFETY: reads current stack_t into stack + // SAFETY: `&mut stack` is valid for writing a `stack_t`. unsafe { sigaltstack(ptr::null(), &mut stack) }; + // Configure alternate signal stack, if one is not already set. if stack.ss_flags & SS_DISABLE != 0 { - // SAFETY: We warned our caller this would happen! - unsafe { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); - } + let stack = get_stack(); + // SAFETY: + // `stack_t` is a freshly allocated stack that's not used anywhere + // else. It contains a guard page, so stack overflows in signal + // handlers will not cause undefined behaviour. We must make the + // fundamental runtime assumption that it is safe to install an + // alternate signal stack if there is none currently installed. + // This might conflict with foreign libraries that use the existence + // of an alternate signal stack as indication that certain runtime + // initialisation by the library has been performed (e.g. old + // versions of `std` assumed that certain thread-locals were already + // accessed and thus initialized in the thread if the stack overflow + // signal was successfully delivered). Such assumptions in other + // libraries are fundamentally flawed, so we pay no regard to them. + unsafe { sigaltstack(&stack, ptr::null_mut()) }; Handler { data: stack.ss_sp as *mut libc::c_void } } else { Handler::null() @@ -270,11 +292,8 @@ mod imp { } /// # Safety - /// Must be called - /// - only with our handler or nullptr - /// - only when done with our altstack - /// This disables the alternate signal stack! - #[forbid(unsafe_op_in_unsafe_fn)] + /// Must only be called with a pointer returned by `make_handler`, and only + /// once per `Handler`. pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); @@ -288,10 +307,22 @@ mod imp { // both ss_sp and ss_size should be ignored in this case. ss_size: sigstack_size, }; - // SAFETY: we warned the caller this disables the alternate signal stack! + // SAFETY: + // We assume that disabling the alternate signal stack is always + // sound, even if the current alternate signal stack is not the one + // we installed in `make_handler`. Any stack overflows from this + // point on will abort the program when the kernel tries to write + // the signal information to the guard page. + // + // FIXME: detect if the stack has changed, and only uninstall if it hasn't. unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; - // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of - // a mapping that started one page earlier, so walk back a page and unmap from there. + // The stack returned by `get_stack` is part of a mapping that + // started one page earlier, so walk back a page and unmap from + // there. + // + // SAFETY: + // This allocation was created by us in `get_stack` and, as the + // alternate signal stack is now disabled, is no longer in use. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } @@ -301,6 +332,7 @@ mod imp { /// Modern kernels on modern hardware can have dynamic signal stack sizes. #[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))] fn sigstack_size() -> usize { + // SAFETY: `getauxval` is always safe to call. let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }; // If getauxval couldn't find the entry, it returns 0, // so take the higher of the "constant" and auxval. @@ -315,26 +347,43 @@ mod imp { } #[cfg(any(target_os = "solaris", target_os = "illumos"))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::stack_getbounds(&mut current_stack), 0); + fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: C types are always zero-initializable. + let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; + // SAFETY: + // The pointer is valid for writing a `stack_t`. + assert_eq!(unsafe { libc::stack_getbounds(&mut current_stack) }, 0); Some(current_stack.ss_sp) } #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackptr = libc::pthread_get_stackaddr_np(th); - Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) + fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: always safe to call. + let th = unsafe { libc::pthread_self() }; + // SAFETY: `th` is a valid `pthread_t`. + unsafe { + let stackptr = libc::pthread_get_stackaddr_np(th); + let stacksize = libc::pthread_get_stacksize_np(th); + Some(stackptr.map_addr(|addr| addr - stacksize)) + } } #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: C types are always zero-initializable. + let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t`. + // * `&mut current_stack` is coerced to a pointer that is valid for writing + // a `stack_t`. + assert_eq!( + unsafe { libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack) }, + 0 + ); let stack_ptr = current_stack.ss_sp; - let stackaddr = if libc::pthread_main_np() == 1 { + // SAFETY: this is always safe to call. + let stackaddr = if unsafe { libc::pthread_main_np() } == 1 { // main thread stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) } else { @@ -352,7 +401,7 @@ mod imp { target_os = "linux", target_os = "l4re" ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + fn get_stack_start() -> Option<*mut libc::c_void> { use crate::pin::pin; use crate::sys::helpers::COpaque; @@ -365,26 +414,44 @@ mod imp { // FIXME(pin-ergonomics): remove the next line. let attr = attr.into_ref(); + // SAFETY: + // The attributes object has not been initialized yet and will not be + // moved until destroyed. #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(attr.get()), 0); + assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` is an initialized attribute object that can be written to. #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get()); + let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` can be written to, and will be initialized by this call. #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get()); + let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; if e == 0 { let mut stackaddr = crate::ptr::null_mut(); let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize), 0); + // SAFETY: + // `attr` is an initialized attribute object and both the pointers + // are valid for writing. + assert_eq!( + unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize) }, + 0 + ); ret = Some(stackaddr); } if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(attr.get()), 0); + // SAFETY: + // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or + // by `pthread_attr_get_np`, and is not used after this point. + assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); } ret } fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> { - let stackptr = unsafe { get_stack_start()? }; + let stackptr = get_stack_start()?; let stackaddr = stackptr.addr(); // Ensure stackaddr is page aligned! A parent process might @@ -401,32 +468,32 @@ mod imp { }) } - #[forbid(unsafe_op_in_unsafe_fn)] + /// # Safety + /// This function must only be called from the main thread, and there must + /// be sufficient stack space remaining to place a stack guard. unsafe fn install_main_guard() -> Option> { let page_size = PAGE_SIZE.load(Ordering::Relaxed); - unsafe { - // this way someone on any unix-y OS can check that all these compile - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - install_main_guard_linux(page_size) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - install_main_guard_linux_musl(page_size) - } else if cfg!(target_os = "freebsd") { - #[cfg(not(target_os = "freebsd"))] - return None; - // The FreeBSD code cannot be checked on non-BSDs. - #[cfg(target_os = "freebsd")] - install_main_guard_freebsd(page_size) - } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { - install_main_guard_bsds(page_size) - } else { - install_main_guard_default(page_size) - } + // this way someone on any unix-y OS can check that all these compile + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + install_main_guard_linux(page_size) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + install_main_guard_linux_musl(page_size) + } else if cfg!(target_os = "freebsd") { + #[cfg(not(target_os = "freebsd"))] + return None; + // The FreeBSD code cannot be checked on non-BSDs. + #[cfg(target_os = "freebsd")] + install_main_guard_freebsd(page_size) + } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { + install_main_guard_bsds(page_size) + } else { + // SAFETY: guaranteed by caller. + unsafe { install_main_guard_default(page_size) } } } - #[forbid(unsafe_op_in_unsafe_fn)] - unsafe fn install_main_guard_linux(page_size: usize) -> Option> { + fn install_main_guard_linux(page_size: usize) -> Option> { // See the corresponding conditional in init(). // Avoid stack_start_aligned, which makes slow syscalls to read /proc/self/maps if cfg!(panic = "immediate-abort") { @@ -447,8 +514,7 @@ mod imp { Some(stackaddr - page_size..stackaddr) } - #[forbid(unsafe_op_in_unsafe_fn)] - unsafe fn install_main_guard_linux_musl(_page_size: usize) -> Option> { + fn install_main_guard_linux_musl(_page_size: usize) -> Option> { // For the main thread, the musl's pthread_attr_getstack // returns the current stack size, rather than maximum size // it can eventually grow to. It cannot be used to determine @@ -456,9 +522,8 @@ mod imp { None } - #[forbid(unsafe_op_in_unsafe_fn)] #[cfg(target_os = "freebsd")] - unsafe fn install_main_guard_freebsd(page_size: usize) -> Option> { + fn install_main_guard_freebsd(page_size: usize) -> Option> { // See the corresponding conditional in install_main_guard_linux(). if cfg!(panic = "immediate-abort") { return None; @@ -473,6 +538,7 @@ mod imp { // by the security.bsd.stack_guard_page sysctl. // By default it is 1, checking once is enough since it is // a boot time config value. + // FIXME(joboet): this function is only called once, remove the caching. static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); let pages = PAGES.get_or_init(|| { @@ -494,8 +560,7 @@ mod imp { Some(guardaddr..guardaddr + pages * page_size) } - #[forbid(unsafe_op_in_unsafe_fn)] - unsafe fn install_main_guard_bsds(page_size: usize) -> Option> { + fn install_main_guard_bsds(page_size: usize) -> Option> { // See the corresponding conditional in install_main_guard_linux(). if cfg!(panic = "immediate-abort") { return None; @@ -512,7 +577,9 @@ mod imp { Some(stackaddr - page_size..stackaddr) } - #[forbid(unsafe_op_in_unsafe_fn)] + /// # Safety + /// This function must only be called from the main thread, and there must + /// be sufficient stack space remaining to place a stack guard. unsafe fn install_main_guard_default(page_size: usize) -> Option> { // Reallocate the last page of the stack. // This ensures SIGBUS will be raised on @@ -523,6 +590,11 @@ mod imp { // read/write permissions and only then mprotect() it to // no permissions at all. See issue #50313. let stackptr = stack_start_aligned(page_size)?; + // SAFETY: + // The memory region from `stackptr..stackptr + page_size` belongs to + // the current thread's stack, and the caller has asserted that there + // is sufficient stack space, which means that this will not overwrite + // any existing allocations. let result = unsafe { mmap64( stackptr, @@ -537,6 +609,10 @@ mod imp { panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); } + // SAFETY: + // Since this function is only called on the main thread, the stack will + // not be reused until program exit, so the runtime will never observe + // that part of the stack has been made unusable in this way. let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) }; if result != 0 { panic!("failed to protect the guard page: {}", io::Error::last_os_error()); @@ -553,8 +629,7 @@ mod imp { target_os = "solaris", target_os = "illumos", ))] - // FIXME: I am probably not unsafe. - unsafe fn current_guard() -> Option> { + fn current_guard() -> Option> { let stackptr = get_stack_start()?; let stackaddr = stackptr.addr(); Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) @@ -568,8 +643,7 @@ mod imp { target_os = "netbsd", target_os = "l4re" ))] - // FIXME: I am probably not unsafe. - unsafe fn current_guard() -> Option> { + fn current_guard() -> Option> { use crate::pin::pin; use crate::sys::helpers::COpaque; @@ -583,15 +657,27 @@ mod imp { // FIXME(pin-ergonomics): remove the next line. let attr = attr.into_ref(); + // SAFETY: + // The attributes object has not been initialized yet and will not be + // moved until destroyed. #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(attr.get()), 0); + assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` is an initialized attribute object that can be written to. #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get()); + let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` can be written to, and will be initialized by this call. #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get()); + let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; if e == 0 { let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(attr.get(), &mut guardsize), 0); + // SAFETY: + // `attr` is an initialized attribute object and the pointer is valid + // for writing. + assert_eq!(unsafe { libc::pthread_attr_getguardsize(attr.get(), &mut guardsize) }, 0); if guardsize == 0 { if cfg!(all(target_os = "linux", target_env = "musl")) { // musl versions before 1.1.19 always reported guard @@ -604,7 +690,13 @@ mod imp { } let mut stackptr = crate::ptr::null_mut::(); let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size), 0); + // SAFETY: + // `attr` is an initialized attribute object and both the pointers + // are valid for writing. + assert_eq!( + unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size) }, + 0 + ); let stackaddr = stackptr.addr(); ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { @@ -625,7 +717,10 @@ mod imp { }; } if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(attr.get()), 0); + // SAFETY: + // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or + // by `pthread_attr_get_np`, and is not used after this point. + assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); } ret } @@ -656,7 +751,7 @@ mod imp { mod imp { pub unsafe fn init() {} - pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { + pub fn make_handler(_main_thread: bool) -> super::Handler { super::Handler::null() } @@ -738,7 +833,7 @@ mod imp { reserve_stack(); } - pub unsafe fn make_handler(main_thread: bool) -> super::Handler { + pub fn make_handler(main_thread: bool) -> super::Handler { if !main_thread { reserve_stack(); } From 9ed2e0c0cac7ce208faf5055b0452abdc04167a0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Sep 2026 20:44:08 +0200 Subject: [PATCH 22/51] reformat itanium mangling test to be more readable --- ...data-id-itanium-cxx-abi-primitive-types.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 93845d0519541..cb8eb13ac75ed 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -15,114 +15,133 @@ pub fn foo2(_: (), _: c_void) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo3(_: (), _: c_void, _: c_void) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo4(_: *mut ()) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: *mut (), _: *mut c_void) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo6(_: *mut (), _: *mut c_void, _: *mut c_void) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo7(_: *const ()) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo8(_: *const (), _: *const c_void) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo9(_: *const (), _: *const c_void, _: *const c_void) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo10(_: bool) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo11(_: bool, _: bool) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo12(_: bool, _: bool, _: bool) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo13(_: i8) {} // CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo14(_: i8, _: i8) {} // CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo15(_: i8, _: i8, _: i8) {} // CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo16(_: i16) {} // CHECK: define{{.*}}5foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo17(_: i16, _: i16) {} // CHECK: define{{.*}}5foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo18(_: i16, _: i16, _: i16) {} // CHECK: define{{.*}}5foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo19(_: i32) {} // CHECK: define{{.*}}5foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo20(_: i32, _: i32) {} // CHECK: define{{.*}}5foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo21(_: i32, _: i32, _: i32) {} // CHECK: define{{.*}}5foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo22(_: i64) {} // CHECK: define{{.*}}5foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo23(_: i64, _: i64) {} // CHECK: define{{.*}}5foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo24(_: i64, _: i64, _: i64) {} // CHECK: define{{.*}}5foo24{{.*}}!type ![[TYPE24:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo25(_: i128) {} // CHECK: define{{.*}}5foo25{{.*}}!type ![[TYPE25:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo26(_: i128, _: i128) {} // CHECK: define{{.*}}5foo26{{.*}}!type ![[TYPE26:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo27(_: i128, _: i128, _: i128) {} // CHECK: define{{.*}}5foo27{{.*}}!type ![[TYPE27:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo28(_: isize) {} // CHECK: define{{.*}}5foo28{{.*}}!type ![[TYPE28:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo29(_: isize, _: isize) {} // CHECK: define{{.*}}5foo29{{.*}}!type ![[TYPE29:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo30(_: isize, _: isize, _: isize) {} // CHECK: define{{.*}}5foo30{{.*}}!type ![[TYPE30:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo31(_: u8) {} // CHECK: define{{.*}}5foo31{{.*}}!type ![[TYPE31:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo32(_: u8, _: u8) {} // CHECK: define{{.*}}5foo32{{.*}}!type ![[TYPE32:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo33(_: u8, _: u8, _: u8) {} // CHECK: define{{.*}}5foo33{{.*}}!type ![[TYPE33:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo34(_: u16) {} // CHECK: define{{.*}}5foo34{{.*}}!type ![[TYPE34:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo35(_: u16, _: u16) {} // CHECK: define{{.*}}5foo35{{.*}}!type ![[TYPE35:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo36(_: u16, _: u16, _: u16) {} // CHECK: define{{.*}}5foo36{{.*}}!type ![[TYPE36:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo37(_: u32) {} // CHECK: define{{.*}}5foo37{{.*}}!type ![[TYPE37:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo38(_: u32, _: u32) {} // CHECK: define{{.*}}5foo38{{.*}}!type ![[TYPE38:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo39(_: u32, _: u32, _: u32) {} // CHECK: define{{.*}}5foo39{{.*}}!type ![[TYPE39:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo40(_: u64) {} // CHECK: define{{.*}}5foo40{{.*}}!type ![[TYPE40:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo41(_: u64, _: u64) {} // CHECK: define{{.*}}5foo41{{.*}}!type ![[TYPE41:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo42(_: u64, _: u64, _: u64) {} // CHECK: define{{.*}}5foo42{{.*}}!type ![[TYPE42:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo43(_: u128) {} // CHECK: define{{.*}}5foo43{{.*}}!type ![[TYPE43:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo44(_: u128, _: u128) {} // CHECK: define{{.*}}5foo44{{.*}}!type ![[TYPE44:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo45(_: u128, _: u128, _: u128) {} // CHECK: define{{.*}}5foo45{{.*}}!type ![[TYPE45:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo46(_: usize) {} // CHECK: define{{.*}}5foo46{{.*}}!type ![[TYPE46:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo47(_: usize, _: usize) {} // CHECK: define{{.*}}5foo47{{.*}}!type ![[TYPE47:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo48(_: usize, _: usize, _: usize) {} // CHECK: define{{.*}}5foo48{{.*}}!type ![[TYPE48:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo49(_: f32) {} // CHECK: define{{.*}}5foo49{{.*}}!type ![[TYPE49:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo50(_: f32, _: f32) {} // CHECK: define{{.*}}5foo50{{.*}}!type ![[TYPE50:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo51(_: f32, _: f32, _: f32) {} // CHECK: define{{.*}}5foo51{{.*}}!type ![[TYPE51:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo52(_: f64) {} // CHECK: define{{.*}}5foo52{{.*}}!type ![[TYPE52:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo53(_: f64, _: f64) {} // CHECK: define{{.*}}5foo53{{.*}}!type ![[TYPE53:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo54(_: f64, _: f64, _: f64) {} // CHECK: define{{.*}}5foo54{{.*}}!type ![[TYPE54:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo55(_: char) {} // CHECK: define{{.*}}5foo55{{.*}}!type ![[TYPE55:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo56(_: char, _: char) {} // CHECK: define{{.*}}5foo56{{.*}}!type ![[TYPE56:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo57(_: char, _: char, _: char) {} // CHECK: define{{.*}}5foo57{{.*}}!type ![[TYPE57:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + pub fn foo58(_: &str) {} // CHECK: define{{.*}}5foo58{{.*}}!type ![[TYPE58:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo59(_: &str, _: &str) {} From 7a44484407077776e30120f5e72cdef2820e8856 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Fri, 24 Jul 2026 23:03:20 +0000 Subject: [PATCH 23/51] fuchsia: Add safestack as a supported sanitizer for x86_64 fuchsia Make it also enabled by default just like it is for clang. --- .../rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs | 4 +++- .../stack-protector/stack-protector-target-support.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs index dbff5e30828a9..b8fb1fb6d3a52 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs @@ -9,7 +9,9 @@ pub(crate) fn target() -> Target { base.features = "+cmpxchg16b,+lahfsahf,+popcnt,+sse3,+sse4.1,+sse4.2,+ssse3".into(); base.max_atomic_width = Some(128); base.stack_probes = StackProbeType::Inline; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK; + base.supported_sanitizers = + SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::SAFESTACK; + base.default_sanitizers = SanitizerSet::SAFESTACK; base.supports_xray = true; Target { diff --git a/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs b/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs index 9f182985d1573..f883e7ac8691f 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs @@ -177,13 +177,15 @@ //@ compile-flags: -C opt-level=2 #![crate_type = "lib"] -#![feature(no_core, lang_items)] +#![feature(no_core, lang_items, sanitize)] #![crate_type = "lib"] #![no_core] extern crate minicore; use minicore::*; +// We only do this because we can't disable default sanitizers via compile-flags. +#[sanitize(safestack = "off")] #[no_mangle] pub fn foo() { // CHECK: foo{{:|()}} From 09db86657d425ed3b4a07fbcd3910a09d6ab7152 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Sep 2026 21:32:43 +0200 Subject: [PATCH 24/51] Add missing `rustdoc-ui` test output --- tests/rustdoc-ui/doctest/sanitizer-option.stdout | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/rustdoc-ui/doctest/sanitizer-option.stdout diff --git a/tests/rustdoc-ui/doctest/sanitizer-option.stdout b/tests/rustdoc-ui/doctest/sanitizer-option.stdout new file mode 100644 index 0000000000000..62040048335f2 --- /dev/null +++ b/tests/rustdoc-ui/doctest/sanitizer-option.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/sanitizer-option.rs - z_flag_is_passed_to_rustc (line 11) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 1d08a6b9ff707d19caf72d8ba427ec2ffa399739 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 1 Sep 2026 12:49:07 -0700 Subject: [PATCH 25/51] Prefer `LLVMGetVersion` for runtime info Our `LLVMRustVersion*` functions get hard-coded `LLVM_VERSION_*` values when we build `RustWrapper.cpp`, but this could be different than the actual LLVM library at runtime. This should never happen with toolchains from `rustup`, but with external LLVM in a distro build, for example, `rustc` and `LLVM` can be upgraded independently. Most of the time when we check the LLVM version, we're only looking at the major version anyway, and we already assert that these are equal in `configure_llvm`. However, for anything that does check the minor or patch version too, the runtime version is probably more relevant. --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 8 ++++++-- compiler/rustc_codegen_llvm/src/llvm_util.rs | 11 ++++++----- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 684bba7a717db..c20b4ccd776da 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2172,9 +2172,13 @@ unsafe extern "C" { pub(crate) safe fn LLVMRustCoverageMappingVersion() -> u32; pub(crate) fn LLVMRustDebugMetadataVersion() -> u32; + + /// Returns the LLVM major version that the compiler was built with. + /// + /// Note that this is hard-coded as `LLVM_VERSION_MAJOR` when `RustWrapper.cpp` is built. This + /// could be different than what the runtime LLVM library reports in `LLVMGetVersion`, so we + /// assert their equality in `configure_llvm`. pub(crate) fn LLVMRustVersionMajor() -> u32; - pub(crate) fn LLVMRustVersionMinor() -> u32; - pub(crate) fn LLVMRustVersionPatch() -> u32; /// Add LLVM module flags. /// diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 82ddcca3e1530..a5441b1ef1135 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -50,10 +50,7 @@ unsafe fn configure_llvm(sess: &Session) { // Check to ensure we're running against the correct LLVM version. unsafe { - let mut llvm_major = 0; - let mut llvm_minor = 0; - let mut llvm_patch = 0; - llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); + let (llvm_major, llvm_minor, llvm_patch) = get_version(); let expected_version = llvm::LLVMRustVersionMajor(); if llvm_major != expected_version { sess.dcx().emit_fatal(diagnostics::LlvmVersionMismatch { @@ -465,7 +462,11 @@ pub(crate) fn print_version() { pub(crate) fn get_version() -> (u32, u32, u32) { // Can be called without initializing LLVM unsafe { - (llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch()) + let mut llvm_major = 0; + let mut llvm_minor = 0; + let mut llvm_patch = 0; + llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); + (llvm_major, llvm_minor, llvm_patch) } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 189296dc9c4c8..6947c4766eccf 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -885,10 +885,6 @@ extern "C" uint32_t LLVMRustDebugMetadataVersion() { return DEBUG_METADATA_VERSION; } -extern "C" uint32_t LLVMRustVersionPatch() { return LLVM_VERSION_PATCH; } - -extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } - extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } // FFI equivalent of LLVM's `llvm::Module::ModFlagBehavior`. From 45b97ea0cc2cf6b6d8d0a9bbb1906dc8d29a53d9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Sep 2026 21:39:11 +0200 Subject: [PATCH 26/51] itanium mangling: use minicore --- ...ype-metadata-id-itanium-cxx-abi-primitive-types.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index cb8eb13ac75ed..52204755f47dd 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -1,13 +1,18 @@ // Verifies that type metadata identifiers for functions are emitted correctly // for primitive types. // +//@ add-minicore //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Cno-prepopulate-passes -Copt-level=0 +//@ compile-flags: -Clto -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer +//@ minicore-compile-flags: -Ccodegen-units=1 #![crate_type = "lib"] +#![feature(no_core)] +#![no_core] -extern crate core; -use core::ffi::*; +extern crate minicore; +use minicore::*; pub fn foo1(_: ()) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} From bb0bcf25cca2f9996c5d374f4fd8d914965a03f6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Sep 2026 21:04:19 +0200 Subject: [PATCH 27/51] add itanium mangling test for `f16` and `f128` --- ...data-id-itanium-cxx-abi-primitive-types.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 52204755f47dd..59e794df69990 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -3,12 +3,12 @@ // //@ add-minicore //@ needs-sanitizer-cfi -//@ compile-flags: -Cno-prepopulate-passes -Copt-level=0 +//@ compile-flags: -Cno-prepopulate-passes -Copt-level=0 -C link-dead-code //@ compile-flags: -Clto -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ minicore-compile-flags: -Ccodegen-units=1 #![crate_type = "lib"] -#![feature(no_core)] +#![feature(no_core, f16, f128)] #![no_core] extern crate minicore; @@ -154,6 +154,20 @@ pub fn foo59(_: &str, _: &str) {} pub fn foo60(_: &str, _: &str, _: &str) {} // CHECK: define{{.*}}5foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo61(_: f16) {} +// CHECK: define{{.*}}5foo61{{.*}}!type ![[TYPE61:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo62(_: f16, _: f16) {} +// CHECK: define{{.*}}5foo62{{.*}}!type ![[TYPE62:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo63(_: f16, _: f16, _: f16) {} +// CHECK: define{{.*}}5foo63{{.*}}!type ![[TYPE63:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + +pub fn foo64(_: f128) {} +// CHECK: define{{.*}}5foo64{{.*}}!type ![[TYPE64:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo65(_: f128, _: f128) {} +// CHECK: define{{.*}}5foo65{{.*}}!type ![[TYPE65:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo66(_: f128, _: f128, _: f128) {} +// CHECK: define{{.*}}5foo66{{.*}}!type ![[TYPE66:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvE"} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_E"} @@ -212,3 +226,9 @@ pub fn foo60(_: &str, _: &str, _: &str) {} // CHECK: ![[TYPE58]] = !{i64 0, !"_ZTSFvu3refIu3strEE"} // CHECK: ![[TYPE59]] = !{i64 0, !"_ZTSFvu3refIu3strES0_E"} // CHECK: ![[TYPE60]] = !{i64 0, !"_ZTSFvu3refIu3strES0_S0_E"} +// CHECK: ![[TYPE61]] = !{i64 0, !"_ZTSFvDhE"} +// CHECK: ![[TYPE62]] = !{i64 0, !"_ZTSFvDhDhE"} +// CHECK: ![[TYPE63]] = !{i64 0, !"_ZTSFvDhDhDhE"} +// CHECK: ![[TYPE64]] = !{i64 0, !"_ZTSFvgE"} +// CHECK: ![[TYPE65]] = !{i64 0, !"_ZTSFvggE"} +// CHECK: ![[TYPE66]] = !{i64 0, !"_ZTSFvgggE"} From 47fc75df4415debfcd437c61cf80b00d125089ce Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Sep 2026 22:01:02 +0200 Subject: [PATCH 28/51] Revert "retrieve supported GCC targets from the sysroot" This reverts commit 5a5b84aa5fbb5cdef0ecc667fd7afdc876dab08e. --- src/tools/compiletest/src/cli.rs | 9 ------- src/tools/compiletest/src/common.rs | 3 --- src/tools/compiletest/src/directives.rs | 26 +------------------ src/tools/compiletest/src/rustdoc_gui_test.rs | 1 - 4 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/tools/compiletest/src/cli.rs b/src/tools/compiletest/src/cli.rs index 45681eabe03a3..893f5b76724b8 100644 --- a/src/tools/compiletest/src/cli.rs +++ b/src/tools/compiletest/src/cli.rs @@ -401,13 +401,6 @@ pub(crate) fn parse_config(args: Vec) -> Config { let iteration_count = args.iteration_count.unwrap_or(Config::DEFAULT_ITERATION_COUNT); assert!(iteration_count > 0, "`--iteration-count` must be a positive integer"); - let gcc_supported_target_tuples = match default_codegen_backend { - CodegenBackend::Gcc => { - directives::find_gcc_supported_targets(&args.sysroot_base, &args.host) - } - CodegenBackend::Llvm | CodegenBackend::Cranelift => vec![], - }; - // FIXME: this run scheme is... confusing. let run = args.run.and_then(|mode| match mode.as_str() { "auto" => None, @@ -455,8 +448,6 @@ pub(crate) fn parse_config(args: Vec) -> Config { force_pass_mode: args.pass, force_rerun: args.force_rerun, - gcc_supported_target_tuples, - gdb: args.gdb, gdb_version, git_hash: args.git_hash, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 4123dcf5b600a..8c580d3520dfd 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -770,9 +770,6 @@ pub(crate) struct Config { /// Whether to ignore `//@ ignore-backends`. pub(crate) bypass_ignore_backends: bool, - /// Target tuples for which we've found libgccjit.so. - pub(crate) gcc_supported_target_tuples: Vec, - /// Number of parallel jobs configured for the build. /// /// This is forwarded from bootstrap's `jobs` configuration. diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 3499821f7b6e0..3459273c922a1 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -851,30 +851,6 @@ pub(crate) fn extract_llvm_version_from_binary(binary_path: &str) -> Option Vec { - // E.g. `lib/rustlib/x86_64-unknown-linux-gnu/codegen-backends/lib`. - let backends_dir = - sysroot_base.join("lib").join("rustlib").join(host).join("codegen-backends").join("lib"); - - match std::fs::read_dir(&backends_dir) { - Ok(entries) => { - // Search for `aarch64-unknown-linux-gnu/libgccjit.so` et cetera. - let target_tuples: Vec<_> = entries - .filter_map(|entry| entry.ok()) - .filter(|entry| entry.path().join("libgccjit.so").exists()) - .filter_map(|entry| entry.file_name().into_string().ok()) - .collect(); - - if target_tuples.is_empty() { - panic!("did not find `libgccjit.so` for any target in {backends_dir}"); - } - - target_tuples - } - Err(e) => panic!("unable to find `libgccjit.so` for any target in {backends_dir}: {e:?}",), - } -} - /// Takes a directive of the form `" [- ]"`, returns the numeric representation /// of `` and `` as tuple: `(, )`. /// @@ -1255,7 +1231,7 @@ fn ignore_unsupported_backend_target(config: &Config, line: &DirectiveLine<'_>) return IgnoreDecision::Continue; }; - if !config.gcc_supported_target_tuples.iter().any(|t| t == target) { + if target != "x86_64-unknown-linux-gnu" { IgnoreDecision::Ignore { reason: format!( "backend `{}` cannot build for target `{target}`", diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index b2d23bcf8ec0a..5a768519f616d 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -143,7 +143,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { default_codegen_backend: CodegenBackend::Llvm, override_codegen_backend: None, bypass_ignore_backends: Default::default(), - gcc_supported_target_tuples: vec![], jobs: Default::default(), parallel_frontend_threads: Config::DEFAULT_PARALLEL_FRONTEND_THREADS, iteration_count: Config::DEFAULT_ITERATION_COUNT, From b0ba8b100ef633a153432dfe94dcaf11486133ca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Sep 2026 15:57:03 +1000 Subject: [PATCH 29/51] Introduce `PerOwnerLoweringState` `LoweringContext` has 14 fields that get swapped in and out in `with_hir_id_owner`. This is fragile and gross. This commit moves those fields into a new struct, `PerOwnerLoweringState`, which means they can be swapped in and out cleanly. Other changes: - All `self.foo` accesses to those 14 fields become `self.curr_owner.foo`. - Field renames: - `current_hir_id_owner` -> `owner_id` - `current_disambiguator` -> `disambiguator` - `LoweringContext::make_owner_info` becomes `PerOwnerLoweringState::into_owner_info`; this makes sense because it consumes the `PerOwnerLoweringState`. - Stronger assertions: `into_owner_info` has assertions that now apply to the `with_lctx` path as well as the `with_hir_id_owner` path. --- .../src/delegation/attributes.rs | 6 +- .../src/delegation/generics.rs | 2 +- .../rustc_ast_lowering/src/delegation/mod.rs | 10 +- .../src/delegation/resolution.rs | 4 +- compiler/rustc_ast_lowering/src/expr.rs | 17 +- compiler/rustc_ast_lowering/src/item.rs | 42 +- compiler/rustc_ast_lowering/src/lib.rs | 393 +++++++++--------- compiler/rustc_ast_lowering/src/pat.rs | 8 +- compiler/rustc_ast_lowering/src/path.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 7 +- 10 files changed, 241 insertions(+), 250 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/delegation/attributes.rs b/compiler/rustc_ast_lowering/src/delegation/attributes.rs index 885ee0d51c730..834f85450a2cd 100644 --- a/compiler/rustc_ast_lowering/src/delegation/attributes.rs +++ b/compiler/rustc_ast_lowering/src/delegation/attributes.rs @@ -43,17 +43,17 @@ impl<'hir> LoweringContext<'_, 'hir> { let &DelegationResolution { span, sig_id, .. } = resolution; const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; - let new_attrs = self.create_new_attrs(span, sig_id, self.attrs.get(&PARENT_ID)); + let new_attrs = self.create_new_attrs(span, sig_id, self.curr_owner.attrs.get(&PARENT_ID)); if !new_attrs.is_empty() { - let new_attrs = match self.attrs.get(&PARENT_ID) { + let new_attrs = match self.curr_owner.attrs.get(&PARENT_ID) { Some(existing_attrs) => self.arena.alloc_from_iter( existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()), ), None => self.arena.alloc_from_iter(new_attrs.into_iter()), }; - self.attrs.insert(PARENT_ID, new_attrs); + self.curr_owner.attrs.insert(PARENT_ID, new_attrs); } } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 911ec5956006d..867ed364433e7 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -587,7 +587,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; // Important: we don't use `self.next_id()` as we want to execute - // `lower_node_id` routine so param's id is added to `self.children`. + // `lower_node_id` routine so param's id is added to `self.curr_owner.children`. let hir_id = self.lower_node_id(node_id); Some(hir::GenericParam { diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index 3b9074e67bdd2..a92b62517e61d 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -140,9 +140,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let id = match source { DelegationSource::Single => None, DelegationSource::List(expn_id) => Some(expn_id), - DelegationSource::Glob => { - Some(self.tcx.expn_that_defined(self.owner.def_id).expect_local()) - } + DelegationSource::Glob => Some( + self.tcx + .expn_that_defined(self.curr_owner.owner.def_id) + .expect_local(), + ), }; id.map(|id| (id, unused_target_expr)) @@ -335,7 +337,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let overwrites = self_resolver.overwrites; // Target expr needs to lower `self` path. - self.ident_and_label_to_local_id.insert(pat_node_id, param_local_id); + self.curr_owner.ident_and_label_to_local_id.insert(pat_node_id, param_local_id); let block = cfg_select! { debug_assertions => { diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index dd1b9518e6d7f..85604223c8509 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -73,7 +73,7 @@ pub(super) mod resolver { #[inline] pub(crate) fn owner_id(&self) -> LocalDefId { - self.0.owner.def_id + self.0.curr_owner.owner.def_id } /// (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`): @@ -91,7 +91,7 @@ pub(super) mod resolver { #[inline] pub(crate) fn is_definition(&self, id: NodeId) -> bool { self.0.resolver.owners.contains_key(&id) - || self.0.owner.node_id_to_def_id.contains_key(&id) + || self.0.curr_owner.owner.node_id_to_def_id.contains_key(&id) } #[inline] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4d5b98fd1ac00..0a4a2ae7145e3 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -172,7 +172,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Merge attributes into the inner expression. if !e.attrs.is_empty() { - let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); + let old_attrs = + self.curr_owner.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); let new_attrs = self .lower_attrs_vec(&e.attrs, e.span, ex.hir_id, Target::from_expr(e)) .into_iter() @@ -181,7 +182,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if new_attrs.is_empty() { return ex; } - self.attrs.insert(ex.hir_id.local_id, new_attrs); + self.curr_owner.attrs.insert(ex.hir_id.local_id, new_attrs); } return ex; } @@ -884,7 +885,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. pub(super) fn maybe_forward_track_caller(&mut self, outer_hir_id: HirId, inner_hir_id: HirId) { if self.tcx.features().async_fn_track_caller() - && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) + && let Some(attrs) = self.curr_owner.attrs.get(&outer_hir_id.local_id) && let Some(t) = attrs.iter().find(|a| { matches!( a, @@ -892,7 +893,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }) { - self.attrs.insert(inner_hir_id.local_id, std::slice::from_ref(t)); + self.curr_owner.attrs.insert(inner_hir_id.local_id, std::slice::from_ref(t)); } } @@ -1505,16 +1506,16 @@ impl<'hir> LoweringContext<'_, 'hir> { dest_hir_id: hir::HirId, ) -> Option