Skip to content

Commit 90a545b

Browse files
Support multiple unstable attributes
1 parent 9b701e7 commit 90a545b

File tree

26 files changed

+482
-319
lines changed

26 files changed

+482
-319
lines changed

compiler/rustc_attr/src/builtin.rs

+105-41
Original file line numberDiff line numberDiff line change
@@ -94,39 +94,66 @@ pub enum OptimizeAttr {
9494
///
9595
/// - `#[stable]`
9696
/// - `#[unstable]`
97-
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
97+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
9898
#[derive(HashStable_Generic)]
9999
pub struct Stability {
100100
pub level: StabilityLevel,
101-
pub feature: Symbol,
102101
}
103102

104103
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
105-
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
104+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
106105
#[derive(HashStable_Generic)]
107106
pub struct ConstStability {
108107
pub level: StabilityLevel,
109-
pub feature: Symbol,
110108
/// whether the function has a `#[rustc_promotable]` attribute
111109
pub promotable: bool,
112110
}
113111

114112
/// The available stability levels.
115-
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
113+
#[derive(Encodable, Decodable, PartialEq, Clone, Debug, Eq, Hash)]
116114
#[derive(HashStable_Generic)]
117115
pub enum StabilityLevel {
118-
// Reason for the current stability level and the relevant rust-lang issue
119-
Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
120-
Stable { since: Symbol },
116+
Unstable { unstables: Vec<Unstability>, is_soft: bool },
117+
Stable { feature: Symbol, since: Symbol, span: Span },
118+
}
119+
120+
impl StabilityLevel {
121+
pub fn spans(&self) -> Vec<Span> {
122+
match self {
123+
StabilityLevel::Stable { span, .. } => vec![*span],
124+
StabilityLevel::Unstable { unstables, .. } => {
125+
unstables.iter().map(|u| u.span).collect()
126+
}
127+
}
128+
}
129+
}
130+
131+
/// An instance of the `#[unstable]` attribute
132+
#[derive(Encodable, Decodable, PartialEq, Clone, Debug, Eq, Hash)]
133+
#[derive(HashStable_Generic)]
134+
pub struct Unstability {
135+
pub feature: Symbol,
136+
pub issue: Option<NonZeroU32>,
137+
pub reason: Option<Symbol>,
138+
pub span: Span,
121139
}
122140

123141
impl StabilityLevel {
124142
pub fn is_unstable(&self) -> bool {
125143
matches!(self, StabilityLevel::Unstable { .. })
126144
}
145+
127146
pub fn is_stable(&self) -> bool {
128147
matches!(self, StabilityLevel::Stable { .. })
129148
}
149+
150+
pub fn has_unstable_feature(&self, sym: Symbol) -> bool {
151+
if let StabilityLevel::Unstable { unstables, .. } = &self {
152+
unstables.iter().any(|u| u.feature == sym)
153+
} else {
154+
false
155+
}
156+
}
130157
}
131158

132159
/// Collects stability info from all stability attributes in `attrs`.
@@ -135,22 +162,22 @@ pub fn find_stability(
135162
sess: &Session,
136163
attrs: &[Attribute],
137164
item_sp: Span,
138-
) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) {
165+
) -> (Option<Stability>, Option<ConstStability>) {
139166
find_stability_generic(sess, attrs.iter(), item_sp)
140167
}
141168

