about summary refs log tree commit diff
diff options
context:
space:
mode:
authortiif <pekyuan@gmail.com>2025-07-14 13:30:36 +0000
committertiif <pekyuan@gmail.com>2025-07-15 13:48:29 +0000
commitfecd99881d545d6096d5489419f096cffe53b171 (patch)
treecc19b27e244877becc4dea7d9969843e64c64baf
parenta9fb6103b05c6ad6eee6bed4c0bb5a2e8e1024c6 (diff)
downloadrust-fecd99881d545d6096d5489419f096cffe53b171.tar.gz
rust-fecd99881d545d6096d5489419f096cffe53b171.zip
Setup unstable feature bound attribute
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs3
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs20
-rw-r--r--compiler/rustc_passes/messages.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs44
-rw-r--r--compiler/rustc_passes/src/errors.rs9
-rw-r--r--tests/ui/unstable-feature-bound/unstable_inherent_method.rs23
-rw-r--r--tests/ui/unstable-feature-bound/unstable_inherent_method.stderr20
7 files changed, 123 insertions, 0 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 995ac514d8d..5f38be346ed 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -417,6 +417,9 @@ pub enum AttributeKind {
     /// Represents `#[rustc_unsafe_specialization_marker]`.
     UnsafeSpecializationMarker(Span),
 
+    /// Represents `#[unstable_feature_bound]`.
+    UnstableFeatureBound(ThinVec<(Symbol, Span)>),
+
     /// Represents `#[used]`
     Used { used_by: UsedBy, span: Span },
     // tidy-alphabetical-end
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 1c51c3eee4e..a6bd2306ec5 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
     }
 }
 
+pub(crate) struct UnstableFeatureBoundParser;
+impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
+    type Item = (Symbol, Span);
+    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
+    const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
+
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> {
+        if !cx.features().staged_api() {
+            cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
+        }
+        parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
+            .into_iter()
+            .zip(iter::repeat(cx.attr_span))
+    }
+}
+
 pub(crate) struct AllowConstFnUnstableParser;
 impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
     const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 3418d997b83..500b4279588 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -667,6 +667,10 @@ passes_rustc_std_internal_symbol =
     attribute should be applied to functions or statics
     .label = not a function or static
 
+passes_rustc_unstable_feature_bound =
+    attribute should be applied to `impl` or free function outside of any `impl` or trait
+    .label = not an `impl` or free function
+
 passes_should_be_applied_to_fn =
     attribute should be applied to a function definition
     .label = {$on_crate ->
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 820255afe27..89000ab2619 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -247,6 +247,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 &Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => {
                     self.check_ffi_pure(attr_span, attrs, target)
                 }
+                Attribute::Parsed(AttributeKind::UnstableFeatureBound(syms)) => {
+                    self.check_unstable_feature_bound(syms.first().unwrap().1, span, target)
+                }
                 Attribute::Parsed(
                     AttributeKind::BodyStability { .. }
                     | AttributeKind::ConstStabilityIndirect
@@ -2267,6 +2270,47 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) {
+        match target {
+            // FIXME(staged_api): There's no reason we can't support more targets here. We're just
+            // being conservative to begin with.
+            Target::Fn | Target::Impl => {}
+            Target::ExternCrate
+            | Target::Use
+            | Target::Static
+            | Target::Const
+            | Target::Closure
+            | Target::Mod
+            | Target::ForeignMod
+            | Target::GlobalAsm
+            | Target::TyAlias
+            | Target::Enum
+            | Target::Variant
+            | Target::Struct
+            | Target::Field
+            | Target::Union
+            | Target::Trait
+            | Target::TraitAlias
+            | Target::Expression
+            | Target::Statement
+            | Target::Arm
+            | Target::AssocConst
+            | Target::Method(_)
+            | Target::AssocTy
+            | Target::ForeignFn
+            | Target::ForeignStatic
+            | Target::ForeignTy
+            | Target::GenericParam(_)
+            | Target::MacroDef
+            | Target::Param
+            | Target::PatField
+            | Target::ExprField
+            | Target::WherePredicate => {
+                self.tcx.dcx().emit_err(errors::RustcUnstableFeatureBound { attr_span, span });
+            }
+        }
+    }
+
     fn check_rustc_std_internal_symbol(&self, attr_span: Span, span: Span, target: Target) {
         match target {
             Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {}
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 093a2b38804..37330c0ed6e 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -687,6 +687,15 @@ pub(crate) struct RustcAllowConstFnUnstable {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_rustc_unstable_feature_bound)]
+pub(crate) struct RustcUnstableFeatureBound {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_rustc_std_internal_symbol)]
 pub(crate) struct RustcStdInternalSymbol {
     #[primary_span]
diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.rs b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs
new file mode 100644
index 00000000000..5f3095430a8
--- /dev/null
+++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs
@@ -0,0 +1,23 @@
+#![allow(internal_features)]
+#![feature(staged_api)]
+#![stable(feature = "a", since = "1.1.1" )]
+
+/// FIXME(tiif): we haven't allowed marking trait and impl method as
+/// unstable yet, but it should be possible.
+
+#[stable(feature = "a", since = "1.1.1" )]
+pub trait Trait {
+    #[unstable(feature = "feat", issue = "none" )]
+    #[unstable_feature_bound(foo)]
+    //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait
+    fn foo();
+}
+
+#[stable(feature = "a", since = "1.1.1" )]
+impl Trait for u8 {
+    #[unstable_feature_bound(foo)]
+    //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait
+    fn foo() {}
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr
new file mode 100644
index 00000000000..fa1c39db259
--- /dev/null
+++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr
@@ -0,0 +1,20 @@
+error: attribute should be applied to `impl` or free function outside of any `impl` or trait
+  --> $DIR/unstable_inherent_method.rs:11:5
+   |
+LL |     #[unstable_feature_bound(foo)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     fn foo();
+   |     --------- not an `impl` or free function
+
+error: attribute should be applied to `impl` or free function outside of any `impl` or trait
+  --> $DIR/unstable_inherent_method.rs:18:5
+   |
+LL |     #[unstable_feature_bound(foo)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     fn foo() {}
+   |     ----------- not an `impl` or free function
+
+error: aborting due to 2 previous errors
+