Skip to content

Commit 93970f9

Browse files
Auto merge of #140399 - tiif:unstable_impl, r=<try>
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`
2 parents 64b185e + dc8f28f commit 93970f9

File tree

82 files changed

+1064
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1064
-13
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ pub enum AttributeKind {
386386
/// Represents `#[rustc_unsafe_specialization_marker]`.
387387
UnsafeSpecializationMarker(Span),
388388

389+
/// Represents `#[unstable_feature_bound]`.
390+
UnstableFeatureBound(ThinVec<(Symbol, Span)>),
391+
389392
/// Represents `#[used]`
390393
Used { used_by: UsedBy, span: Span },
391394
// tidy-alphabetical-end

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl AttributeKind {
6868
TrackCaller(..) => Yes,
6969
TypeConst(..) => Yes,
7070
UnsafeSpecializationMarker(..) => No,
71+
UnstableFeatureBound(..) => No,
7172
Used { .. } => No,
7273
// tidy-alphabetical-end
7374
}

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ attr_parsing_unrecognized_repr_hint =
133133
attr_parsing_unstable_cfg_target_compact =
134134
compact `cfg(target(..))` is experimental and subject to change
135135
136+
attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable
137+
.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]`
138+
136139
attr_parsing_unsupported_literal_cfg_boolean =
137140
literal in `cfg` predicate value must be a boolean
138141
attr_parsing_unsupported_literal_cfg_string =

compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
2727
}
2828
}
2929

30+
pub(crate) struct UnstableFeatureBoundParser;
31+
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
32+
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
33+
type Item = (Symbol, Span);
34+
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
35+
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
36+
37+
fn extend<'c>(
38+
cx: &'c mut AcceptContext<'_, '_, S>,
39+
args: &'c ArgParser<'_>,
40+
) -> impl IntoIterator<Item = Self::Item> {
41+
if !cx.features().staged_api() {
42+
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
43+
}
44+
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
45+
.into_iter()
46+
.zip(iter::repeat(cx.attr_span))
47+
}
48+
}
49+
3050
pub(crate) struct AllowConstFnUnstableParser;
3151
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
3252
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];

compiler/rustc_attr_parsing/src/attributes/stability.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
9898
}
9999
}
100100

101+
if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability {
102+
for other_attr in cx.all_attrs {
103+
if other_attr.word_is(sym::unstable_feature_bound) {
104+
cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability {
105+
span: cx.target_span,
106+
});
107+
}
108+
}
109+
}
110+
101111
let (stability, span) = self.stability?;
102112

103113
Some(AttributeKind::Stability { stability, span })

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI
1414
use rustc_session::Session;
1515
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1616

17-
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
17+
use crate::attributes::allow_unstable::{
18+
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
19+
};
1820
use crate::attributes::codegen_attrs::{
1921
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
2022
TrackCallerParser, UsedParser,
@@ -134,6 +136,7 @@ attribute_parsers!(
134136
Combine<AllowInternalUnstableParser>,
135137
Combine<ReprParser>,
136138
Combine<TargetFeatureParser>,
139+
Combine<UnstableFeatureBoundParser>,
137140
// tidy-alphabetical-end
138141

139142
// tidy-alphabetical-start

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,14 @@ pub(crate) struct UnrecognizedReprHint {
503503
pub span: Span,
504504
}
505505

506+
#[derive(Diagnostic)]
507+
#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)]
508+
#[help]
509+
pub(crate) struct UnstableFeatureBoundIncompatibleStability {
510+
#[primary_span]
511+
pub span: Span,
512+
}
513+
506514
#[derive(Diagnostic)]
507515
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
508516
pub(crate) struct NakedFunctionIncompatibleAttribute {

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
683683
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
684684
EncodeCrossCrate::Yes
685685
),
686+
ungated!(
687+
unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."),
688+
DuplicatesOk, EncodeCrossCrate::No,
689+
),
686690
ungated!(
687691
rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
688692
DuplicatesOk, EncodeCrossCrate::Yes

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
22122212
let implied_obligations = traits::elaborate(tcx, predicates_with_span);
22132213

22142214
for (pred, obligation_span) in implied_obligations {
2215-
// We lower empty bounds like `Vec<dyn Copy>:` as
2216-
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
2217-
// regular WF checking
2218-
if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() {
2219-
continue;
2215+
match pred.kind().skip_binder() {
2216+
// We lower empty bounds like `Vec<dyn Copy>:` as
2217+
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
2218+
// regular WF checking
2219+
ty::ClauseKind::WellFormed(..)
2220+
// Unstable feature goals cannot be proven in an empty environment so skip them
2221+
| ty::ClauseKind::UnstableFeature(..) => continue,
2222+
_ => {}
22202223
}
2224+
22212225
// Match the existing behavior.
22222226
if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) {
22232227
let pred = self.normalize(span, None, pred);

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::assert_matches::assert_matches;
22

33
use hir::Node;
4+
use rustc_attr_data_structures::{AttributeKind, find_attr};
45
use rustc_data_structures::fx::FxIndexSet;
56
use rustc_hir as hir;
67
use rustc_hir::def::DefKind;
@@ -333,6 +334,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
333334
predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates));
334335
}
335336

337+
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
338+
// FIXME(staged_api): We might want to look at the normal stability attributes too but
339+
// first we would need a way to let std/core use APIs with unstable feature bounds from
340+
// within stable APIs.
341+
let allow_unstable_feature_attr =
342+
find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)
343+
.map(|i| i.as_slice())
344+
.unwrap_or_default();
345+
346+
for (feat_name, span) in allow_unstable_feature_attr {
347+
predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span));
348+
}
349+
336350
let mut predicates: Vec<_> = predicates.into_iter().collect();
337351

338352
// Subtle: before we store the predicates into the tcx, we
@@ -764,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
764778
ty::ClauseKind::RegionOutlives(_)
765779
| ty::ClauseKind::ConstArgHasType(_, _)
766780
| ty::ClauseKind::WellFormed(_)
781+
| ty::ClauseKind::UnstableFeature(_)
767782
| ty::ClauseKind::ConstEvaluatable(_) => {
768783
bug!(
769784
"unexpected non-`Self` predicate when computing \
@@ -791,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
791806
| ty::ClauseKind::ConstArgHasType(_, _)
792807
| ty::ClauseKind::WellFormed(_)
793808
| ty::ClauseKind::ConstEvaluatable(_)
809+
| ty::ClauseKind::UnstableFeature(_)
794810
| ty::ClauseKind::HostEffect(..) => {
795811
bug!(
796812
"unexpected non-`Self` predicate when computing \

0 commit comments

Comments
 (0)