@@ -158,6 +158,58 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
158158 }
159159}
160160
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+
161213/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
162214/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
163215/// 2nd component of the return value, respectively).
@@ -174,7 +226,7 @@ pub fn cfg(
174226) -> ( Vec < Symbol > , Vec < Symbol > ) {
175227 // Compute which of the known target features are enabled in the 'base' target machine. We only
176228 // 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
178230 . target
179231 . rust_target_features ( )
180232 . iter ( )
@@ -189,43 +241,23 @@ pub fn cfg(
189241 . collect ( ) ;
190242
191243 // 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`.
204250 #[ 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) ) ;
225257 }
226- } ) ;
227- }
228- }
258+ }
259+ } ,
260+ ) ;
229261
230262 // Filter enabled features based on feature gates.
231263 let f = |allow_unstable| {
@@ -288,85 +320,81 @@ pub fn flag_to_backend_features<'a, const N: usize>(
288320
289321 // Compute implied features
290322 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| {
305328 if diagnostics {
306329 sess. dcx ( ) . emit_warn ( error:: UnknownCTargetFeaturePrefix { feature } ) ;
307330 }
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+ }
319337
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+ }
332364 } 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 } ) ;
345385 }
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 } ) ;
361386 }
362387 }
363388 }
389+ } ,
390+ ) ;
364391
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+ ) {
370398 sess. dcx ( ) . emit_err ( error:: TargetFeatureDisableOrEnable {
371399 features : f,
372400 span : None ,
0 commit comments