@@ -336,13 +336,18 @@ pub fn find_stability(
336336) -> Option < ( Stability , StabilitySpans ) > {
337337 let mut level: Option < StabilityLevel > = None ;
338338 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
339+ let mut features = smallvec ! [ ] ;
339340 let mut allowed_through_unstable_modules = false ;
340341
341342 for attr in attrs {
342343 match attr. name_or_empty ( ) {
343344 sym:: rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true ,
344- sym:: unstable => try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ,
345- sym:: stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
345+ sym:: unstable => {
346+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
347+ }
348+ sym:: stable => {
349+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
350+ }
346351 _ => { }
347352 }
348353 }
@@ -375,6 +380,7 @@ pub fn find_const_stability(
375380) -> Option < ( ConstStability , ConstStabilitySpans ) > {
376381 let mut level: Option < StabilityLevel > = None ;
377382 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
383+ let mut features = smallvec ! [ ] ;
378384 let mut promotable = false ;
379385 let mut const_stable_indirect = None ;
380386
@@ -383,9 +389,11 @@ pub fn find_const_stability(
383389 sym:: rustc_promotable => promotable = true ,
384390 sym:: rustc_const_stable_indirect => const_stable_indirect = Some ( attr. span ) ,
385391 sym:: rustc_const_unstable => {
386- try_add_unstability ( sess, attr, & mut level, & mut stab_spans)
392+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
393+ }
394+ sym:: rustc_const_stable => {
395+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
387396 }
388- sym:: rustc_const_stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
389397 _ => { }
390398 }
391399 }
@@ -441,76 +449,63 @@ pub fn find_body_stability(
441449) -> Option < ( DefaultBodyStability , StabilitySpans ) > {
442450 let mut level: Option < StabilityLevel > = None ;
443451 let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
452+ let mut features = smallvec ! [ ] ;
444453
445454 for attr in attrs {
446455 if attr. has_name ( sym:: rustc_default_body_unstable) {
447- try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ;
456+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features , parse_unstability ) ;
448457 }
449458 }
450459
451460 Some ( ( DefaultBodyStability { level : level? } , stab_spans) )
452461}
453462
454- /// Collects stability info from one `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
455- /// attribute, `attr`. Emits an error if the info it collects is inconsistent .
456- fn try_add_unstability (
463+ /// Collects stability info from one stability attribute, `attr`.
464+ /// Emits an error if multiple stability levels are found for the same feature .
465+ fn add_level (
457466 sess : & Session ,
458467 attr : & Attribute ,
459- level : & mut Option < StabilityLevel > ,
468+ total_level : & mut Option < StabilityLevel > ,
460469 stab_spans : & mut StabilitySpans ,
470+ features : & mut SmallVec < [ Symbol ; 1 ] > ,
471+ parse_level : impl FnOnce ( & Session , & Attribute ) -> Option < ( Symbol , StabilityLevel ) > ,
461472) {
462473 use StabilityLevel :: * ;
463474
464- match level {
465- // adding #[unstable] to an item with #[stable] is not permitted
466- Some ( Stable { .. } ) => {
467- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
468- }
469- // if other unstable attributes have been found, attempt to merge them
470- Some ( Unstable { unstables, is_soft } )
471- if let Some ( Unstable { unstables : new_unstable, is_soft : new_soft } ) =
472- parse_unstability ( sess, attr) =>
473- {
474- // sanity check: is this the only unstable attr of its kind for its feature?
475- // FIXME(dianne): should this have a new error associated with it or is "multiple
476- // stability levels" clear enough, given an update to E0544.md?
477- // should MultipleStabilityLevels have more fields for diagnostics?
478- if unstables. iter ( ) . any ( |u| new_unstable. iter ( ) . any ( |v| u. feature == v. feature ) ) {
479- sess. dcx ( )
480- . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
481- return ;
482- }
483- unstables. extend ( new_unstable. clone ( ) ) ;
475+ let Some ( ( feature, feature_level) ) = parse_level ( sess, attr) else { return } ;
476+
477+ // sanity check: is this the only stability level of its kind for its feature?
478+ if features. contains ( & feature) {
479+ sess. dcx ( )
480+ . emit_err ( session_diagnostics:: MultipleStabilityLevels { feature, span : attr. span } ) ;
481+ }
482+ features. push ( feature) ;
483+ stab_spans. 0 . push ( ( feature_level. clone ( ) , attr. span ) ) ;
484+
485+ match ( total_level, feature_level) {
486+ ( level @ None , new_level) => * level = Some ( new_level) ,
487+ // if multiple unstable attributes have been found, merge them
488+ (
489+ Some ( Unstable { unstables, is_soft } ) ,
490+ Unstable { unstables : new_unstable, is_soft : new_soft } ,
491+ ) => {
492+ unstables. extend ( new_unstable) ;
484493 // Make the unstability soft if any unstable attributes are marked 'soft'; if an
485494 // unstable item is allowed in stable rust, another attribute shouldn't break that.
486495 // FIXME(dianne): should there be a check that all unstables are soft if any are?
487496 * is_soft |= new_soft;
488- stab_spans. 0 . push ( ( Unstable { unstables : new_unstable, is_soft : new_soft } , attr. span ) ) ;
489497 }
490- // if this is the first unstability of its kind on an item, collect it
491- None if let Some ( new_level) = parse_unstability ( sess, attr) => {
492- * level = Some ( new_level. clone ( ) ) ;
493- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
498+ // an item with some stable and some unstable features is unstable
499+ ( Some ( Unstable { .. } ) , Stable { .. } ) => { }
500+ ( Some ( level @ Stable { .. } ) , new_level @ Unstable { .. } ) => * level = new_level,
501+ // if multiple stable attributes have been found, use the most recent stabilization date
502+ (
503+ Some ( Stable { since, allowed_through_unstable_modules } ) ,
504+ Stable { since : new_since, allowed_through_unstable_modules : new_allowed } ,
505+ ) => {
506+ * since = StableSince :: max ( * since, new_since) ;
507+ * allowed_through_unstable_modules |= new_allowed;
494508 }
495- // if there was an error in `parse_unstability`, it's already been emitted; do nothing
496- _ => { }
497- }
498- }
499-
500- /// Collects stability info from a single `stable`/`rustc_const_stable` attribute, `attr`.
501- /// Emits an error if the info it collects is inconsistent.
502- fn try_add_stability (
503- sess : & Session ,
504- attr : & Attribute ,
505- level : & mut Option < StabilityLevel > ,
506- stab_spans : & mut StabilitySpans ,
507- ) {
508- // at most one #[stable] attribute is permitted, and not when #[unstable] is present
509- if level. is_some ( ) {
510- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
511- } else if let Some ( new_level) = parse_stability ( sess, attr) {
512- * level = Some ( new_level. clone ( ) ) ;
513- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
514509 }
515510}
516511
@@ -532,7 +527,7 @@ fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -
532527
533528/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
534529/// its stability information.
535- fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
530+ fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
536531 let meta = attr. meta ( ) ?;
537532 let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
538533
@@ -586,16 +581,17 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel> {
586581 } ;
587582
588583 match feature {
589- Ok ( _feature) => {
590- Some ( StabilityLevel :: Stable { since, allowed_through_unstable_modules : false } )
591- }
584+ Ok ( feature) => Some ( ( feature, StabilityLevel :: Stable {
585+ since,
586+ allowed_through_unstable_modules : false ,
587+ } ) ) ,
592588 Err ( ErrorGuaranteed { .. } ) => None ,
593589 }
594590}
595591
596592/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
597593/// attribute, and return the feature name and its stability information.
598- fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
594+ fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
599595 let meta = attr. meta ( ) ?;
600596 let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
601597
@@ -680,7 +676,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel>
680676 issue : issue_num,
681677 implied_by,
682678 } ;
683- Some ( StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } )
679+ Some ( ( feature , StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } ) )
684680 }
685681 ( Err ( ErrorGuaranteed { .. } ) , _) | ( _, Err ( ErrorGuaranteed { .. } ) ) => None ,
686682 }
0 commit comments