From e836a2f5ff6d3317db06891feb3087e6164c282b Mon Sep 17 00:00:00 2001 From: Jonathan Gruner Date: Thu, 24 Apr 2025 21:14:47 +0200 Subject: [PATCH 01/14] implement continue_ok and break_ok for ControlFlow --- library/core/src/ops/control_flow.rs | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 26661b20c12d6..ee450209f4476 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -187,6 +187,28 @@ impl ControlFlow { } } + /// Converts the `ControlFlow` into an `Result` which is `Ok` if the + /// `ControlFlow` was `Break` and `Err` if otherwise. + /// + /// # Examples + /// + /// ``` + /// #![feature(control_flow_ok)] + /// + /// use std::ops::ControlFlow; + /// + /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_ok(), Ok("Stop right there!")); + /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_ok(), Err(3)); + /// ``` + #[inline] + #[unstable(feature = "control_flow_ok", issue = "140266")] + pub fn break_ok(self) -> Result { + match self { + ControlFlow::Continue(c) => Err(c), + ControlFlow::Break(b) => Ok(b), + } + } + /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the break value in case it exists. #[inline] @@ -218,6 +240,28 @@ impl ControlFlow { } } + /// Converts the `ControlFlow` into an `Result` which is `Ok` if the + /// `ControlFlow` was `Continue` and `Err` if otherwise. + /// + /// # Examples + /// + /// ``` + /// #![feature(control_flow_ok)] + /// + /// use std::ops::ControlFlow; + /// + /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_ok(), Err("Stop right there!")); + /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_ok(), Ok(3)); + /// ``` + #[inline] + #[unstable(feature = "control_flow_ok", issue = "140266")] + pub fn continue_ok(self) -> Result { + match self { + ControlFlow::Continue(c) => Ok(c), + ControlFlow::Break(b) => Err(b), + } + } + /// Maps `ControlFlow` to `ControlFlow` by applying a function /// to the continue value in case it exists. #[inline] From b981b84e031b0316c9e1ba244395c7bb2fdb68a9 Mon Sep 17 00:00:00 2001 From: Jonathan Gruner Date: Sat, 26 Apr 2025 12:57:12 +0200 Subject: [PATCH 02/14] moved simple test to coretests, introduced more fleshed out doctests for break_ok/continue_ok --- library/core/src/ops/control_flow.rs | 111 +++++++++++++++++++- library/coretests/tests/lib.rs | 1 + library/coretests/tests/ops/control_flow.rs | 12 +++ 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index ee450209f4476..7489a8bb6e74b 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -197,8 +197,60 @@ impl ControlFlow { /// /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_ok(), Ok("Stop right there!")); - /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_ok(), Err(3)); + /// struct TreeNode { + /// value: T, + /// left: Option>>, + /// right: Option>>, + /// } + /// + /// impl TreeNode { + /// fn find<'a>(&'a self, mut predicate: impl FnMut(&T) -> bool) -> Result<&'a T, ()> { + /// let mut f = |t: &'a T| -> ControlFlow<&'a T> { + /// if predicate(t) { + /// ControlFlow::Break(t) + /// } else { + /// ControlFlow::Continue(()) + /// } + /// }; + /// + /// self.traverse_inorder(&mut f).break_ok() + /// } + /// + /// fn traverse_inorder<'a, B>( + /// &'a self, + /// f: &mut impl FnMut(&'a T) -> ControlFlow, + /// ) -> ControlFlow { + /// if let Some(left) = &self.left { + /// left.traverse_inorder(f)?; + /// } + /// f(&self.value)?; + /// if let Some(right) = &self.right { + /// right.traverse_inorder(f)?; + /// } + /// ControlFlow::Continue(()) + /// } + /// + /// fn leaf(value: T) -> Option>> { + /// Some(Box::new(Self { + /// value, + /// left: None, + /// right: None, + /// })) + /// } + /// } + /// + /// let node = TreeNode { + /// value: 0, + /// left: TreeNode::leaf(1), + /// right: Some(Box::new(TreeNode { + /// value: -1, + /// left: TreeNode::leaf(5), + /// right: TreeNode::leaf(2), + /// })), + /// }; + /// + /// let res = node.find(|val: &i32| *val > 3); + /// assert_eq!(res, Ok(&5)); /// ``` #[inline] #[unstable(feature = "control_flow_ok", issue = "140266")] @@ -250,8 +302,59 @@ impl ControlFlow { /// /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_ok(), Err("Stop right there!")); - /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_ok(), Ok(3)); + /// struct TreeNode { + /// value: T, + /// left: Option>>, + /// right: Option>>, + /// } + /// + /// impl TreeNode { + /// fn validate(&self, f: &mut impl FnMut(&T) -> ControlFlow) -> Result<(), B> { + /// self.traverse_inorder(f).continue_ok() + /// } + /// + /// fn traverse_inorder(&self, f: &mut impl FnMut(&T) -> ControlFlow) -> ControlFlow { + /// if let Some(left) = &self.left { + /// left.traverse_inorder(f)?; + /// } + /// f(&self.value)?; + /// if let Some(right) = &self.right { + /// right.traverse_inorder(f)?; + /// } + /// ControlFlow::Continue(()) + /// } + /// + /// fn leaf(value: T) -> Option>> { + /// Some(Box::new(Self { + /// value, + /// left: None, + /// right: None, + /// })) + /// } + /// } + /// + /// let node = TreeNode { + /// value: 0, + /// left: TreeNode::leaf(1), + /// right: Some(Box::new(TreeNode { + /// value: -1, + /// left: TreeNode::leaf(5), + /// right: TreeNode::leaf(2), + /// })), + /// }; + /// + /// let res = node.validate(&mut |val| { + /// if *val < 0 { + /// return ControlFlow::Break("negative value detected"); + /// } + /// + /// if *val > 4 { + /// return ControlFlow::Break("too big value detected"); + /// } + /// + /// ControlFlow::Continue(()) + /// }); + /// assert_eq!(res, Err("too big value detected")); /// ``` #[inline] #[unstable(feature = "control_flow_ok", issue = "140266")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0a9c0c61c9584..a0c594d2d5961 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -22,6 +22,7 @@ #![feature(const_ref_cell)] #![feature(const_result_trait_fn)] #![feature(const_trait_impl)] +#![feature(control_flow_ok)] #![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] diff --git a/library/coretests/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs index eacfd63a6c48f..1df6599ac4a5a 100644 --- a/library/coretests/tests/ops/control_flow.rs +++ b/library/coretests/tests/ops/control_flow.rs @@ -16,3 +16,15 @@ fn control_flow_discriminants_match_result() { discriminant_value(&Result::::Ok(3)), ); } + +#[test] +fn control_flow_break_ok() { + assert_eq!(ControlFlow::::Break('b').break_ok(), Ok('b')); + assert_eq!(ControlFlow::::Continue(3).break_ok(), Err(3)); +} + +#[test] +fn control_flow_continue_ok() { + assert_eq!(ControlFlow::::Break('b').continue_ok(), Err('b')); + assert_eq!(ControlFlow::::Continue(3).continue_ok(), Ok(3)); +} From 6dbac3f09e67c853f343df9d75a7eb213f16c959 Mon Sep 17 00:00:00 2001 From: Jed Brown Date: Wed, 26 Feb 2025 20:06:25 -0700 Subject: [PATCH 03/14] add nvptx_target_feature Add target features for sm_* and ptx*, both of which form a partial order, but cannot be combined to a single partial order. These mirror the LLVM target features, but we do not provide LLVM target processors (which imply both an sm_* and ptx* feature). Add some documentation for the nvptx target. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 9 +++ compiler/rustc_feature/src/unstable.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/target_features.rs | 69 ++++++++++++++++++- library/core/src/lib.rs | 1 + .../platform-support/nvptx64-nvidia-cuda.md | 40 +++++++++++ tests/ui/check-cfg/target_feature.stderr | 56 +++++++++++++++ tests/ui/target-feature/gate.rs | 1 + tests/ui/target-feature/gate.stderr | 2 +- 9 files changed, 178 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6fd07d562afd8..202b9641e5675 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -262,6 +262,15 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), + // NVPTX targets added in LLVM 20 + ("nvptx64", "sm_100") if get_version().0 < 20 => None, + ("nvptx64", "sm_100a") if get_version().0 < 20 => None, + ("nvptx64", "sm_101") if get_version().0 < 20 => None, + ("nvptx64", "sm_101a") if get_version().0 < 20 => None, + ("nvptx64", "sm_120") if get_version().0 < 20 => None, + ("nvptx64", "sm_120a") if get_version().0 < 20 => None, + ("nvptx64", "ptx86") if get_version().0 < 20 => None, + ("nvptx64", "ptx87") if get_version().0 < 20 => None, // Filter out features that are not supported by the current LLVM version ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") if get_version().0 < 20 => diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 91715851226bb..bc48b45bce17f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -329,6 +329,7 @@ declare_features! ( (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, movrs_target_feature, "1.88.0", Some(137976)), + (unstable, nvptx_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index da69f6c44927b..b0dd144bf47e4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1509,6 +1509,7 @@ symbols! { not, notable_trait, note, + nvptx_target_feature, object_safe_for_dispatch, of, off, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 3eea1e070a669..3449c16ee4aea 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -517,6 +517,71 @@ const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +const NVPTX_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("sm_20", Unstable(sym::nvptx_target_feature), &[]), + ("sm_21", Unstable(sym::nvptx_target_feature), &["sm_20"]), + ("sm_30", Unstable(sym::nvptx_target_feature), &["sm_21"]), + ("sm_32", Unstable(sym::nvptx_target_feature), &["sm_30"]), + ("sm_35", Unstable(sym::nvptx_target_feature), &["sm_32"]), + ("sm_37", Unstable(sym::nvptx_target_feature), &["sm_35"]), + ("sm_50", Unstable(sym::nvptx_target_feature), &["sm_37"]), + ("sm_52", Unstable(sym::nvptx_target_feature), &["sm_50"]), + ("sm_53", Unstable(sym::nvptx_target_feature), &["sm_52"]), + ("sm_60", Unstable(sym::nvptx_target_feature), &["sm_53"]), + ("sm_61", Unstable(sym::nvptx_target_feature), &["sm_60"]), + ("sm_62", Unstable(sym::nvptx_target_feature), &["sm_61"]), + ("sm_70", Unstable(sym::nvptx_target_feature), &["sm_62"]), + ("sm_72", Unstable(sym::nvptx_target_feature), &["sm_70"]), + ("sm_75", Unstable(sym::nvptx_target_feature), &["sm_72"]), + ("sm_80", Unstable(sym::nvptx_target_feature), &["sm_75"]), + ("sm_86", Unstable(sym::nvptx_target_feature), &["sm_80"]), + ("sm_87", Unstable(sym::nvptx_target_feature), &["sm_86"]), + ("sm_89", Unstable(sym::nvptx_target_feature), &["sm_87"]), + ("sm_90", Unstable(sym::nvptx_target_feature), &["sm_89"]), + ("sm_90a", Unstable(sym::nvptx_target_feature), &["sm_90"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("sm_100", Unstable(sym::nvptx_target_feature), &["sm_90"]), + ("sm_100a", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101a", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120a", Unstable(sym::nvptx_target_feature), &["sm_120"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("ptx32", Unstable(sym::nvptx_target_feature), &[]), + ("ptx40", Unstable(sym::nvptx_target_feature), &["ptx32"]), + ("ptx41", Unstable(sym::nvptx_target_feature), &["ptx40"]), + ("ptx42", Unstable(sym::nvptx_target_feature), &["ptx41"]), + ("ptx43", Unstable(sym::nvptx_target_feature), &["ptx42"]), + ("ptx50", Unstable(sym::nvptx_target_feature), &["ptx43"]), + ("ptx60", Unstable(sym::nvptx_target_feature), &["ptx50"]), + ("ptx61", Unstable(sym::nvptx_target_feature), &["ptx60"]), + ("ptx62", Unstable(sym::nvptx_target_feature), &["ptx61"]), + ("ptx63", Unstable(sym::nvptx_target_feature), &["ptx62"]), + ("ptx64", Unstable(sym::nvptx_target_feature), &["ptx63"]), + ("ptx65", Unstable(sym::nvptx_target_feature), &["ptx64"]), + ("ptx70", Unstable(sym::nvptx_target_feature), &["ptx65"]), + ("ptx71", Unstable(sym::nvptx_target_feature), &["ptx70"]), + ("ptx72", Unstable(sym::nvptx_target_feature), &["ptx71"]), + ("ptx73", Unstable(sym::nvptx_target_feature), &["ptx72"]), + ("ptx74", Unstable(sym::nvptx_target_feature), &["ptx73"]), + ("ptx75", Unstable(sym::nvptx_target_feature), &["ptx74"]), + ("ptx76", Unstable(sym::nvptx_target_feature), &["ptx75"]), + ("ptx77", Unstable(sym::nvptx_target_feature), &["ptx76"]), + ("ptx78", Unstable(sym::nvptx_target_feature), &["ptx77"]), + ("ptx80", Unstable(sym::nvptx_target_feature), &["ptx78"]), + ("ptx81", Unstable(sym::nvptx_target_feature), &["ptx80"]), + ("ptx82", Unstable(sym::nvptx_target_feature), &["ptx81"]), + ("ptx83", Unstable(sym::nvptx_target_feature), &["ptx82"]), + ("ptx84", Unstable(sym::nvptx_target_feature), &["ptx83"]), + ("ptx85", Unstable(sym::nvptx_target_feature), &["ptx84"]), + ("ptx86", Unstable(sym::nvptx_target_feature), &["ptx85"]), + ("ptx87", Unstable(sym::nvptx_target_feature), &["ptx86"]), + // tidy-alphabetical-end +]; + static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("a", Stable, &["zaamo", "zalrsc"]), @@ -782,6 +847,7 @@ pub fn all_rust_features() -> impl Iterator { .chain(HEXAGON_FEATURES.iter()) .chain(POWERPC_FEATURES.iter()) .chain(MIPS_FEATURES.iter()) + .chain(NVPTX_FEATURES.iter()) .chain(RISCV_FEATURES.iter()) .chain(WASM_FEATURES.iter()) .chain(BPF_FEATURES.iter()) @@ -847,6 +913,7 @@ impl Target { "x86" | "x86_64" => X86_FEATURES, "hexagon" => HEXAGON_FEATURES, "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES, + "nvptx64" => NVPTX_FEATURES, "powerpc" | "powerpc64" => POWERPC_FEATURES, "riscv32" | "riscv64" => RISCV_FEATURES, "wasm32" | "wasm64" => WASM_FEATURES, @@ -873,7 +940,7 @@ impl Target { "sparc" | "sparc64" => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI, "hexagon" => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI, "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI, - "bpf" | "m68k" => &[], // no vector ABI + "nvptx64" | "bpf" | "m68k" => &[], // no vector ABI "csky" => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI, // FIXME: for some tier3 targets, we are overly cautious and always give warnings // when passing args in vector registers. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 39d5399101da6..4ff142bf5d05f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -192,6 +192,7 @@ #![feature(hexagon_target_feature)] #![feature(loongarch_target_feature)] #![feature(mips_target_feature)] +#![feature(nvptx_target_feature)] #![feature(powerpc_target_feature)] #![feature(riscv_target_feature)] #![feature(rtm_target_feature)] diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 106ec562bfc79..36598982481bf 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -10,6 +10,46 @@ platform. [@RDambrosio016](https://github.com/RDambrosio016) [@kjetilkjeka](https://github.com/kjetilkjeka) +## Requirements + +This target is `no_std` and will typically be built with crate-type `cdylib` and `-C linker-flavor=llbc`, which generates PTX. +The necessary components for this workflow are: + +- `rustup toolchain add nightly` +- `rustup component add llvm-tools --toolchain nightly` +- `rustup component add llvm-bitcode-linker --toolchain nightly` + +There are two options for using the core library: + +- `rustup component add rust-src --toolchain nightly` and build using `-Z build-std=core`. +- `rustup target add nvptx64-nvidia-cuda --toolchain nightly` + +### Target and features + +It is generally necessary to specify the target, such as `-C target-cpu=sm_89`, because the default is very old. This implies two target features: `sm_89` and `ptx78` (and all preceding features within `sm_*` and `ptx*`). Rust will default to using the oldest PTX version that supports the target processor (see [this table](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#release-notes-ptx-release-history)), which maximizes driver compatibility. +One can use `-C target-feature=+ptx80` to choose a later PTX version without changing the target (the default in this case, `ptx78`, requires CUDA driver version 11.8, while `ptx80` would require driver version 12.0). +Later PTX versions may allow more efficient code generation. + +Although Rust follows LLVM in representing `ptx*` and `sm_*` as target features, they should be thought of as having crate granularity, set via (either via `-Ctarget-cpu` and optionally `-Ctarget-feature`). +While the compiler accepts `#[target_feature(enable = "ptx80", enable = "sm_89")]`, it is not supported, may not behave as intended, and may become erroneous in the future. + +## Building Rust kernels + +A `no_std` crate containing one or more functions with `extern "ptx-kernel"` can be compiled to PTX using a command like the following. + +```console +$ RUSTFLAGS='-Ctarget-cpu=sm_89' cargo +nightly rustc --target=nvptx64-nvidia-cuda -Zbuild-std=core --crate-type=cdylib -- -Clinker-flavor=llbc -Zunstable-options +``` + +Intrinsics in `core::arch::nvptx` may use `#[cfg(target_feature = "...")]`, thus it's necessary to use `-Zbuild-std=core` with appropriate `RUSTFLAGS`. The following components are needed for this workflow: + +```console +$ rustup component add rust-src --toolchain nightly +$ rustup component add llvm-tools --toolchain nightly +$ rustup component add llvm-bitcode-linker --toolchain nightly +``` + + $DIR/gate.rs:29:18 + --> $DIR/gate.rs:30:18 | LL | #[target_feature(enable = "x87")] | ^^^^^^^^^^^^^^ From 35a485ddd86229101c4c17d9167f23cf75cae644 Mon Sep 17 00:00:00 2001 From: Jed Brown Date: Wed, 21 May 2025 22:08:51 -0600 Subject: [PATCH 04/14] target-feature: enable rust target features implied by target-cpu Normally LLVM and rustc agree about what features are implied by target-cpu, but for NVPTX, LLVM considers sm_* and ptx* features to be exclusive, which makes sense for codegen purposes. But in Rust, we want to think of them as: sm_{sver} means that the target supports the hardware features of sver ptx{pver} means the driver supports PTX ISA pver Intrinsics usually require a minimum sm_{sver} and ptx{pver}. Prior to this commit, -Ctarget-cpu=sm_70 would activate only sm_70 and ptx60 (the minimum PTX version that supports sm_70, which maximizes driver compatibility). With this commit, it also activates all the implied target features (sm_20, ..., sm_62; ptx32, ..., ptx50). --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 9 ++---- .../rustc_codegen_ssa/src/target_features.rs | 15 ++++++++-- .../target-feature/implied-features-nvptx.rs | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 tests/ui/target-feature/implied-features-nvptx.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 202b9641e5675..2c1882e24bed5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -333,15 +333,12 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { - // Add base features for the target. - // We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below. - // The reason is that if LLVM considers a feature implied but we do not, we don't want that to - // show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of - // the target CPU, that is still expanded to target features (with all their implied features) - // by LLVM. let target_machine = create_informational_target_machine(sess, true); let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| { + // This closure determines whether the target CPU has the feature according to LLVM. We do + // *not* consider the `-Ctarget-feature`s here, as that will be handled later in + // `cfg_target_feature`. if let Some(feat) = to_llvm_features(sess, feature) { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 67ac619091bea..e291fb8649c19 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -224,7 +224,10 @@ fn parse_rust_feature_flag<'a>( /// 2nd component of the return value, respectively). /// /// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is -/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`. +/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`. Note that LLVM +/// may consider features to be implied that we do not and vice-versa. We want `cfg` to be entirely +/// consistent with Rust feature implications, and thus only consult LLVM to expand the target CPU +/// to target features. /// /// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. pub fn cfg_target_feature( @@ -238,7 +241,15 @@ pub fn cfg_target_feature( .rust_target_features() .iter() .filter(|(feature, _, _)| target_base_has_feature(feature)) - .map(|(feature, _, _)| Symbol::intern(feature)) + .flat_map(|(base_feature, _, _)| { + // Expand the direct base feature into all transitively-implied features. Note that we + // cannot simply use the `implied` field of the tuple since that only contains + // directly-implied features. + // + // Iteration order is irrelevant because we're collecting into an `UnordSet`. + #[allow(rustc::potential_query_instability)] + sess.target.implied_target_features(base_feature).into_iter().map(|f| Symbol::intern(f)) + }) .collect(); // Add enabled and remove disabled features. diff --git a/tests/ui/target-feature/implied-features-nvptx.rs b/tests/ui/target-feature/implied-features-nvptx.rs new file mode 100644 index 0000000000000..1550c99f67a8d --- /dev/null +++ b/tests/ui/target-feature/implied-features-nvptx.rs @@ -0,0 +1,28 @@ +//@ assembly-output: ptx-linker +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_80 -Z unstable-options -Clinker-flavor=llbc +//@ only-nvptx64 +//@ build-pass +#![no_std] +#![allow(dead_code)] + +#[panic_handler] +pub fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +// -Ctarget-cpu=sm_80 directly enables sm_80 and ptx70 +#[cfg(not(all(target_feature = "sm_80", target_feature = "ptx70")))] +compile_error!("direct target features not enabled"); + +// -Ctarget-cpu=sm_80 implies all earlier sm_* and ptx* features. +#[cfg(not(all( + target_feature = "sm_60", + target_feature = "sm_70", + target_feature = "ptx50", + target_feature = "ptx60", +)))] +compile_error!("implied target features not enabled"); + +// -Ctarget-cpu=sm_80 implies all earlier sm_* and ptx* features. +#[cfg(target_feature = "ptx71")] +compile_error!("sm_80 requires only ptx70, but ptx71 enabled"); From 4f1c4e29f02ebee5d886844a30e0298fb1ca2a54 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 9 Jul 2025 02:36:44 -0700 Subject: [PATCH 05/14] don't schedule unnecessary drops when lowering or-patterns This avoids scheduling drops and immediately unscheduling them. Drops for guard bindings/temporaries are still scheduled and unscheduled as before. --- .../src/builder/matches/mod.rs | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2c29b8628417f..9abb44143df51 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -5,11 +5,11 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. -use std::assert_matches::assert_matches; use std::borrow::Borrow; use std::mem; use std::sync::Arc; +use itertools::{Itertools, Position}; use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -561,16 +561,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // return: it isn't bound by move until right before enter the arm. // To handle this we instead unschedule it's drop after each time // we lower the guard. + // As a result, we end up with the drop order of the last sub-branch we lower. To use + // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163). + // TODO: I'm saving the breaking change for the next commit. For now, a stopgap: + let sub_branch_to_use_the_drops_from = + if arm_match_scope.is_some() { Position::Last } else { Position::First }; let target_block = self.cfg.start_new_block(); - let mut schedule_drops = ScheduleDrops::Yes; - let arm = arm_match_scope.unzip().0; - // We keep a stack of all of the bindings and type ascriptions - // from the parent candidates that we visit, that also need to - // be bound for each candidate. - for sub_branch in branch.sub_branches { - if let Some(arm) = arm { - self.clear_top_scope(arm.scope); - } + for (pos, sub_branch) in branch.sub_branches.into_iter().with_position() { + debug_assert!(pos != Position::Only); + let schedule_drops = if pos == sub_branch_to_use_the_drops_from { + ScheduleDrops::Yes + } else { + ScheduleDrops::No + }; let binding_end = self.bind_and_guard_matched_candidate( sub_branch, fake_borrow_temps, @@ -579,9 +582,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { schedule_drops, emit_storage_live, ); - if arm.is_none() { - schedule_drops = ScheduleDrops::No; - } self.cfg.goto(binding_end, outer_source_info, target_block); } @@ -2453,11 +2453,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. - self.bind_matched_candidate_for_guard( - block, - schedule_drops, - sub_branch.bindings.iter(), - ); + // This always schedules storage drops, so we may need to unschedule them below. + self.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter()); let guard_frame = GuardFrame { locals: sub_branch .bindings @@ -2489,6 +2486,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) }); + // If this isn't the final sub-branch being lowered, we need to unschedule drops of + // bindings and temporaries created for and by the guard. As a result, the drop order + // for the arm will correspond to the binding order of the final sub-branch lowered. + if matches!(schedule_drops, ScheduleDrops::No) { + self.clear_top_scope(arm.scope); + } + let source_info = self.source_info(guard_span); let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span)); let guard_frame = self.guard_context.pop().unwrap(); @@ -2538,14 +2542,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause = FakeReadCause::ForGuardBinding; self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id)); } - assert_matches!( - schedule_drops, - ScheduleDrops::Yes, - "patterns with guards must schedule drops" - ); + // Only schedule drops for the last sub-branch we lower. self.bind_matched_candidate_for_arm_body( post_guard_block, - ScheduleDrops::Yes, + schedule_drops, by_value_bindings, emit_storage_live, ); @@ -2671,7 +2671,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_matched_candidate_for_guard<'b>( &mut self, block: BasicBlock, - schedule_drops: ScheduleDrops, bindings: impl IntoIterator>, ) where 'tcx: 'b, @@ -2690,12 +2689,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // a reference R: &T pointing to the location matched by // the pattern, and every occurrence of P within a guard // denotes *R. + // Drops must be scheduled to emit `StorageDead` on the guard's failure/break branches. let ref_for_guard = self.storage_live_binding( block, binding.var_id, binding.span, RefWithinGuard, - schedule_drops, + ScheduleDrops::Yes, ); match binding.binding_mode.0 { ByRef::No => { @@ -2705,13 +2705,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } ByRef::Yes(mutbl) => { - // The arm binding will be by reference, so eagerly create it now. + // The arm binding will be by reference, so eagerly create it now. Drops must + // be scheduled to emit `StorageDead` on the guard's failure/break branches. let value_for_arm = self.storage_live_binding( block, binding.var_id, binding.span, OutsideGuard, - schedule_drops, + ScheduleDrops::Yes, ); let rvalue = From fee2dfd593f47079b2da15c199cb8f47c0fe9684 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 9 Jul 2025 02:49:31 -0700 Subject: [PATCH 06/14] base drop order on the first sub-branch --- .../src/builder/matches/mod.rs | 12 ++----- ...fg-initial.after-ElaborateDrops.after.diff | 32 +++++++++---------- tests/ui/drop/or-pattern-drop-order.rs | 8 ++--- .../dropck/eager-by-ref-binding-for-guards.rs | 4 +-- .../eager-by-ref-binding-for-guards.stderr | 16 +++++----- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 9abb44143df51..29c5670b0bfcc 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -563,17 +563,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // we lower the guard. // As a result, we end up with the drop order of the last sub-branch we lower. To use // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163). - // TODO: I'm saving the breaking change for the next commit. For now, a stopgap: - let sub_branch_to_use_the_drops_from = - if arm_match_scope.is_some() { Position::Last } else { Position::First }; let target_block = self.cfg.start_new_block(); - for (pos, sub_branch) in branch.sub_branches.into_iter().with_position() { + for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() { debug_assert!(pos != Position::Only); - let schedule_drops = if pos == sub_branch_to_use_the_drops_from { - ScheduleDrops::Yes - } else { - ScheduleDrops::No - }; + let schedule_drops = + if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No }; let binding_end = self.bind_and_guard_matched_candidate( sub_branch, fake_borrow_temps, diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff index b3eb3e1f8b9d2..484bc7dad1289 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -85,11 +85,11 @@ _8 = &(_2.2: std::string::String); - _3 = &fake shallow (_2.0: bool); - _4 = &fake shallow (_2.1: bool); - StorageLive(_12); - StorageLive(_13); - _13 = copy _1; -- switchInt(move _13) -> [0: bb16, otherwise: bb15]; -+ switchInt(move _13) -> [0: bb13, otherwise: bb12]; + StorageLive(_9); + StorageLive(_10); + _10 = copy _1; +- switchInt(move _10) -> [0: bb12, otherwise: bb11]; ++ switchInt(move _10) -> [0: bb9, otherwise: bb8]; } - bb9: { @@ -100,11 +100,11 @@ _8 = &(_2.2: std::string::String); - _3 = &fake shallow (_2.0: bool); - _4 = &fake shallow (_2.1: bool); - StorageLive(_9); - StorageLive(_10); - _10 = copy _1; -- switchInt(move _10) -> [0: bb12, otherwise: bb11]; -+ switchInt(move _10) -> [0: bb9, otherwise: bb8]; + StorageLive(_12); + StorageLive(_13); + _13 = copy _1; +- switchInt(move _13) -> [0: bb16, otherwise: bb15]; ++ switchInt(move _13) -> [0: bb13, otherwise: bb12]; } - bb10: { @@ -139,7 +139,7 @@ - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); - _5 = copy (_2.1: bool); + _5 = copy (_2.0: bool); StorageLive(_7); _7 = move (_2.2: std::string::String); - goto -> bb10; @@ -152,8 +152,8 @@ StorageDead(_9); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb1, imaginary: bb1]; -+ goto -> bb1; +- falseEdge -> [real: bb3, imaginary: bb3]; ++ goto -> bb2; } - bb15: { @@ -181,7 +181,7 @@ - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); - _5 = copy (_2.0: bool); + _5 = copy (_2.1: bool); StorageLive(_7); _7 = move (_2.2: std::string::String); - goto -> bb10; @@ -194,8 +194,8 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb3, imaginary: bb3]; -+ goto -> bb2; +- falseEdge -> [real: bb1, imaginary: bb1]; ++ goto -> bb1; } - bb19: { diff --git a/tests/ui/drop/or-pattern-drop-order.rs b/tests/ui/drop/or-pattern-drop-order.rs index fdc28225c3591..a4e4d24995bc8 100644 --- a/tests/ui/drop/or-pattern-drop-order.rs +++ b/tests/ui/drop/or-pattern-drop-order.rs @@ -65,12 +65,12 @@ fn main() { match (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) { (x, Ok(y) | Err(y), z) => {} } }); assert_drop_order(1..=2, |o| { - // The last or-pattern alternative determines the bindings' drop order: `x`, `y`. - match (true, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} } + // The first or-pattern alternative determines the bindings' drop order: `y`, `x`. + match (true, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} } }); assert_drop_order(1..=2, |o| { - // That drop order is used regardless of which or-pattern alternative matches: `x`, `y`. - match (false, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} } + // That drop order is used regardless of which or-pattern alternative matches: `y`, `x`. + match (false, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} } }); // Function params are visited one-by-one, and the order of bindings within a param's pattern is diff --git a/tests/ui/dropck/eager-by-ref-binding-for-guards.rs b/tests/ui/dropck/eager-by-ref-binding-for-guards.rs index 3f47583917172..90ff9a747aece 100644 --- a/tests/ui/dropck/eager-by-ref-binding-for-guards.rs +++ b/tests/ui/dropck/eager-by-ref-binding-for-guards.rs @@ -17,15 +17,15 @@ fn main() { (mut long2, ref short2) if true => long2.0 = &short2, _ => unreachable!(), } - // This depends on the binding modes of the final or-pattern alternatives (see #142163): + // This depends on the binding modes of the first or-pattern alternatives: let res: &Result = &Ok(1); match (Struct(&&0), res) { (mut long3, Ok(short3) | &Err(short3)) if true => long3.0 = &short3, - //~^ ERROR `short3` does not live long enough _ => unreachable!(), } match (Struct(&&0), res) { (mut long4, &Err(short4) | Ok(short4)) if true => long4.0 = &short4, + //~^ ERROR `short4` does not live long enough _ => unreachable!(), } } diff --git a/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr b/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr index cb1a04cd4447b..2648ce6f99c34 100644 --- a/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr +++ b/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr @@ -11,15 +11,15 @@ LL | (mut long1, ref short1) => long1.0 = &short1, | = note: values in a scope are dropped in the opposite order they are defined -error[E0597]: `short3` does not live long enough - --> $DIR/eager-by-ref-binding-for-guards.rs:23:69 +error[E0597]: `short4` does not live long enough + --> $DIR/eager-by-ref-binding-for-guards.rs:27:69 | -LL | (mut long3, Ok(short3) | &Err(short3)) if true => long3.0 = &short3, - | ------ ^^^^^^- - | | | | - | | | `short3` dropped here while still borrowed - | | | borrow might be used here, when `long3` is dropped and runs the `Drop` code for type `Struct` - | binding `short3` declared here borrowed value does not live long enough +LL | (mut long4, &Err(short4) | Ok(short4)) if true => long4.0 = &short4, + | ------ ^^^^^^- + | | | | + | | | `short4` dropped here while still borrowed + | | | borrow might be used here, when `long4` is dropped and runs the `Drop` code for type `Struct` + | binding `short4` declared here borrowed value does not live long enough | = note: values in a scope are dropped in the opposite order they are defined From 19ce23cadf943933776ad465fcc2ef0d90acdf15 Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 10 Jul 2025 16:38:53 -0700 Subject: [PATCH 07/14] lower bindings in the order they're written --- .../src/builder/matches/match_pair.rs | 20 +++-- .../src/builder/matches/mod.rs | 82 +++++++++++++++++-- .../src/builder/matches/util.rs | 8 +- tests/ui/drop/or-pattern-drop-order.rs | 45 ++++++---- .../bindings-after-at/bind-by-copy-or-pat.rs | 7 +- .../bind-by-copy-or-pat.stderr | 31 ------- 6 files changed, 128 insertions(+), 65 deletions(-) delete mode 100644 tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.stderr diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 3a7854a5e118d..7a848536d0e33 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -124,9 +124,19 @@ impl<'tcx> MatchPairTree<'tcx> { let test_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, - PatKind::Or { ref pats } => Some(TestCase::Or { - pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), - }), + PatKind::Or { ref pats } => { + let pats: Box<[FlatPat<'tcx>]> = + pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); + if !pats[0].extra_data.bindings.is_empty() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // or-patterns that will be simplified by `merge_trivial_subcandidates`. In + // other words, we can assume this expands into subcandidates. + // FIXME(@dianne): this needs updating/removing if we always merge or-patterns + extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + } + Some(TestCase::Or { pats }) + } PatKind::Range(ref range) => { if range.is_full_range(cx.tcx) == Some(true) { @@ -194,12 +204,12 @@ impl<'tcx> MatchPairTree<'tcx> { // Then push this binding, after any bindings in the subpattern. if let Some(source) = place { - extra_data.bindings.push(super::Binding { + extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { span: pattern.span, source, var_id: var, binding_mode: mode, - }); + })); } None diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 29c5670b0bfcc..4a60ff30dba86 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -990,7 +990,7 @@ struct PatternExtraData<'tcx> { span: Span, /// Bindings that must be established. - bindings: Vec>, + bindings: Vec>, /// Types that must be asserted. ascriptions: Vec>, @@ -1005,6 +1005,15 @@ impl<'tcx> PatternExtraData<'tcx> { } } +#[derive(Debug, Clone)] +enum SubpatternBindings<'tcx> { + /// A single binding. + One(Binding<'tcx>), + /// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the + /// order the primary bindings appear. See rust-lang/rust#142163 for more information. + FromOrPattern, +} + /// A pattern in a form suitable for lowering the match tree, with all irrefutable /// patterns simplified away. /// @@ -1220,7 +1229,7 @@ fn traverse_candidate<'tcx, C, T, I>( } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] struct Binding<'tcx> { span: Span, source: Place<'tcx>, @@ -1446,12 +1455,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { span: candidate.extra_data.span, success_block: candidate.pre_binding_block.unwrap(), otherwise_block: candidate.otherwise_block.unwrap(), - bindings: parent_data - .iter() - .flat_map(|d| &d.bindings) - .chain(&candidate.extra_data.bindings) - .cloned() - .collect(), + bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings), ascriptions: parent_data .iter() .flat_map(|d| &d.ascriptions) @@ -1484,6 +1488,68 @@ impl<'tcx> MatchTreeBranch<'tcx> { } } +/// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the +/// pattern, as though the or-alternatives chosen in this sub-branch were inlined. +fn sub_branch_bindings<'tcx>( + parents: &[PatternExtraData<'tcx>], + leaf_bindings: &[SubpatternBindings<'tcx>], +) -> Vec> { + // In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings. + let mut all_bindings = Vec::with_capacity(leaf_bindings.len()); + let mut remainder = parents + .iter() + .map(|parent| parent.bindings.as_slice()) + .chain([leaf_bindings]) + // Skip over unsimplified or-patterns without bindings. + .filter(|bindings| !bindings.is_empty()); + if let Some(candidate_bindings) = remainder.next() { + push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); + } + // Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not + // have collected all bindings yet, since we only check the first alternative when determining + // whether to inline subcandidates' bindings. + // FIXME(@dianne): prevent ill-formed patterns from getting here + while let Some(candidate_bindings) = remainder.next() { + ty::tls::with(|tcx| { + tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + }); + // To recover, we collect the rest in an arbitrary order. + push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); + } + all_bindings +} + +/// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into +/// `flattened`. Bindings in or-patterns are collected recursively from `remainder`. +fn push_sub_branch_bindings<'c, 'tcx: 'c>( + flattened: &mut Vec>, + candidate_bindings: &'c [SubpatternBindings<'tcx>], + remainder: &mut impl Iterator]>, +) { + for subpat_bindings in candidate_bindings { + match subpat_bindings { + SubpatternBindings::One(binding) => flattened.push(*binding), + SubpatternBindings::FromOrPattern => { + // Inline bindings from an or-pattern. By construction, this always + // corresponds to a subcandidate and its closest descendants (i.e. those + // from nested or-patterns, but not adjacent or-patterns). To handle + // adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to + // point to the first descendant candidate from outside this or-pattern. + if let Some(subcandidate_bindings) = remainder.next() { + push_sub_branch_bindings(flattened, subcandidate_bindings, remainder); + } else { + // For ill-formed patterns like `x | _`, we may not have any subcandidates left + // to inline bindings from. + // FIXME(@dianne): prevent ill-formed patterns from getting here + ty::tls::with(|tcx| { + tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + }); + }; + } + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum HasMatchGuard { Yes, diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 589e350a03fc3..2c8ad95b6afdb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -138,7 +138,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { - self.visit_binding(binding); + if let super::SubpatternBindings::One(binding) = binding { + self.visit_binding(binding); + } } for match_pair in &candidate.match_pairs { self.visit_match_pair(match_pair); @@ -147,7 +149,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) { for binding in &flat_pat.extra_data.bindings { - self.visit_binding(binding); + if let super::SubpatternBindings::One(binding) = binding { + self.visit_binding(binding); + } } for match_pair in &flat_pat.match_pairs { self.visit_match_pair(match_pair); diff --git a/tests/ui/drop/or-pattern-drop-order.rs b/tests/ui/drop/or-pattern-drop-order.rs index a4e4d24995bc8..cca81673ac3ac 100644 --- a/tests/ui/drop/or-pattern-drop-order.rs +++ b/tests/ui/drop/or-pattern-drop-order.rs @@ -1,6 +1,7 @@ //@ run-pass //! Test drop order for different ways of declaring pattern bindings involving or-patterns. -//! Currently, it's inconsistent between language constructs (#142163). +//! In particular, are ordered based on the order in which the first occurrence of each binding +//! appears (i.e. the "primary" bindings). Regression test for #142163. use std::cell::RefCell; use std::ops::Drop; @@ -43,11 +44,10 @@ fn main() { y = LogDrop(o, 1); }); - // When bindings are declared with `let pat = expr;`, bindings within or-patterns are seen last, - // thus they're dropped first. + // `let pat = expr;` should have the same drop order. assert_drop_order(1..=3, |o| { - // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`. - let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)); + // Drops are right-to-left: `z`, `y`, `x`. + let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)); }); assert_drop_order(1..=2, |o| { // The first or-pattern alternative determines the bindings' drop order: `y`, `x`. @@ -58,11 +58,10 @@ fn main() { let ((true, x, y) | (false, y, x)) = (false, LogDrop(o, 1), LogDrop(o, 2)); }); - // `match` treats or-patterns as last like `let pat = expr;`, but also determines drop order - // using the order of the bindings in the *last* or-pattern alternative. + // `match` should have the same drop order. assert_drop_order(1..=3, |o| { - // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`. - match (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) { (x, Ok(y) | Err(y), z) => {} } + // Drops are right-to-left: `z`, `y` `x`. + match (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) { (x, Ok(y) | Err(y), z) => {} } }); assert_drop_order(1..=2, |o| { // The first or-pattern alternative determines the bindings' drop order: `y`, `x`. @@ -74,14 +73,14 @@ fn main() { }); // Function params are visited one-by-one, and the order of bindings within a param's pattern is - // the same as `let pat = expr`; + // the same as `let pat = expr;` assert_drop_order(1..=3, |o| { // Among separate params, the drop order is right-to-left: `z`, `y`, `x`. (|x, (Ok(y) | Err(y)), z| {})(LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)); }); assert_drop_order(1..=3, |o| { - // Within a param's pattern, or-patterns are treated as rightmost: `y`, `z`, `x`. - (|(x, Ok(y) | Err(y), z)| {})((LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2))); + // Within a param's pattern, likewise: `z`, `y`, `x`. + (|(x, Ok(y) | Err(y), z)| {})((LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1))); }); assert_drop_order(1..=2, |o| { // The first or-pattern alternative determines the bindings' drop order: `y`, `x`. @@ -89,12 +88,11 @@ fn main() { }); // `if let` and `let`-`else` see bindings in the same order as `let pat = expr;`. - // Vars in or-patterns are seen last (dropped first), and the first alternative's order is used. assert_drop_order(1..=3, |o| { - if let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) {} + if let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) {} }); assert_drop_order(1..=3, |o| { - let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) else { + let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) else { unreachable!(); }; }); @@ -106,4 +104,21 @@ fn main() { unreachable!(); }; }); + + // Test nested and adjacent or-patterns, including or-patterns without bindings under a guard. + assert_drop_order(1..=6, |o| { + // The `LogDrop`s that aren't moved into bindings are dropped last. + match [ + [LogDrop(o, 6), LogDrop(o, 4)], + [LogDrop(o, 3), LogDrop(o, 2)], + [LogDrop(o, 1), LogDrop(o, 5)], + ] { + [ + [_ | _, w | w] | [w | w, _ | _], + [x | x, y | y] | [y | y, x | x], + [z | z, _ | _] | [_ | _, z | z], + ] if true => {} + _ => unreachable!(), + } + }); } diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.rs b/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.rs index 1555da2fd1fc3..dd23acfa23549 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.rs +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.rs @@ -1,16 +1,15 @@ -//@ known-bug: unknown +//@ run-pass #![allow(unused)] struct A(u32); pub fn main() { - // The or-pattern bindings are lowered after `x`, which triggers the error. + // Bindings are lowered in the order they appear syntactically, so this works. let x @ (A(a) | A(a)) = A(10); - // ERROR: use of moved value assert!(x.0 == 10); assert!(a == 10); - // This works. + // This also works. let (x @ A(a) | x @ A(a)) = A(10); assert!(x.0 == 10); assert!(a == 10); diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.stderr b/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.stderr deleted file mode 100644 index 7980818635836..0000000000000 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy-or-pat.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0382]: use of moved value - --> $DIR/bind-by-copy-or-pat.rs:8:16 - | -LL | let x @ (A(a) | A(a)) = A(10); - | - ^ ----- move occurs because value has type `A`, which does not implement the `Copy` trait - | | | - | | value used here after move - | value moved here - | -help: borrow this binding in the pattern to avoid moving the value - | -LL | let ref x @ (A(a) | A(a)) = A(10); - | +++ - -error[E0382]: use of moved value - --> $DIR/bind-by-copy-or-pat.rs:8:23 - | -LL | let x @ (A(a) | A(a)) = A(10); - | - ^ ----- move occurs because value has type `A`, which does not implement the `Copy` trait - | | | - | | value used here after move - | value moved here - | -help: borrow this binding in the pattern to avoid moving the value - | -LL | let ref x @ (A(a) | A(a)) = A(10); - | +++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0382`. From ec40ee4c4eb6b105e67b7fb6a7a488d89a64fafb Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 30 Jul 2025 09:30:10 +0000 Subject: [PATCH 08/14] Add documentation for unstable_feature_bound --- src/doc/rustc-dev-guide/src/stability.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index 230925252bac3..7a5efdb8ad72f 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -183,4 +183,7 @@ the `deprecated_in_future` lint is triggered which is default `allow`, but most of the standard library raises it to a warning with `#![warn(deprecated_in_future)]`. +## unstable_feature_bound +The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core , an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`. + [blog]: https://www.ralfj.de/blog/2018/07/19/const.html From 712c28e8324fc507c986f982755b8bfde919b5dc Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 30 Jul 2025 11:54:32 +0200 Subject: [PATCH 09/14] Remove space Co-authored-by: Tshepang Mbambo --- src/doc/rustc-dev-guide/src/stability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index 7a5efdb8ad72f..e8589b7f4310c 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -184,6 +184,6 @@ of the standard library raises it to a warning with `#![warn(deprecated_in_future)]`. ## unstable_feature_bound -The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core , an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`. +The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`. [blog]: https://www.ralfj.de/blog/2018/07/19/const.html From 1fe1bf59b7bf1795a394d917bec9519f8bd970b6 Mon Sep 17 00:00:00 2001 From: ash Date: Sun, 3 Aug 2025 14:12:18 -0600 Subject: [PATCH 10/14] explicit tail call tests with indirect operands in LLVM, small test for indexing into a function table as described by RFC 3407 --- tests/ui/explicit-tail-calls/drop-order.rs | 2 - tests/ui/explicit-tail-calls/indexer.rs | 22 +++++++++ .../recursion-etc-u64wrapper.rs | 49 +++++++++++++++++++ .../recursion-etc-u64wrapper.stderr | 19 +++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/ui/explicit-tail-calls/indexer.rs create mode 100644 tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.rs create mode 100644 tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.stderr diff --git a/tests/ui/explicit-tail-calls/drop-order.rs b/tests/ui/explicit-tail-calls/drop-order.rs index 242336be4845e..58e1afbdf0c51 100644 --- a/tests/ui/explicit-tail-calls/drop-order.rs +++ b/tests/ui/explicit-tail-calls/drop-order.rs @@ -1,5 +1,3 @@ -// FIXME(explicit_tail_calls): enable this test once rustc_codegen_ssa supports tail calls -//@ ignore-test: tail calls are not implemented in rustc_codegen_ssa yet, so this causes 🧊 //@ run-pass #![expect(incomplete_features)] #![feature(explicit_tail_calls)] diff --git a/tests/ui/explicit-tail-calls/indexer.rs b/tests/ui/explicit-tail-calls/indexer.rs new file mode 100644 index 0000000000000..5644506b2f584 --- /dev/null +++ b/tests/ui/explicit-tail-calls/indexer.rs @@ -0,0 +1,22 @@ +//@ run-pass +// Indexing taken from +// https://github.com/phi-go/rfcs/blob/guaranteed-tco/text%2F0000-explicit-tail-calls.md#tail-call-elimination +// no other test has utilized the "function table" +// described in the RFC aside from this one at this point. +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn f0(_: usize) {} +fn f1(_: usize) {} +fn f2(_: usize) {} + +fn indexer(idx: usize) { + let v: [fn(usize); 3] = [f0, f1, f2]; + become v[idx](idx) +} + +fn main() { + for idx in 0..3 { + indexer(idx); + } +} diff --git a/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.rs b/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.rs new file mode 100644 index 0000000000000..849c11bf6c298 --- /dev/null +++ b/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.rs @@ -0,0 +1,49 @@ +//@ build-fail +//@ normalize-stderr: "note: .*\n\n" -> "" +//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" +//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ normalize-stderr: "/rustc-dev/[^:]+" -> "$COMPILER_DIR_REAL" +//@ rustc-env:RUST_BACKTRACE=0 +//@ known-bug: #144293 +//@ failure-status: 101 +// Same as recursion-etc but eggs LLVM emission into giving indirect arguments. +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +use std::hint::black_box; + +struct U64Wrapper { + pub x: u64, + pub arbitrary: String, +} + +fn count(curr: U64Wrapper, top: U64Wrapper) -> U64Wrapper { + if black_box(curr.x) >= top.x { + curr + } else { + become count( + U64Wrapper { + x: curr.x + 1, + arbitrary: curr.arbitrary, + }, + top, + ) + } +} + +fn main() { + println!( + "{}", + count( + U64Wrapper { + x: 0, + arbitrary: "hello!".into() + }, + black_box(U64Wrapper { + x: 1000000, + arbitrary: "goodbye!".into() + }) + ) + .x + ); +} diff --git a/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.stderr b/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.stderr new file mode 100644 index 0000000000000..533f1c841e0c9 --- /dev/null +++ b/tests/ui/explicit-tail-calls/recursion-etc-u64wrapper.stderr @@ -0,0 +1,19 @@ +error: internal compiler error: $COMPILER_DIR_REAL/rustc_codegen_ssa/src/mir/block.rs:LL:CC: arguments using PassMode::Indirect are currently not supported for tail calls + --> $DIR/recursion-etc-u64wrapper.rs:24:16 + | +LL | become count( + | ________________^ +LL | | U64Wrapper { +LL | | x: curr.x + 1, +LL | | arbitrary: curr.arbitrary, +LL | | }, +LL | | top, +LL | | ) + | |_________^ + + +Box +query stack during panic: +end of query stack +error: aborting due to 1 previous error + From 23e6be2d900cb140c09ab36e3c166842a230d696 Mon Sep 17 00:00:00 2001 From: Anne Stijns Date: Sat, 12 Jul 2025 23:50:47 +0200 Subject: [PATCH 11/14] Port #[macro_export] to the new attribute parsing infrastructure --- compiler/rustc_attr_parsing/messages.ftl | 5 + .../src/attributes/macro_attrs.rs | 61 +++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_attr_parsing/src/lints.rs | 12 +++ compiler/rustc_expand/src/base.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir/src/lints.rs | 21 ++++- compiler/rustc_lint/src/non_local_def.rs | 10 +- compiler/rustc_lint_defs/src/builtin.rs | 7 +- compiler/rustc_passes/messages.ftl | 4 - compiler/rustc_passes/src/check_attr.rs | 40 +++----- compiler/rustc_passes/src/errors.rs | 6 -- src/librustdoc/html/render/search_index.rs | 23 +++-- src/librustdoc/json/conversions.rs | 9 +- src/librustdoc/passes/strip_hidden.rs | 5 +- src/librustdoc/visit_ast.rs | 10 +- .../src/macro_metavars_in_unsafe.rs | 7 +- ...invalid_macro_export_argument.allow.stderr | 40 ++++++++ .../invalid_macro_export_argument.deny.stderr | 93 +++++++++++++++++-- .../invalid_macro_export_argument.rs | 12 ++- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 21 ++--- ...43106-gating-of-builtin-attrs-error.stderr | 30 +++--- .../lint/unused/unused-attr-duplicate.stderr | 24 ++--- 25 files changed, 328 insertions(+), 127 deletions(-) create mode 100644 tests/ui/attributes/invalid_macro_export_argument.allow.stderr diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 35ff48cb5f24b..aa8046add7145 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -32,6 +32,11 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions -> *[other] valid forms for the attribute are {$suggestions} } +attr_parsing_invalid_macro_export_arguments = {$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + } + attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 886f7a889d303..122d80cca046e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,10 +1,14 @@ use rustc_errors::DiagArgValue; use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::{AttributeKind, MacroUseArgs}; +use rustc_hir::lints::AttributeLintKind; use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; -use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate}; +use crate::attributes::{ + AcceptMapping, AttributeOrder, AttributeParser, NoArgsAttributeParser, OnDuplicate, + SingleAttributeParser, +}; use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; @@ -113,3 +117,58 @@ impl AttributeParser for MacroUseParser { Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state }) } } + +pub(crate) struct MacroExportParser; + +impl SingleAttributeParser for crate::attributes::macro_attrs::MacroExportParser { + const PATH: &[Symbol] = &[sym::macro_export]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word, List: "local_inner_macros"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let suggestions = + || >::TEMPLATE.suggestions(false, "macro_export"); + let local_inner_macros = match args { + ArgParser::NoArgs => false, + ArgParser::List(list) => { + let Some(l) = list.single() else { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + }; + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::local_inner_macros) => true, + _ => { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + } + } + } + ArgParser::NameValue(_) => { + let span = cx.attr_span; + let suggestions = suggestions(); + cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + return None; + } + }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b51db9b4b9e38..85db54709840d 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -32,7 +32,7 @@ use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; -use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser}; +use crate::attributes::macro_attrs::{MacroEscapeParser, MacroExportParser, MacroUseParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; @@ -164,6 +164,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 22f5531bc8079..359b0ef0dd12c 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -34,5 +34,17 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi *first_span, session_diagnostics::EmptyAttributeList { attr_span: *first_span }, ), + AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 1a9832b2fe26a..e1fd184533f6f 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -910,9 +910,9 @@ impl SyntaxExtension { let allow_internal_unsafe = ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some(); - let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) - .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); + let local_inner_macros = + *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) + .unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 80618422b56d6..c8b49322b9801 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -360,6 +360,9 @@ pub enum AttributeKind { /// Represents `#[macro_escape]`. MacroEscape(Span), + /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). + MacroExport { span: Span, local_inner_macros: bool }, + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 9644a597a3117..55488f0c08731 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -46,6 +46,7 @@ impl AttributeKind { LinkSection { .. } => Yes, // Needed for rustdoc LoopMatch(..) => No, MacroEscape(..) => No, + MacroExport { .. } => Yes, MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index c55a41eb2b76b..b096c671edabe 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,7 +31,22 @@ pub struct AttributeLint { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { this: Span, other: Span, warning: bool }, - IllFormedAttributeInput { suggestions: Vec }, - EmptyAttribute { first_span: Span }, + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, + IllFormedAttributeInput { + suggestions: Vec, + }, + EmptyAttribute { + first_span: Span, + }, + + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see + InvalidMacroExportArguments { + suggestions: Vec, + }, } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index b877f909fc029..d0254eccb39ef 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,11 +1,12 @@ use rustc_errors::MultiSpan; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, MacroKind, Span, kw, sym}; +use rustc_span::{ExpnKind, MacroKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -241,7 +242,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ) } ItemKind::Macro(_, _macro, MacroKind::Bang) - if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => + if find_attr!( + cx.tcx.get_all_attrs(item.owner_id.def_id), + AttributeKind::MacroExport { .. } + ) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3b84c6b611016..9e5ceb8977c13 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4188,8 +4188,13 @@ declare_lint! { /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. /// pub INVALID_MACRO_EXPORT_ARGUMENTS, - Warn, + Deny, "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #57571 ", + report_in_deps: true, + }; } declare_lint! { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6a28fe2617edf..f9c0f43803bc8 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -399,10 +399,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument - -passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments - passes_lang_item_fn = {$name -> [panic_impl] `#[panic_handler]` *[other] `{$name}` lang item diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2663d5fe99c7a..98494810768b0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -38,9 +38,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, - USELESS_DEPRECATED, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, USELESS_DEPRECATED, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -291,6 +290,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Dummy | AttributeKind::RustcBuiltinMacro { .. }, ) => { /* do nothing */ } + Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { + self.check_macro_export(hir_id, *span, target) + } Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target) } @@ -383,7 +385,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), [sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod), - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::should_panic, ..] => { self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn) } @@ -2432,32 +2433,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { if target != Target::MacroDef { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MacroExport::Normal, ); - } else if let Some(meta_item_list) = attr.meta_item_list() - && !meta_item_list.is_empty() - { - if meta_item_list.len() > 1 { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - attr.span(), - errors::MacroExport::TooManyItems, - ); - } else if !meta_item_list[0].has_name(sym::local_inner_macros) { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - meta_item_list[0].span(), - errors::MacroExport::InvalidArgument, - ); - } } else { // special case when `#[macro_export]` is applied to a macro 2.0 let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); @@ -2467,7 +2450,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MacroExport::OnDeclMacro, ); } @@ -2820,7 +2803,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { + if macro_def.macro_rules + && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2950,7 +2935,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // which were unsuccessfully resolved due to cannot determine // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ - sym::macro_export, sym::rustc_main, sym::derive, sym::test, @@ -2975,6 +2959,8 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { (*span, sym::path) } else if let Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) = attr { (*span, sym::automatically_derived) + } else if let Attribute::Parsed(AttributeKind::MacroExport { span, .. }) = attr { + (*span, sym::macro_export) } else { continue; }; diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c6ab6b0d60179..ed9973df0e1d2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -786,12 +786,6 @@ pub(crate) enum MacroExport { #[diag(passes_macro_export_on_decl_macro)] #[note] OnDeclMacro, - - #[diag(passes_invalid_macro_export_arguments)] - InvalidArgument, - - #[diag(passes_invalid_macro_export_arguments_too_many_items)] - TooManyItems, } #[derive(Subdiagnostic)] diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e2f86b8a8549e..3304c61380993 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -6,6 +6,8 @@ use std::collections::{BTreeMap, VecDeque}; use encode::{bitmap_to_string, write_vlqhex_to_string}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -421,16 +423,17 @@ pub(crate) fn build_index( if fqp.last() != Some(&item.name) { return None; } - let path = - if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { - // `#[macro_export]` always exports to the crate root. - tcx.crate_name(defid.krate).to_string() - } else { - if fqp.len() < 2 { - return None; - } - join_path_syms(&fqp[..fqp.len() - 1]) - }; + let path = if item.ty == ItemType::Macro + && find_attr!(tcx.get_all_attrs(defid), AttributeKind::MacroExport { .. }) + { + // `#[macro_export]` always exports to the crate root. + tcx.crate_name(defid.krate).to_string() + } else { + if fqp.len() < 2 { + return None; + } + join_path_syms(&fqp[..fqp.len() - 1]) + }; if path == item.path { return None; } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f966d9265628b..dd54b4e30d15c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -908,12 +908,8 @@ fn maybe_from_hir_attr( hir::Attribute::Parsed(kind) => kind, hir::Attribute::Unparsed(_) => { - return Some(if attr.has_name(sym::macro_export) { - Attribute::MacroExport - // FIXME: We should handle `#[doc(hidden)]`. - } else { - other_attr(tcx, attr) - }); + // FIXME: We should handle `#[doc(hidden)]`. + return Some(other_attr(tcx, attr)); } }; @@ -921,6 +917,7 @@ fn maybe_from_hir_attr( AK::Deprecation { .. } => return None, // Handled separately into Item::deprecation. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), + AK::MacroExport { .. } => Attribute::MacroExport, AK::MustUse { reason, span: _ } => { Attribute::MustUse { reason: reason.map(|s| s.to_string()) } } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 3388ae46f056c..525d05b6a9860 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -2,9 +2,10 @@ use std::mem; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; use tracing::debug; use crate::clean::utils::inherits_doc_hidden; @@ -114,7 +115,7 @@ impl DocFolder for Stripper<'_, '_> { // If the macro has the `#[macro_export]` attribute, it means it's accessible at the // crate level so it should be handled differently. clean::MacroItem(..) => { - i.attrs.other_attrs.iter().any(|attr| attr.has_name(sym::macro_export)) + find_attr!(&i.attrs.other_attrs, AttributeKind::MacroExport { .. }) } _ => false, }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 9058277d72eea..428149b95f468 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,10 +5,11 @@ use std::mem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{CRATE_HIR_ID, Node}; +use rustc_hir::{CRATE_HIR_ID, Node, find_attr}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -167,7 +168,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if !child.reexport_chain.is_empty() && let Res::Def(DefKind::Macro(_), def_id) = child.res && let Some(local_def_id) = def_id.as_local() - && self.cx.tcx.has_attr(def_id, sym::macro_export) + && find_attr!(self.cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) && inserted.insert(def_id) { let item = self.cx.tcx.hir_expect_item(local_def_id); @@ -407,7 +408,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { || match item.kind { hir::ItemKind::Impl(..) => true, hir::ItemKind::Macro(_, _, MacroKind::Bang) => { - self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) + find_attr!(self.cx.tcx.get_all_attrs(item.owner_id.def_id), AttributeKind::MacroExport{..}) } _ => false, } @@ -525,7 +526,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let def_id = item.owner_id.to_def_id(); let is_macro_2_0 = !macro_def.macro_rules; - let nonexported = !tcx.has_attr(def_id, sym::macro_export); + let nonexported = + !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }); if is_macro_2_0 || nonexported || self.inlining { self.add_to_current_mod(item, renamed, import_id); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 9071c9c95f9d7..c5acaf0999332 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,10 +5,12 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; @@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + ( cx.effective_visibilities.is_exported(def_id) || + find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) && !cx.tcx.is_doc_hidden(def_id) } diff --git a/tests/ui/attributes/invalid_macro_export_argument.allow.stderr b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr new file mode 100644 index 0000000000000..2db60a6897282 --- /dev/null +++ b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr @@ -0,0 +1,40 @@ +Future incompatibility report: Future breakage diagnostic: +warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + diff --git a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr index 9d44bd162c7b7..fd50b824d0a26 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr +++ b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr @@ -1,26 +1,103 @@ -error: `#[macro_export]` can only take 1 or 0 arguments +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` --> $DIR/invalid_macro_export_argument.rs:7:1 | LL | #[macro_export(hello, world)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 note: the lint level is defined here --> $DIR/invalid_macro_export_argument.rs:4:24 | LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:13:16 +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 | LL | #[macro_export(not_local_inner_macros)] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:33:16 +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 | LL | #[macro_export("blah")] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error: aborting due to 4 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +Future breakage diagnostic: +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/invalid_macro_export_argument.rs b/tests/ui/attributes/invalid_macro_export_argument.rs index c5fe39d062a4e..05a40913434e8 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.rs +++ b/tests/ui/attributes/invalid_macro_export_argument.rs @@ -5,13 +5,15 @@ #![cfg_attr(allow, allow(invalid_macro_export_arguments))] #[macro_export(hello, world)] -//[deny]~^ ERROR `#[macro_export]` can only take 1 or 0 arguments +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! a { () => () } #[macro_export(not_local_inner_macros)] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! b { () => () } @@ -20,18 +22,22 @@ macro_rules! b { macro_rules! c { () => () } + #[macro_export(local_inner_macros)] macro_rules! d { () => () } #[macro_export()] +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! e { () => () } #[macro_export("blah")] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! f { () => () } diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 0d5bf69d548ce..76e2e931f17b8 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -209,7 +209,7 @@ extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] #[macro_export = 18] -//~^ ERROR malformed `macro_export` attribute input +//~^ ERROR valid forms for the attribute are #[allow_internal_unsafe = 1] //~^ ERROR malformed //~| ERROR allow_internal_unsafe side-steps the unsafe_code lint diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 1b51075b4e888..62cea759f9ccc 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -136,21 +136,6 @@ error: malformed `no_link` attribute input LL | #[no_link()] | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]` -error: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:211:1 - | -LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL - #[macro_export = 18] -LL + #[macro_export(local_inner_macros)] - | -LL - #[macro_export = 18] -LL + #[macro_export] - | - error: malformed `allow_internal_unsafe` attribute input --> $DIR/malformed-attrs.rs:213:1 | @@ -558,6 +543,12 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and ` LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ +error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` + --> $DIR/malformed-attrs.rs:211:1 + | +LL | #[macro_export = 18] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0565]: malformed `type_const` attribute input --> $DIR/malformed-attrs.rs:140:5 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 4cba54bf67c12..6d3b1e4e27380 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -91,21 +91,6 @@ error[E0518]: attribute should be applied to function or closure LL | #![inline] | ^^^^^^^^^^ not a function or closure -error: `macro_export` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 - | -LL | #![macro_export] - | ^^^^^^^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![macro_export] -LL + #[macro_export] - | - error: `rustc_main` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 | @@ -136,6 +121,21 @@ LL - #![repr()] LL + #[repr()] | +error: `macro_export` attribute cannot be used at crate level + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 + | +LL | #![macro_export] + | ^^^^^^^^^^^^^^^^ +... +LL | mod inline { + | ------ the inner attribute doesn't annotate this module + | +help: perhaps you meant to use an outer attribute + | +LL - #![macro_export] +LL + #[macro_export] + | + error: `path` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index e277f5203c698..817556aa165be 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -104,18 +104,6 @@ note: attribute also specified here LL | #![no_builtins] | ^^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:40:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:39:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:37:1 | @@ -128,6 +116,18 @@ note: attribute also specified here LL | #[macro_use] | ^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:40:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:39:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:47:1 | From f6ce4ac9d38fca2df5bc408de8f4c2567d2f2709 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 2 Aug 2025 17:36:34 +0000 Subject: [PATCH 12/14] Anonymize binders in tail call sig --- compiler/rustc_mir_build/src/check_tail_calls.rs | 6 +++++- tests/ui/explicit-tail-calls/higher-ranked-arg.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/ui/explicit-tail-calls/higher-ranked-arg.rs diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 6ed100899d8c5..036e20c22b8f4 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -60,9 +60,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { let BodyTy::Fn(caller_sig) = self.thir.body_type else { span_bug!( call.span, - "`become` outside of functions should have been disallowed by hit_typeck" + "`become` outside of functions should have been disallowed by hir_typeck" ) }; + // While the `caller_sig` does have its regions erased, it does not have its + // binders anonymized. We call `erase_regions` once again to anonymize any binders + // within the signature, such as in function pointer or `dyn Trait` args. + let caller_sig = self.tcx.erase_regions(caller_sig); let ExprKind::Scope { value, .. } = call.kind else { span_bug!(call.span, "expected scope, found: {call:?}") diff --git a/tests/ui/explicit-tail-calls/higher-ranked-arg.rs b/tests/ui/explicit-tail-calls/higher-ranked-arg.rs new file mode 100644 index 0000000000000..e60686ab5111d --- /dev/null +++ b/tests/ui/explicit-tail-calls/higher-ranked-arg.rs @@ -0,0 +1,13 @@ +// Regression test for . +//@ check-pass + +#![feature(explicit_tail_calls)] +#![expect(incomplete_features)] + +fn foo(x: fn(&i32)) { + become bar(x); +} + +fn bar(_: fn(&i32)) {} + +fn main() {} From 904e2af3a97e81637b9816d71411d52e26bd1550 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Fri, 1 Aug 2025 18:25:57 +0200 Subject: [PATCH 13/14] Port `#[coroutine]` to the new attribute system Related to https://github.com/rust-lang/rust/issues/131229#issue-2565886367. --- compiler/rustc_ast_lowering/src/expr.rs | 18 ++++++------------ .../rustc_attr_parsing/src/attributes/body.rs | 15 +++++++++++++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 ++ .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 10 +++++----- tests/ui/attributes/malformed-attrs.stderr | 15 +++++++++------ 8 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/body.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1245d48975470..fb42cfea30b41 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -98,7 +98,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let expr_hir_id = self.lower_node_id(e.id); - self.lower_attrs(expr_hir_id, &e.attrs, e.span); + let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span); let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), @@ -232,10 +232,10 @@ impl<'hir> LoweringContext<'_, 'hir> { *fn_arg_span, ), None => self.lower_expr_closure( + attrs, binder, *capture_clause, e.id, - expr_hir_id, *constness, *movability, fn_decl, @@ -1052,10 +1052,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_closure( &mut self, + attrs: &[rustc_hir::Attribute], binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, - closure_hir_id: hir::HirId, constness: Const, movability: Movability, decl: &FnDecl, @@ -1067,15 +1067,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let (binder_clause, generic_params) = self.lower_closure_binder(binder); let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { - let mut coroutine_kind = if this - .attrs - .get(&closure_hir_id.local_id) - .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine))) - { - Some(hir::CoroutineKind::Coroutine(Movability::Movable)) - } else { - None - }; + + let mut coroutine_kind = find_attr!(attrs, AttributeKind::Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable)); + // FIXME(contracts): Support contracts on closures? let body_id = this.lower_fn_body(decl, None, |this| { this.coroutine_kind = coroutine_kind; diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs new file mode 100644 index 0000000000000..ab9330216f6c6 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/body.rs @@ -0,0 +1,15 @@ +//! Attributes that can be found in function body. + +use rustc_hir::attrs::AttributeKind; +use rustc_span::{Symbol, sym}; + +use super::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; + +pub(crate) struct CoroutineParser; + +impl NoArgsAttributeParser for CoroutineParser { + const PATH: &[Symbol] = &[sym::coroutine]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const CREATE: fn(rustc_span::Span) -> AttributeKind = |span| AttributeKind::Coroutine(span); +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index c574ef78bdf79..f7946ade6d2b2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -26,6 +26,7 @@ use crate::parser::ArgParser; use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; +pub(crate) mod body; pub(crate) mod cfg; pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b51db9b4b9e38..ec74f6fd1c6f4 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,6 +16,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; +use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, @@ -184,6 +185,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 80618422b56d6..d9d2ec48948e7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -297,6 +297,9 @@ pub enum AttributeKind { /// Represents `#[const_trait]`. ConstTrait(Span), + /// Represents `#[coroutine]`. + Coroutine(Span), + /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 9644a597a3117..b66e5bbeabe73 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -28,6 +28,7 @@ impl AttributeKind { ConstStability { .. } => Yes, ConstStabilityIndirect => No, ConstTrait(..) => No, + Coroutine(..) => No, Coverage(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2663d5fe99c7a..890028d977d30 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -324,6 +324,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => { self.check_coverage(attr_span, span, target) } + &Attribute::Parsed(AttributeKind::Coroutine(attr_span)) => { + self.check_coroutine(attr_span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -390,9 +393,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } - [sym::coroutine, ..] => { - self.check_coroutine(attr, target); - } [sym::linkage, ..] => self.check_linkage(attr, span, target), [ // ok @@ -2651,11 +2651,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_coroutine(&self, attr: &Attribute, target: Target) { + fn check_coroutine(&self, attr_span: Span, target: Target) { match target { Target::Closure => return, _ => { - self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span() }); + self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr_span }); } } } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 1b51075b4e888..e8ae4715398f4 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -55,12 +55,6 @@ error: malformed `patchable_function_entry` attribute input LL | #[patchable_function_entry] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: malformed `coroutine` attribute input - --> $DIR/malformed-attrs.rs:108:5 - | -LL | #[coroutine = 63] || {} - | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[coroutine]` - error: malformed `must_not_suspend` attribute input --> $DIR/malformed-attrs.rs:129:1 | @@ -436,6 +430,15 @@ LL | #[proc_macro = 18] | | didn't expect any arguments here | help: must be of the form: `#[proc_macro]` +error[E0565]: malformed `coroutine` attribute input + --> $DIR/malformed-attrs.rs:108:5 + | +LL | #[coroutine = 63] || {} + | ^^^^^^^^^^^^----^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[coroutine]` + error[E0565]: malformed `proc_macro_attribute` attribute input --> $DIR/malformed-attrs.rs:113:1 | From 91e606b715ac4e8f59fac86d42ec2ad23f4ef169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 28 Feb 2025 23:37:37 +0000 Subject: [PATCH 14/14] Tweak auto trait errors Make suggestions to remove params and super traits tool-only, and make the suggestion span more accurate. ``` error[E0567]: auto traits cannot have generic parameters --> $DIR/auto-trait-validation.rs:6:19 | LL | auto trait Generic {} | -------^^^ | | | auto trait cannot have generic parameters error[E0568]: auto traits cannot have super traits or lifetime bounds --> $DIR/auto-trait-validation.rs:8:20 | LL | auto trait Bound : Copy {} | ----- ^^^^ | | | auto traits cannot have super traits or lifetime bounds ``` ``` error[E0380]: auto traits cannot have associated items --> $DIR/issue-23080.rs:5:8 | LL | unsafe auto trait Trait { | ----- auto traits cannot have associated items LL | fn method(&self) { | ^^^^^^ ``` --- compiler/rustc_ast_passes/messages.ftl | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 16 +++-- compiler/rustc_ast_passes/src/errors.rs | 9 +-- tests/ui/auto-traits/assoc-ty.current.stderr | 2 +- tests/ui/auto-traits/assoc-ty.next.stderr | 2 +- .../auto-traits/auto-trait-validation.fixed | 11 ++++ tests/ui/auto-traits/auto-trait-validation.rs | 15 +++++ .../auto-traits/auto-trait-validation.stderr | 65 ++++++++++++++++--- .../ui/auto-traits/bad-generics-on-dyn.stderr | 2 +- tests/ui/auto-traits/has-arguments.stderr | 2 +- tests/ui/auto-traits/issue-117789.stderr | 2 +- .../auto-traits/issue-23080-2.current.stderr | 2 +- .../ui/auto-traits/issue-23080-2.next.stderr | 2 +- tests/ui/auto-traits/issue-23080.stderr | 11 ++-- tests/ui/auto-traits/issue-84075.stderr | 2 +- .../typeck-auto-trait-no-supertraits-2.stderr | 6 +- .../typeck-auto-trait-no-supertraits.stderr | 4 +- tests/ui/methods/issues/issue-105732.stderr | 2 +- .../supertrait-auto-trait.stderr | 4 +- 19 files changed, 118 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index af93d55c89826..9b9c1105e47f2 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -40,7 +40,7 @@ ast_passes_auto_generic = auto traits cannot have generic parameters ast_passes_auto_items = auto traits cannot have associated items .label = {ast_passes_auto_items} - .suggestion = remove these associated items + .suggestion = remove the associated items ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetime bounds .label = {ast_passes_auto_super_lifetime} diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 895a457ec1d56..e99e5cc4ae65b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -699,19 +699,23 @@ impl<'a> AstValidator<'a> { } } - fn deny_super_traits(&self, bounds: &GenericBounds, ident_span: Span) { + fn deny_super_traits(&self, bounds: &GenericBounds, ident: Span) { if let [.., last] = &bounds[..] { - let span = ident_span.shrink_to_hi().to(last.span()); - self.dcx().emit_err(errors::AutoTraitBounds { span, ident: ident_span }); + let span = bounds.iter().map(|b| b.span()).collect(); + let removal = ident.shrink_to_hi().to(last.span()); + self.dcx().emit_err(errors::AutoTraitBounds { span, removal, ident }); } } - fn deny_where_clause(&self, where_clause: &WhereClause, ident_span: Span) { + fn deny_where_clause(&self, where_clause: &WhereClause, ident: Span) { if !where_clause.predicates.is_empty() { // FIXME: The current diagnostic is misleading since it only talks about // super trait and lifetime bounds while we should just say “bounds”. - self.dcx() - .emit_err(errors::AutoTraitBounds { span: where_clause.span, ident: ident_span }); + self.dcx().emit_err(errors::AutoTraitBounds { + span: vec![where_clause.span], + removal: where_clause.span, + ident, + }); } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index fd4b2528541e3..2e3b1510755f1 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -344,7 +344,7 @@ pub(crate) struct ModuleNonAscii { #[diag(ast_passes_auto_generic, code = E0567)] pub(crate) struct AutoTraitGeneric { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] pub span: Span, #[label] pub ident: Span, @@ -354,8 +354,9 @@ pub(crate) struct AutoTraitGeneric { #[diag(ast_passes_auto_super_lifetime, code = E0568)] pub(crate) struct AutoTraitBounds { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] - pub span: Span, + pub span: Vec, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub removal: Span, #[label] pub ident: Span, } @@ -365,7 +366,7 @@ pub(crate) struct AutoTraitBounds { pub(crate) struct AutoTraitItems { #[primary_span] pub spans: Vec, - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] pub total: Span, #[label] pub ident: Span, diff --git a/tests/ui/auto-traits/assoc-ty.current.stderr b/tests/ui/auto-traits/assoc-ty.current.stderr index 77a1c8fb654f1..d793ae6652675 100644 --- a/tests/ui/auto-traits/assoc-ty.current.stderr +++ b/tests/ui/auto-traits/assoc-ty.current.stderr @@ -5,7 +5,7 @@ LL | auto trait Trait { | ----- auto traits cannot have associated items LL | LL | type Output; - | -----^^^^^^- help: remove these associated items + | ^^^^^^ error[E0658]: auto traits are experimental and possibly buggy --> $DIR/assoc-ty.rs:8:1 diff --git a/tests/ui/auto-traits/assoc-ty.next.stderr b/tests/ui/auto-traits/assoc-ty.next.stderr index 4ce00d1747564..a41f7d9927858 100644 --- a/tests/ui/auto-traits/assoc-ty.next.stderr +++ b/tests/ui/auto-traits/assoc-ty.next.stderr @@ -5,7 +5,7 @@ LL | auto trait Trait { | ----- auto traits cannot have associated items LL | LL | type Output; - | -----^^^^^^- help: remove these associated items + | ^^^^^^ error[E0658]: auto traits are experimental and possibly buggy --> $DIR/assoc-ty.rs:8:1 diff --git a/tests/ui/auto-traits/auto-trait-validation.fixed b/tests/ui/auto-traits/auto-trait-validation.fixed index 8a445448c8581..b24dc1cb2c3d3 100644 --- a/tests/ui/auto-traits/auto-trait-validation.fixed +++ b/tests/ui/auto-traits/auto-trait-validation.fixed @@ -11,4 +11,15 @@ auto trait LifetimeBound {} //~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait MyTrait { } //~^ ERROR auto traits cannot have associated items [E0380] +auto trait AssocTy { } +//~^ ERROR auto traits cannot have associated items [E0380] +auto trait All { + //~^ ERROR auto traits cannot have generic parameters [E0567] + +} +// We can't test both generic params and super-traits because the suggestion span overlaps. +auto trait All2 { + //~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] + +} fn main() {} diff --git a/tests/ui/auto-traits/auto-trait-validation.rs b/tests/ui/auto-traits/auto-trait-validation.rs index b5e7505d86a24..9665e5bc3932f 100644 --- a/tests/ui/auto-traits/auto-trait-validation.rs +++ b/tests/ui/auto-traits/auto-trait-validation.rs @@ -11,4 +11,19 @@ auto trait LifetimeBound : 'static {} //~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait MyTrait { fn foo() {} } //~^ ERROR auto traits cannot have associated items [E0380] +auto trait AssocTy { type Bar; } +//~^ ERROR auto traits cannot have associated items [E0380] +auto trait All<'a, T> { + //~^ ERROR auto traits cannot have generic parameters [E0567] + type Bar; + //~^ ERROR auto traits cannot have associated items [E0380] + fn foo() {} +} +// We can't test both generic params and super-traits because the suggestion span overlaps. +auto trait All2: Copy + 'static { + //~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] + type Bar; + //~^ ERROR auto traits cannot have associated items [E0380] + fn foo() {} +} fn main() {} diff --git a/tests/ui/auto-traits/auto-trait-validation.stderr b/tests/ui/auto-traits/auto-trait-validation.stderr index a6e5ac54869d2..60757db6d1ed2 100644 --- a/tests/ui/auto-traits/auto-trait-validation.stderr +++ b/tests/ui/auto-traits/auto-trait-validation.stderr @@ -2,23 +2,23 @@ error[E0567]: auto traits cannot have generic parameters --> $DIR/auto-trait-validation.rs:6:19 | LL | auto trait Generic {} - | -------^^^ help: remove the parameters + | -------^^^ | | | auto trait cannot have generic parameters error[E0568]: auto traits cannot have super traits or lifetime bounds - --> $DIR/auto-trait-validation.rs:8:17 + --> $DIR/auto-trait-validation.rs:8:20 | LL | auto trait Bound : Copy {} - | -----^^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^ | | | auto traits cannot have super traits or lifetime bounds error[E0568]: auto traits cannot have super traits or lifetime bounds - --> $DIR/auto-trait-validation.rs:10:25 + --> $DIR/auto-trait-validation.rs:10:28 | LL | auto trait LifetimeBound : 'static {} - | -------------^^^^^^^^^^ help: remove the super traits or lifetime bounds + | ------------- ^^^^^^^ | | | auto traits cannot have super traits or lifetime bounds @@ -26,12 +26,59 @@ error[E0380]: auto traits cannot have associated items --> $DIR/auto-trait-validation.rs:12:25 | LL | auto trait MyTrait { fn foo() {} } - | ------- ---^^^----- - | | | - | | help: remove these associated items + | ------- ^^^ + | | + | auto traits cannot have associated items + +error[E0380]: auto traits cannot have associated items + --> $DIR/auto-trait-validation.rs:14:27 + | +LL | auto trait AssocTy { type Bar; } + | ------- ^^^ + | | | auto traits cannot have associated items -error: aborting due to 4 previous errors +error[E0567]: auto traits cannot have generic parameters + --> $DIR/auto-trait-validation.rs:16:15 + | +LL | auto trait All<'a, T> { + | ---^^^^^^^ + | | + | auto trait cannot have generic parameters + +error[E0380]: auto traits cannot have associated items + --> $DIR/auto-trait-validation.rs:18:10 + | +LL | auto trait All<'a, T> { + | --- auto traits cannot have associated items +LL | +LL | type Bar; + | ^^^ +LL | +LL | fn foo() {} + | ^^^ + +error[E0568]: auto traits cannot have super traits or lifetime bounds + --> $DIR/auto-trait-validation.rs:23:18 + | +LL | auto trait All2: Copy + 'static { + | ---- ^^^^ ^^^^^^^ + | | + | auto traits cannot have super traits or lifetime bounds + +error[E0380]: auto traits cannot have associated items + --> $DIR/auto-trait-validation.rs:25:10 + | +LL | auto trait All2: Copy + 'static { + | ---- auto traits cannot have associated items +LL | +LL | type Bar; + | ^^^ +LL | +LL | fn foo() {} + | ^^^ + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0380, E0567, E0568. For more information about an error, try `rustc --explain E0380`. diff --git a/tests/ui/auto-traits/bad-generics-on-dyn.stderr b/tests/ui/auto-traits/bad-generics-on-dyn.stderr index 06c7cbcd76da6..a6977c2037e7f 100644 --- a/tests/ui/auto-traits/bad-generics-on-dyn.stderr +++ b/tests/ui/auto-traits/bad-generics-on-dyn.stderr @@ -2,7 +2,7 @@ error[E0567]: auto traits cannot have generic parameters --> $DIR/bad-generics-on-dyn.rs:3:18 | LL | auto trait Trait1<'a> {} - | ------^^^^ help: remove the parameters + | ------^^^^ | | | auto trait cannot have generic parameters diff --git a/tests/ui/auto-traits/has-arguments.stderr b/tests/ui/auto-traits/has-arguments.stderr index b8a680e6a5cad..5228b6d2d1ac8 100644 --- a/tests/ui/auto-traits/has-arguments.stderr +++ b/tests/ui/auto-traits/has-arguments.stderr @@ -2,7 +2,7 @@ error[E0567]: auto traits cannot have generic parameters --> $DIR/has-arguments.rs:3:18 | LL | auto trait Trait1<'outer> {} - | ------^^^^^^^^ help: remove the parameters + | ------^^^^^^^^ | | | auto trait cannot have generic parameters diff --git a/tests/ui/auto-traits/issue-117789.stderr b/tests/ui/auto-traits/issue-117789.stderr index 99efb21341758..1e047c7d2e07b 100644 --- a/tests/ui/auto-traits/issue-117789.stderr +++ b/tests/ui/auto-traits/issue-117789.stderr @@ -2,7 +2,7 @@ error[E0567]: auto traits cannot have generic parameters --> $DIR/issue-117789.rs:1:17 | LL | auto trait Trait

