@@ -145,6 +145,58 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
145
145
}
146
146
}
147
147
148
+ /// Parse the value of `-Ctarget-feature`, also expanding implied features,
149
+ /// and call the closure for each (expanded) Rust feature. If the list contains
150
+ /// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked.
151
+ fn parse_rust_feature_flag < ' a > (
152
+ sess : & Session ,
153
+ target_feature_flag : & ' a str ,
154
+ err_callback : impl Fn ( & ' a str ) ,
155
+ mut callback : impl FnMut (
156
+ /* base_feature */ & ' a str ,
157
+ /* with_implied */ FxHashSet < & ' a str > ,
158
+ /* enable */ bool ,
159
+ ) ,
160
+ ) {
161
+ // A cache for the backwards implication map.
162
+ let mut inverse_implied_features: Option < FxHashMap < & str , FxHashSet < & str > > > = None ;
163
+
164
+ for feature in target_feature_flag. split ( ',' ) {
165
+ if let Some ( base_feature) = feature. strip_prefix ( '+' ) {
166
+ callback ( base_feature, sess. target . implied_target_features ( base_feature) , true )
167
+ } else if let Some ( base_feature) = feature. strip_prefix ( '-' ) {
168
+ // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical contraposition.
169
+ // So we have to find all the reverse implications of `base_feature` and disable them, too.
170
+
171
+ let inverse_implied_features = inverse_implied_features. get_or_insert_with ( || {
172
+ let mut set: FxHashMap < & str , FxHashSet < & str > > = FxHashMap :: default ( ) ;
173
+ for ( f, _, is) in sess. target . rust_target_features ( ) {
174
+ for i in is. iter ( ) {
175
+ set. entry ( i) . or_default ( ) . insert ( f) ;
176
+ }
177
+ }
178
+ set
179
+ } ) ;
180
+
181
+ // Inverse mplied target features have their own inverse implied target features, so we
182
+ // traverse the map until there are no more features to add.
183
+ let mut features = FxHashSet :: default ( ) ;
184
+ let mut new_features = vec ! [ base_feature] ;
185
+ while let Some ( new_feature) = new_features. pop ( ) {
186
+ if features. insert ( new_feature) {
187
+ if let Some ( implied_features) = inverse_implied_features. get ( & new_feature) {
188
+ new_features. extend ( implied_features)
189
+ }
190
+ }
191
+ }
192
+
193
+ callback ( base_feature, features, false )
194
+ } else if !feature. is_empty ( ) {
195
+ err_callback ( feature)
196
+ }
197
+ }
198
+ }
199
+
148
200
/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
149
201
/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
150
202
/// 2nd component of the return value, respectively).
@@ -161,7 +213,7 @@ pub fn cfg(
161
213
) -> ( Vec < Symbol > , Vec < Symbol > ) {
162
214
// Compute which of the known target features are enabled in the 'base' target machine. We only
163
215
// consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
164
- let mut features: FxHashSet < Symbol > = sess
216
+ let mut features: UnordSet < Symbol > = sess
165
217
. target
166
218
. rust_target_features ( )
167
219
. iter ( )
@@ -176,43 +228,23 @@ pub fn cfg(
176
228
. collect ( ) ;
177
229
178
230
// Add enabled and remove disabled features.
179
- for ( enabled, feature) in
180
- target_feature_flag. split ( ',' ) . filter_map ( |s| match s. chars ( ) . next ( ) {
181
- Some ( '+' ) => Some ( ( true , Symbol :: intern ( & s[ 1 ..] ) ) ) ,
182
- Some ( '-' ) => Some ( ( false , Symbol :: intern ( & s[ 1 ..] ) ) ) ,
183
- _ => None ,
184
- } )
185
- {
186
- if enabled {
187
- // Also add all transitively implied features.
188
-
189
- // We don't care about the order in `features` since the only thing we use it for is the
190
- // `features.contains` below.
191
- #[ allow( rustc:: potential_query_instability) ]
192
- features. extend (
193
- sess. target
194
- . implied_target_features ( feature. as_str ( ) )
195
- . iter ( )
196
- . map ( |s| Symbol :: intern ( s) ) ,
197
- ) ;
198
- } else {
199
- // Remove transitively reverse-implied features.
200
-
201
- // We don't care about the order in `features` since the only thing we use it for is the
202
- // `features.contains` below.
231
+ parse_rust_feature_flag (
232
+ sess,
233
+ target_feature_flag,
234
+ /* err_callback */ |_| { } ,
235
+ |_base_feature, new_features, enabled| {
236
+ // Iteration order is irrelevant since this only influences an `UnordSet`.
203
237
#[ allow( rustc:: potential_query_instability) ]
204
- features. retain ( |f| {
205
- if sess. target . implied_target_features ( f. as_str ( ) ) . contains ( & feature. as_str ( ) ) {
206
- // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
207
- // remove `f`. (This is the standard logical contraposition principle.)
208
- false
209
- } else {
210
- // We can keep `f`.
211
- true
238
+ if enabled {
239
+ features. extend ( new_features. into_iter ( ) . map ( |f| Symbol :: intern ( f) ) ) ;
240
+ } else {
241
+ // Remove `new_features` from `features`.
242
+ for new in new_features {
243
+ features. remove ( & Symbol :: intern ( new) ) ;
212
244
}
213
- } ) ;
214
- }
215
- }
245
+ }
246
+ } ,
247
+ ) ;
216
248
217
249
// Filter enabled features based on feature gates.
218
250
let f = |allow_unstable| {
@@ -275,85 +307,81 @@ pub fn flag_to_backend_features<'a, const N: usize>(
275
307
276
308
// Compute implied features
277
309
let mut rust_features = vec ! [ ] ;
278
- for feature in sess. opts . cg . target_feature . split ( ',' ) {
279
- if let Some ( feature) = feature. strip_prefix ( '+' ) {
280
- rust_features. extend (
281
- UnordSet :: from ( sess. target . implied_target_features ( feature) )
282
- . to_sorted_stable_ord ( )
283
- . iter ( )
284
- . map ( |& & s| ( true , s) ) ,
285
- )
286
- } else if let Some ( feature) = feature. strip_prefix ( '-' ) {
287
- // FIXME: Why do we not remove implied features on "-" here?
288
- // We do the equivalent above in `target_config`.
289
- // See <https://github.com/rust-lang/rust/issues/134792>.
290
- rust_features. push ( ( false , feature) ) ;
291
- } else if !feature. is_empty ( ) {
310
+ parse_rust_feature_flag (
311
+ sess,
312
+ & sess. opts . cg . target_feature ,
313
+ /* err_callback */
314
+ |feature| {
292
315
if diagnostics {
293
316
sess. dcx ( ) . emit_warn ( error:: UnknownCTargetFeaturePrefix { feature } ) ;
294
317
}
295
- }
296
- }
297
- // Remove features that are meant for rustc, not the backend.
298
- rust_features. retain ( |( _, feature) | {
299
- // Retain if it is not a rustc feature
300
- !RUSTC_SPECIFIC_FEATURES . contains ( feature)
301
- } ) ;
302
-
303
- // Check feature validity.
304
- if diagnostics {
305
- let mut featsmap = FxHashMap :: default ( ) ;
318
+ } ,
319
+ |base_feature, new_features, enable| {
320
+ // Skip features that are meant for rustc, not the backend.
321
+ if RUSTC_SPECIFIC_FEATURES . contains ( & base_feature) {
322
+ return ;
323
+ }
306
324
307
- for & ( enable, feature) in & rust_features {
308
- let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == feature) ;
309
- match feature_state {
310
- None => {
311
- // This is definitely not a valid Rust feature name. Maybe it is a backend feature name?
312
- // If so, give a better error message.
313
- let rust_feature = known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
314
- let backend_features = to_backend_features ( rust_feature) ;
315
- if backend_features. contains ( & feature)
316
- && !backend_features. contains ( & rust_feature)
317
- {
318
- Some ( rust_feature)
325
+ rust_features. extend (
326
+ UnordSet :: from ( new_features) . to_sorted_stable_ord ( ) . iter ( ) . map ( |& & s| ( enable, s) ) ,
327
+ ) ;
328
+ // Check feature validity.
329
+ if diagnostics {
330
+ let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == base_feature) ;
331
+ match feature_state {
332
+ None => {
333
+ // This is definitely not a valid Rust feature name. Maybe it is a backend feature name?
334
+ // If so, give a better error message.
335
+ let rust_feature =
336
+ known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
337
+ let backend_features = to_backend_features ( rust_feature) ;
338
+ if backend_features. contains ( & base_feature)
339
+ && !backend_features. contains ( & rust_feature)
340
+ {
341
+ Some ( rust_feature)
342
+ } else {
343
+ None
344
+ }
345
+ } ) ;
346
+ let unknown_feature = if let Some ( rust_feature) = rust_feature {
347
+ error:: UnknownCTargetFeature {
348
+ feature : base_feature,
349
+ rust_feature : error:: PossibleFeature :: Some { rust_feature } ,
350
+ }
319
351
} else {
320
- None
321
- }
322
- } ) ;
323
- let unknown_feature = if let Some ( rust_feature) = rust_feature {
324
- error:: UnknownCTargetFeature {
325
- feature,
326
- rust_feature : error:: PossibleFeature :: Some { rust_feature } ,
327
- }
328
- } else {
329
- error:: UnknownCTargetFeature {
330
- feature,
331
- rust_feature : error:: PossibleFeature :: None ,
352
+ error:: UnknownCTargetFeature {
353
+ feature : base_feature,
354
+ rust_feature : error:: PossibleFeature :: None ,
355
+ }
356
+ } ;
357
+ sess. dcx ( ) . emit_warn ( unknown_feature) ;
358
+ }
359
+ Some ( ( _, stability, _) ) => {
360
+ if let Err ( reason) = stability. toggle_allowed ( ) {
361
+ sess. dcx ( ) . emit_warn ( error:: ForbiddenCTargetFeature {
362
+ feature : base_feature,
363
+ enabled : if enable { "enabled" } else { "disabled" } ,
364
+ reason,
365
+ } ) ;
366
+ } else if stability. requires_nightly ( ) . is_some ( ) {
367
+ // An unstable feature. Warn about using it. It makes little sense
368
+ // to hard-error here since we just warn about fully unknown
369
+ // features above.
370
+ sess. dcx ( )
371
+ . emit_warn ( error:: UnstableCTargetFeature { feature : base_feature } ) ;
332
372
}
333
- } ;
334
- sess. dcx ( ) . emit_warn ( unknown_feature) ;
335
- }
336
- Some ( ( _, stability, _) ) => {
337
- if let Err ( reason) = stability. toggle_allowed ( ) {
338
- sess. dcx ( ) . emit_warn ( error:: ForbiddenCTargetFeature {
339
- feature,
340
- enabled : if enable { "enabled" } else { "disabled" } ,
341
- reason,
342
- } ) ;
343
- } else if stability. requires_nightly ( ) . is_some ( ) {
344
- // An unstable feature. Warn about using it. It makes little sense
345
- // to hard-error here since we just warn about fully unknown
346
- // features above.
347
- sess. dcx ( ) . emit_warn ( error:: UnstableCTargetFeature { feature } ) ;
348
373
}
349
374
}
350
375
}
376
+ } ,
377
+ ) ;
351
378
352
- // FIXME(nagisa): figure out how to not allocate a full hashset here.
353
- featsmap. insert ( feature, enable) ;
354
- }
355
-
356
- if let Some ( f) = check_tied_features ( sess, & featsmap) {
379
+ if diagnostics {
380
+ // FIXME(nagisa): figure out how to not allocate a full hashmap here.
381
+ if let Some ( f) = check_tied_features (
382
+ sess,
383
+ & FxHashMap :: from_iter ( rust_features. iter ( ) . map ( |& ( enable, feature) | ( feature, enable) ) ) ,
384
+ ) {
357
385
sess. dcx ( ) . emit_err ( error:: TargetFeatureDisableOrEnable {
358
386
features : f,
359
387
span : None ,
0 commit comments