From fecd99881d545d6096d5489419f096cffe53b171 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:30:36 +0000 Subject: [PATCH 1/8] Setup unstable feature bound attribute --- .../src/attributes.rs | 3 ++ .../src/attributes/allow_unstable.rs | 20 +++++++++ compiler/rustc_passes/messages.ftl | 4 ++ compiler/rustc_passes/src/check_attr.rs | 44 +++++++++++++++++++ compiler/rustc_passes/src/errors.rs | 9 ++++ .../unstable_inherent_method.rs | 23 ++++++++++ .../unstable_inherent_method.stderr | 20 +++++++++ 7 files changed, 123 insertions(+) create mode 100644 tests/ui/unstable-feature-bound/unstable_inherent_method.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_inherent_method.stderr diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 995ac514d8d6c..5f38be346edd3 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 1c51c3eee4ef1..a6bd2306ec508 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 CombineAttributeParser for AllowInternalUnstableParser { } } +pub(crate) struct UnstableFeatureBoundParser; +impl CombineAttributeParser for UnstableFeatureBoundParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; + type Item = (Symbol, Span); + const CONVERT: ConvertFn = |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 { + if !cx.features().staged_api() { + cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span }); + } + parse_unstable(cx, args, >::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_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3418d997b834f..500b4279588af 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 820255afe274d..89000ab2619a2 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 093a2b38804fd..37330c0ed6ebf 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -686,6 +686,15 @@ 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 { 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 0000000000000..5f3095430a806 --- /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 0000000000000..fa1c39db259a3 --- /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 + From 2586185831d318fda4de40b9fcc7c2737351292a Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:31:30 +0000 Subject: [PATCH 2/8] Lower the UnstableFeatureBound predicate to UnstableFeature predicate --- .../src/collect/predicates_of.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a93e58b101fe5..60ccc484e0026 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; @@ -333,6 +334,19 @@ 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)); + // FIXME(staged_api): We might want to look at the normal stability attributes too but + // first we would need a way to let std/core use APIs with unstable feature bounds from + // within stable APIs. + let allow_unstable_feature_attr = + find_attr!(attrs, AttributeKind::UnstableFeatureBound(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 From 1e5c7b28772bb99b8af4ac350aaabbb28168dc99 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:35:06 +0000 Subject: [PATCH 3/8] Add the core logic in old and new solvers --- compiler/rustc_middle/src/ty/context.rs | 7 ++++ .../src/solve/eval_ctxt/mod.rs | 12 ++++++ .../rustc_next_trait_solver/src/solve/mod.rs | 14 +++++++ .../src/traits/fulfill.rs | 11 ++++- .../src/traits/select/mod.rs | 10 ++++- compiler/rustc_type_ir/src/infer_ctxt.rs | 42 +++++++++++++++++++ compiler/rustc_type_ir/src/inherent.rs | 2 + 7 files changed, 96 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 98b2ce01d89b2..290213cab5e66 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -833,6 +833,13 @@ impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_featu fn associated_const_equality(self) -> bool { self.associated_const_equality() } + + fn feature_bound_holds_in_crate(self, symbol: Symbol) -> bool { + // We don't consider feature bounds to hold in the crate when `staged_api` feature is + // enabled, even if it is enabled through `#[feature]`. + // This is to prevent accidentally leaking unstable APIs to stable. + !self.staged_api() && self.enabled(symbol) + } } impl<'tcx> rustc_type_ir::inherent::Span> for Span { 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 b42587618b57b..ce9b794d40d30 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 @@ -22,6 +22,7 @@ use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; +use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind, GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, @@ -550,6 +551,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 }) } @@ -1177,6 +1181,14 @@ where ) -> T { BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0 } + + pub(super) fn may_use_unstable_feature( + &self, + param_env: I::ParamEnv, + symbol: I::Symbol, + ) -> bool { + may_use_unstable_feature(&**self.delegate, param_env, symbol) + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index e68ea22c7a27b..5ea3f0d10617d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -148,6 +148,20 @@ where } } + fn compute_unstable_feature_goal( + &mut self, + param_env: ::ParamEnv, + symbol: ::Symbol, + ) -> QueryResult { + if self.may_use_unstable_feature(param_env, symbol) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + 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 64a51e0550ba1..3e13e50426674 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -11,7 +11,9 @@ use rustc_infer::traits::{ use rustc_middle::bug; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{ + self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode, may_use_unstable_feature, +}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, debug_span, instrument}; @@ -767,6 +769,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + if may_use_unstable_feature(self.selcx.infcx, obligation.param_env, symbol) { + ProcessResult::Changed(Default::default()) + } else { + 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 2e65750db25de..10bcf861d35b4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, - TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, + TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature, }; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -832,6 +832,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + if may_use_unstable_feature(self.infcx, obligation.param_env, symbol) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToAmbig) + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 6c77a90250a4a..b558d0164483d 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -285,3 +285,45 @@ pub trait InferCtxtLike: Sized { fn reset_opaque_types(&self); } + +pub fn may_use_unstable_feature<'a, I: Interner, Infcx>( + infcx: &'a Infcx, + param_env: I::ParamEnv, + symbol: I::Symbol, +) -> bool +where + Infcx: InferCtxtLike, +{ + // 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 true; + } + } + } + + // During codegen we must assume that all feature bounds hold as we may be + // monomorphizing a body from an upstream crate which had an unstable feature + // enabled that we do not. + // + // Coherence should already report overlap errors involving unstable impls + // as the affected code would otherwise break when stabilizing this feature. + // It is also easily possible to accidentally cause unsoundness this way as + // we have to always enable unstable impls during codegen. + // + // Return ambiguity can also prevent people from writing code which depends on inference guidance + // that might no longer work after the impl is stabilised, + // tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs is one of the example. + // + // Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled + // if we are in std/core even if there is a corresponding `feature` attribute on the crate. + + if (infcx.typing_mode() == TypingMode::PostAnalysis) + || infcx.cx().features().feature_bound_holds_in_crate(symbol) + { + return true; + } else { + return false; + } +} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 2754d40fd36ce..0e307e15d5b4d 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -630,6 +630,8 @@ pub trait Features: Copy { fn coroutine_clone(self) -> bool; fn associated_const_equality(self) -> bool; + + fn feature_bound_holds_in_crate(self, symbol: I::Symbol) -> bool; } pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { From ab29256b96660ffd01ab0257588cb6cbd08355a8 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:36:19 +0000 Subject: [PATCH 4/8] Make stability attribute not to error when unstable feature bound is in effect --- compiler/rustc_passes/src/stability.rs | 32 +++++++++++++++-- .../unstable-feature-bound-no-effect.rs | 35 +++++++++++++++++++ .../unstable-feature-bound-no-effect.stderr | 11 ++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.stderr diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index adda94fda8f0b..e5530d526866a 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::UnstableFeatureBound(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/unstable-feature-bound/unstable-feature-bound-no-effect.rs b/tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.rs new file mode 100644 index 0000000000000..99501893ae0a5 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.rs @@ -0,0 +1,35 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![allow(dead_code)] +#![stable(feature = "a", since = "1.1.1" )] + +/// 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/unstable-feature-bound/unstable-feature-bound-no-effect.stderr b/tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.stderr new file mode 100644 index 0000000000000..4c8af2bbe56ff --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-bound-no-effect.stderr @@ -0,0 +1,11 @@ +error: an `#[unstable]` annotation here has no effect + --> $DIR/unstable-feature-bound-no-effect.rs:22: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: aborting due to 1 previous error + From dd067a689a05e50b80206536de22a2400277a7f8 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:37:51 +0000 Subject: [PATCH 5/8] Lint against having both #[unstable_feature_bound] and #[stable] on the same item --- compiler/rustc_attr_parsing/messages.ftl | 3 +++ .../rustc_attr_parsing/src/attributes/stability.rs | 10 ++++++++++ .../rustc_attr_parsing/src/session_diagnostics.rs | 8 ++++++++ ...nstable_feature_bound_incompatible_stability.rs | 14 ++++++++++++++ ...ble_feature_bound_incompatible_stability.stderr | 10 ++++++++++ 5 files changed, 45 insertions(+) create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 8d0ead63a8d82..35ff48cb5f24b 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -136,6 +136,9 @@ attr_parsing_unrecognized_repr_hint = attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change +attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable + .help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` + attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean attr_parsing_unsupported_literal_cfg_string = diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6bccd0042a80d..8f405e5aad972 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -98,6 +98,16 @@ impl AttributeParser for StabilityParser { } } + if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability { + for other_attr in cx.all_attrs { + if other_attr.word_is(sym::unstable_feature_bound) { + cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability { + span: cx.target_span, + }); + } + } + } + let (stability, span) = self.stability?; Some(AttributeKind::Stability { stability, span }) diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 97bf3d1c54955..5b0bf0e6662f0 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -503,6 +503,14 @@ pub(crate) struct UnrecognizedReprHint { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] +#[help] +pub(crate) struct UnstableFeatureBoundIncompatibleStability { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs new file mode 100644 index 0000000000000..1a9652c102309 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs @@ -0,0 +1,14 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![allow(dead_code)] +#![stable(feature = "a", since = "1.1.1" )] + +// Lint against the usage of both #[unstable_feature_bound] and #[stable] on the +// same item. + +#[stable(feature = "a", since = "1.1.1")] +#[unstable_feature_bound(feat_bar)] +fn bar() {} +//~^ ERROR Item annotated with `#[unstable_feature_bound]` should not be stable + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr new file mode 100644 index 0000000000000..9cb6a181beffc --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr @@ -0,0 +1,10 @@ +error: Item annotated with `#[unstable_feature_bound]` should not be stable + --> $DIR/unstable_feature_bound_incompatible_stability.rs:11:1 + | +LL | fn bar() {} + | ^^^^^^^^^^^ + | + = help: If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` + +error: aborting due to 1 previous error + From 7356ff7517f4d95583f8fd6d631441893c87dd3f Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:38:19 +0000 Subject: [PATCH 6/8] Implement other logics --- .../src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 5 ++- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++ .../rustc_hir_analysis/src/check/wfcheck.rs | 14 ++++--- .../src/collect/predicates_of.rs | 2 + .../src/impl_wf_check/min_specialization.rs | 1 + .../src/outlives/explicit.rs | 1 + .../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 | 1 + compiler/rustc_middle/src/ty/predicate.rs | 3 ++ compiler/rustc_middle/src/ty/print/pretty.rs | 1 + compiler/rustc_privacy/src/lib.rs | 1 + .../src/unstable/convert/stable/ty.rs | 3 ++ compiler/rustc_session/src/parse.rs | 40 +++++++++++++++++++ compiler/rustc_span/src/symbol.rs | 1 + .../src/error_reporting/traits/ambiguity.rs | 25 ++++++++++++ .../traits/fulfillment_errors.rs | 2 + .../src/traits/auto_trait.rs | 1 + .../src/traits/dyn_compatibility.rs | 2 + .../src/traits/fulfill.rs | 3 ++ .../query/type_op/implied_outlives_bounds.rs | 1 + .../rustc_trait_selection/src/traits/util.rs | 1 + .../rustc_trait_selection/src/traits/wf.rs | 2 + .../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/interner.rs | 1 + compiler/rustc_type_ir/src/predicate_kind.rs | 10 +++++ src/librustdoc/clean/mod.rs | 3 +- 31 files changed, 132 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 494b570c86c73..84c3bd1eb6147 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -70,6 +70,7 @@ impl AttributeKind { TrackCaller(..) => Yes, TypeConst(..) => Yes, UnsafeSpecializationMarker(..) => No, + UnstableFeatureBound(..) => No, Used { .. } => No, // tidy-alphabetical-end } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1f6675b4c5a8a..390246239ed16 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -13,7 +13,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, UnstableFeatureBoundParser, +}; use crate::attributes::codegen_attrs::{ ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, @@ -133,6 +135,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, // tidy-alphabetical-end // tidy-alphabetical-start diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7d9915d7f68b7..e2827f227abf8 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -683,6 +683,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, EncodeCrossCrate::Yes ), + ungated!( + unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."), + DuplicatesOk, EncodeCrossCrate::No, + ), ungated!( rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk, EncodeCrossCrate::Yes diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 607028f4d9aef..14ec82ede1c5f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { let implied_obligations = traits::elaborate(tcx, predicates_with_span); for (pred, obligation_span) in implied_obligations { - // 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() { + // We lower empty bounds like `Vec:` as + // `WellFormed(Vec)`, which will later get checked by + // regular WF checking + ty::ClauseKind::WellFormed(..) + // Unstable feature goals cannot be proven in an empty environment so skip them + | 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/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 60ccc484e0026..af78130899e42 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -778,6 +778,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 \ @@ -805,6 +806,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 \ 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 574d19a5aa5a4..0043f0c71176a 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 94c93e7362749..3261327a9fda9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -921,6 +921,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 255ff56f62b1d..8244f0bed4c93 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1519,8 +1519,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 290213cab5e66..915b062417f2f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -137,6 +137,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; 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 7c48a4b0885e6..b857788b8b7e3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3237,6 +3237,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_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index ab2433234aa90..80c13e44d7d8e 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_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 75c2978878751..6b226b8a24d12 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -764,6 +764,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { ClauseKind::HostEffect(..) => { todo!() } + ClauseKind::UnstableFeature(_) => { + todo!() + } } } } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 0118cdb1fc274..ea7a524efe6d4 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -205,6 +205,46 @@ pub fn add_feature_diagnostics_for_issue( } } +/// This is only used by unstable_feature_bound as it does not have issue number information for now. +/// This is basically the same as `feature_err_issue` +/// but without the feature issue note. If we can do a lookup for issue number from feature name, +/// then we should directly use `feature_err_issue` for ambiguity error of +/// #[unstable_feature_bound]. +#[track_caller] +pub fn feature_err_unstable_feature_bound( + sess: &Session, + feature: Symbol, + span: impl Into, + explain: impl Into, +) -> Diag<'_> { + let span = span.into(); + + // Cancel an earlier warning for this same error, if it exists. + if let Some(span) = span.primary_span() { + if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { + err.cancel() + } + } + + let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); + + // #23973: do not suggest `#![feature(...)]` if we are in beta/stable + if sess.psess.unstable_features.is_nightly_build() { + err.subdiagnostic(FeatureDiagnosticHelp { feature }); + + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { + err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); + } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { + err.subdiagnostic(suggestion); + } + } + err +} + /// Info about a parsing session. pub struct ParseSess { dcx: DiagCtxt, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b12edf426c1b..bd34055f85133 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2284,6 +2284,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/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 712e88300fff9..98f67257fd137 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -12,6 +12,7 @@ use rustc_infer::traits::{ Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, }; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; +use rustc_session::parse::feature_err_unstable_feature_bound; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use tracing::{debug, instrument}; @@ -611,6 +612,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) .with_span_label(span, format!("cannot normalize `{alias}`")) } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(sym)) => { + if let Some(e) = self.tainted_by_errors() { + return e; + } + + let mut err; + + if self.tcx.features().staged_api() { + err = self.dcx().struct_span_err( + span, + format!("unstable feature `{sym}` is used without being enabled."), + ); + + err.help(format!("The feature can be enabled by marking the current item with `#[unstable_feature_bound({sym})]`")); + } else { + err = feature_err_unstable_feature_bound( + &self.tcx.sess, + sym, + span, + format!("use of unstable library feature `{sym}`"), + ); + } + err + } _ => { if let Some(e) = self.tainted_by_errors() { 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 cd3e6c4bc543f..1ac309da101f1 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 9a4f3887bbb59..ea1eed9572330 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/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3e13e50426674..2b5a41ef5a71b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -406,6 +406,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)) => { 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 141454bfe3754..25cd7787a3496 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -80,6 +80,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 fed9f254cdf83..adce9850b594c 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) @@ -1095,6 +1096,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 7ffcf7b5d9654..380082487579e 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -234,6 +234,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..a231908f874c2 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(_sym)) => {} + ty::PredicateKind::Ambiguous => {} } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index dd3cf1fc1811f..0ec326d21169e 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: ParamLike; type BoundTy: BoundVarLike; type PlaceholderTy: PlaceholderLike; + type Symbol: Copy + Hash + PartialEq + Eq + Debug; // 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..8bc15ec4ff557 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -46,6 +46,13 @@ pub enum ClauseKind { /// corresponding trait clause; this just enforces the *constness* of that /// implementation. HostEffect(ty::HostEffectPredicate), + + /// Support marking impl as unstable. + UnstableFeature( + #[type_foldable(identity)] + #[type_visitable(ignore)] + I::Symbol, + ), } #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] @@ -134,6 +141,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 e7a1f4d8397b4..3df04091f1631 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -390,7 +390,8 @@ pub(crate) fn clean_predicate<'tcx>( ty::ClauseKind::ConstEvaluatable(..) | ty::ClauseKind::WellFormed(..) | ty::ClauseKind::ConstArgHasType(..) - // FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `[const]`. + | ty::ClauseKind::UnstableFeature(..) + // FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `~const`. | ty::ClauseKind::HostEffect(_) => None, } } From 7dd28181e2243c3715efc7e0ce49848bfd256c86 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 13:38:36 +0000 Subject: [PATCH 7/8] Add tests --- .../auxiliary/unstable_feature.rs | 25 +++++++++++ .../auxiliary/unstable_impl_codegen_aux1.rs | 19 +++++++++ .../auxiliary/unstable_impl_codegen_aux2.rs | 13 ++++++ .../auxiliary/unstable_impl_coherence_aux.rs | 11 +++++ .../unstable_impl_method_selection_aux.rs | 20 +++++++++ .../unstable-feature-bound-two-error.rs | 12 ++++++ .../unstable-feature-bound-two-error.stderr | 13 ++++++ ...ature-cross-crate-exact-symbol.fail.stderr | 13 ++++++ ...stable-feature-cross-crate-exact-symbol.rs | 18 ++++++++ ...ble-feature-cross-crate-multiple-symbol.rs | 11 +++++ ...ture-cross-crate-require-bound.fail.stderr | 13 ++++++ ...table-feature-cross-crate-require-bound.rs | 14 +++++++ .../unstable-feature-exact-symbol.fail.stderr | 17 ++++++++ .../unstable-feature-exact-symbol.rs | 42 +++++++++++++++++++ .../unstable-impl-assoc-type.fail.stderr | 22 ++++++++++ .../unstable-impl-assoc-type.rs | 28 +++++++++++++ ...stable-impl-cannot-use-feature.fail.stderr | 17 ++++++++ .../unstable-impl-cannot-use-feature.rs | 30 +++++++++++++ .../unstable-impl-multiple-symbol.rs | 27 ++++++++++++ ...unstable_feature_bound_free_fn.fail.stderr | 17 ++++++++ .../unstable_feature_bound_free_fn.rs | 40 ++++++++++++++++++ .../unstable_feature_bound_multi_attr.rs | 36 ++++++++++++++++ .../unstable_feature_bound_multi_attr.stderr | 18 ++++++++ .../unstable_feature_bound_staged_api.rs | 13 ++++++ .../unstable_feature_bound_staged_api.stderr | 9 ++++ .../unstable_impl_codegen.rs | 13 ++++++ .../unstable_impl_coherence.disabled.stderr | 13 ++++++ .../unstable_impl_coherence.enabled.stderr | 13 ++++++ .../unstable_impl_coherence.rs | 17 ++++++++ .../unstable_impl_method_selection.rs | 15 +++++++ .../unstable_impl_method_selection.stderr | 14 +++++++ 31 files changed, 583 insertions(+) create mode 100644 tests/ui/unstable-feature-bound/auxiliary/unstable_feature.rs create mode 100644 tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux1.rs create mode 100644 tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux2.rs create mode 100644 tests/ui/unstable-feature-bound/auxiliary/unstable_impl_coherence_aux.rs create mode 100644 tests/ui/unstable-feature-bound/auxiliary/unstable_impl_method_selection_aux.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-cross-crate-multiple-symbol.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-impl-assoc-type.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-impl-assoc-type.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.rs create mode 100644 tests/ui/unstable-feature-bound/unstable-impl-multiple-symbol.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_codegen.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_coherence.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs create mode 100644 tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr diff --git a/tests/ui/unstable-feature-bound/auxiliary/unstable_feature.rs b/tests/ui/unstable-feature-bound/auxiliary/unstable_feature.rs new file mode 100644 index 0000000000000..3749deb76273f --- /dev/null +++ b/tests/ui/unstable-feature-bound/auxiliary/unstable_feature.rs @@ -0,0 +1,25 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![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() {} +} diff --git a/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux1.rs b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux1.rs new file mode 100644 index 0000000000000..8c0d14a1bab67 --- /dev/null +++ b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux1.rs @@ -0,0 +1,19 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![stable(feature = "a", since = "1.1.1" )] + +/// Aux crate for unstable impl codegen test. + +#[stable(feature = "a", since = "1.1.1" )] +pub trait Trait { + #[stable(feature = "a", since = "1.1.1" )] + fn method(&self); +} + +#[unstable_feature_bound(foo)] +#[unstable(feature = "foo", issue = "none" )] +impl Trait for T { + fn method(&self) { + println!("hi"); + } +} diff --git a/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux2.rs b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux2.rs new file mode 100644 index 0000000000000..1b0e2b2eec316 --- /dev/null +++ b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_codegen_aux2.rs @@ -0,0 +1,13 @@ +//@ aux-build:unstable_impl_codegen_aux1.rs +#![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 + +pub fn foo(a: T) { + a.method(); +} diff --git a/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_coherence_aux.rs b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_coherence_aux.rs new file mode 100644 index 0000000000000..2e05510121633 --- /dev/null +++ b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_coherence_aux.rs @@ -0,0 +1,11 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![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 {} diff --git a/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_method_selection_aux.rs b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_method_selection_aux.rs new file mode 100644 index 0000000000000..3a433007b5d27 --- /dev/null +++ b/tests/ui/unstable-feature-bound/auxiliary/unstable_impl_method_selection_aux.rs @@ -0,0 +1,20 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![stable(feature = "a", since = "1.1.1" )] + +#[stable(feature = "a", since = "1.1.1" )] +pub trait Trait { + #[stable(feature = "a", since = "1.1.1" )] + fn foo(&self) {} +} + +#[stable(feature = "a", since = "1.1.1" )] +impl Trait for Vec { + fn foo(&self) {} +} + +#[unstable_feature_bound(bar)] +#[unstable(feature = "bar", issue = "none" )] +impl Trait for Vec { + fn foo(&self) {} +} diff --git a/tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.rs b/tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.rs new file mode 100644 index 0000000000000..798c2d8562c0a --- /dev/null +++ b/tests/ui/unstable-feature-bound/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: use of unstable library feature `feat_bar` [E0658] + Moo::foo(); +} diff --git a/tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.stderr b/tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.stderr new file mode 100644 index 0000000000000..673d10ce2ad95 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-bound-two-error.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `feat_bar` + --> $DIR/unstable-feature-bound-two-error.rs:9:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: add `#![feature(feat_bar)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.fail.stderr b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.fail.stderr new file mode 100644 index 0000000000000..ce8d7358cfcff --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.fail.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `feat_moo` + --> $DIR/unstable-feature-cross-crate-exact-symbol.rs:16:5 + | +LL | Moo::foo(); + | ^^^ + | + = help: add `#![feature(feat_moo)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `Moo` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.rs b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-exact-symbol.rs new file mode 100644 index 0000000000000..a427bb8eb7ed9 --- /dev/null +++ b/tests/ui/unstable-feature-bound/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:use of unstable library feature `feat_moo` [E0658] +} diff --git a/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-multiple-symbol.rs b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-multiple-symbol.rs new file mode 100644 index 0000000000000..5b09c898a08bf --- /dev/null +++ b/tests/ui/unstable-feature-bound/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}; + +/// Bar::foo() should still be usable even if we enable multiple feature. + +fn main() { + Bar::foo(); +} diff --git a/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.fail.stderr b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.fail.stderr new file mode 100644 index 0000000000000..87a2ad41fc13a --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.fail.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `feat_bar` + --> $DIR/unstable-feature-cross-crate-require-bound.rs:12:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: add `#![feature(feat_bar)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.rs b/tests/ui/unstable-feature-bound/unstable-feature-cross-crate-require-bound.rs new file mode 100644 index 0000000000000..8be214b5324cc --- /dev/null +++ b/tests/ui/unstable-feature-bound/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: use of unstable library feature `feat_bar` [E0658] +} diff --git a/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.fail.stderr b/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.fail.stderr new file mode 100644 index 0000000000000..b8cb63ec65f3e --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.fail.stderr @@ -0,0 +1,17 @@ +error: unstable feature `feat_moo` is used without being enabled. + --> $DIR/unstable-feature-exact-symbol.rs:37:5 + | +LL | Bar::moo(); + | ^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_moo)]` +note: required for `Bar` to implement `Moo` + --> $DIR/unstable-feature-exact-symbol.rs:29:6 + | +LL | #[unstable_feature_bound(feat_moo)] + | ----------------------------------- unsatisfied trait bound introduced here +LL | impl Moo for Bar { + | ^^^ ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.rs b/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.rs new file mode 100644 index 0000000000000..2de9e6a857ead --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-feature-exact-symbol.rs @@ -0,0 +1,42 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![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 unstable feature `feat_moo` is used without being enabled. + +} + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.fail.stderr b/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.fail.stderr new file mode 100644 index 0000000000000..db9759b4cc3a5 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.fail.stderr @@ -0,0 +1,22 @@ +error: unstable feature `feat_foo` is used without being enabled. + --> $DIR/unstable-impl-assoc-type.rs:23:16 + | +LL | type Assoc = Self; + | ^^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_foo)]` +note: required for `Foo` to implement `Bar` + --> $DIR/unstable-impl-assoc-type.rs:19: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:13:17 + | +LL | type Assoc: Bar; + | ^^^ required by this bound in `Trait::Assoc` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.rs b/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.rs new file mode 100644 index 0000000000000..e31dc688dfab9 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-impl-assoc-type.rs @@ -0,0 +1,28 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![unstable(feature = "feat_foo", issue = "none" )] + +/// Test that you can't leak unstable impls through item bounds on associated types. + +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: unstable feature `feat_foo` is used without being enabled. + +} + +fn main(){} diff --git a/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.fail.stderr b/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.fail.stderr new file mode 100644 index 0000000000000..d56072362fe23 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.fail.stderr @@ -0,0 +1,17 @@ +error: unstable feature `feat_foo` is used without being enabled. + --> $DIR/unstable-impl-cannot-use-feature.rs:26:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_foo)]` +note: required for `Bar` to implement `Foo` + --> $DIR/unstable-impl-cannot-use-feature.rs:20:6 + | +LL | #[unstable_feature_bound(feat_foo)] + | ----------------------------------- unsatisfied trait bound introduced here +LL | impl Foo for Bar { + | ^^^ ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.rs b/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.rs new file mode 100644 index 0000000000000..0da618445fd21 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-impl-cannot-use-feature.rs @@ -0,0 +1,30 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![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: unstable feature `feat_foo` is used without being enabled. +} + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable-impl-multiple-symbol.rs b/tests/ui/unstable-feature-bound/unstable-impl-multiple-symbol.rs new file mode 100644 index 0000000000000..c9a9029b0a060 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable-impl-multiple-symbol.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![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() {} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.fail.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.fail.stderr new file mode 100644 index 0000000000000..f83542568289a --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.fail.stderr @@ -0,0 +1,17 @@ +error: unstable feature `feat_bar` is used without being enabled. + --> $DIR/unstable_feature_bound_free_fn.rs:36:5 + | +LL | bar(); + | ^^^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_bar)]` +note: required by a bound in `bar` + --> $DIR/unstable_feature_bound_free_fn.rs:29:1 + | +LL | #[unstable_feature_bound(feat_bar)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `bar` +LL | fn bar() { + | --- required by a bound in this function + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.rs new file mode 100644 index 0000000000000..30e2ab3d65ef2 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_free_fn.rs @@ -0,0 +1,40 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![allow(dead_code)] +#![stable(feature = "a", since = "1.1.1" )] + +/// When a free function with #[unstable_feature_bound(feat_bar)] is called by another +/// free function, that function should be annotated with +/// #[unstable_feature_bound(feat_bar)] too. + +#[stable(feature = "a", since = "1.1.1")] +trait Foo { + #[stable(feature = "a", since = "1.1.1")] + fn foo() { + } +} +#[stable(feature = "a", since = "1.1.1")] +pub struct Bar; + +#[unstable_feature_bound(feat_bar)] +#[unstable(feature = "feat_bar", issue = "none" )] +impl Foo for Bar { + fn foo() {} +} + + +#[unstable_feature_bound(feat_bar)] +fn bar() { + Bar::foo(); +} + +#[cfg_attr(pass, unstable_feature_bound(feat_bar))] +fn bar2() { + bar(); + //[fail]~^ERROR unstable feature `feat_bar` is used without being enabled. +} + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.rs new file mode 100644 index 0000000000000..3d6b52ba51771 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.rs @@ -0,0 +1,36 @@ +#![allow(internal_features)] +#![feature(staged_api)] +#![allow(dead_code)] +#![unstable(feature = "feat_bar", issue = "none" )] + +/// Test the behaviour of multiple unstable_feature_bound attribute. + +trait Foo { + fn foo(); +} +struct Bar; + +#[unstable_feature_bound(feat_bar, feat_koo)] +#[unstable_feature_bound(feat_foo, feat_moo)] +impl Foo for Bar { + fn foo(){} +} + +#[unstable_feature_bound(feat_bar, feat_koo)] +#[unstable_feature_bound(feat_foo, feat_moo)] +fn moo() { + Bar::foo(); +} + +#[unstable_feature_bound(feat_bar, feat_koo, feat_foo, feat_moo)] +fn koo() { + Bar::foo(); +} + +#[unstable_feature_bound(feat_koo, feat_foo, feat_moo)] +fn boo() { + Bar::foo(); + //~^ ERROR: unstable feature `feat_bar` is used without being enabled. +} + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.stderr new file mode 100644 index 0000000000000..936c70c197963 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_multi_attr.stderr @@ -0,0 +1,18 @@ +error: unstable feature `feat_bar` is used without being enabled. + --> $DIR/unstable_feature_bound_multi_attr.rs:32:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_bar)]` +note: required for `Bar` to implement `Foo` + --> $DIR/unstable_feature_bound_multi_attr.rs:15:6 + | +LL | #[unstable_feature_bound(feat_bar, feat_koo)] + | --------------------------------------------- unsatisfied trait bound introduced here +LL | #[unstable_feature_bound(feat_foo, feat_moo)] +LL | impl Foo for Bar { + | ^^^ ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.rs new file mode 100644 index 0000000000000..51e388f7dd323 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.rs @@ -0,0 +1,13 @@ +/// Unstable feature bound can only be used only when +/// #[feature(staged_api)] is enabled. + +pub trait Foo { +} +pub struct Bar; + +#[unstable_feature_bound(feat_bar)] +//~^ ERROR: stability attributes may not be used outside of the standard library +impl Foo for Bar { +} + +fn main(){} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.stderr new file mode 100644 index 0000000000000..35ab89e6ad99c --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_staged_api.stderr @@ -0,0 +1,9 @@ +error[E0734]: stability attributes may not be used outside of the standard library + --> $DIR/unstable_feature_bound_staged_api.rs:8:1 + | +LL | #[unstable_feature_bound(feat_bar)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0734`. diff --git a/tests/ui/unstable-feature-bound/unstable_impl_codegen.rs b/tests/ui/unstable-feature-bound/unstable_impl_codegen.rs new file mode 100644 index 0000000000000..285a64d2250af --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_impl_codegen.rs @@ -0,0 +1,13 @@ +//@ aux-build:unstable_impl_codegen_aux2.rs +//@ run-pass + +/// 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); +} diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr new file mode 100644 index 0000000000000..c3147558b03f9 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` + --> $DIR/unstable_impl_coherence.rs:14: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/unstable-feature-bound/unstable_impl_coherence.enabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr new file mode 100644 index 0000000000000..c3147558b03f9 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` + --> $DIR/unstable_impl_coherence.rs:14: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/unstable-feature-bound/unstable_impl_coherence.rs b/tests/ui/unstable-feature-bound/unstable_impl_coherence.rs new file mode 100644 index 0000000000000..22100f85f715b --- /dev/null +++ b/tests/ui/unstable-feature-bound/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(){} diff --git a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs new file mode 100644 index 0000000000000..acf521d3130c4 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs @@ -0,0 +1,15 @@ +//@ aux-build:unstable_impl_method_selection_aux.rs + +extern crate unstable_impl_method_selection_aux as aux; +use aux::Trait; + +// The test below should not infer the type based on the fact +// that `impl Trait for Vec` is unstable. This would cause breakage +// in downstream crate once `impl Trait for Vec` is stabilised. + +fn bar() { + vec![].foo(); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr new file mode 100644 index 0000000000000..c2bb10f043b2e --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr @@ -0,0 +1,14 @@ +error[E0283]: type annotations needed + --> $DIR/unstable_impl_method_selection.rs:11:12 + | +LL | vec![].foo(); + | ^^^ cannot infer type for struct `Vec<_>` + | + = note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_method_selection_aux` crate: + - impl Trait for Vec; + - impl Trait for Vec + where unstable feature: `bar`; + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. From e331de149f4fe5ddafe7f4bad13acb1499a6108e Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 14 Jul 2025 14:33:54 +0000 Subject: [PATCH 8/8] Fix CI --- compiler/rustc_session/src/parse.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index ea7a524efe6d4..9097b27b86c4c 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -209,7 +209,7 @@ pub fn add_feature_diagnostics_for_issue( /// This is basically the same as `feature_err_issue` /// but without the feature issue note. If we can do a lookup for issue number from feature name, /// then we should directly use `feature_err_issue` for ambiguity error of -/// #[unstable_feature_bound]. +/// `#[unstable_feature_bound]`. #[track_caller] pub fn feature_err_unstable_feature_bound( sess: &Session, diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index b558d0164483d..e86a2305e2334 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -319,11 +319,6 @@ where // Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled // if we are in std/core even if there is a corresponding `feature` attribute on the crate. - if (infcx.typing_mode() == TypingMode::PostAnalysis) + (infcx.typing_mode() == TypingMode::PostAnalysis) || infcx.cx().features().feature_bound_holds_in_crate(symbol) - { - return true; - } else { - return false; - } }