Auto merge of #140399 - tiif:unstable_impl, r=lcnr,BoxyUwU

Implement unstable trait impl

This PR allows marking impls of stable trait with stable type as unstable.

## Approach

In std/core, an impl can be marked as unstable by annotating it with ``#[unstable_feature_bound(feat_name)]``. This will add a ``ClauseKind::Unstable_Feature(feat_name)`` to the list of predicates in ``predicates_of`` .

When an unstable impl's function is called, we will first iterate through all the goals in ``param_env`` to check if there is any ``ClauseKind::UnstableFeature(feat_name)`` in ``param_env``.

The existence of ``ClauseKind::Unstable_Feature(feat_name)`` in ``param_env`` means an``#[unstable_feature_bound(feat_name)]`` is present at the call site of the function, so we allow the check to succeed in this case.

If ``ClauseKind::UnstableFeature(feat_name)`` does not exist in ``param_env``, we will still allow the check to succeed for either of the cases below:
1. The feature is enabled through ``#[feature(feat_name)]`` outside of std / core.
2. We are in codegen because we may be monomorphizing a body from an upstream crate which had an unstable feature enabled that the downstream crate do not.

For the rest of the case, it will fail with ambiguity.

## Limitation

In this PR, we do not support:
1. using items that need ``#[unstable_feature_bound]`` within stable APIs
2. annotate main function with ``#[unstable_feature_bound]``
3. annotate ``#[unstable_feature_bound]`` on items other than free function and impl

## Acknowledgement
The design and mentoring are done by `@BoxyUwU`
This commit is contained in:
bors 2025-07-17 01:57:55 +00:00
commit 014bd8290f
82 changed files with 1064 additions and 13 deletions

View file

@ -420,6 +420,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_unsafe_specialization_marker]`.
UnsafeSpecializationMarker(Span),
/// Represents `#[unstable_feature_bound]`.
UnstableFeatureBound(ThinVec<(Symbol, Span)>),
/// Represents `#[used]`
Used { used_by: UsedBy, span: Span },
// tidy-alphabetical-end

View file

@ -71,6 +71,7 @@ impl AttributeKind {
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
UnsafeSpecializationMarker(..) => No,
UnstableFeatureBound(..) => No,
Used { .. } => No,
// tidy-alphabetical-end
}

View file

@ -136,6 +136,9 @@ attr_parsing_unrecognized_repr_hint =
attr_parsing_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change
attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
attr_parsing_unsupported_literal_cfg_boolean =
literal in `cfg` predicate value must be a boolean
attr_parsing_unsupported_literal_cfg_string =

View file

@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
}
}
pub(crate) struct UnstableFeatureBoundParser;
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> {
if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
}
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
.into_iter()
.zip(iter::repeat(cx.attr_span))
}
}
pub(crate) struct AllowConstFnUnstableParser;
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];

View file

@ -98,6 +98,16 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
}
}
if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability {
for other_attr in cx.all_attrs {
if other_attr.word_is(sym::unstable_feature_bound) {
cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability {
span: cx.target_span,
});
}
}
}
let (stability, span) = self.stability?;
Some(AttributeKind::Stability { stability, span })

View file

@ -13,7 +13,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
@ -133,6 +135,7 @@ attribute_parsers!(
Combine<AllowInternalUnstableParser>,
Combine<ReprParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end
// tidy-alphabetical-start

View file

@ -503,6 +503,14 @@ pub(crate) struct UnrecognizedReprHint {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)]
#[help]
pub(crate) struct UnstableFeatureBoundIncompatibleStability {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
pub(crate) struct NakedFunctionIncompatibleAttribute {

View file

@ -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

View file

@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
let implied_obligations = traits::elaborate(tcx, predicates_with_span);
for (pred, obligation_span) in implied_obligations {
// We lower empty bounds like `Vec<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, 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<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, 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);

View file

@ -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
@ -764,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
bug!(
"unexpected non-`Self` predicate when computing \
@ -791,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {
bug!(
"unexpected non-`Self` predicate when computing \

View file

@ -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,
}
}

View file

@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View file

@ -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,
}
}

View file

@ -921,6 +921,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => None,
}
});

View file

@ -1519,8 +1519,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
ClauseKind::TypeOutlives(..) |
ClauseKind::RegionOutlives(..) => "lifetime",
ClauseKind::UnstableFeature(_)
// `ConstArgHasType` is never global as `ct` is always a param
ClauseKind::ConstArgHasType(..)
| ClauseKind::ConstArgHasType(..)
// Ignore projections, as they can only be global
// if the trait bound is global
| ClauseKind::Projection(..)

