@@ -10,7 +10,7 @@ use rustc_middle::query::Providers;
10
10
use rustc_middle:: ty:: TyCtxt ;
11
11
use rustc_session:: parse:: feature_err;
12
12
use rustc_span:: { Span , Symbol , sym} ;
13
- use rustc_target:: target_features;
13
+ use rustc_target:: target_features:: { self , Stability } ;
14
14
15
15
use crate :: errors;
16
16
@@ -87,12 +87,17 @@ pub(crate) fn from_target_feature_attr(
87
87
// But ensure the ABI does not forbid enabling this.
88
88
// Here we do assume that LLVM doesn't add even more implied features
89
89
// we don't know about, at least no features that would have ABI effects!
90
- if abi_feature_constraints. incompatible . contains ( & name. as_str ( ) ) {
91
- tcx. dcx ( ) . emit_err ( errors:: ForbiddenTargetFeatureAttr {
92
- span : item. span ( ) ,
93
- feature : name. as_str ( ) ,
94
- reason : "this feature is incompatible with the target ABI" ,
95
- } ) ;
90
+ // We skip this logic in rustdoc, where we want to allow all target features of
91
+ // all targets, so we can't check their ABI compatibility and anyway we are not
92
+ // generating code so "it's fine".
93
+ if !tcx. sess . opts . actually_rustdoc {
94
+ if abi_feature_constraints. incompatible . contains ( & name. as_str ( ) ) {
95
+ tcx. dcx ( ) . emit_err ( errors:: ForbiddenTargetFeatureAttr {
96
+ span : item. span ( ) ,
97
+ feature : name. as_str ( ) ,
98
+ reason : "this feature is incompatible with the target ABI" ,
99
+ } ) ;
100
+ }
96
101
}
97
102
target_features. push ( TargetFeature { name, implied : name != feature_sym } )
98
103
}
@@ -142,11 +147,38 @@ pub(crate) fn provide(providers: &mut Providers) {
142
147
rust_target_features : |tcx, cnum| {
143
148
assert_eq ! ( cnum, LOCAL_CRATE ) ;
144
149
if tcx. sess . opts . actually_rustdoc {
145
- // rustdoc needs to be able to document functions that use all the features, so
146
- // whitelist them all
147
- rustc_target:: target_features:: all_rust_features ( )
148
- . map ( |( a, b) | ( a. to_string ( ) , b) )
149
- . collect ( )
150
+ // HACK: rustdoc would like to pretend that we have all the target features, so we
151
+ // have to merge all the lists into one. To ensure an unstable target never prevents
152
+ // a stable one from working, we merge the stability info of all instances of the
153
+ // same target feature name, with the "most stable" taking precedence. And then we
154
+ // hope that this doesn't cause issues anywhere else in the compiler...
155
+ let mut result: UnordMap < String , Stability > = Default :: default ( ) ;
156
+ for ( name, stability) in rustc_target:: target_features:: all_rust_features ( ) {
157
+ use std:: collections:: hash_map:: Entry ;
158
+ match result. entry ( name. to_owned ( ) ) {
159
+ Entry :: Vacant ( vacant_entry) => {
160
+ vacant_entry. insert ( stability) ;
161
+ }
162
+ Entry :: Occupied ( mut occupied_entry) => {
163
+ // Merge the two stabilities, "more stable" taking precedence.
164
+ match ( occupied_entry. get ( ) , stability) {
165
+ ( Stability :: Stable , _)
166
+ | (
167
+ Stability :: Unstable { .. } ,
168
+ Stability :: Unstable { .. } | Stability :: Forbidden { .. } ,
169
+ )
170
+ | ( Stability :: Forbidden { .. } , Stability :: Forbidden { .. } ) => {
171
+ // The stability in the entry is at least as good as the new one, just keep it.
172
+ }
173
+ _ => {
174
+ // Overwrite stabilite.
175
+ occupied_entry. insert ( stability) ;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ result
150
182
} else {
151
183
tcx. sess
152
184
. target
0 commit comments