@@ -266,13 +266,18 @@ pub fn find_stability(
266266) -> Option < ( Stability , StabilitySpans ) > {
267267 let mut level: Option < StabilityLevel > = None ;
268268 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
269+ let mut features = smallvec ! [ ] ;
269270 let mut allowed_through_unstable_modules = false ;
270271
271272 for attr in attrs {
272273 match attr. name_or_empty ( ) {
273274 sym:: rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true ,
274- sym:: unstable => try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ,
275- sym:: stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
275+ sym:: unstable => {
276+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
277+ }
278+ sym:: stable => {
279+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
280+ }
276281 _ => { }
277282 }
278283 }
@@ -301,15 +306,18 @@ pub fn find_const_stability(
301306) -> Option < ( ConstStability , StabilitySpans ) > {
302307 let mut level: Option < StabilityLevel > = None ;
303308 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
309+ let mut features = smallvec ! [ ] ;
304310 let mut promotable = false ;
305311
306312 for attr in attrs {
307313 match attr. name_or_empty ( ) {
308314 sym:: rustc_promotable => promotable = true ,
309315 sym:: rustc_const_unstable => {
310- try_add_unstability ( sess, attr, & mut level, & mut stab_spans)
316+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
317+ }
318+ sym:: rustc_const_stable => {
319+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
311320 }
312- sym:: rustc_const_stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
313321 _ => { }
314322 }
315323 }
@@ -333,76 +341,63 @@ pub fn find_body_stability(
333341) -> Option < ( DefaultBodyStability , StabilitySpans ) > {
334342 let mut level: Option < StabilityLevel > = None ;
335343 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
344+ let mut features = smallvec ! [ ] ;
336345
337346 for attr in attrs {
338347 if attr. has_name ( sym:: rustc_default_body_unstable) {
339- try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ;
348+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features , parse_unstability ) ;
340349 }
341350 }
342351
343352 Some ( ( DefaultBodyStability { level : level? } , stab_spans) )
344353}
345354
346- /// Collects stability info from one `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
347- /// attribute, `attr`. Emits an error if the info it collects is inconsistent .
348- fn try_add_unstability (
355+ /// Collects stability info from one stability attribute, `attr`.
356+ /// Emits an error if multiple stability levels are found for the same feature .
357+ fn add_level (
349358 sess : & Session ,
350359 attr : & Attribute ,
351- level : & mut Option < StabilityLevel > ,
360+ total_level : & mut Option < StabilityLevel > ,
352361 stab_spans : & mut StabilitySpans ,
362+ features : & mut SmallVec < [ Symbol ; 1 ] > ,
363+ parse_level : impl FnOnce ( & Session , & Attribute ) -> Option < ( Symbol , StabilityLevel ) > ,
353364) {
354365 use StabilityLevel :: * ;
355366
356- match level {
357- // adding #[unstable] to an item with #[stable] is not permitted
358- Some ( Stable { .. } ) => {
359- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
360- }
361- // if other unstable attributes have been found, attempt to merge them
362- Some ( Unstable { unstables, is_soft } )
363- if let Some ( Unstable { unstables : new_unstable, is_soft : new_soft } ) =
364- parse_unstability ( sess, attr) =>
365- {
366- // sanity check: is this the only unstable attr of its kind for its feature?
367- // FIXME(dianne): should this have a new error associated with it or is "multiple
368- // stability levels" clear enough, given an update to E0544.md?
369- // should MultipleStabilityLevels have more fields for diagnostics?
370- if unstables. iter ( ) . any ( |u| new_unstable. iter ( ) . any ( |v| u. feature == v. feature ) ) {
371- sess. dcx ( )
372- . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
373- return ;
374- }
375- unstables. extend ( new_unstable. clone ( ) ) ;
367+ let Some ( ( feature, feature_level) ) = parse_level ( sess, attr) else { return } ;
368+
369+ // sanity check: is this the only stability level of its kind for its feature?
370+ if features. contains ( & feature) {
371+ sess. dcx ( )
372+ . emit_err ( session_diagnostics:: MultipleStabilityLevels { feature, span : attr. span } ) ;
373+ }
374+ features. push ( feature) ;
375+ stab_spans. 0 . push ( ( feature_level. clone ( ) , attr. span ) ) ;
376+
377+ match ( total_level, feature_level) {
378+ ( level @ None , new_level) => * level = Some ( new_level) ,
379+ // if multiple unstable attributes have been found, merge them
380+ (
381+ Some ( Unstable { unstables, is_soft } ) ,
382+ Unstable { unstables : new_unstable, is_soft : new_soft } ,
383+ ) => {
384+ unstables. extend ( new_unstable) ;
376385 // Make the unstability soft if any unstable attributes are marked 'soft'; if an
377386 // unstable item is allowed in stable rust, another attribute shouldn't break that.
378387 // FIXME(dianne): should there be a check that all unstables are soft if any are?
379388 * is_soft |= new_soft;
380- stab_spans. 0 . push ( ( Unstable { unstables : new_unstable, is_soft : new_soft } , attr. span ) ) ;
381389 }
382- // if this is the first unstability of its kind on an item, collect it
383- None if let Some ( new_level) = parse_unstability ( sess, attr) => {
384- * level = Some ( new_level. clone ( ) ) ;
385- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
390+ // an item with some stable and some unstable features is unstable
391+ ( Some ( Unstable { .. } ) , Stable { .. } ) => { }
392+ ( Some ( level @ Stable { .. } ) , new_level @ Unstable { .. } ) => * level = new_level,
393+ // if multiple stable attributes have been found, use the most recent stabilization date
394+ (
395+ Some ( Stable { since, allowed_through_unstable_modules } ) ,
396+ Stable { since : new_since, allowed_through_unstable_modules : new_allowed } ,
397+ ) => {
398+ * since = StableSince :: max ( * since, new_since) ;
399+ * allowed_through_unstable_modules |= new_allowed;
386400 }
387- // if there was an error in `parse_unstability`, it's already been emitted; do nothing
388- _ => { }
389- }
390- }
391-
392- /// Collects stability info from a single `stable`/`rustc_const_stable` attribute, `attr`.
393- /// Emits an error if the info it collects is inconsistent.
394- fn try_add_stability (
395- sess : & Session ,
396- attr : & Attribute ,
397- level : & mut Option < StabilityLevel > ,
398- stab_spans : & mut StabilitySpans ,
399- ) {
400- // at most one #[stable] attribute is permitted, and not when #[unstable] is present
401- if level. is_some ( ) {
402- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
403- } else if let Some ( new_level) = parse_stability ( sess, attr) {
404- * level = Some ( new_level. clone ( ) ) ;
405- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
406401 }
407402}
408403
@@ -424,7 +419,7 @@ fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -
424419
425420/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
426421/// its stability information.
427- fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
422+ fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
428423 let meta = attr. meta ( ) ?;
429424 let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
430425
@@ -478,16 +473,17 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel> {
478473 } ;
479474
480475 match feature {
481- Ok ( _feature) => {
482- Some ( StabilityLevel :: Stable { since, allowed_through_unstable_modules : false } )
483- }
476+ Ok ( feature) => Some ( ( feature, StabilityLevel :: Stable {
477+ since,
478+ allowed_through_unstable_modules : false ,
479+ } ) ) ,
484480 Err ( ErrorGuaranteed { .. } ) => None ,
485481 }
486482}
487483
488484/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
489485/// attribute, and return the feature name and its stability information.
490- fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
486+ fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
491487 let meta = attr. meta ( ) ?;
492488 let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
493489
@@ -572,7 +568,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel>
572568 issue : issue_num,
573569 implied_by,
574570 } ;
575- Some ( StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } )
571+ Some ( ( feature , StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } ) )
576572 }
577573 ( Err ( ErrorGuaranteed { .. } ) , _) | ( _, Err ( ErrorGuaranteed { .. } ) ) => None ,
578574 }
0 commit comments