View file

@ -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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> for Span {

View file

@ -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(..)

View file

@ -3237,6 +3237,7 @@ define_print! {
ty::ClauseKind::ConstEvaluatable(ct) => {
p!("the constant `", print(ct), "` can be evaluated")
}
ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)),
}
}

View file

@ -22,6 +22,7 @@ use crate::delegate::SolverDelegate;
use crate::placeholder::BoundVarReplacer;
use crate::solve::inspect::{self, ProofTreeBuilder};
use crate::solve::search_graph::SearchGraph;
use crate::solve::ty::may_use_unstable_feature;
use crate::solve::{
CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind,
GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput,
@ -550,6 +551,9 @@ where
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
self.compute_unstable_feature_goal(param_env, symbol)
}
ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate })
}
@ -1177,6 +1181,14 @@ where
) -> T {
BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0
}
pub(super) fn may_use_unstable_feature(
&self,
param_env: I::ParamEnv,
symbol: I::Symbol,
) -> bool {
may_use_unstable_feature(&**self.delegate, param_env, symbol)
}
}
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`

View file

@ -148,6 +148,20 @@ where
}
}
fn compute_unstable_feature_goal(
&mut self,
param_env: <I as Interner>::ParamEnv,
symbol: <I as Interner>::Symbol,
) -> QueryResult<I> {
if self.may_use_unstable_feature(param_env, symbol) {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
MaybeCause::Ambiguity,
))
}
}
#[instrument(level = "trace", skip(self))]
fn compute_const_evaluatable_goal(
&mut self,

View file

@ -667,6 +667,10 @@ passes_rustc_std_internal_symbol =
attribute should be applied to functions or statics
.label = not a function or static
passes_rustc_unstable_feature_bound =
attribute should be applied to `impl` or free function outside of any `impl` or trait
.label = not an `impl` or free function
passes_should_be_applied_to_fn =
attribute should be applied to a function definition
.label = {$on_crate ->

View file

@ -247,6 +247,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
&Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => {
self.check_ffi_pure(attr_span, attrs, target)
}
Attribute::Parsed(AttributeKind::UnstableFeatureBound(syms)) => {
self.check_unstable_feature_bound(syms.first().unwrap().1, span, target)
}
Attribute::Parsed(
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
@ -2267,6 +2270,47 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) {
match target {
// FIXME(staged_api): There's no reason we can't support more targets here. We're just
// being conservative to begin with.
Target::Fn | Target::Impl => {}
Target::ExternCrate
| Target::Use
| Target::Static
| Target::Const
| Target::Closure
| Target::Mod
| Target::ForeignMod
| Target::GlobalAsm
| Target::TyAlias
| Target::Enum
| Target::Variant
| Target::Struct
| Target::Field
| Target::Union
| Target::Trait
| Target::TraitAlias
| Target::Expression
| Target::Statement
| Target::Arm
| Target::AssocConst
| Target::Method(_)
| Target::AssocTy
| Target::ForeignFn
| Target::ForeignStatic
| Target::ForeignTy
| Target::GenericParam(_)
| Target::MacroDef
| Target::Param
| Target::PatField
| Target::ExprField
| Target::WherePredicate => {
self.tcx.dcx().emit_err(errors::RustcUnstableFeatureBound { attr_span, span });
}
}
}
fn check_rustc_std_internal_symbol(&self, attr_span: Span, span: Span, target: Target) {
match target {
Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {}

View file

@ -686,6 +686,15 @@ pub(crate) struct RustcAllowConstFnUnstable {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_rustc_unstable_feature_bound)]
pub(crate) struct RustcUnstableFeatureBound {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_rustc_std_internal_symbol)]
pub(crate) struct RustcStdInternalSymbol {

View file

@ -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(),

View file

@ -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(),
}
}

View file

@ -764,6 +764,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
ClauseKind::HostEffect(..) => {
todo!()
}
ClauseKind::UnstableFeature(_) => {
todo!()
}
}
}
}

View file

@ -205,6 +205,46 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
}
}
/// This is only used by unstable_feature_bound as it does not have issue number information for now.
/// This is basically the same as `feature_err_issue`
/// but without the feature issue note. If we can do a lookup for issue number from feature name,
/// then we should directly use `feature_err_issue` for ambiguity error of
/// `#[unstable_feature_bound]`.
#[track_caller]
pub fn feature_err_unstable_feature_bound(
sess: &Session,
feature: Symbol,
span: impl Into<MultiSpan>,
explain: impl Into<DiagMessage>,
) -> Diag<'_> {
let span = span.into();
// Cancel an earlier warning for this same error, if it exists.
if let Some(span) = span.primary_span() {
if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) {
err.cancel()
}
}
let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
if sess.psess.unstable_features.is_nightly_build() {
err.subdiagnostic(FeatureDiagnosticHelp { feature });
if feature == sym::rustc_attrs {
// We're unlikely to stabilize something out of `rustc_attrs`
// without at least renaming it, so pointing out how old
// the compiler is will do little good.
} else if sess.opts.unstable_opts.ui_testing {
err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
} else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
err.subdiagnostic(suggestion);
}
}
err
}
/// Info about a parsing session.
pub struct ParseSess {
dcx: DiagCtxt,

View file

@ -2284,6 +2284,7 @@ symbols! {
unsized_locals,
unsized_tuple_coercion,
unstable,
unstable_feature_bound,
unstable_location_reason_default: "this crate is being loaded from the sysroot, an \
unstable location; did you mean to load this crate \
from crates.io via `Cargo.toml` instead?",

View file

@ -12,6 +12,7 @@ use rustc_infer::traits::{
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
use rustc_session::parse::feature_err_unstable_feature_bound;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use tracing::{debug, instrument};
@ -611,6 +612,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)
.with_span_label(span, format!("cannot normalize `{alias}`"))
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(sym)) => {
if let Some(e) = self.tainted_by_errors() {
return e;
}
let mut err;
if self.tcx.features().staged_api() {
err = self.dcx().struct_span_err(
span,
format!("unstable feature `{sym}` is used without being enabled."),
);
err.help(format!("The feature can be enabled by marking the current item with `#[unstable_feature_bound({sym})]`"));
} else {
err = feature_err_unstable_feature_bound(
&self.tcx.sess,
sym,
span,
format!("use of unstable library feature `{sym}`"),
);
}
err
}
_ => {
if let Some(e) = self.tainted_by_errors() {

View file

@ -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 { .. }) => {

View file

@ -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,
};

View file

@ -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,
})
}

