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