@@ -159,6 +159,58 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
159159 }
160160}
161161
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+
162214/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
163215/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
164216/// 2nd component of the return value, respectively).
@@ -175,7 +227,7 @@ pub fn cfg_target_feature(
175227) -> ( Vec < Symbol > , Vec < Symbol > ) {
176228 // Compute which of the known target features are enabled in the 'base' target machine. We only
177229 // 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
179231 . target
180232 . rust_target_features ( )
181233 . iter ( )
@@ -190,43 +242,23 @@ pub fn cfg_target_feature(
190242 . collect ( ) ;
191243
192244 // 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`.
205251 #[ 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) ) ;
226258 }
227- } ) ;
228- }
229- }
259+ }
260+ } ,
261+ ) ;
230262
231263 // Filter enabled features based on feature gates.
232264 let f = |allow_unstable| {
@@ -289,85 +321,82 @@ pub fn flag_to_backend_features<'a, const N: usize>(
289321
290322 // Compute implied features
291323 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| {
306329 if diagnostics {
307330 sess. dcx ( ) . emit_warn ( errors:: UnknownCTargetFeaturePrefix { feature } ) ;
308331 }
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+ }
320338
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+ }
333365 } 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+ } ) ;
346387 }
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 } ) ;
362388 }
363389 }
364390 }
391+ } ,
392+ ) ;
365393
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+ ) {
371400 sess. dcx ( ) . emit_err ( errors:: TargetFeatureDisableOrEnable {
372401 features : f,
373402 span : None ,
0 commit comments