{} - | -----^^^ help: remove the parameters + | -----^^^ | | | auto trait cannot have generic parameters diff --git a/tests/ui/auto-traits/issue-23080-2.current.stderr b/tests/ui/auto-traits/issue-23080-2.current.stderr index 62c7b37041fea..3af97f57d484a 100644 --- a/tests/ui/auto-traits/issue-23080-2.current.stderr +++ b/tests/ui/auto-traits/issue-23080-2.current.stderr @@ -4,7 +4,7 @@ error[E0380]: auto traits cannot have associated items LL | unsafe auto trait Trait { | ----- auto traits cannot have associated items LL | type Output; - | -----^^^^^^- help: remove these associated items + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/auto-traits/issue-23080-2.next.stderr b/tests/ui/auto-traits/issue-23080-2.next.stderr index 62c7b37041fea..3af97f57d484a 100644 --- a/tests/ui/auto-traits/issue-23080-2.next.stderr +++ b/tests/ui/auto-traits/issue-23080-2.next.stderr @@ -4,7 +4,7 @@ error[E0380]: auto traits cannot have associated items LL | unsafe auto trait Trait { | ----- auto traits cannot have associated items LL | type Output; - | -----^^^^^^- help: remove these associated items + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/auto-traits/issue-23080.stderr b/tests/ui/auto-traits/issue-23080.stderr index 5cea45060c824..02a7551910291 100644 --- a/tests/ui/auto-traits/issue-23080.stderr +++ b/tests/ui/auto-traits/issue-23080.stderr @@ -1,13 +1,10 @@ error[E0380]: auto traits cannot have associated items --> $DIR/issue-23080.rs:5:8 | -LL | unsafe auto trait Trait { - | ----- auto traits cannot have associated items -LL | fn method(&self) { - | _____- ^^^^^^ -LL | | println!("Hello"); -LL | | } - | |_____- help: remove these associated items +LL | unsafe auto trait Trait { + | ----- auto traits cannot have associated items +LL | fn method(&self) { + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/auto-traits/issue-84075.stderr b/tests/ui/auto-traits/issue-84075.stderr index 943d521ce9e27..4edf2ecdf06d3 100644 --- a/tests/ui/auto-traits/issue-84075.stderr +++ b/tests/ui/auto-traits/issue-84075.stderr @@ -2,7 +2,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds --> $DIR/issue-84075.rs:5:18 | LL | auto trait Magic where Self: Copy {} - | ----- ^^^^^^^^^^^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^^^^^^^^^^^^^ | | | auto traits cannot have super traits or lifetime bounds diff --git a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr index 27e38ce06a435..bc17fefc944da 100644 --- a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr +++ b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr @@ -1,8 +1,8 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds - --> $DIR/typeck-auto-trait-no-supertraits-2.rs:4:17 + --> $DIR/typeck-auto-trait-no-supertraits-2.rs:4:20 | LL | auto trait Magic : Sized where Option : Magic {} - | -----^^^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^^ | | | auto traits cannot have super traits or lifetime bounds @@ -10,7 +10,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds --> $DIR/typeck-auto-trait-no-supertraits-2.rs:4:26 | LL | auto trait Magic : Sized where Option : Magic {} - | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | auto traits cannot have super traits or lifetime bounds diff --git a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr index 23aae13639c77..bc9791a5799c8 100644 --- a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr +++ b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr @@ -1,8 +1,8 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds - --> $DIR/typeck-auto-trait-no-supertraits.rs:28:17 + --> $DIR/typeck-auto-trait-no-supertraits.rs:28:19 | LL | auto trait Magic: Copy {} - | -----^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^ | | | auto traits cannot have super traits or lifetime bounds diff --git a/tests/ui/methods/issues/issue-105732.stderr b/tests/ui/methods/issues/issue-105732.stderr index 6244f983550ee..93ce695f27b84 100644 --- a/tests/ui/methods/issues/issue-105732.stderr +++ b/tests/ui/methods/issues/issue-105732.stderr @@ -4,7 +4,7 @@ error[E0380]: auto traits cannot have associated items LL | auto trait Foo { | --- auto traits cannot have associated items LL | fn g(&self); - | ---^-------- help: remove these associated items + | ^ error[E0599]: no method named `g` found for reference `&Self` in the current scope --> $DIR/issue-105732.rs:10:14 diff --git a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr index 3a3b99f6c5b10..45602d676b31f 100644 --- a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr +++ b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr @@ -1,8 +1,8 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds - --> $DIR/supertrait-auto-trait.rs:8:17 + --> $DIR/supertrait-auto-trait.rs:8:19 | LL | auto trait Magic: Copy {} - | -----^^^^^^ help: remove the super traits or lifetime bounds + | ----- ^^^^ | | | auto traits cannot have super traits or lifetime bounds