What does flags & ACK_MASK == ACK_MASK test when & binds more loosely than ==?
answer
- read it the way the parser reads it
- which of the two operators groups first?
- what number does a true comparison contribute?
- the mask never reaches the AND
- it ends up testing bit zero
basics
~20 sIt tests the lowest bit of flags, not the ACK bit. The comparison binds tighter, runs first and yields 1, so the line reduces to flags AND 1 — a silent bug that still compiles.
solid answer
~50 sThe parser groups the comparison first, so the line means `flags & (ACK_MASK == ACK_MASK)`. Comparing a constant with itself is always true, a true value contributes 1 in an arithmetic context, and the whole expression collapses to `flags & 1` — a test of bit 0, with the ACK mask never reaching the AND at all. It is not "always true", which is the usual wrong guess; it is a test of the wrong bit, so it passes whenever the lowest flag happens to be set and fails otherwise. Nothing warns you, because the expression is well-formed. The fix is parentheses: `(flags & ACK_MASK) == ACK_MASK`, or the equivalent `(flags & ACK_MASK) != 0` for a single-bit mask. Precedence for the bitwise operators is one of the few places mainstream languages genuinely disagree, so parenthesising mixed bitwise-and-comparison expressions is the habit that travels.
code
pseudocode · 13 linesACK_MASK = 0x10 // the ACK flag lives in bit 4
...
// as written in the diff:
if flags & ACK_MASK == ACK_MASK
handle_ack()
// grouped by a parser that binds & looser than ==:
if flags & (ACK_MASK == ACK_MASK) // the comparison yields 1
handle_ack() // so this tests bit 0 of flags
// intended:
if (flags & ACK_MASK) == ACK_MASK
handle_ack()go deeper
Know that a comparison groups before a bitwise AND in many languages, and that the safe habit is to parenthesise the mask test. Be able to say what the expression collapses to.
Walk the parse out loud: comparison first, true contributes 1, result is a test of bit 0. Explain why that is a wrong-bit bug rather than an always-true condition, and give the fix.
Demonstrate how you keep the class of bug out: parenthesise mixed expressions on sight, prefer named flag predicates over open-coded masks, and require a negative-path test that fails when the wrong bit is tested.
Decide the standard rather than the instance: a lint rule that flags unparenthesised bitwise-with-comparison expressions, and a convention that flag tests go through generated or hand-written predicates, so precedence is answered once for the codebase.
### What the parser sees Operator precedence decides how an expression is grouped before anything is evaluated. In the family of languages that place the bitwise operators *below* the equality comparison, the line `if flags & ACK_MASK == ACK_MASK` groups as `if flags & (ACK_MASK == ACK_MASK)` The comparison is evaluated first. A constant equals itself, so the comparison is true, and in an arithmetic context a true value contributes the numeric value 1. What remains is `flags & 1`: a test of the lowest bit of `flags`. The mask is consumed by the comparison and never participates in the AND. ### Why "always true" is the wrong diagnosis Most people who spot that something is off say the condition is always true. It is not. It is a correct-looking test of the *wrong bit*, and that is worse: - If the flag in bit 0 happens to correlate with ACK in your test fixtures — a very common accident, since fixtures often set several flags together — the tests pass. - In production, packets that set ACK without bit 0 are silently dropped from the handler, and packets that set bit 0 without ACK are handled as if they were acknowledgements. - The failure is data-dependent, so it reproduces only with the right traffic, which is exactly the shape of bug that survives a release. ### Why nothing stops it The expression is well-formed. In languages where integers and booleans interconvert, `int & bool` is a perfectly legal arithmetic expression. Stricter type systems reject it — which is a genuine safety win — but the same shape reappears there in the form `mask & FLAG != 0`, where the comparison against zero is grouped first and the AND is applied to a boolean-ish result, or is rejected only after someone has already been confused by it. This is one of the concepts where mainstream ecosystems made different calls. C, C++, Java and JavaScript inherited a precedence table that binds `&` *looser* than `==`, so the trap is live. Python, Go and Rust bind the bitwise operators *tighter* than the comparisons, so the very same line means what it looks like. Two engineers can therefore both be certain about this line and both be right, for different codebases. The portable rule is not to memorise the table but to parenthesise whenever a bitwise operator and a comparison meet. ### The neighbouring trap: `&` where `&&` was meant The same slip of one character has a different consequence: - `&&` is a logical operator. It short-circuits — the right operand is not evaluated when the left is false — and it yields a truth value. - `&` is bitwise. It evaluates both operands, always, and on integers it produces a *bit pattern*. So `if (isReady & hasCapacity)` with the integer values 2 and 4 yields 0, and the branch is skipped even though both operands are non-zero and would both be considered true. Two "true" flags AND to false because they occupy different bit positions. And because `&` does not short-circuit, a guard of the form `if (p != NULL && p->ready)` written with a single `&` loses its protection: the right side runs regardless. ### Reviewing for it A few checks that catch this class in a diff without needing the precedence table in your head: - **Any line mixing a bitwise operator with a comparison** gets parentheses, or gets a comment explaining why not. - **A single-bit mask** is more clearly tested as `(flags & ACK_MASK) != 0` than by comparing against the mask; the compare-against-the-mask form exists for multi-bit masks where you require *all* the selected bits. - **Prefer a named predicate** — one small `is_ack(flags)` helper — so the precedence question is answered once instead of at every call site. - **Test the negative case.** A test that asserts a packet *without* the flag is not handled catches the wrong-bit bug; a test that only asserts the positive path does not. ### The one-line summary Precedence changes what a program *means*, not just how it looks, and the compiler will not tell you when your reading and the parser's reading differ. When bitwise operators meet comparisons, parenthesise.
- A teammate writes a guard as isReady & hasCapacity with two integer flags — what is wrong?Two things. The bitwise AND of two non-zero integers can be zero when they occupy different bit positions, so 2 and 4 combine to 0 and the branch is skipped though both flags are set. And the bitwise operator does not short-circuit, so a left operand that was meant to protect the right one — a null or bounds check — no longer protects anything.
- For a single-bit mask, is comparing against the mask or against zero better?Against zero: `(flags & MASK) != 0` says "any selected bit is set", which is the whole meaning for a one-bit mask, and it stays correct if the mask is later widened to mean "any of these". Comparing against the mask means "all selected bits are set", which is the right form only when you genuinely require every bit in a multi-bit mask.
- Why might this bug pass code review and the test suite?The expression compiles and reads exactly like the intended test, so the eye supplies the parentheses the parser did not. Fixtures usually set several flags at once, so the wrong bit is often set alongside the right one and the positive-path assertions pass. Only a test that asserts the handler is *not* invoked for a packet lacking the flag exposes it.
saying these in an interview costs you the question
- The condition is always true
- Parentheses there are only a style preference
- The compiler warns about this everywhere
- Bitwise and logical AND are interchangeable in conditions
- Operator precedence is identical across languages