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