View file

@ -11,7 +11,9 @@ use rustc_infer::traits::{
use rustc_middle::bug;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode};
use rustc_middle::ty::{
self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode, may_use_unstable_feature,
};
use thin_vec::{ThinVec, thin_vec};
use tracing::{debug, debug_span, instrument};
@ -404,6 +406,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used by the new solver")
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {
unreachable!("unexpected higher ranked `UnstableFeature` goal")
}
},
Some(pred) => match pred {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
@ -767,6 +772,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
if may_use_unstable_feature(self.selcx.infcx, obligation.param_env, symbol) {
ProcessResult::Changed(Default::default())
} else {
ProcessResult::Unchanged
}
}
},
}
}

View file

@ -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

View file

@ -28,7 +28,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
use rustc_middle::ty::{
self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt,
TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate,
TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature,
};
use rustc_span::{Symbol, sym};
use tracing::{debug, instrument, trace};
@ -832,6 +832,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
if may_use_unstable_feature(self.infcx, obligation.param_env, symbol) {
Ok(EvaluatedToOk)
} else {
Ok(EvaluatedToAmbig)
}
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.infcx,

View file

@ -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(..) => {}
}
}

View file

@ -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,
}
})

View file

@ -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(..))

View file

@ -234,6 +234,9 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
ty::ClauseKind::ConstArgHasType(..) => {
// Nothing to elaborate
}
ty::ClauseKind::UnstableFeature(_) => {
// Nothing to elaborate
}
}
}
}

View file

@ -401,7 +401,6 @@ impl<I: Interner> FlagComputation<I> {
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<I: Interner> FlagComputation<I> {
self.add_term(t1);
self.add_term(t2);
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_sym)) => {}
ty::PredicateKind::Ambiguous => {}
}
}

View file

@ -285,3 +285,40 @@ pub trait InferCtxtLike: Sized {
fn reset_opaque_types(&self);
}
pub fn may_use_unstable_feature<'a, I: Interner, Infcx>(
infcx: &'a Infcx,
param_env: I::ParamEnv,
symbol: I::Symbol,
) -> bool
where
Infcx: InferCtxtLike<Interner = I>,
{
// Iterate through all goals in param_env to find the one that has the same symbol.
for pred in param_env.caller_bounds().iter() {
if let ty::ClauseKind::UnstableFeature(sym) = pred.kind().skip_binder() {
if sym == symbol {
return true;
}
}
}
// During codegen we must assume that all feature bounds hold as we may be
// monomorphizing a body from an upstream crate which had an unstable feature
// enabled that we do not.
//
// Coherence should already report overlap errors involving unstable impls
// as the affected code would otherwise break when stabilizing this feature.
// It is also easily possible to accidentally cause unsoundness this way as
// we have to always enable unstable impls during codegen.
//
// Return ambiguity can also prevent people from writing code which depends on inference guidance
// that might no longer work after the impl is stabilised,
// tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs is one of the example.
//
// Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled
// if we are in std/core even if there is a corresponding `feature` attribute on the crate.
(infcx.typing_mode() == TypingMode::PostAnalysis)
|| infcx.cx().features().feature_bound_holds_in_crate(symbol)
}

View file

@ -630,6 +630,8 @@ pub trait Features<I: Interner>: 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<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {

View file

@ -106,6 +106,7 @@ pub trait Interner:
type ParamTy: ParamLike;
type BoundTy: BoundVarLike<Self>;
type PlaceholderTy: PlaceholderLike<Self, Bound = Self::BoundTy>;
type Symbol: Copy + Hash + PartialEq + Eq + Debug;
// Things stored inside of tys
type ErrorGuaranteed: Copy + Debug + Hash + Eq;

View file

@ -46,6 +46,13 @@ pub enum ClauseKind<I: Interner> {
/// corresponding trait clause; this just enforces the *constness* of that
/// implementation.
HostEffect(ty::HostEffectPredicate<I>),
/// 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<I: Interner> fmt::Debug for ClauseKind<I> {
ClauseKind::ConstEvaluatable(ct) => {
write!(f, "ConstEvaluatable({ct:?})")
}
ClauseKind::UnstableFeature(feature_name) => {
write!(f, "UnstableFeature({feature_name:?})")
}
}
}
}

View file

@ -390,7 +390,8 @@ pub(crate) fn clean_predicate<'tcx>(
ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::WellFormed(..)
| ty::ClauseKind::ConstArgHasType(..)
// FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `[const]`.
| ty::ClauseKind::UnstableFeature(..)
// FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `~const`.
| ty::ClauseKind::HostEffect(_) => None,
}
}

View file

@ -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() {}
}

View file

@ -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<T> Trait for T {
fn method(&self) {
println!("hi");
}
}

View file

@ -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<T>(a: T) {
a.method();
}

View file

@ -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 <T> Trait for T {}

View file

@ -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<u32> {
fn foo(&self) {}
}
#[unstable_feature_bound(bar)]
#[unstable(feature = "bar", issue = "none" )]
impl Trait for Vec<u64> {
fn foo(&self) {}
}

View file

@ -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() {}

View file

@ -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 <https://github.com/rust-lang/rust/issues/55436> for more information
= note: `#[deny(ineffective_unstable_trait_impl)]` on by default
error: aborting due to 1 previous error

View file

@ -0,0 +1,12 @@
//@ aux-build:unstable_feature.rs
extern crate unstable_feature;
use unstable_feature::{Foo, Bar, Moo};
// FIXME: both `feat_bar` and `feat_moo` are needed to pass this test,
// but the diagnostic only will point out `feat_bar`.
fn main() {
Bar::foo();
//~^ ERROR: use of unstable library feature `feat_bar` [E0658]
Moo::foo();
}

View file

@ -0,0 +1,13 @@
error[E0658]: use of unstable library feature `feat_bar`
--> $DIR/unstable-feature-bound-two-error.rs:9:5
|
LL | Bar::foo();
| ^^^
|
= help: add `#![feature(feat_bar)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: required for `Bar` to implement `Foo`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,13 @@
error[E0658]: use of unstable library feature `feat_moo`
--> $DIR/unstable-feature-cross-crate-exact-symbol.rs:16:5
|
LL | Moo::foo();
| ^^^
|
= help: add `#![feature(feat_moo)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: required for `Moo` to implement `Foo`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,18 @@
//@ aux-build:unstable_feature.rs
//@ revisions: pass fail
//@[pass] check-pass
#![cfg_attr(pass, feature(feat_bar, feat_moo))]
#![cfg_attr(fail, feature(feat_bar))]
extern crate unstable_feature;
use unstable_feature::{Foo, Bar, Moo};
/// To use impls gated by both `feat_foo` and `feat_moo`,
/// both features must be enabled.
fn main() {
Bar::foo();
Moo::foo();
//[fail]~^ ERROR:use of unstable library feature `feat_moo` [E0658]
}

View file

@ -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();
}

View file

