Skip to content

refactor(bitflags): Simplify conditions with testForAny, testForAll and remove unexpected assert in BitFlags::testForAll#2476

Merged
xezon merged 2 commits intoTheSuperHackers:mainfrom
xezon:xezon/refactor-bitflags-conditions
Mar 21, 2026
Merged

refactor(bitflags): Simplify conditions with testForAny, testForAll and remove unexpected assert in BitFlags::testForAll#2476
xezon merged 2 commits intoTheSuperHackers:mainfrom
xezon:xezon/refactor-bitflags-conditions

Conversation

@xezon
Copy link

@xezon xezon commented Mar 19, 2026

This change simplifies a few conditions with testForAny, testForAll and removes the unexpected assert in BitFlags::testForAll

TODO

  • Replicate in Generals

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing labels Mar 19, 2026
@greptile-apps
Copy link

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR refactors several BitFlags usage sites to use the more expressive testForNone / testForAll methods directly, removing manual any() short-circuit guards that were previously needed to avoid a (now-removed) DEBUG_ASSERTCRASH inside testForAll. All simplifications are semantically equivalent to the code they replace.

  • BitFlags::testForAll assert removed — the assert fired whenever that was an empty (zero) flag set. Removing it lets the function exhibit correct vacuous-truth semantics: "all bits in an empty set are satisfied" → returns true. This is the necessary prerequisite for the callers below.
  • DieModule.cpp (Generals & GeneralsMD)m_exemptStatus.any() && testForAny(exempt) is replaced by !testForNone(exempt), and m_requiredStatus.any() && !testForAll(required) is replaced by !testForAll(required). Both are semantically identical because testForNone(∅) = true and testForAll(∅) = true, so the empty-mask cases still pass through correctly.
  • UpgradeModule.cpp (Generals & GeneralsMD)!keyMask.any() || !keyMask.testForAny(conflicting) is replaced by keyMask.testForNone(conflicting). When keyMask is all-zero, testForNone returns true (no bits in common with anything), which matches the old short-circuit.
  • StealthUpdate.cpp (GeneralsMD)m_requiredStatus.any() && !testForAll(required) is simplified to !testForAll(required), relying on the same vacuous-truth property.
  • The open TODO to replicate the StealthUpdate.cpp change in Generals/ is noted in the PR description.

Confidence Score: 5/5

  • Safe to merge — all simplifications are semantically equivalent to the original code.
  • Every changed condition was verified to produce identical results for both the empty-mask and non-empty-mask cases. The assert removal is well-motivated and is the prerequisite for the cleaner call sites. The open StealthUpdate TODO for Generals is tracked in the PR description and does not block this change.
  • No files require special attention.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Include/Common/BitFlags.h Removes DEBUG_ASSERTCRASH from testForAll when called with zero flags, enabling vacuous-truth semantics (empty set → always true) required by the callers below.
GeneralsMD/Code/GameEngine/Include/Common/BitFlags.h Mirrors the Generals BitFlags.h change — removes same DEBUG_ASSERTCRASH from testForAll for Zero Hour.
Generals/Code/GameEngine/Source/GameLogic/Object/Die/DieModule.cpp Simplifies exempt/required status guards: replaces manual any()-guard + testForAny/testForAll with direct testForNone/testForAll calls, leveraging correct empty-set semantics.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Die/DieModule.cpp Identical simplification to Generals DieModule.cpp — semantically equivalent refactor for Zero Hour.
Generals/Code/GameEngine/Source/GameLogic/Object/Upgrade/UpgradeModule.cpp Simplifies the conflicting-upgrade guard from !keyMask.any()
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/UpgradeModule.cpp Mirrors the Generals UpgradeModule.cpp simplification for Zero Hour.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp Drops the redundant m_requiredStatus.any() guard before testForAll, relying on the now-correct vacuous-truth return of testForAll with an empty argument.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["testForAll(that)"] --> B{"that is empty?"}
    B -- "Before PR (assert fires)" --> C["DEBUG_ASSERTCRASH ❌"]
    B -- "After PR (assert removed)" --> D["Return true (vacuous)"]
    B -- "that has bits" --> E["Flip self, AND with that, return empty?"]

    F["isDieApplicable"] --> G{"!testForNone(exemptStatus)"}
    G -- "true: exempt bit set" --> H["return false"]
    G -- "false: no exempt bits" --> I{"!testForAll(requiredStatus)"}
    I -- "true: missing required bit" --> J["return false"]
    I -- "false: all required bits set" --> K["return true"]

    L["testUpgradeConditions"] --> M{"testForNone(conflicting)"}
    M -- "false: conflict found" --> N["skip activation block"]
    M -- "true: no conflict" --> O{"activation.any()"}
    O -- "true" --> P["check activation conditions"]
Loading

Last reviewed commit: "Replicate in General..."

…nd remove unexpected assert in BitFlags::testForAll
@xezon xezon force-pushed the xezon/refactor-bitflags-conditions branch from 12fc6e7 to 795dcd8 Compare March 19, 2026 18:53
Copy link

@Mauller Mauller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much cleaner.

@xezon
Copy link
Author

xezon commented Mar 21, 2026

Replicated in Generals with conflicts

D:\Projects\TheSuperHackers\GeneralsGameCode>FOR /F "delims=" %b IN ('git merge-base --fork-point main') DO git diff %b  1>changes.patch

D:\Projects\TheSuperHackers\GeneralsGameCode>git diff 39647c3b49d6c1ea5bcf15245c8060075ce2b58a  1>changes.patch

D:\Projects\TheSuperHackers\GeneralsGameCode>git apply -p2 --directory=Generals --reject --whitespace=fix changes.patch
Checking patch Generals/Code/GameEngine/Include/Common/BitFlags.h...
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Die/DieModule.cpp...
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp...
error: while searching for:
        }

        //We need all required status or else we fail
        // If we have any requirements
        if( data->m_requiredStatus.any()  &&  !self->getStatusBits().testForAll( data->m_requiredStatus ) )
                return FALSE;

        //If we have any forbidden statii, then fail

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp:327
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Upgrade/UpgradeModule.cpp...
Hunk #1 succeeded at 162 (offset -10 lines).
Applied patch Generals/Code/GameEngine/Include/Common/BitFlags.h cleanly.
Applied patch Generals/Code/GameEngine/Source/GameLogic/Object/Die/DieModule.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp with 1 reject...
Rejected hunk #1.
Applied patch Generals/Code/GameEngine/Source/GameLogic/Object/Upgrade/UpgradeModule.cpp cleanly

@xezon xezon merged commit 26c07f7 into TheSuperHackers:main Mar 21, 2026
23 checks passed
@xezon xezon deleted the xezon/refactor-bitflags-conditions branch March 21, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants