@@ -145,6 +145,58 @@ 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 (
156+ /* base_feature */ & ' a str ,
157+ /* with_implied */ FxHashSet < & ' a str > ,
158+ /* enable */ bool ,
159+ ) ,
160+ ) {
161+ // A cache for the backwards implication map.
162+ let mut inverse_implied_features: Option < FxHashMap < & str , FxHashSet < & str > > > = None ;
163+
164+ for feature in target_feature_flag. split ( ',' ) {
165+ if let Some ( base_feature) = feature. strip_prefix ( '+' ) {
166+ callback ( base_feature, sess. target . implied_target_features ( base_feature) , true )
167+ } else if let Some ( base_feature) = feature. strip_prefix ( '-' ) {
168+ // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical contraposition.
169+ // So we have to find all the reverse implications of `base_feature` and disable them, too.
170+
171+ let inverse_implied_features = inverse_implied_features. get_or_insert_with ( || {
172+ let mut set: FxHashMap < & str , FxHashSet < & str > > = FxHashMap :: default ( ) ;
173+ for ( f, _, is) in sess. target . rust_target_features ( ) {
174+ for i in is. iter ( ) {
175+ set. entry ( i) . or_default ( ) . insert ( f) ;
176+ }
177+ }
178+ set
179+ } ) ;
180+
181+ // Inverse mplied target features have their own inverse implied target features, so we
182+ // traverse the map until there are no more features to add.
183+ let mut features = FxHashSet :: default ( ) ;
184+ let mut new_features = vec ! [ base_feature] ;
185+ while let Some ( new_feature) = new_features. pop ( ) {
186+ if features. insert ( new_feature) {
187+ if let Some ( implied_features) = inverse_implied_features. get ( & new_feature) {
188+ new_features. extend ( implied_features)
189+ }
190+ }
191+ }
192+
193+ callback ( base_feature, features, false )
194+ } else if !feature. is_empty ( ) {
195+ err_callback ( feature)
196+ }
197+ }
198+ }
199+
148200/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
149201/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
150202/// 2nd component of the return value, respectively).
@@ -161,7 +213,7 @@ pub fn cfg(
161213) -> ( Vec < Symbol > , Vec < Symbol > ) {
162214 // Compute which of the known target features are enabled in the 'base' target machine. We only
163215 // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
164- let mut features: FxHashSet < Symbol > = sess
216+ let mut features: UnordSet < Symbol > = sess
165217 . target
166218 . rust_target_features ( )
167219 . iter ( )
@@ -176,43 +228,23 @@ pub fn cfg(
176228 . collect ( ) ;
177229
178230 // 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.
231+ parse_rust_feature_flag (
232+ sess,
233+ target_feature_flag,
234+ /* err_callback */ |_| { } ,
235+ |_base_feature, new_features, enabled| {
236+ // Iteration order is irrelevant since this only influences an `UnordSet`.
203237 #[ 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
238+ if enabled {
239+ features. extend ( new_features. into_iter ( ) . map ( |f| Symbol :: intern ( f) ) ) ;
240+ } else {
241+ // Remove `new_features` from `features`.
242+ for new in new_features {
243+ features. remove ( & Symbol :: intern ( new) ) ;
212244 }
213- } ) ;
214- }
215- }
245+ }
246+ } ,
247+ ) ;
216248
217249 // Filter enabled features based on feature gates.
218250 let f = |allow_unstable| {
@@ -275,85 +307,81 @@ pub fn flag_to_backend_features<'a, const N: usize>(
275307
276308 // Compute implied features
277309 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 ( ) {
310+ parse_rust_feature_flag (
311+ sess,
312+ & sess. opts . cg . target_feature ,
313+ /* err_callback */
314+ |feature| {
292315 if diagnostics {
293316 sess. dcx ( ) . emit_warn ( error:: UnknownCTargetFeaturePrefix { feature } ) ;
294317 }
295- }
296- }
297- // Remove features that are meant for rustc, not the backend.
298- rust_features. retain ( |( _, feature) | {
299- // Retain if it is not a rustc feature
300- !RUSTC_SPECIFIC_FEATURES . contains ( feature)
301- } ) ;
302-
303- // Check feature validity.
304- if diagnostics {
305- let mut featsmap = FxHashMap :: default ( ) ;
318+ } ,
319+ |base_feature, new_features, enable| {
320+ // Skip features that are meant for rustc, not the backend.
321+ if RUSTC_SPECIFIC_FEATURES . contains ( & base_feature) {
322+ return ;
323+ }
306324
307- for & ( enable, feature) in & rust_features {
308- let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == feature) ;
309- match feature_state {
310- None => {
311- // This is definitely not a valid Rust feature name. Maybe it is a backend feature name?
312- // If so, give a better error message.
313- let rust_feature = known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
314- let backend_features = to_backend_features ( rust_feature) ;
315- if backend_features. contains ( & feature)
316- && !backend_features. contains ( & rust_feature)
317- {
318- Some ( rust_feature)
325+ rust_features. extend (
326+ UnordSet :: from ( new_features) . to_sorted_stable_ord ( ) . iter ( ) . map ( |& & s| ( enable, s) ) ,
327+ ) ;
328+ // Check feature validity.
329+ if diagnostics {
330+ let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == base_feature) ;
331+ match feature_state {
332+ None => {
333+ // This is definitely not a valid Rust feature name. Maybe it is a backend feature name?
334+ // If so, give a better error message.
335+ let rust_feature =
336+ known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
337+ let backend_features = to_backend_features ( rust_feature) ;
338+ if backend_features. contains ( & base_feature)
339+ && !backend_features. contains ( & rust_feature)
340+ {
341+ Some ( rust_feature)
342+ } else {
343+ None
344+ }
345+ } ) ;
346+ let unknown_feature = if let Some ( rust_feature) = rust_feature {
347+ error:: UnknownCTargetFeature {
348+ feature : base_feature,
349+ rust_feature : error:: PossibleFeature :: Some { rust_feature } ,
350+ }
319351 } else {
320- None
321- }
322- } ) ;
323- let unknown_feature = if let Some ( rust_feature) = rust_feature {
324- error:: UnknownCTargetFeature {
325- feature,
326- rust_feature : error:: PossibleFeature :: Some { rust_feature } ,
327- }
328- } else {
329- error:: UnknownCTargetFeature {
330- feature,
331- rust_feature : error:: PossibleFeature :: None ,
352+ error:: UnknownCTargetFeature {
353+ feature : base_feature,
354+ rust_feature : error:: PossibleFeature :: None ,
355+ }
356+ } ;
357+ sess. dcx ( ) . emit_warn ( unknown_feature) ;
358+ }
359+ Some ( ( _, stability, _) ) => {
360+ if let Err ( reason) = stability. toggle_allowed ( ) {
361+ sess. dcx ( ) . emit_warn ( error:: ForbiddenCTargetFeature {
362+ feature : base_feature,
363+ enabled : if enable { "enabled" } else { "disabled" } ,
364+ reason,
365+ } ) ;
366+ } else if stability. requires_nightly ( ) . is_some ( ) {
367+ // An unstable feature. Warn about using it. It makes little sense
368+ // to hard-error here since we just warn about fully unknown
369+ // features above.
370+ sess. dcx ( )
371+ . emit_warn ( error:: UnstableCTargetFeature { feature : base_feature } ) ;
332372 }
333- } ;
334- sess. dcx ( ) . emit_warn ( unknown_feature) ;
335- }
336- Some ( ( _, stability, _) ) => {
337- if let Err ( reason) = stability. toggle_allowed ( ) {
338- sess. dcx ( ) . emit_warn ( error:: ForbiddenCTargetFeature {
339- feature,
340- enabled : if enable { "enabled" } else { "disabled" } ,
341- reason,
342- } ) ;
343- } else if stability. requires_nightly ( ) . is_some ( ) {
344- // An unstable feature. Warn about using it. It makes little sense
345- // to hard-error here since we just warn about fully unknown
346- // features above.
347- sess. dcx ( ) . emit_warn ( error:: UnstableCTargetFeature { feature } ) ;
348373 }
349374 }
350375 }
376+ } ,
377+ ) ;
351378
352- // FIXME(nagisa): figure out how to not allocate a full hashset here.
353- featsmap. insert ( feature, enable) ;
354- }
355-
356- if let Some ( f) = check_tied_features ( sess, & featsmap) {
379+ if diagnostics {
380+ // FIXME(nagisa): figure out how to not allocate a full hashmap here.
381+ if let Some ( f) = check_tied_features (
382+ sess,
383+ & FxHashMap :: from_iter ( rust_features. iter ( ) . map ( |& ( enable, feature) | ( feature, enable) ) ) ,
384+ ) {
357385 sess. dcx ( ) . emit_err ( error:: TargetFeatureDisableOrEnable {
358386 features : f,
359387 span : None ,
0 commit comments