142169
fn find_stability_generic<'a, I>(
143170
sess: &Session,
144171
attrs_iter: I,
145172
item_sp: Span,
146-
) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>)
173+
) -> (Option<Stability>, Option<ConstStability>)
147174
where
148175
I: Iterator<Item = &'a Attribute>,
149176
{
150177
use StabilityLevel::*;
151178

152-
let mut stab: Option<(Stability, Span)> = None;
153-
let mut const_stab: Option<(ConstStability, Span)> = None;
179+
let mut stab: Option<Stability> = None;
180+
let mut const_stab: Option<ConstStability> = None;
154181
let mut promotable = false;
155182

156183
let diagnostic = &sess.parse_sess.span_diagnostic;
@@ -198,22 +225,6 @@ where
198225
let meta_name = meta.name_or_empty();
199226
match meta_name {
200227
sym::rustc_const_unstable | sym::unstable => {
201-
if meta_name == sym::unstable && stab.is_some() {
202-
handle_errors(
203-
&sess.parse_sess,
204-
attr.span,
205-
AttrError::MultipleStabilityLevels,
206-
);
207-
break;
208-
} else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
209-
handle_errors(
210-
&sess.parse_sess,
211-
attr.span,
212-
AttrError::MultipleStabilityLevels,
213-
);
214-
break;
215-
}
216-
217228
let mut feature = None;
218229
let mut reason = None;
219230
let mut issue = None;
@@ -308,14 +319,70 @@ where
308319
);
309320
continue;
310321
}
311-
let level = Unstable { reason, issue: issue_num, is_soft };
322+
let unstability =
323+
Unstability { feature, issue: issue_num, reason, span: attr.span };
312324
if sym::unstable == meta_name {
313-
stab = Some((Stability { level, feature }, attr.span));
325+
match &mut stab {
326+
stab @ None => {
327+
*stab = Some(Stability {
328+
level: StabilityLevel::Unstable {
329+
unstables: vec![unstability],
330+
is_soft,
331+
},
332+
})
333+
}
334+
// Merge multiple unstability attributes into one
335+
Some(Stability {
336+
level:
337+
StabilityLevel::Unstable { unstables, is_soft: old_is_soft },
338+
}) => {
339+
unstables.push(unstability);
340+
// FIXME(compiler-errors): Do we want this behavior: is_soft iff all are is_soft?
341+
*old_is_soft &= is_soft;
342+
}
343+
_ => {
344+
handle_errors(
345+
&sess.parse_sess,
346+
attr.span,
347+
AttrError::MultipleStabilityLevels,
348+
);
349+
}
350+
}
314351
} else {
315-
const_stab = Some((
316-
ConstStability { level, feature, promotable: false },
317-
attr.span,
318-
));
352+
match &mut const_stab {
353+
const_stab @ None => {
354+
*const_stab = Some(ConstStability {
355+
level: StabilityLevel::Unstable {
356+
unstables: vec![unstability],
357+
is_soft,
358+
},
359+
promotable: false,
360+
})
361+
}
362+
Some(ConstStability {
363+
level:
364+
StabilityLevel::Unstable {
365+
unstables: unstability,
366+
is_soft: old_is_soft,
367+
},
368+
..
369+
}) => {
370+
unstability.push(Unstability {
371+
feature,
372+
issue: issue_num,
373+
reason,
374+
span: attr.span,
375+
});
376+
*old_is_soft &= is_soft;
377+
}
378+
_ => {
379+
handle_errors(
380+
&sess.parse_sess,
381+
attr.span,
382+
AttrError::MultipleStabilityLevels,
383+
);
384+
}
385+
}
319386
}
320387
}
321388
(None, _, _) => {
@@ -386,14 +453,11 @@ where
386453

387454
match (feature, since) {
388455
(Some(feature), Some(since)) => {
389-
let level = Stable { since };
456+
let level = Stable { since, feature, span: attr.span };
390457
if sym::stable == meta_name {
391-
stab = Some((Stability { level, feature }, attr.span));
458+
stab = Some(Stability { level });
392459
} else {
393-
const_stab = Some((
394-
ConstStability { level, feature, promotable: false },
395-
attr.span,
396-
));
460+
const_stab = Some(ConstStability { level, promotable: false });
397461
}
398462
}
399463
(None, _) => {
@@ -413,7 +477,7 @@ where
413477

414478
// Merge the const-unstable info into the stability info
415479
if promotable {
416-
if let Some((ref mut stab, _)) = const_stab {
480+
if let Some(stab) = &mut const_stab {
417481
stab.promotable = promotable;
418482
} else {
419483
struct_span_err!(

compiler/rustc_const_eval/src/const_eval/fn_queries.rs

-11
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,8 @@ use rustc_hir as hir;
22
use rustc_hir::def_id::{DefId, LocalDefId};
33
use rustc_middle::ty::query::Providers;
44
use rustc_middle::ty::TyCtxt;
5-
use rustc_span::symbol::Symbol;
65
use rustc_target::spec::abi::Abi;
76

8-
/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
9-
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
10-
if tcx.is_const_fn_raw(def_id) {
11-
let const_stab = tcx.lookup_const_stability(def_id)?;
12-
if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
13-
} else {
14-
None
15-
}
16-
}
17-
187
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
198
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
209
let parent_id = tcx.hir().get_parent_node(hir_id);

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+60-41
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use super::ops::{self, NonConstOp, Status};
2424
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
2525
use super::resolver::FlowSensitiveAnalysis;
2626
use super::{ConstCx, Qualif};
27-
use crate::const_eval::is_unstable_const_fn;
2827

2928
type QualifResults<'mir, 'tcx, Q> =
3029
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
@@ -902,54 +901,74 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
902901
}
903902

904903
// If the `const fn` we are trying to call is not const-stable, ensure that we have
905-
// the proper feature gate enabled.
906-
if let Some(gate) = is_unstable_const_fn(tcx, callee) {
907-
trace!(?gate, "calling unstable const fn");
908-
if self.span.allows_unstable(gate) {
909-
return;
904+
// the proper feature gates enabled.
905+
let gates = if tcx.is_const_fn_raw(callee) {
906+
match tcx.lookup_const_stability(callee) {
907+
Some(rustc_attr::ConstStability {
908+
level: rustc_attr::StabilityLevel::Unstable { unstables, .. },
909+
..
910+
}) => unstables.as_slice(),
911+
_ => &[],
910912
}
913+
} else {
914+
&[]
915+
};
911916

912-
// Calling an unstable function *always* requires that the corresponding gate
913-
// be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
914-
if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) {
915-
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
916-
return;
917-
}
917+
if !gates.is_empty() {
918+
trace!(?gates, "calling unstable const fn");
919+
// Features that are not enabled, but must be to make this call const
920+
let mut bad_gates = vec![];
921+
for gate in gates {
922+
let feature = gate.feature;
918923

919-
// If this crate is not using stability attributes, or the caller is not claiming to be a
920-
// stable `const fn`, that is all that is required.
921-
if !self.ccx.is_const_stable_const_fn() {
922-
trace!("crate not using stability attributes or caller not stably const");
923-
return;
924-
}
924+
if self.span.allows_unstable(feature) {
925+
continue;
926+
}
925927

926-
// Otherwise, we are something const-stable calling a const-unstable fn.
928+
// Calling an unstable function *always* requires that the corresponding gate
929+
// be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
930+
if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature)
931+
{
932+
bad_gates.push(feature);
933+
continue;
934+
}
935+
// If this crate is not using stability attributes, or the caller is not claiming to be a
936+
// stable `const fn`, that is all that is required.
937+
if !self.ccx.is_const_stable_const_fn() {
938+
trace!(
939+
"crate not using stability attributes or caller not stably const"
940+
);
941+
continue;
942+
}
943+
// Otherwise, we are something const-stable calling a const-unstable fn.
944+
if super::rustc_allow_const_fn_unstable(tcx, caller, feature) {
945+
trace!("rustc_allow_const_fn_unstable gate active");
946+
continue;
947+
}
927948

928-
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
929-
trace!("rustc_allow_const_fn_unstable gate active");
930-
return;
949+
bad_gates.push(feature);
931950
}
932-
933-
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
934-
return;
935-
}
936-
937-
// FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that
938-
// have no `rustc_const_stable` attributes to be const-unstable as well. This
939-
// should be fixed later.
940-
let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
941-
&& tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable());
942-
if callee_is_unstable_unmarked {
943-
trace!("callee_is_unstable_unmarked");
944-
// We do not use `const` modifiers for intrinsic "functions", as intrinsics are
945-
// `extern` funtions, and these have no way to get marked `const`. So instead we
946-
// use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const`
947-
if self.ccx.is_const_stable_const_fn() || is_intrinsic {
948-
self.check_op(ops::FnCallUnstable(callee, None));
949-
return;
951+
if !bad_gates.is_empty() {
952+
self.check_op(ops::FnCallUnstable(callee, bad_gates));
953+
}
954+
} else {
955+
// FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that
956+
// have no `rustc_const_stable` attributes to be const-unstable as well. This
957+
// should be fixed later.
958+
let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
959+
&& tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable());
960+
if callee_is_unstable_unmarked {
961+
trace!("callee_is_unstable_unmarked");
962+
// We do not use `const` modifiers for intrinsic "functions", as intrinsics are
963+
// `extern` funtions, and these have no way to get marked `const`. So instead we
964+
// use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const`
965+
if self.ccx.is_const_stable_const_fn() || is_intrinsic {
966+
self.check_op(ops::FnCallUnstable(callee, vec![]));
967+
return;
968+
}
950969
}
970+
trace!("permitting call");
951971
}
952-
trace!("permitting call");
953972
}
954973

955974
// Forbid all `Drop` terminators unless the place being dropped is a local with no

0 commit comments

Comments
 (0)