@@ -145,6 +145,54 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
145145 }
146146}
147147
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+
148196/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
149197/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
150198/// 2nd component of the return value, respectively).
@@ -161,7 +209,7 @@ pub fn cfg(
161209) -> ( Vec < Symbol > , Vec < Symbol > ) {
162210 // Compute which of the known target features are enabled in the 'base' target machine. We only
163211 // 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
165213 . target
166214 . rust_target_features ( )
167215 . iter ( )
@@ -176,43 +224,23 @@ pub fn cfg(
176224 . collect ( ) ;
177225
178226 // 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`.
203233 #[ 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) ) ;
212240 }
213- } ) ;
214- }
215- }
241+ }
242+ } ,
243+ ) ;
216244
217245 // Filter enabled features based on feature gates.
218246 let f = |allow_unstable| {
@@ -275,25 +303,22 @@ pub fn flag_to_backend_features<'a, const N: usize>(
275303
276304 // Compute implied features
277305 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| {
292311 if diagnostics {
293312 sess. dcx ( ) . emit_warn ( error:: UnknownCTargetFeaturePrefix { feature } ) ;
294313 }
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+
297322 // Remove features that are meant for rustc, not the backend.
298323 rust_features. retain ( |( _, feature) | {
299324 // Retain if it is not a rustc feature
0 commit comments