diff --git a/Cargo.lock b/Cargo.lock index e95cacf1f6d3d..1a0443586d66b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4567,6 +4567,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_transmute", + "rustc_type_ir", "smallvec", "thin-vec", "tracing", diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index d755afad59fb8..e077164d4ad8b 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -285,5 +285,8 @@ pub enum AttributeKind { /// Represents `#[track_caller]` TrackCaller(Span), + + /// Represents `#[unstable_feature_bound]`. + UnstableFeatureBound(ThinVec<(Symbol, Span)>), // tidy-alphabetical-end } 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 d4402f4e2e63d..6d982d5540638 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -38,6 +38,7 @@ impl AttributeKind { PubTransparent(..) => Yes, SkipDuringMethodDispatch { .. } => No, TrackCaller(..) => Yes, + UnstableFeatureBound(..) => No, } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 21b01a8d071bf..d761ff8c44f28 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -26,6 +26,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 = AttributeKind::UnstableFeatureBound; + 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_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 83e3c75dedb4e..407ac4429e12a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -14,7 +14,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, OptimizeParser, TrackCallerParser, }; @@ -109,6 +111,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 8e1392998d406..af7e7df4db30f 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 00f9347b1cc81..b29680908612e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2310,12 +2310,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 c337765c5fec3..0b9792a49d1b0 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 @@ -762,6 +776,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 \ @@ -789,6 +804,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 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 be0eb13cace67..860b4bcb04dd7 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 172f3372483ae..c8433f5cf086a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1526,8 +1526,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 457a4f4d5027a..cafdd7680454a 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; @@ -833,6 +834,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_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 c10277c75a79b..3cd1b9c7cdb73 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3276,6 +3276,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 dd9ccadf6cf64..4598a5b990c13 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 @@ -551,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 }) } @@ -1184,6 +1187,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 { + self.delegate.may_use_unstable_feature(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 4f845ef9cd94c..e7f5440d39941 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) { + 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_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index e2995daadfe56..6b7eb5eef5c40 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -662,6 +662,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 491b3699f4c32..24b1e7d2b193d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -183,6 +183,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => { self.check_track_caller(hir_id, *attr_span, attrs, span, target) } + Attribute::Parsed(AttributeKind::UnstableFeatureBound(syms)) => { + self.check_unstable_feature_bound(syms.first().unwrap().1, span, target) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -2234,6 +2237,46 @@ 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: &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 3286ccc94f210..e7f13e2c8d306 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -692,6 +692,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/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 56d9f5bf78577..7179e1ede6f6a 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/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 7abec488151c7..6ae1b66e9909d 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 368944ffd5cce..237d42ec17bd7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2283,6 +2283,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/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 1071105522d11..fc6d205b3f53b 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -20,6 +20,7 @@ rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } +rustc_type_ir = {path = "../rustc_type_ir"} smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2" tracing = "0.1" 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 39f115ce0cd56..8523b8297208c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -610,6 +610,24 @@ 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 = self.dcx().struct_span_err( + span, + format!("unstable feature `{sym}` is used without being enabled."), + ); + + if self.tcx.features().staged_api() { + err.help(format!("The feature can be enabled by marking the current item with `#[unstable_feature_bound({sym})]`")); + } else { + err.help(format!("The feature can be enabled by adding `#![feature({sym})]` to the crate root")); + } + + 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 65e31557bb628..0d2eaa3626691 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 bdfe48a3928eb..60c38bbea9038 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 64a51e0550ba1..46c6c86ea7c2e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -12,6 +12,7 @@ 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_type_ir::InferCtxtLike; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, debug_span, instrument}; @@ -404,6 +405,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)) => { @@ -767,6 +771,14 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + #[allow(rustc::usage_of_type_ir_traits)] + if self.selcx.infcx.may_use_unstable_feature(obligation.param_env, symbol) { + return ProcessResult::Changed(Default::default()); + } else { + return ProcessResult::Unchanged; + } + } }, } } 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/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 316b4dfc15fd2..9f0be92a61ca9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -31,6 +31,7 @@ use rustc_middle::ty::{ TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, }; use rustc_span::{Symbol, sym}; +use rustc_type_ir::InferCtxtLike; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -844,6 +845,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + #[allow(rustc::usage_of_type_ir_traits)] + if self.infcx.may_use_unstable_feature(obligation.param_env, symbol) { + return Ok(EvaluatedToOk); + } else { + return Ok(EvaluatedToAmbig); + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index a05bae535663d..fce0c7cfe0f85 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 d4e6a23f0eb6b..9fd101161676f 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 852949d707bff..582561f06205d 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/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 2bc12d0a23bfe..a1f4e2ab17a26 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -276,4 +276,43 @@ pub trait InferCtxtLike: Sized { ); fn reset_opaque_types(&self); + + fn may_use_unstable_feature( + &self, + param_env: ::ParamEnv, + symbol: ::Symbol, + ) -> bool { + // 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_coherence_inherence.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 (self.typing_mode() == TypingMode::PostAnalysis) + || self.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 272fca5b026c5..0d836c8db989f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -631,6 +631,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 { 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 d77bdf09d010a..5d543794ade9a 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, } 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_coherence_inference_aux.rs b/tests/ui/unstable-feature_bound/auxiliary/unstable_impl_coherence_inference_aux.rs new file mode 100644 index 0000000000000..3a433007b5d27 --- /dev/null +++ b/tests/ui/unstable-feature_bound/auxiliary/unstable_impl_coherence_inference_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-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 + 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..bcbde4cc315a7 --- /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: unstable feature `feat_bar` is used without being enabled. + 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..65f78c576ef8e --- /dev/null +++ b/tests/ui/unstable-feature_bound/unstable-feature-bound-two-error.stderr @@ -0,0 +1,11 @@ +error: unstable feature `feat_bar` is used without being enabled. + --> $DIR/unstable-feature-bound-two-error.rs:9:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: The feature can be enabled by adding `#![feature(feat_bar)]` to the crate root + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + 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..6990c893fb2d4 --- /dev/null +++ b/tests/ui/unstable-feature_bound/unstable-feature-cross-crate-exact-symbol.fail.stderr @@ -0,0 +1,11 @@ +error: unstable feature `feat_moo` is used without being enabled. + --> $DIR/unstable-feature-cross-crate-exact-symbol.rs:16:5 + | +LL | Moo::foo(); + | ^^^ + | + = help: The feature can be enabled by adding `#![feature(feat_moo)]` to the crate root + = note: required for `Moo` to implement `Foo` + +error: aborting due to 1 previous error + 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..2053694952b6c --- /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: unstable feature `feat_moo` is used without being enabled. +} 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..75894b8bd20f0 --- /dev/null +++ b/tests/ui/unstable-feature_bound/unstable-feature-cross-crate-require-bound.fail.stderr @@ -0,0 +1,11 @@ +error: unstable feature `feat_bar` is used without being enabled. + --> $DIR/unstable-feature-cross-crate-require-bound.rs:12:5 + | +LL | Bar::foo(); + | ^^^ + | + = help: The feature can be enabled by adding `#![feature(feat_bar)]` to the crate root + = note: required for `Bar` to implement `Foo` + +error: aborting due to 1 previous error + 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..eabe897364602 --- /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: unstable feature `feat_bar` is used without being enabled. +} 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_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_coherence_inherence.rs b/tests/ui/unstable-feature_bound/unstable_impl_coherence_inherence.rs new file mode 100644 index 0000000000000..e067a3d50e358 --- /dev/null +++ b/tests/ui/unstable-feature_bound/unstable_impl_coherence_inherence.rs @@ -0,0 +1,15 @@ +//@ aux-build:unstable_impl_coherence_inference_aux.rs + +extern crate unstable_impl_coherence_inference_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_coherence_inherence.stderr b/tests/ui/unstable-feature_bound/unstable_impl_coherence_inherence.stderr new file mode 100644 index 0000000000000..e22a09c28f694 --- /dev/null +++ b/tests/ui/unstable-feature_bound/unstable_impl_coherence_inherence.stderr @@ -0,0 +1,14 @@ +error[E0283]: type annotations needed + --> $DIR/unstable_impl_coherence_inherence.rs:11:12 + | +LL | vec![].foo(); + | ^^^ cannot infer type for struct `Vec<_>` + | + = note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_coherence_inference_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`. 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 +