Skip to content

prim/BitFlag: assert bit index in makeMask - #280

Open
AllyOmega wants to merge 2 commits into
open-ead:masterfrom
AllyOmega:prim-bitflag-assertions
Open

prim/BitFlag: assert bit index in makeMask#280
AllyOmega wants to merge 2 commits into
open-ead:masterfrom
AllyOmega:prim-bitflag-assertions

Conversation

@AllyOmega

@AllyOmega AllyOmega commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #85.

Change

sead::BitFlag<T>::makeMask gains the missing bit-index assertion:

static T makeMask(int bit)
{
    SEAD_ASSERT(static_cast<u32>(bit) < sizeof(T) * 8);
    return T(1) << bit;
}

Rationale

  • Issue prim/BitFlag: Missing assertions in isOnBit and other functions #85 asks for the missing static_cast<u32>(bit) < sizeof(T)*8 assertions in the
    BitFlag bit-index functions (setBit, resetBit, changeBit, toggleBit,
    isOnBit, isOffBit, testAndClearBit).
  • The original sead lineage (aboood40091/sead,
    engine/library/include/prim/seadBitFlag.h) ships this assertion inside makeMask
    (currently commented out there), covering all of the above in one place — they are all
    makeMask callers.
  • BitFlagUtil::countRightOnBit in the same module already carries the identical
    assertion pattern; this restores consistency for the template.

Verification

  • Release codegen is unchanged: a TU instantiating BitFlag32/BitFlag64 and
    exercising all bit-index functions compiles to a byte-identical .o with and without
    this patch (clang 15.0.7, -O3, -fno-exceptions, no SEAD_DEBUG), because
    SEAD_ASSERT collapses to if (false) in release builds.
  • With -DSEAD_DEBUG=1 the same TU compiles with the asserts active (assert call sites
    present; TU size grows 1520 -> 2792 bytes).
  • clang-format 12.0.1 (the CI-pinned version) passes on the modified header.

Caveat

The issue text ends with "and more" — without the original game's debug symbols for these
functions, further missing assertions cannot be enumerated with confidence. This PR is the
minimal, lineage-backed restoration; more can follow with evidence.


This change is Reviewable

@german77

german77 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

This assert should generate the following pseudo code. Currently just based on the missing cBitNum this implementation is incorrect

    if (0xf < param_3) {
      sead::system::HaltWithDetailNoFormat("...include/prim/seadBitFlag.h",  ...LineNumber... ,"static_cast<u32>(bit) < cBitNum");
    }
    *(ushort *)(this + 0x70) = *(ushort *)(this + 0x70) | (ushort)(1 << (ulong)(param_3 & 0x1f));

Per decompiled-binary evidence shared in the PR discussion, the original
assert condition is 'static_cast<u32>(bit) < cBitNum'. Expose cBitNum as a
public constant of BitFlag<T> and use it in makeMask.
@AllyOmega

Copy link
Copy Markdown
Author

Thanks for the pseudo code — that settles it. Updated: BitFlag<T> now exposes static constexpr u32 cBitNum = sizeof(T) * 8; and makeMask asserts static_cast<u32>(bit) < cBitNum, so the assert string matches the binary ("static_cast<u32>(bit) < cBitNum"), and the halt fires for bit > 15 on a 16-bit flag exactly as in your pseudo code (0xf < param_3).

Re-verified after the change: release codegen is still byte-identical to the pre-patch baseline (clang 15.0.7, -O3, no SEAD_DEBUG), the debug build compiles with asserts active, and clang-format 12 passes.

If the cBitNum placement/usage still doesn't match your evidence (e.g. it should live in BitFlagUtil or be an enum), tell me and I'll adjust or close — I don't have the binary you're looking at, so your decompiled line is the ground truth here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

prim/BitFlag: Missing assertions in isOnBit and other functions

2 participants