Skip to content

Implement unstable trait impl #140399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,5 +285,8 @@ pub enum AttributeKind {

/// Represents `#[track_caller]`
TrackCaller(Span),

/// Represents `#[unstable_feature_bound]`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a space above this simply to be consistent with the other ones (minor hehe)

UnstableFeatureBound(ThinVec<(Symbol, Span)>),
// tidy-alphabetical-end
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl AttributeKind {
PubTransparent(..) => Yes,
SkipDuringMethodDispatch { .. } => No,
TrackCaller(..) => Yes,
UnstableFeatureBound(..) => No,
}
}
}
20 changes: 20 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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> = AttributeKind::UnstableFeatureBound;
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];
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
};
Expand Down Expand Up @@ -109,6 +111,7 @@ attribute_parsers!(
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<ReprParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end

// tidy-alphabetical-start
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're moving lots of this info into the attribute system, that's why it duplicates the template for now. When you'll rebase on master you'll also have to duplicate the cross-crate-encoding somewhere else to make it compile

),
ungated!(
rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
DuplicatesOk, EncodeCrossCrate::Yes
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2310,12 +2310,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
let implied_obligations = traits::elaborate(tcx, predicates_with_span);

for (pred, obligation_span) in implied_obligations {
// We lower empty bounds like `Vec<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);
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -762,6 +776,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
bug!(
"unexpected non-`Self` predicate when computing \
Expand Down Expand Up @@ -789,6 +804,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {
bug!(
"unexpected non-`Self` predicate when computing \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/outlives/explicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
});
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1526,8 +1526,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
ClauseKind::TypeOutlives(..) |
ClauseKind::RegionOutlives(..) => "lifetime",

ClauseKind::UnstableFeature(_)
// `ConstArgHasType` is never global as `ct` is always a param
ClauseKind::ConstArgHasType(..)
| ClauseKind::ConstArgHasType(..)
// Ignore projections, as they can only be global
// if the trait bound is global
| ClauseKind::Projection(..)
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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: <TyCtxt<'tcx> as Interner>::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 {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_)
Expand Down Expand Up @@ -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(..)
Expand All @@ -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(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3276,6 +3276,7 @@ define_print! {
ty::ClauseKind::ConstEvaluatable(ct) => {
p!("the constant `", print(ct), "` can be evaluated")
}
ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)),
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,9 @@ where
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
self.compute_unstable_feature_goal(param_env, symbol)
}
ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate })
}
Expand Down
31 changes: 31 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,37 @@ where
}
}

fn compute_unstable_feature_goal(
&mut self,
param_env: <I as Interner>::ParamEnv,
symbol: <I as Interner>::Symbol,
) -> QueryResult<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 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
}
}

// 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.
//
// Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled
// if we are in std/core even if there is a corresponding `feature` attribute on the crate.
if self.cx().features().feature_bound_holds_in_crate(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,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,10 @@ passes_rustc_std_internal_symbol =
attribute should be applied to functions or statics
.label = not a function or static

passes_rustc_unstable_feature_bound =
attribute should be applied to `impl` or free function outside of any `impl` or trait
.label = not an `impl` or free function

passes_should_be_applied_to_fn =
attribute should be applied to a function definition
.label = {$on_crate ->
Expand Down
43 changes: 43 additions & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {
self.check_track_caller(hir_id, *attr_span, attrs, span, target)
}
Attribute::Parsed(AttributeKind::UnstableFeatureBound(syms)) => {
self.check_unstable_feature_bound(syms.first().unwrap().1, span, target)
}
Attribute::Parsed(
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
Expand Down Expand Up @@ -2234,6 +2237,46 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}

fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) {
match target {
// FIXME(staged_api): There's no reason we can't support more targets here. We're just
// being conservative to begin with.
Target::Fn | Target::Impl => {}
Target::ExternCrate
| Target::Use
| Target::Static
| Target::Const
| Target::Closure
| Target::Mod
| Target::ForeignMod
| Target::GlobalAsm
| Target::TyAlias
| Target::Enum
| Target::Variant
| Target::Struct
| Target::Field
| Target::Union
| Target::Trait
| Target::TraitAlias
| Target::Expression
| Target::Statement
| Target::Arm
| Target::AssocConst
| Target::Method(_)
| Target::AssocTy
| Target::ForeignFn
| Target::ForeignStatic
| Target::ForeignTy
| Target::GenericParam(_)
| Target::MacroDef
| Target::Param
| Target::PatField
| Target::ExprField
| Target::WherePredicate => {
self.tcx.dcx().emit_err(errors::RustcUnstableFeatureBound { attr_span, span });
}
}
}
fn check_rustc_std_internal_symbol(&self, attr: &Attribute, span: Span, target: Target) {
match target {
Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {}
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,15 @@ pub(crate) struct RustcAllowConstFnUnstable {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(passes_rustc_unstable_feature_bound)]
pub(crate) struct RustcUnstableFeatureBound {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(passes_rustc_std_internal_symbol)]
pub(crate) struct RustcStdInternalSymbol {
Expand Down
Loading
Loading