From 54b0b685124b77ec4bf4f70a8ac57ace5fff6949 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 08:53:29 +0000 Subject: [PATCH 01/21] Setup feature gate and attribute --- .../rustc_attr_data_structures/src/attributes.rs | 3 ++- .../src/attributes/allow_unstable.rs | 14 ++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 ++++- compiler/rustc_feature/src/builtin_attrs.rs | 5 +++++ compiler/rustc_feature/src/unstable.rs | 2 ++ .../feature-gates/feature-gate-impl-stability.rs | 11 +++++++++++ .../feature-gate-impl-stability.stderr | 12 ++++++++++++ 7 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-impl-stability.rs create mode 100644 tests/ui/feature-gates/feature-gate-impl-stability.stderr diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 845e4d5e5d0fa..9a22dfd30ea23 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -157,7 +157,8 @@ pub enum AttributeKind { /// Represents `#[allow_internal_unstable]`. AllowInternalUnstable(ThinVec<(Symbol, Span)>), - + /// Represents `#[unstable_feature_bound]`. + AllowUnstableFeature(ThinVec<(Symbol, Span)>), /// Represents `#[rustc_default_body_unstable]`. BodyStability { stability: DefaultBodyStability, diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 81192f902a232..24b9b57c6cdf1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -24,6 +24,20 @@ impl CombineAttributeParser for AllowInternalUnstableParser { } } +pub(crate) struct AllowUnstableFeatureParser; +impl CombineAttributeParser for AllowUnstableFeatureParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; + type Item = (Symbol, Span); + const CONVERT: ConvertFn = AttributeKind::AllowUnstableFeature; + + fn extend<'a>( + cx: &'a AcceptContext<'a>, + args: &'a ArgParser<'a>, + ) -> impl IntoIterator + 'a { + parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span)) + } +} + pub(crate) struct AllowConstFnUnstableParser; impl CombineAttributeParser for AllowConstFnUnstableParser { const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 47f72232828ee..2b0544c0fef59 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,7 +15,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; -use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; +use crate::attributes::allow_unstable::{ + AllowConstFnUnstableParser, AllowInternalUnstableParser, AllowUnstableFeatureParser, +}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::repr::ReprParser; @@ -98,6 +100,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, Combine, // tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 73a21789c5d25..e912ffc0d1938 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -670,6 +670,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ DuplicatesOk, EncodeCrossCrate::Yes, "allow_internal_unstable side-steps feature gating and stability checks", ), + gated!( + unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."), + DuplicatesOk, EncodeCrossCrate::No, impl_stability, + "used internally to mark impl as unstable", + ), gated!( allow_internal_unsafe, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 594021d78d2e4..a094e26b7f710 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -213,6 +213,8 @@ declare_features! ( (internal, custom_mir, "1.65.0", None), /// Outputs useful `assert!` messages (unstable, generic_assert, "1.63.0", None), + /// Allow declaring an impl as unstable. + (internal, impl_stability, "CURRENT_RUSTC_VERSION", None), /// Allows using the #[rustc_intrinsic] attribute. (internal, intrinsics, "1.0.0", None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. diff --git a/tests/ui/feature-gates/feature-gate-impl-stability.rs b/tests/ui/feature-gates/feature-gate-impl-stability.rs new file mode 100644 index 0000000000000..5f3ff5d385ae7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-impl-stability.rs @@ -0,0 +1,11 @@ +/// Test for impl_stability gate. + +trait Foo{} +struct Bar; + +#[unstable_feature_bound(feat_foo)] +//~^ ERROR: used internally to mark impl as unstable +impl Foo for Bar{} + +fn main() { +} diff --git a/tests/ui/feature-gates/feature-gate-impl-stability.stderr b/tests/ui/feature-gates/feature-gate-impl-stability.stderr new file mode 100644 index 0000000000000..62edbc7c07854 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-impl-stability.stderr @@ -0,0 +1,12 @@ +error[E0658]: used internally to mark impl as unstable + --> $DIR/feature-gate-impl-stability.rs:6:1 + | +LL | #[unstable_feature_bound(feat_foo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(impl_stability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From fc9a0e6c8b8dc5eb83f8ad1b05b2b05013aed16a Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 08:57:28 +0000 Subject: [PATCH 02/21] Lower the attribute to UnstableFeature predicate --- .../rustc_hir_analysis/src/collect/predicates_of.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ce0f83d0ec288..6be49789d379d 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,6 +1,7 @@ use std::assert_matches::assert_matches; use hir::Node; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -318,6 +319,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates)); } + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + let allow_unstable_feature_attr = + find_attr!(attrs, AttributeKind::AllowUnstableFeature(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + + for (feat_name, span) in allow_unstable_feature_attr { + predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span)); + } + let mut predicates: Vec<_> = predicates.into_iter().collect(); // Subtle: before we store the predicates into the tcx, we @@ -747,6 +758,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::ConstEvaluatable(_) => { bug!( "unexpected non-`Self` predicate when computing \ @@ -774,6 +786,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => { bug!( "unexpected non-`Self` predicate when computing \ From 1ca27ccd9af37efaf5b419603f477da85f5b0014 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 08:58:31 +0000 Subject: [PATCH 03/21] Add the core logic in old solver and new solver --- .../rustc_next_trait_solver/src/solve/mod.rs | 34 +++++++++++++++++++ .../src/traits/fulfill.rs | 28 +++++++++++++++ .../src/traits/select/mod.rs | 26 ++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index e4e0aba7b5022..74d6f0d032b39 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -148,6 +148,40 @@ where } } + fn compute_unstable_feature_goal( + &mut self, + param_env: ::ParamEnv, + symbol: ::Symbol, + ) -> QueryResult { + // Iterate through all goals in param_env to find the one that has the same symbol. + for pred in param_env.caller_bounds().iter() { + if let ty::ClauseKind::UnstableFeature(sym) = pred.kind().skip_binder() { + if sym == symbol { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + } + } + + if self.cx().features().impl_stability() { + // If we are in std/core, and the feature is not enabled through #[unstable_feature_bound(..)] + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Ambiguity, + )); + } else { + // Outside of std/core, check if feature is enabled at crate level with #[feature(..)] + // or if we are currently in codegen. + if self.cx().features().enabled(symbol) + || (self.typing_mode() == TypingMode::PostAnalysis) + { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Ambiguity, + )); + } + } + } + #[instrument(level = "trace", skip(self))] fn compute_const_evaluatable_goal( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 951dfb879aed7..ddc737e2eaf98 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -404,6 +404,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::AliasRelate(..) => { bug!("AliasRelate is only used by the new solver") } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => { + unreachable!("unexpected higher ranked `UnstableFeature` goal") + } }, Some(pred) => match pred { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { @@ -761,6 +764,31 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + // Iterate through all goals in param_env to find the one that has the same symbol. + for pred in obligation.param_env.caller_bounds().iter() { + if let ty::ClauseKind::UnstableFeature(sym) = pred.kind().skip_binder() { + if sym == symbol { + return ProcessResult::Changed(Default::default()); + } + } + } + + if self.selcx.tcx().features().impl_stability() { + // If we are in std/core, and the feature is not enabled through #[unstable_feature_bound(..)]. + return ProcessResult::Unchanged; + } else { + // Outside of std/core, check if feature is enabled at crate level with #[feature(..)] + // or if we are currently in codegen. + if self.selcx.tcx().features().enabled(symbol) + || (self.selcx.infcx.typing_mode() == TypingMode::PostAnalysis) + { + return ProcessResult::Changed(Default::default()); + } else { + return ProcessResult::Unchanged; + } + } + } }, } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3a2f9e8ca179f..2aaed4892bbfb 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -838,6 +838,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + // Iterate through all goals in param_env to find the one that has the same symbol. + for pred in obligation.param_env.caller_bounds().iter() { + if let ty::ClauseKind::UnstableFeature(sym) = pred.kind().skip_binder() { + if sym == symbol { + return Ok(EvaluatedToOk); + } + } + } + + if self.tcx().features().impl_stability() { + // If we are in std/core, and the feature is not enabled through #[unstable_feature_bound(..)]. + return Ok(EvaluatedToAmbig); + } else { + // Outside of std/core, check if feature is enabled at crate level with #[feature(..)] + // or if we are currently in codegen. + if self.tcx().features().enabled(symbol) + || (self.infcx.typing_mode() == TypingMode::PostAnalysis) + { + return Ok(EvaluatedToOk); + } else { + return Ok(EvaluatedToAmbig); + } + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, From a7c61c0f5a4cc2103c68961bc777dc4f75605793 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 09:00:36 +0000 Subject: [PATCH 04/21] Other logics --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 7 +++++-- .../src/impl_wf_check/min_specialization.rs | 1 + compiler/rustc_hir_analysis/src/outlives/explicit.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs | 1 + compiler/rustc_hir_typeck/src/method/probe.rs | 1 + compiler/rustc_lint/src/builtin.rs | 3 ++- compiler/rustc_middle/src/ty/context.rs | 9 +++++++++ compiler/rustc_middle/src/ty/predicate.rs | 3 +++ compiler/rustc_middle/src/ty/print/pretty.rs | 1 + .../rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs | 3 +++ compiler/rustc_privacy/src/lib.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/ty.rs | 3 +++ compiler/rustc_span/src/symbol.rs | 2 ++ .../src/error_reporting/traits/fulfillment_errors.rs | 2 ++ compiler/rustc_trait_selection/src/traits/auto_trait.rs | 1 + .../src/traits/dyn_compatibility.rs | 2 ++ .../src/traits/query/type_op/implied_outlives_bounds.rs | 1 + compiler/rustc_trait_selection/src/traits/util.rs | 1 + compiler/rustc_trait_selection/src/traits/wf.rs | 2 ++ compiler/rustc_traits/src/normalize_erasing_regions.rs | 1 + compiler/rustc_type_ir/src/elaborate.rs | 3 +++ compiler/rustc_type_ir/src/flags.rs | 3 ++- compiler/rustc_type_ir/src/inherent.rs | 4 ++++ compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/predicate_kind.rs | 6 ++++++ src/librustdoc/clean/mod.rs | 1 + 26 files changed, 60 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b8dc01cbc03cf..9ebdbfad083d8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2368,9 +2368,12 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { // We lower empty bounds like `Vec:` as // `WellFormed(Vec)`, which will later get checked by // regular WF checking - if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() { - continue; + + match pred.kind().skip_binder() { + ty::ClauseKind::WellFormed(..) | ty::ClauseKind::UnstableFeature(..) => continue, + _ => {} } + // Match the existing behavior. if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) { let pred = self.normalize(span, None, pred); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 309221f9a127a..1bb6b1c475e33 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -499,6 +499,7 @@ fn trait_specialization_kind<'tcx>( | ty::ClauseKind::ConstArgHasType(..) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(..) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => None, } } diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 2c1d443f9512f..d3a57a4d8e5d3 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => {} } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index e4c62bf027bd3..367e2b6b372ae 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -54,6 +54,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::Ambiguous => false, } } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a3fdf200c8ea0..7f12678eb8a6b 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -926,6 +926,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => None, } }); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index dedea54f8e081..419be690d5f69 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1521,8 +1521,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { ClauseKind::TypeOutlives(..) | ClauseKind::RegionOutlives(..) => "lifetime", + ClauseKind::UnstableFeature(_) // `ConstArgHasType` is never global as `ct` is always a param - ClauseKind::ConstArgHasType(..) + | ClauseKind::ConstArgHasType(..) // Ignore projections, as they can only be global // if the trait bound is global | ClauseKind::Projection(..) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6a47000fc8529..e81111263f219 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -136,6 +136,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type FnInputTys = &'tcx [Ty<'tcx>]; type ParamTy = ParamTy; type BoundTy = ty::BoundTy; + type Symbol = Symbol; type PlaceholderTy = ty::PlaceholderType; type ErrorGuaranteed = ErrorGuaranteed; @@ -831,6 +832,14 @@ impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_featu fn associated_const_equality(self) -> bool { self.associated_const_equality() } + + fn impl_stability(self) -> bool { + self.impl_stability() + } + + fn enabled(self, symbol: as Interner>::Symbol) -> bool { + self.enabled(symbol) + } } impl<'tcx> rustc_type_ir::inherent::Span> for Span { diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index bc2ac42b6b1f8..ec2224877a8a1 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -131,6 +131,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) | PredicateKind::Clause(ClauseKind::Projection(_)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::DynCompatible(_) | PredicateKind::Subtype(_) | PredicateKind::Coerce(_) @@ -649,6 +650,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(ClauseKind::Projection(..)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) @@ -670,6 +672,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(ClauseKind::Trait(..)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 673a89a8134f3..ec4080eb9e89f 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3250,6 +3250,7 @@ define_print! { ty::ClauseKind::ConstEvaluatable(ct) => { p!("the constant `", print(ct), "` can be evaluated") } + ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 345ece20b7e53..3747ecad1613e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -607,6 +607,9 @@ where ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + self.compute_unstable_feature_goal(param_env, symbol) + } ty::PredicateKind::Subtype(predicate) => { self.compute_subtype_goal(Goal { param_env, predicate }) } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 963f4c77d809d..b2983c94d3b55 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -156,6 +156,7 @@ where } ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self), ty::ClauseKind::WellFormed(term) => term.visit_with(self), + ty::ClauseKind::UnstableFeature(_) => V::Result::output(), } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index b0c9dba78a659..d4dffe9608c0a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -648,6 +648,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { ClauseKind::HostEffect(..) => { todo!() } + ClauseKind::UnstableFeature(_) => { + todo!() + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3429d28e1d5c2..63c6359690223 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1175,6 +1175,7 @@ symbols! { ignore, impl_header_lifetime_elision, impl_lint_pass, + impl_stability, impl_trait_in_assoc_type, impl_trait_in_bindings, impl_trait_in_fn_trait_return, @@ -2266,6 +2267,7 @@ symbols! { unsized_locals, unsized_tuple_coercion, unstable, + unstable_feature_bound, unstable_location_reason_default: "this crate is being loaded from the sysroot, an \ unstable location; did you mean to load this crate \ from crates.io via `Cargo.toml` instead?", diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 0c88bd3dcbc54..749c34b985cf1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ty::PredicateKind::ConstEquate { .. } // Ambiguous predicates should never error | ty::PredicateKind::Ambiguous + // We never return Err when proving UnstableFeature goal. + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature{ .. }) | ty::PredicateKind::NormalizesTo { .. } | ty::PredicateKind::AliasRelate { .. } | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType { .. }) => { diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 3ae908ec16b8e..759db1d18c013 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -800,6 +800,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {} ty::PredicateKind::Ambiguous => return false, }; diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 220a847cc230f..d5941a7937da4 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -238,6 +238,7 @@ fn predicate_references_self<'tcx>( // FIXME(generic_const_exprs): this can mention `Self` | ty::ClauseKind::ConstEvaluatable(..) | ty::ClauseKind::HostEffect(..) + | ty::ClauseKind::UnstableFeature(_) => None, } } @@ -278,6 +279,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => false, }) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e294f7839aac7..7540cbe3fd1a4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -110,6 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 035fd38c48aad..4a693040b3a9b 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -76,6 +76,7 @@ pub fn expand_trait_aliases<'tcx>( | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => {} } } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 416865e861ea8..ae46a2a864676 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -197,6 +197,7 @@ pub fn clause_obligations<'tcx>( ty::ClauseKind::ConstEvaluatable(ct) => { wf.add_wf_preds_for_term(ct.into()); } + ty::ClauseKind::UnstableFeature(_) => {} } wf.normalize(infcx) @@ -1087,6 +1088,7 @@ pub fn object_region_bounds<'tcx>( | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::ConstEvaluatable(_) => None, } }) diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 68ff66bbce7cf..c1b848a2e79da 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -57,6 +57,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index b11bcff1d8bbb..413d0d3fe5279 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -200,6 +200,9 @@ impl> Elaborator { ty::ClauseKind::ConstArgHasType(..) => { // Nothing to elaborate } + ty::ClauseKind::UnstableFeature(_) => { + // Nothing to elaborate + } } } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 37cc2baa402a0..9208f846d0968 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -401,7 +401,6 @@ impl FlagComputation { self.add_const(expected); self.add_const(found); } - ty::PredicateKind::Ambiguous => {} ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => { self.add_alias_term(alias); self.add_term(term); @@ -410,6 +409,8 @@ impl FlagComputation { self.add_term(t1); self.add_term(t2); } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) + | ty::PredicateKind::Ambiguous => {} } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index fa88bcb891a9c..d1a5522de8db7 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -583,6 +583,10 @@ pub trait Features: Copy { fn coroutine_clone(self) -> bool; fn associated_const_equality(self) -> bool; + + fn impl_stability(self) -> bool; + + fn enabled(self, symbol: I::Symbol) -> bool; } pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cc0925b2c32a4..fc792b265fc10 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -106,6 +106,7 @@ pub trait Interner: type ParamTy: Copy + Debug + Hash + Eq + ParamLike; type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike; type PlaceholderTy: PlaceholderLike; + type Symbol: Copy + Hash + PartialEq + Eq + TypeFoldable + TypeVisitable; // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 4e41fd16ffd76..f97b0142733cf 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -46,6 +46,9 @@ pub enum ClauseKind { /// corresponding trait clause; this just enforces the *constness* of that /// implementation. HostEffect(ty::HostEffectPredicate), + + /// Support marking impl as unstable. + UnstableFeature(I::Symbol), } #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] @@ -134,6 +137,9 @@ impl fmt::Debug for ClauseKind { ClauseKind::ConstEvaluatable(ct) => { write!(f, "ConstEvaluatable({ct:?})") } + ClauseKind::UnstableFeature(feature_name) => { + write!(f, "UnstableFeature({feature_name:?})") + } } } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index db4bcdaeb6c1a..f4e98147ca9d0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -395,6 +395,7 @@ pub(crate) fn clean_predicate<'tcx>( ty::ClauseKind::ConstEvaluatable(..) | ty::ClauseKind::WellFormed(..) | ty::ClauseKind::ConstArgHasType(..) + | ty::ClauseKind::UnstableFeature(..) // FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `~const`. | ty::ClauseKind::HostEffect(_) => None, } From 3b5713c80fba29b5a80e68365e12a0618b1837ff Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 09:02:31 +0000 Subject: [PATCH 05/21] Make stability attribute not to error if unstable_feature_bound is in effect --- compiler/rustc_passes/src/stability.rs | 32 +++++++++++++++-- .../unstable-feature-bound-no-effect.rs | 36 +++++++++++++++++++ .../unstable-feature-bound-no-effect.stderr | 22 ++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/ui/internal/unstable-feature-bound-no-effect.rs create mode 100644 tests/ui/internal/unstable-feature-bound-no-effect.stderr diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 56d9f5bf78577..cae330499b90b 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -802,12 +802,28 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); + let unstable_feature_stab = + find_attr!(attrs, AttributeKind::AllowUnstableFeature(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 + // + // The exception is when there are both #[unstable_feature_bound(..)] and + // #![unstable(feature = "..", issue = "..")] that have the same symbol because + // that can effectively mark an impl as unstable. + // + // For example: + // ``` + // #[unstable_feature_bound(feat_foo)] + // #![unstable(feature = "feat_foo", issue = "none")] + // impl Foo for Bar {} + // ``` if let Some(( - Stability { level: attrs::StabilityLevel::Unstable { .. }, .. }, + Stability { level: attrs::StabilityLevel::Unstable { .. }, feature }, span, )) = stab { @@ -815,9 +831,21 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { c.visit_ty_unambig(self_ty); c.visit_trait_ref(t); + // Skip the lint if the impl is marked as unstable using + // #[unstable_feature_bound(..)] + let mut unstable_feature_bound_in_effect = false; + for (unstable_bound_feat_name, _) in unstable_feature_stab { + if *unstable_bound_feat_name == feature { + unstable_feature_bound_in_effect = true; + } + } + // do not lint when the trait isn't resolved, since resolution error should // be fixed first - if t.path.res != Res::Err && c.fully_stable { + if t.path.res != Res::Err + && c.fully_stable + && !unstable_feature_bound_in_effect + { self.tcx.emit_node_span_lint( INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id(), diff --git a/tests/ui/internal/unstable-feature-bound-no-effect.rs b/tests/ui/internal/unstable-feature-bound-no-effect.rs new file mode 100644 index 0000000000000..6898863f73934 --- /dev/null +++ b/tests/ui/internal/unstable-feature-bound-no-effect.rs @@ -0,0 +1,36 @@ +#![allow(internal_features)] +//~^ ERROR: module has missing stability attribute +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] + +/// If #[unstable(..)] and #[unstable_feature_name(..)] have the same feature name, +/// the error should not be thrown as it can effectively mark an impl as unstable. +/// +/// If the feature name in #[feature] does not exist in #[unstable_feature_bound(..)] +/// an error should still be thrown because that feature will not be unstable. + +#[stable(feature = "a", since = "1.1.1" )] +trait Moo {} +#[stable(feature = "a", since = "1.1.1" )] +trait Foo {} +#[stable(feature = "a", since = "1.1.1" )] +trait Boo {} +#[stable(feature = "a", since = "1.1.1" )] +pub struct Bar; + + +#[unstable(feature = "feat_moo", issue = "none" )] +#[unstable_feature_bound(feat_foo)] //~^ ERROR: an `#[unstable]` annotation here has no effect +impl Moo for Bar {} + +#[unstable(feature = "feat_foo", issue = "none" )] +#[unstable_feature_bound(feat_foo)] +impl Foo for Bar {} + + +#[unstable(feature = "feat_foo", issue = "none" )] +#[unstable_feature_bound(feat_foo, feat_bar)] +impl Boo for Bar {} + +fn main() {} diff --git a/tests/ui/internal/unstable-feature-bound-no-effect.stderr b/tests/ui/internal/unstable-feature-bound-no-effect.stderr new file mode 100644 index 0000000000000..4e7164024eb4d --- /dev/null +++ b/tests/ui/internal/unstable-feature-bound-no-effect.stderr @@ -0,0 +1,22 @@ +error: an `#[unstable]` annotation here has no effect + --> $DIR/unstable-feature-bound-no-effect.rs:23:1 + | +LL | #[unstable(feature = "feat_moo", issue = "none" )] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #55436 for more information + = note: `#[deny(ineffective_unstable_trait_impl)]` on by default + +error: module has missing stability attribute + --> $DIR/unstable-feature-bound-no-effect.rs:1:1 + | +LL | / #![allow(internal_features)] +LL | | +LL | | #![feature(staged_api)] +LL | | #![feature(impl_stability)] +... | +LL | | fn main() {} + | |____________^ + +error: aborting due to 2 previous errors + From 0da5266b5ccd3ac7c60d55f15120607d8ab6bac6 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 9 Jun 2025 09:03:03 +0000 Subject: [PATCH 06/21] Tests --- .../ui/internal/auxiliary/unstable_feature.rs | 30 +++++++++++++ .../unstable-feature-bound-two-error.rs | 12 ++++++ .../unstable-feature-bound-two-error.stderr | 11 +++++ ...ature-cross-crate-exact-symbol.fail.stderr | 11 +++++ ...stable-feature-cross-crate-exact-symbol.rs | 18 ++++++++ ...ble-feature-cross-crate-multiple-symbol.rs | 11 +++++ ...ture-cross-crate-require-bound.fail.stderr | 11 +++++ ...table-feature-cross-crate-require-bound.rs | 14 +++++++ .../unstable-feature-exact-symbol.fail.stderr | 17 ++++++++ .../internal/unstable-feature-exact-symbol.rs | 42 +++++++++++++++++++ .../unstable-impl-assoc-type.fail.stderr | 22 ++++++++++ tests/ui/internal/unstable-impl-assoc-type.rs | 28 +++++++++++++ ...stable-impl-cannot-use-feature.fail.stderr | 17 ++++++++ .../unstable-impl-cannot-use-feature.rs | 31 ++++++++++++++ .../internal/unstable-impl-multiple-symbol.rs | 28 +++++++++++++ 15 files changed, 303 insertions(+) create mode 100644 tests/ui/internal/auxiliary/unstable_feature.rs create mode 100644 tests/ui/internal/unstable-feature-bound-two-error.rs create mode 100644 tests/ui/internal/unstable-feature-bound-two-error.stderr create mode 100644 tests/ui/internal/unstable-feature-cross-crate-exact-symbol.fail.stderr create mode 100644 tests/ui/internal/unstable-feature-cross-crate-exact-symbol.rs create mode 100644 tests/ui/internal/unstable-feature-cross-crate-multiple-symbol.rs create mode 100644 tests/ui/internal/unstable-feature-cross-crate-require-bound.fail.stderr create mode 100644 tests/ui/internal/unstable-feature-cross-crate-require-bound.rs create mode 100644 tests/ui/internal/unstable-feature-exact-symbol.fail.stderr create mode 100644 tests/ui/internal/unstable-feature-exact-symbol.rs create mode 100644 tests/ui/internal/unstable-impl-assoc-type.fail.stderr create mode 100644 tests/ui/internal/unstable-impl-assoc-type.rs create mode 100644 tests/ui/internal/unstable-impl-cannot-use-feature.fail.stderr create mode 100644 tests/ui/internal/unstable-impl-cannot-use-feature.rs create mode 100644 tests/ui/internal/unstable-impl-multiple-symbol.rs diff --git a/tests/ui/internal/auxiliary/unstable_feature.rs b/tests/ui/internal/auxiliary/unstable_feature.rs new file mode 100644 index 0000000000000..a7ab162632906 --- /dev/null +++ b/tests/ui/internal/auxiliary/unstable_feature.rs @@ -0,0 +1,30 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] +#![stable(feature = "a", since = "1.1.1" )] + +#[stable(feature = "a", since = "1.1.1" )] +pub trait Foo { + #[stable(feature = "a", since = "1.1.1" )] + fn foo(); +} +#[stable(feature = "a", since = "1.1.1" )] +pub struct Bar; +#[stable(feature = "a", since = "1.1.1" )] +pub struct Moo; + +#[unstable_feature_bound(feat_bar)] +#[unstable(feature = "feat_bar", issue = "none" )] +impl Foo for Bar { + fn foo() {} +} + +#[unstable_feature_bound(feat_moo)] +#[unstable(feature = "feat_moo", issue = "none" )] +impl Foo for Moo { + fn foo() {} +} + +fn main() { +} diff --git a/tests/ui/internal/unstable-feature-bound-two-error.rs b/tests/ui/internal/unstable-feature-bound-two-error.rs new file mode 100644 index 0000000000000..04961f2a5812d --- /dev/null +++ b/tests/ui/internal/unstable-feature-bound-two-error.rs @@ -0,0 +1,12 @@ +//@ aux-build:unstable_feature.rs +extern crate unstable_feature; +use unstable_feature::{Foo, Bar, Moo}; + +// FIXME: both `feat_bar`` and `feat_moo` are needed to pass this test, +// but the diagnostic only will point out `feat_bar`. + +fn main() { + Bar::foo(); + //~^ ERROR: type annotations needed: cannot satisfy `unstable feature: `feat_bar`` + Moo::foo(); +} diff --git a/tests/ui/internal/unstable-feature-bound-two-error.stderr b/tests/ui/internal/unstable-feature-bound-two-error.stderr new file mode 100644 index 0000000000000..6463b84ec63fb --- /dev/null +++ b/tests/ui/internal/unstable-feature-bound-two-error.stderr @@ -0,0 +1,11 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_bar`` + --> $DIR/unstable-feature-bound-two-error.rs:9:5 + | +LL | Bar::foo(); + | ^^^ cannot satisfy `unstable feature: `feat_bar`` + | + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.fail.stderr b/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.fail.stderr new file mode 100644 index 0000000000000..affac1d4225d4 --- /dev/null +++ b/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.fail.stderr @@ -0,0 +1,11 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_moo`` + --> $DIR/unstable-feature-cross-crate-exact-symbol.rs:16:5 + | +LL | Moo::foo(); + | ^^^ cannot satisfy `unstable feature: `feat_moo`` + | + = note: required for `Moo` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.rs b/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.rs new file mode 100644 index 0000000000000..4a76fe90fa5d1 --- /dev/null +++ b/tests/ui/internal/unstable-feature-cross-crate-exact-symbol.rs @@ -0,0 +1,18 @@ +//@ aux-build:unstable_feature.rs +//@ revisions: pass fail +//@[pass] check-pass + +#![cfg_attr(pass, feature(feat_bar, feat_moo))] +#![cfg_attr(fail, feature(feat_bar))] + +extern crate unstable_feature; +use unstable_feature::{Foo, Bar, Moo}; + +/// To use impls gated by both `feat_foo` and `feat_moo`, +/// both features must be enabled. + +fn main() { + Bar::foo(); + Moo::foo(); + //[fail]~^ ERROR: type annotations needed: cannot satisfy `unstable feature: `feat_moo`` +} diff --git a/tests/ui/internal/unstable-feature-cross-crate-multiple-symbol.rs b/tests/ui/internal/unstable-feature-cross-crate-multiple-symbol.rs new file mode 100644 index 0000000000000..8d337d465b545 --- /dev/null +++ b/tests/ui/internal/unstable-feature-cross-crate-multiple-symbol.rs @@ -0,0 +1,11 @@ +//@ aux-build:unstable_feature.rs +//@ check-pass +#![feature(feat_bar, feat_moo)] +extern crate unstable_feature; +use unstable_feature::{Foo, Bar, Moo}; + +/// Bar::foo() should still be usable even if we enable multiple feature. + +fn main() { + Bar::foo(); +} diff --git a/tests/ui/internal/unstable-feature-cross-crate-require-bound.fail.stderr b/tests/ui/internal/unstable-feature-cross-crate-require-bound.fail.stderr new file mode 100644 index 0000000000000..cf46d159163a0 --- /dev/null +++ b/tests/ui/internal/unstable-feature-cross-crate-require-bound.fail.stderr @@ -0,0 +1,11 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_bar`` + --> $DIR/unstable-feature-cross-crate-require-bound.rs:12:5 + | +LL | Bar::foo(); + | ^^^ cannot satisfy `unstable feature: `feat_bar`` + | + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-feature-cross-crate-require-bound.rs b/tests/ui/internal/unstable-feature-cross-crate-require-bound.rs new file mode 100644 index 0000000000000..85c5ada8cf99a --- /dev/null +++ b/tests/ui/internal/unstable-feature-cross-crate-require-bound.rs @@ -0,0 +1,14 @@ +//@ aux-build:unstable_feature.rs +//@ revisions: pass fail +//@[pass] check-pass + +#![cfg_attr(pass, feature(feat_bar))] +extern crate unstable_feature; +use unstable_feature::{Foo, Bar}; + +/// #[feature(..)] is required to use unstable impl. + +fn main() { + Bar::foo(); + //[fail]~^ ERROR: cannot satisfy `unstable feature: `feat_bar`` +} diff --git a/tests/ui/internal/unstable-feature-exact-symbol.fail.stderr b/tests/ui/internal/unstable-feature-exact-symbol.fail.stderr new file mode 100644 index 0000000000000..94862d1a1a75d --- /dev/null +++ b/tests/ui/internal/unstable-feature-exact-symbol.fail.stderr @@ -0,0 +1,17 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_moo`` + --> $DIR/unstable-feature-exact-symbol.rs:38:5 + | +LL | Bar::moo(); + | ^^^ cannot satisfy `unstable feature: `feat_moo`` + | +note: required for `Bar` to implement `Moo` + --> $DIR/unstable-feature-exact-symbol.rs:30:6 + | +LL | #[unstable_feature_bound(feat_moo)] + | ----------------------------------- unsatisfied trait bound introduced here +LL | impl Moo for Bar { + | ^^^ ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-feature-exact-symbol.rs b/tests/ui/internal/unstable-feature-exact-symbol.rs new file mode 100644 index 0000000000000..221999b86d096 --- /dev/null +++ b/tests/ui/internal/unstable-feature-exact-symbol.rs @@ -0,0 +1,42 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] +#![unstable(feature = "feat_foo", issue = "none" )] + +/// In staged-api crate, impl that is marked as unstable with +/// feature name `feat_moo` should not be accessible +/// if only `feat_foo` is enabled. + +pub trait Foo { + fn foo(); +} + +pub trait Moo { + fn moo(); +} + +pub struct Bar; + +#[unstable_feature_bound(feat_foo)] +impl Foo for Bar { + fn foo() {} +} + +#[unstable_feature_bound(feat_moo)] +impl Moo for Bar { + fn moo() {} +} + +#[cfg_attr(fail, unstable_feature_bound(feat_foo))] +#[cfg_attr(pass, unstable_feature_bound(feat_foo, feat_moo))] +fn bar() { + Bar::foo(); + Bar::moo(); + //[fail]~^ ERROR cannot satisfy `unstable feature: `feat_moo`` +} + +fn main() {} diff --git a/tests/ui/internal/unstable-impl-assoc-type.fail.stderr b/tests/ui/internal/unstable-impl-assoc-type.fail.stderr new file mode 100644 index 0000000000000..3004e624fe38c --- /dev/null +++ b/tests/ui/internal/unstable-impl-assoc-type.fail.stderr @@ -0,0 +1,22 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_foo`` + --> $DIR/unstable-impl-assoc-type.rs:24:16 + | +LL | type Assoc = Self; + | ^^^^ cannot satisfy `unstable feature: `feat_foo`` + | +note: required for `Foo` to implement `Bar` + --> $DIR/unstable-impl-assoc-type.rs:20:6 + | +LL | #[unstable_feature_bound(feat_foo)] + | ----------------------------------- unsatisfied trait bound introduced here +LL | impl Bar for Foo {} + | ^^^ ^^^ +note: required by a bound in `Trait::Assoc` + --> $DIR/unstable-impl-assoc-type.rs:14:17 + | +LL | type Assoc: Bar; + | ^^^ required by this bound in `Trait::Assoc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-impl-assoc-type.rs b/tests/ui/internal/unstable-impl-assoc-type.rs new file mode 100644 index 0000000000000..f2282e15c9f4c --- /dev/null +++ b/tests/ui/internal/unstable-impl-assoc-type.rs @@ -0,0 +1,28 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![unstable(feature = "feat_foo", issue = "none" )] + +/// FIXME: add a description here. + +trait Bar {} + +trait Trait { + type Assoc: Bar; +} + +struct Foo; + +#[unstable_feature_bound(feat_foo)] +impl Bar for Foo {} + +#[cfg_attr(pass, unstable_feature_bound(feat_foo))] +impl Trait for Foo { + type Assoc = Self; + //[fail]~^ ERROR: cannot satisfy `unstable feature: `feat_foo`` +} + +fn main(){} diff --git a/tests/ui/internal/unstable-impl-cannot-use-feature.fail.stderr b/tests/ui/internal/unstable-impl-cannot-use-feature.fail.stderr new file mode 100644 index 0000000000000..ee707e0fd25c3 --- /dev/null +++ b/tests/ui/internal/unstable-impl-cannot-use-feature.fail.stderr @@ -0,0 +1,17 @@ +error[E0284]: type annotations needed: cannot satisfy `unstable feature: `feat_foo`` + --> $DIR/unstable-impl-cannot-use-feature.rs:27:5 + | +LL | Bar::foo(); + | ^^^ cannot satisfy `unstable feature: `feat_foo`` + | +note: required for `Bar` to implement `Foo` + --> $DIR/unstable-impl-cannot-use-feature.rs:21:6 + | +LL | #[unstable_feature_bound(feat_foo)] + | ----------------------------------- unsatisfied trait bound introduced here +LL | impl Foo for Bar { + | ^^^ ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/internal/unstable-impl-cannot-use-feature.rs b/tests/ui/internal/unstable-impl-cannot-use-feature.rs new file mode 100644 index 0000000000000..0f8583385e078 --- /dev/null +++ b/tests/ui/internal/unstable-impl-cannot-use-feature.rs @@ -0,0 +1,31 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] +#![unstable(feature = "feat_foo", issue = "none" )] + +#![cfg_attr(fail, feature(feat_foo))] + +/// In staged-api crate, using an unstable impl requires +/// #[unstable_feature_bound(..)], not #[feature(..)]. + +pub trait Foo { + fn foo(); +} +pub struct Bar; + +#[unstable_feature_bound(feat_foo)] +impl Foo for Bar { + fn foo() {} +} + +#[cfg_attr(pass, unstable_feature_bound(feat_foo))] +fn bar() { + Bar::foo(); + //[fail]~^ ERROR: cannot satisfy `unstable feature: `feat_foo`` +} + +fn main() {} diff --git a/tests/ui/internal/unstable-impl-multiple-symbol.rs b/tests/ui/internal/unstable-impl-multiple-symbol.rs new file mode 100644 index 0000000000000..863774dd11bdc --- /dev/null +++ b/tests/ui/internal/unstable-impl-multiple-symbol.rs @@ -0,0 +1,28 @@ +//@ check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] +#![unstable(feature = "feat_foo", issue = "none" )] + +/// In staged-api crate, if feat_foo is only needed to use an impl, +/// having both `feat_foo` and `feat_bar` will still make it pass. + +pub trait Foo { + fn foo(); +} +pub struct Bar; + +// Annotate the impl as unstable. +#[unstable_feature_bound(feat_foo)] +impl Foo for Bar { + fn foo() {} +} + +#[unstable_feature_bound(feat_foo, feat_bar)] +fn bar() { + Bar::foo(); +} + +fn main() {} From 25cfd96d7b2730575a5fbbc7fedf1dd6b9c769c8 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 10:02:31 +0000 Subject: [PATCH 07/21] Add the test description --- tests/ui/internal/unstable-impl-assoc-type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/internal/unstable-impl-assoc-type.rs b/tests/ui/internal/unstable-impl-assoc-type.rs index f2282e15c9f4c..169f78fe3b042 100644 --- a/tests/ui/internal/unstable-impl-assoc-type.rs +++ b/tests/ui/internal/unstable-impl-assoc-type.rs @@ -6,7 +6,7 @@ #![feature(impl_stability)] #![unstable(feature = "feat_foo", issue = "none" )] -/// FIXME: add a description here. +/// Test that you can't leak unstable impls through item bounds on associated types. trait Bar {} From 3ca0475e28426941cf8a9ec6030b5c88db763626 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 10:22:28 +0000 Subject: [PATCH 08/21] Add coherence test --- .../auxiliary/unstable_impl_coherence_aux.rs | 15 +++++++++++++++ .../unstable_impl_coherence.disabled.stderr | 13 +++++++++++++ .../unstable_impl_coherence.enabled.stderr | 13 +++++++++++++ tests/ui/internal/unstable_impl_coherence.rs | 17 +++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 tests/ui/internal/auxiliary/unstable_impl_coherence_aux.rs create mode 100644 tests/ui/internal/unstable_impl_coherence.disabled.stderr create mode 100644 tests/ui/internal/unstable_impl_coherence.enabled.stderr create mode 100644 tests/ui/internal/unstable_impl_coherence.rs diff --git a/tests/ui/internal/auxiliary/unstable_impl_coherence_aux.rs b/tests/ui/internal/auxiliary/unstable_impl_coherence_aux.rs new file mode 100644 index 0000000000000..0e1bb6e45cc33 --- /dev/null +++ b/tests/ui/internal/auxiliary/unstable_impl_coherence_aux.rs @@ -0,0 +1,15 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![allow(dead_code)] +#![stable(feature = "a", since = "1.1.1" )] + +#[stable(feature = "a", since = "1.1.1" )] +pub trait Trait {} + +#[unstable_feature_bound(foo)] +#[unstable(feature = "foo", issue = "none" )] +impl Trait for T {} + +fn main() { +} diff --git a/tests/ui/internal/unstable_impl_coherence.disabled.stderr b/tests/ui/internal/unstable_impl_coherence.disabled.stderr new file mode 100644 index 0000000000000..5e6eae8f41220 --- /dev/null +++ b/tests/ui/internal/unstable_impl_coherence.disabled.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` + --> $DIR/unstable_impl_coherence.rs:12:1 + | +LL | impl aux::Trait for LocalTy{} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `unstable_impl_coherence_aux`: + - impl Trait for T + where unstable feature: `foo`; + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/internal/unstable_impl_coherence.enabled.stderr b/tests/ui/internal/unstable_impl_coherence.enabled.stderr new file mode 100644 index 0000000000000..5e6eae8f41220 --- /dev/null +++ b/tests/ui/internal/unstable_impl_coherence.enabled.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` + --> $DIR/unstable_impl_coherence.rs:12:1 + | +LL | impl aux::Trait for LocalTy{} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `unstable_impl_coherence_aux`: + - impl Trait for T + where unstable feature: `foo`; + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/internal/unstable_impl_coherence.rs b/tests/ui/internal/unstable_impl_coherence.rs new file mode 100644 index 0000000000000..74f3aa232f569 --- /dev/null +++ b/tests/ui/internal/unstable_impl_coherence.rs @@ -0,0 +1,17 @@ +//@ aux-build:unstable_impl_coherence_aux.rs +//@ revisions: enabled disabled + +#![cfg_attr(enabled, feature(foo))] +extern crate unstable_impl_coherence_aux as aux; +use aux::Trait; + +/// Coherence test for unstable impl. +/// No matter feature `foo` is enabled or not, the impl +/// for aux::Trait will be rejected by coherence checking. + +struct LocalTy; + +impl aux::Trait for LocalTy{} +//~^ ERROR: conflicting implementations of trait `Trait` for type `LocalTy` + +fn main(){} From 4ba8acbb52d79fcd6b5179798d98b82a6a9c2d58 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 10:48:47 +0000 Subject: [PATCH 09/21] Add codegen test --- .../auxiliary/unstable_impl_codegen_aux1.rs | 22 +++++++++++++++++++ .../auxiliary/unstable_impl_codegen_aux2.rs | 20 +++++++++++++++++ tests/ui/internal/unstable_impl_codegen.rs | 9 ++++++++ 3 files changed, 51 insertions(+) create mode 100644 tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs create mode 100644 tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs create mode 100644 tests/ui/internal/unstable_impl_codegen.rs diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs new file mode 100644 index 0000000000000..1a9df5b96a885 --- /dev/null +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs @@ -0,0 +1,22 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![stable(feature = "a", since = "1.1.1" )] + +/// Aux crate for unstable impl codegen test. + +#[stable(feature = "a", since = "1.1.1" )] +trait Trait { + fn method(&self); +} + +#[unstable_feature_bound(foo)] +impl Trait for T { +// FIXME: this line above failed with cannot find type `T` in this scope + fn method(&self) { + println!("hi"); + } +} + +fn main() { +} diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs new file mode 100644 index 0000000000000..d508ad9a6be2e --- /dev/null +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs @@ -0,0 +1,20 @@ +//@ aux-build:unstable_impl_codegen_aux1.rs + +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![stable(feature = "a", since = "1.1.1" )] +#[feature(foo)] + +extern crate unstable_impl_codegen_aux1 as aux; + +/// Upstream crate for unstable impl codegen test +/// that depends on aux crate in +/// unstable_impl_codegen_aux1.rs + +fn foo(a:T) { + a.method(); +} + +fn main() { +} diff --git a/tests/ui/internal/unstable_impl_codegen.rs b/tests/ui/internal/unstable_impl_codegen.rs new file mode 100644 index 0000000000000..f15be1bfeaeac --- /dev/null +++ b/tests/ui/internal/unstable_impl_codegen.rs @@ -0,0 +1,9 @@ +//@ aux-build:unstable_impl_codegen_aux2.rs + +/// Downstream crate for unstable impl codegen test +/// that depends on upstream crate in +/// unstable_impl_codegen_aux2.rs + +fn main() { + foo(1_u8); +} From 9e24e104e436c0b3d99fe122d743c3a721920b5e Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 10:50:00 +0000 Subject: [PATCH 10/21] Bless test --- tests/ui/internal/unstable_impl_coherence.disabled.stderr | 2 +- tests/ui/internal/unstable_impl_coherence.enabled.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/internal/unstable_impl_coherence.disabled.stderr b/tests/ui/internal/unstable_impl_coherence.disabled.stderr index 5e6eae8f41220..96eacb84a4ff9 100644 --- a/tests/ui/internal/unstable_impl_coherence.disabled.stderr +++ b/tests/ui/internal/unstable_impl_coherence.disabled.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` - --> $DIR/unstable_impl_coherence.rs:12:1 + --> $DIR/unstable_impl_coherence.rs:14:1 | LL | impl aux::Trait for LocalTy{} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/internal/unstable_impl_coherence.enabled.stderr b/tests/ui/internal/unstable_impl_coherence.enabled.stderr index 5e6eae8f41220..96eacb84a4ff9 100644 --- a/tests/ui/internal/unstable_impl_coherence.enabled.stderr +++ b/tests/ui/internal/unstable_impl_coherence.enabled.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` - --> $DIR/unstable_impl_coherence.rs:12:1 + --> $DIR/unstable_impl_coherence.rs:14:1 | LL | impl aux::Trait for LocalTy{} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 9583e1f88bade5513f8b63097025a92744907f42 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 10:51:19 +0000 Subject: [PATCH 11/21] tidy --- tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs | 2 +- tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs | 4 ++-- tests/ui/internal/unstable_impl_codegen.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs index 1a9df5b96a885..4b1e55fa1d1c8 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs @@ -11,7 +11,7 @@ trait Trait { } #[unstable_feature_bound(foo)] -impl Trait for T { +impl Trait for T { // FIXME: this line above failed with cannot find type `T` in this scope fn method(&self) { println!("hi"); diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs index d508ad9a6be2e..188af915de715 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs @@ -8,8 +8,8 @@ extern crate unstable_impl_codegen_aux1 as aux; -/// Upstream crate for unstable impl codegen test -/// that depends on aux crate in +/// Upstream crate for unstable impl codegen test +/// that depends on aux crate in /// unstable_impl_codegen_aux1.rs fn foo(a:T) { diff --git a/tests/ui/internal/unstable_impl_codegen.rs b/tests/ui/internal/unstable_impl_codegen.rs index f15be1bfeaeac..975b861da34f9 100644 --- a/tests/ui/internal/unstable_impl_codegen.rs +++ b/tests/ui/internal/unstable_impl_codegen.rs @@ -1,7 +1,7 @@ //@ aux-build:unstable_impl_codegen_aux2.rs -/// Downstream crate for unstable impl codegen test -/// that depends on upstream crate in +/// Downstream crate for unstable impl codegen test +/// that depends on upstream crate in /// unstable_impl_codegen_aux2.rs fn main() { From c6976b24f0f8dbbb48c51040e774aa067e9deff8 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 12:51:50 +0000 Subject: [PATCH 12/21] Fix codegen test --- .../internal/auxiliary/unstable_impl_codegen_aux1.rs | 7 ++++--- .../internal/auxiliary/unstable_impl_codegen_aux2.rs | 10 +++------- tests/ui/internal/unstable_impl_codegen.rs | 5 +++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs index 4b1e55fa1d1c8..b2508885e951b 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs @@ -6,13 +6,14 @@ /// Aux crate for unstable impl codegen test. #[stable(feature = "a", since = "1.1.1" )] -trait Trait { +pub trait Trait { + #[stable(feature = "a", since = "1.1.1" )] fn method(&self); } #[unstable_feature_bound(foo)] -impl Trait for T { -// FIXME: this line above failed with cannot find type `T` in this scope +#[unstable(feature = "foo", issue = "none" )] +impl Trait for T { fn method(&self) { println!("hi"); } diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs index 188af915de715..eca5e6c451004 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs @@ -1,18 +1,14 @@ //@ aux-build:unstable_impl_codegen_aux1.rs - -#![allow(internal_features)] -#![feature(staged_api)] -#![feature(impl_stability)] -#![stable(feature = "a", since = "1.1.1" )] -#[feature(foo)] +#![feature(foo)] extern crate unstable_impl_codegen_aux1 as aux; +use aux::Trait; /// Upstream crate for unstable impl codegen test /// that depends on aux crate in /// unstable_impl_codegen_aux1.rs -fn foo(a:T) { +pub fn foo(a:T) { a.method(); } diff --git a/tests/ui/internal/unstable_impl_codegen.rs b/tests/ui/internal/unstable_impl_codegen.rs index 975b861da34f9..80cf4fcc73029 100644 --- a/tests/ui/internal/unstable_impl_codegen.rs +++ b/tests/ui/internal/unstable_impl_codegen.rs @@ -1,9 +1,14 @@ //@ aux-build:unstable_impl_codegen_aux2.rs +//@ check-pass +#![feature(foo)] /// Downstream crate for unstable impl codegen test /// that depends on upstream crate in /// unstable_impl_codegen_aux2.rs +extern crate unstable_impl_codegen_aux2 as aux; +use aux::foo; + fn main() { foo(1_u8); } From bd1072beb92da017cef69b4c1b0ea1e4bf23c8b3 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 13:39:30 +0000 Subject: [PATCH 13/21] Fix rebase --- compiler/rustc_passes/src/check_attr.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4e2be8ff0b817..2494a9a09c204 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -142,6 +142,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ } + Attribute::Parsed(AttributeKind::AllowUnstableFeature(_)) => { + // FIXME: handle this later. + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect From e88370f7a84cf79d2e6a0aaaa00addb477a7ba0a Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 14:26:54 +0000 Subject: [PATCH 14/21] Fix codegen test by removing T:Trait bound --- tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs | 2 +- tests/ui/internal/unstable_impl_codegen.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs index eca5e6c451004..713abdb0400bd 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs @@ -8,7 +8,7 @@ use aux::Trait; /// that depends on aux crate in /// unstable_impl_codegen_aux1.rs -pub fn foo(a:T) { +pub fn foo(a:T) { a.method(); } diff --git a/tests/ui/internal/unstable_impl_codegen.rs b/tests/ui/internal/unstable_impl_codegen.rs index 80cf4fcc73029..29b169cd08042 100644 --- a/tests/ui/internal/unstable_impl_codegen.rs +++ b/tests/ui/internal/unstable_impl_codegen.rs @@ -1,6 +1,5 @@ //@ aux-build:unstable_impl_codegen_aux2.rs //@ check-pass -#![feature(foo)] /// Downstream crate for unstable impl codegen test /// that depends on upstream crate in From fc2926fe01aeeed33e486261f4c352c986bacc41 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 14:36:42 +0000 Subject: [PATCH 15/21] Clean up aux crate --- tests/ui/internal/auxiliary/unstable_feature.rs | 4 ---- tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs | 3 --- tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs | 3 --- 3 files changed, 10 deletions(-) diff --git a/tests/ui/internal/auxiliary/unstable_feature.rs b/tests/ui/internal/auxiliary/unstable_feature.rs index a7ab162632906..827717d0eba5f 100644 --- a/tests/ui/internal/auxiliary/unstable_feature.rs +++ b/tests/ui/internal/auxiliary/unstable_feature.rs @@ -1,7 +1,6 @@ #![allow(internal_features)] #![feature(staged_api)] #![feature(impl_stability)] -#![allow(dead_code)] #![stable(feature = "a", since = "1.1.1" )] #[stable(feature = "a", since = "1.1.1" )] @@ -25,6 +24,3 @@ impl Foo for Bar { impl Foo for Moo { fn foo() {} } - -fn main() { -} diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs index b2508885e951b..61fd8dac480ed 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux1.rs @@ -18,6 +18,3 @@ impl Trait for T { println!("hi"); } } - -fn main() { -} diff --git a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs index 713abdb0400bd..b21561f0fe098 100644 --- a/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs +++ b/tests/ui/internal/auxiliary/unstable_impl_codegen_aux2.rs @@ -11,6 +11,3 @@ use aux::Trait; pub fn foo(a:T) { a.method(); } - -fn main() { -} From cdb277347dacbf7163e7caab6b1bb63b79533457 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 11 Jun 2025 16:27:05 +0000 Subject: [PATCH 16/21] Use run-pass test --- tests/ui/internal/unstable_impl_codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/internal/unstable_impl_codegen.rs b/tests/ui/internal/unstable_impl_codegen.rs index 29b169cd08042..285a64d2250af 100644 --- a/tests/ui/internal/unstable_impl_codegen.rs +++ b/tests/ui/internal/unstable_impl_codegen.rs @@ -1,5 +1,5 @@ //@ aux-build:unstable_impl_codegen_aux2.rs -//@ check-pass +//@ run-pass /// Downstream crate for unstable impl codegen test /// that depends on upstream crate in From 788d9bef59a0e918450ed7223c0baf2a8080e5d3 Mon Sep 17 00:00:00 2001 From: tiif Date: Thu, 12 Jun 2025 15:36:02 +0000 Subject: [PATCH 17/21] Add test for using unstable_feature_bound on method --- tests/ui/internal/unstable_inherent_method.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/ui/internal/unstable_inherent_method.rs diff --git a/tests/ui/internal/unstable_inherent_method.rs b/tests/ui/internal/unstable_inherent_method.rs new file mode 100644 index 0000000000000..f15fbbff1ed1e --- /dev/null +++ b/tests/ui/internal/unstable_inherent_method.rs @@ -0,0 +1,23 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![feature(impl_stability)] +#![stable(feature = "a", since = "1.1.1" )] + + +/// FIXME(tiif): we haven't allowed marking 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)] + fn foo(); +} + +#[stable(feature = "a", since = "1.1.1" )] +impl Trait for u8 { + #[unstable_feature_bound(foo)] + fn foo() {} +} + +fn main() {} \ No newline at end of file From b2eabf24574c04dd7a48157f96721dfb43bcfacd Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 13 Jun 2025 15:48:00 +0000 Subject: [PATCH 18/21] Implement check_attr --- compiler/rustc_passes/messages.ftl | 4 +++ compiler/rustc_passes/src/check_attr.rs | 45 +++++++++++++++++++++++-- compiler/rustc_passes/src/errors.rs | 10 ++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7c237d708c0ad..f9e659238eb4e 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -597,6 +597,10 @@ passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` .label = not a `const fn` +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_rustc_const_stable_indirect_pairing = `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied passes_rustc_dirty_clean = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2494a9a09c204..d82871b03593c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -142,8 +142,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ } - Attribute::Parsed(AttributeKind::AllowUnstableFeature(_)) => { - // FIXME: handle this later. + Attribute::Parsed(AttributeKind::AllowUnstableFeature(syms)) => { + self.check_unstable_feature_bound(syms.first().unwrap().1, span, target) } Attribute::Parsed( AttributeKind::BodyStability { .. } @@ -2277,6 +2277,47 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) { + match target { + 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: &Attribute, 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 f0d4b610f6384..f6bfa85449d06 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -662,6 +662,16 @@ pub(crate) struct RustcAllowConstFnUnstable { pub span: Span, } +#[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 { From 4e816e1476999d95d700b7dc9fa36ba67b46dd5b Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 13 Jun 2025 15:50:59 +0000 Subject: [PATCH 19/21] Add test for disallowed attribute --- tests/ui/internal/unstable_inherent_method.rs | 5 +++-- .../internal/unstable_inherent_method.stderr | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/ui/internal/unstable_inherent_method.stderr diff --git a/tests/ui/internal/unstable_inherent_method.rs b/tests/ui/internal/unstable_inherent_method.rs index f15fbbff1ed1e..be9f43ef4f5c2 100644 --- a/tests/ui/internal/unstable_inherent_method.rs +++ b/tests/ui/internal/unstable_inherent_method.rs @@ -3,20 +3,21 @@ #![feature(impl_stability)] #![stable(feature = "a", since = "1.1.1" )] - -/// FIXME(tiif): we haven't allowed marking impl method as +/// 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() {} } diff --git a/tests/ui/internal/unstable_inherent_method.stderr b/tests/ui/internal/unstable_inherent_method.stderr new file mode 100644 index 0000000000000..01a2f43642f46 --- /dev/null +++ b/tests/ui/internal/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:12: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:19:5 + | +LL | #[unstable_feature_bound(foo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not an `impl` or free function + +error: aborting due to 2 previous errors + From 6e4c9025163b5fae0a992d5b2de59d11df961296 Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 13 Jun 2025 15:53:56 +0000 Subject: [PATCH 20/21] tidy --- compiler/rustc_passes/messages.ftl | 8 +-- compiler/rustc_passes/src/check_attr.rs | 66 +++++++++---------- compiler/rustc_passes/src/errors.rs | 1 - tests/ui/internal/unstable_inherent_method.rs | 2 +- 4 files changed, 37 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f9e659238eb4e..7f66c9f2b0586 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -597,10 +597,6 @@ passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` .label = not a `const fn` -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_rustc_const_stable_indirect_pairing = `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied passes_rustc_dirty_clean = @@ -658,6 +654,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 d82871b03593c..b5b5418cf535e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2280,40 +2280,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) { match target { 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 }); + 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 }); } } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f6bfa85449d06..9ec792729558d 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -671,7 +671,6 @@ pub(crate) struct RustcUnstableFeatureBound { pub span: Span, } - #[derive(Diagnostic)] #[diag(passes_rustc_std_internal_symbol)] pub(crate) struct RustcStdInternalSymbol { diff --git a/tests/ui/internal/unstable_inherent_method.rs b/tests/ui/internal/unstable_inherent_method.rs index be9f43ef4f5c2..9a776f3ba20b0 100644 --- a/tests/ui/internal/unstable_inherent_method.rs +++ b/tests/ui/internal/unstable_inherent_method.rs @@ -21,4 +21,4 @@ impl Trait for u8 { fn foo() {} } -fn main() {} \ No newline at end of file +fn main() {} From b6bfa2d6a38df9df28b6ba82e99711f834e9383a Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 13 Jun 2025 16:37:56 +0000 Subject: [PATCH 21/21] Fix build error --- .../src/attributes/allow_unstable.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 24b9b57c6cdf1..c5be547c889cd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -25,16 +25,18 @@ impl CombineAttributeParser for AllowInternalUnstableParser { } pub(crate) struct AllowUnstableFeatureParser; -impl CombineAttributeParser for AllowUnstableFeatureParser { +impl CombineAttributeParser for AllowUnstableFeatureParser { const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; type Item = (Symbol, Span); const CONVERT: ConvertFn = AttributeKind::AllowUnstableFeature; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator + 'a { - parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span)) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator { + parse_unstable(cx, args, >::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) } }