@ -0,0 +1,13 @@
error[E0658]: use of unstable library feature `feat_bar`
--> $DIR/unstable-feature-cross-crate-require-bound.rs:12:5
|
LL | Bar::foo();
| ^^^
|
= help: add `#![feature(feat_bar)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: required for `Bar` to implement `Foo`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,14 @@
//@ aux-build:unstable_feature.rs
//@ revisions: pass fail
//@[pass] check-pass
#![cfg_attr(pass, feature(feat_bar))]
extern crate unstable_feature;
use unstable_feature::{Foo, Bar};
/// #[feature(..)] is required to use unstable impl.
fn main() {
Bar::foo();
//[fail]~^ ERROR: use of unstable library feature `feat_bar` [E0658]
}

View file

@ -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

View file

@ -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() {}

View file

@ -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

View file

@ -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(){}

View file

@ -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

View file

@ -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() {}

View file

@ -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() {}

View file

@ -0,0 +1,17 @@
error: unstable feature `feat_bar` is used without being enabled.
--> $DIR/unstable_feature_bound_free_fn.rs:36:5
|
LL | bar();
| ^^^^^
|
= help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(feat_bar)]`
note: required by a bound in `bar`
--> $DIR/unstable_feature_bound_free_fn.rs:29:1
|
LL | #[unstable_feature_bound(feat_bar)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `bar`
LL | fn bar() {
| --- required by a bound in this function
error: aborting due to 1 previous error

View file

@ -0,0 +1,40 @@
//@ revisions: pass fail
//@[pass] check-pass
#![allow(internal_features)]
#![feature(staged_api)]
#![allow(dead_code)]
#![stable(feature = "a", since = "1.1.1" )]
/// When a free function with #[unstable_feature_bound(feat_bar)] is called by another
/// free function, that function should be annotated with
/// #[unstable_feature_bound(feat_bar)] too.
#[stable(feature = "a", since = "1.1.1")]
trait Foo {
#[stable(feature = "a", since = "1.1.1")]
fn foo() {
}
}
#[stable(feature = "a", since = "1.1.1")]
pub struct Bar;
#[unstable_feature_bound(feat_bar)]
#[unstable(feature = "feat_bar", issue = "none" )]
impl Foo for Bar {
fn foo() {}
}
#[unstable_feature_bound(feat_bar)]
fn bar() {
Bar::foo();
}
#[cfg_attr(pass, unstable_feature_bound(feat_bar))]
fn bar2() {
bar();
//[fail]~^ERROR unstable feature `feat_bar` is used without being enabled.
}
fn main() {}

View file

@ -0,0 +1,14 @@
#![allow(internal_features)]
#![feature(staged_api)]
#![allow(dead_code)]
#![stable(feature = "a", since = "1.1.1" )]
// Lint against the usage of both #[unstable_feature_bound] and #[stable] on the
// same item.
#[stable(feature = "a", since = "1.1.1")]
#[unstable_feature_bound(feat_bar)]
fn bar() {}
//~^ ERROR Item annotated with `#[unstable_feature_bound]` should not be stable
fn main() {}

View file

@ -0,0 +1,10 @@
error: Item annotated with `#[unstable_feature_bound]` should not be stable
--> $DIR/unstable_feature_bound_incompatible_stability.rs:11:1
|
LL | fn bar() {}
| ^^^^^^^^^^^
|
= help: If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
error: aborting due to 1 previous error

View file

@ -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() {}

View file

@ -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

View file

@ -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(){}

View file

@ -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`.

View file

@ -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);
}

View file

@ -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<T> Trait for T
where unstable feature: `foo`;
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0119`.

View file

@ -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<T> Trait for T
where unstable feature: `foo`;
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0119`.

View file

@ -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(){}

View file

@ -0,0 +1,15 @@
//@ aux-build:unstable_impl_method_selection_aux.rs
extern crate unstable_impl_method_selection_aux as aux;
use aux::Trait;
// The test below should not infer the type based on the fact
// that `impl Trait for Vec<u64>` is unstable. This would cause breakage
// in downstream crate once `impl Trait for Vec<u64>` is stabilised.
fn bar() {
vec![].foo();
//~^ ERROR type annotations needed
}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0283]: type annotations needed
--> $DIR/unstable_impl_method_selection.rs:11:12
|
LL | vec![].foo();
| ^^^ cannot infer type for struct `Vec<_>`
|
= note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_method_selection_aux` crate:
- impl Trait for Vec<u32>;
- impl Trait for Vec<u64>
where unstable feature: `bar`;
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0283`.

View file

@ -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() {}

View file

@ -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