22#![ feature( macro_metavar_expr) ]
33#![ feature( rustc_private) ]
44#![ feature( unwrap_infallible) ]
5+ #![ feature( control_flow_into_value) ]
56#![ recursion_limit = "512" ]
67#![ allow( clippy:: missing_errors_doc, clippy:: missing_panics_doc, clippy:: must_use_candidate) ]
78#![ warn(
@@ -2475,36 +2476,51 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
24752476 }
24762477}
24772478
2478- /// Checks if the provided `CfgEntry` evaluation to `true` implies something one the value of the
2479- /// `test` attribute .
2479+ /// Checks if the provided `CfgEntry` evaluation to `true` implies the `test` attribute is equal to
2480+ /// `value` .
24802481///
2481- /// When the predicate can be determined statically, returns `Some(true)` or `Some(false)`,
2482- /// depending on whether the entry implies `test` or `not(test)` respectively.
2483- /// If nothing can be said regarding the value of `test`, returns `None`.
2484- fn cfg_implies_test ( cfg : & CfgEntry ) -> Option < bool > {
2485- match cfg {
2486- CfgEntry :: NameValue { name : sym:: test, .. } => Some ( true ) ,
2487- CfgEntry :: All ( subs, _) => subs
2488- . iter ( )
2489- . map ( cfg_implies_test)
2490- . reduce ( |a, b| match ( a, b) {
2491- ( Some ( true ) , Some ( true ) ) => Some ( true ) ,
2492- ( Some ( false ) , Some ( false ) ) => Some ( false ) ,
2493- ( None , Some ( a) ) | ( Some ( a) , None ) => Some ( a) ,
2494- _ => None ,
2495- } )
2496- . unwrap_or ( None ) ,
2497- CfgEntry :: Any ( subs, _) => subs
2498- . iter ( )
2499- . map ( cfg_implies_test)
2500- . reduce ( |a, b| match ( a, b) {
2501- ( Some ( true ) , Some ( true ) ) => Some ( true ) ,
2502- ( Some ( false ) , Some ( false ) ) => Some ( false ) ,
2503- _ => None ,
2504- } )
2505- . unwrap_or ( None ) ,
2506- _ => None ,
2482+ /// Returns `Some(true)` if $$cfg \implies test = value$$ is a tautology.
2483+ /// Returns `Some(false)` if $$cfg \implies test = value$$ is a contradiction.
2484+ /// If the algorithm cannot determine the implication, returns `None`.
2485+ ///
2486+ /// # Note
2487+ /// This function is not a generic implication check and might return `None` even if the implication
2488+ /// could be proven. But itshould be sufficient for most real world configuration predicates.
2489+ fn cfg_implies_test_is ( value : bool , cfg : & CfgEntry ) -> Option < bool > {
2490+ /// Evaluates the Shannon cofactor of `cfg` with respect to `test = value`.
2491+ ///
2492+ /// Returns `Some(true)` if the cofactor is a tautology, `Some(false)` if it's a contradiction,
2493+ /// and `None` if the algorithm cannot determine the cofactor's value.
2494+ fn evaluate_shannon_cofactor ( cfg : & CfgEntry , value : bool ) -> Option < bool > {
2495+ match cfg {
2496+ CfgEntry :: NameValue { name : sym:: test, .. } => Some ( value) ,
2497+ CfgEntry :: All ( subs, _) => subs
2498+ . iter ( )
2499+ . try_fold ( Some ( true ) , |a, c| match evaluate_shannon_cofactor ( c, value) {
2500+ Some ( false ) => ControlFlow :: Break ( Some ( false ) ) ,
2501+ Some ( true ) => ControlFlow :: Continue ( a) ,
2502+ None => ControlFlow :: Continue ( None ) ,
2503+ } )
2504+ . into_value ( ) ,
2505+ CfgEntry :: Any ( subs, _) => subs
2506+ . iter ( )
2507+ . try_fold ( Some ( false ) , |a, c| match evaluate_shannon_cofactor ( c, value) {
2508+ Some ( true ) => ControlFlow :: Break ( Some ( true ) ) ,
2509+ Some ( false ) => ControlFlow :: Continue ( a) ,
2510+ None => ControlFlow :: Continue ( None ) ,
2511+ } )
2512+ . into_value ( ) ,
2513+ CfgEntry :: Not ( sub, _) => evaluate_shannon_cofactor ( sub, value) . map ( |x| !x) ,
2514+ CfgEntry :: Bool ( val, _) => Some ( * val) ,
2515+ CfgEntry :: NameValue { .. } | CfgEntry :: Version { .. } => None ,
2516+ }
25072517 }
2518+
2519+ // According to Boole's expansion theorem, $$cfg = (test \land cfg_{test}) \lor (\lnot test
2520+ // \land cfg_{\lnot test})$$
2521+ // Therefore, $$cfg \implies test = value$$ is equivalent to $$cfg_{test=\lnot value}$$ being a
2522+ // contradiction.
2523+ evaluate_shannon_cofactor ( cfg, !value) . map ( |x| !x)
25082524}
25092525
25102526/// Checks if `id` has a `ConfigurationPredicate` attribute applied that evaluates to true only if
@@ -2518,7 +2534,7 @@ pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
25182534 . iter ( )
25192535 // If the dependency cannot be determined statically, we conservatively assume that it does compile even
25202536 // without `test` enabled.
2521- . any ( |( cfg, _) | matches ! ( cfg_implies_test ( cfg) , Some ( true ) ) )
2537+ . any ( |( cfg, _) | matches ! ( cfg_implies_test_is ( true , cfg) , Some ( true ) ) )
25222538 {
25232539 true
25242540 } else {
0 commit comments