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