@@ -145,6 +145,54 @@ 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 ( FxHashSet < & ' a str > , /* enable */ bool ) ,
156
+ ) {
157
+ // A cache for the backwards implication map.
158
+ let mut inverse_implied_features: Option < FxHashMap < & str , FxHashSet < & str > > > = None ;
159
+
160
+ for feature in target_feature_flag. split ( ',' ) {
161
+ if let Some ( base_feature) = feature. strip_prefix ( '+' ) {
162
+ callback ( sess. target . implied_target_features ( base_feature) , true )
163
+ } else if let Some ( base_feature) = feature. strip_prefix ( '-' ) {
164
+ // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical contraposition.
165
+ // So we have to find all the reverse implications of `base_feature` and disable them, too.
166
+
167
+ let inverse_implied_features = inverse_implied_features. get_or_insert_with ( || {
168
+ let mut set: FxHashMap < & str , FxHashSet < & str > > = FxHashMap :: default ( ) ;
169
+ for ( f, _, is) in sess. target . rust_target_features ( ) {
170
+ for i in is. iter ( ) {
171
+ set. entry ( i) . or_default ( ) . insert ( f) ;
172
+ }
173
+ }
174
+ set
175
+ } ) ;
176
+
177
+ // Inverse mplied target features have their own inverse implied target features, so we
178
+ // traverse the map until there are no more features to add.
179
+ let mut features = FxHashSet :: default ( ) ;
180
+ let mut new_features = vec ! [ base_feature] ;
181
+ while let Some ( new_feature) = new_features. pop ( ) {
182
+ if features. insert ( new_feature) {
183
+ if let Some ( implied_features) = inverse_implied_features. get ( & new_feature) {
184
+ new_features. extend ( implied_features)
185
+ }
186
+ }
187
+ }
188
+
189
+ callback ( features, false )
190
+ } else if !feature. is_empty ( ) {
191
+ err_callback ( feature)
192
+ }
193
+ }
194
+ }
195
+
148
196
/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
149
197
/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
150
198
/// 2nd component of the return value, respectively).
@@ -161,7 +209,7 @@ pub fn cfg(
161
209
) -> ( Vec < Symbol > , Vec < Symbol > ) {
162
210
// Compute which of the known target features are enabled in the 'base' target machine. We only
163
211
// consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
164
- let mut features: FxHashSet < Symbol > = sess
212
+ let mut features: UnordSet < Symbol > = sess
165
213
. target
166
214
. rust_target_features ( )
167
215
. iter ( )
@@ -176,43 +224,23 @@ pub fn cfg(
176
224
. collect ( ) ;
177
225
178
226
// 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.
227
+ parse_rust_feature_flag (
228
+ sess,
229
+ target_feature_flag,
230
+ /* err_callback */ |_| { } ,
231
+ |new_features, enabled| {
232
+ // Iteration order is irrelevant since this only influences an `UnordSet`.
203
233
#[ 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
234
+ if enabled {
235
+ features. extend ( new_features. into_iter ( ) . map ( |f| Symbol :: intern ( f) ) ) ;
236
+ } else {
237
+ // Remove `new_features` from `features`.
238
+ for new in new_features {
239
+ features. remove ( & Symbol :: intern ( new) ) ;
212
240
}
213
- } ) ;
214
- }
215
- }
241
+ }
242
+ } ,
243
+ ) ;
216
244
217
245
// Filter enabled features based on feature gates.
218
246
let f = |allow_unstable| {
@@ -275,25 +303,22 @@ pub fn flag_to_backend_features<'a, const N: usize>(
275
303
276
304
// Compute implied features
277
305
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 ( ) {
306
+ parse_rust_feature_flag (
307
+ sess,
308
+ & sess. opts . cg . target_feature ,
309
+ /* err_callback */
310
+ |feature| {
292
311
if diagnostics {
293
312
sess. dcx ( ) . emit_warn ( error:: UnknownCTargetFeaturePrefix { feature } ) ;
294
313
}
295
- }
296
- }
314
+ } ,
315
+ |new_features, enabled| {
316
+ rust_features. extend (
317
+ UnordSet :: from ( new_features) . to_sorted_stable_ord ( ) . iter ( ) . map ( |& & s| ( enabled, s) ) ,
318
+ ) ;
319
+ } ,
320
+ ) ;
321
+
297
322
// Remove features that are meant for rustc, not the backend.
298
323
rust_features. retain ( |( _, feature) | {
299
324
// Retain if it is not a rustc feature
0 commit comments