skip to content

Where do infix function calls sit in Kotlin's operator precedence, and how does that affect an expression like `1 + 2 shl 3` or `a == b and c`?

level: middleimportance: should knowfreq 40%

answer

  1. Infix < arithmetic/range, but > comparison/equality/&&/||
  2. `1 + 2 shl 3` = `(1 + 2) shl 3`
  3. `a == b and c` = `a == (b and c)`
  4. Opposite of C: `and`/`or` bind tighter than `==`
  5. Left-associative; parenthesize when mixing

basics

~10 s

Infix calls have lower precedence than arithmetic operators (+, -, *) but higher than comparisons, equality, and the logical &&/||. So arithmetic binds first, then infix, then comparisons.

solid answer

~40 s

Named infix functions share one precedence level that sits **below** the named-operator arithmetic (`*`, `/`, `%`, `+`, `-`, range `..`) and **above** comparison (`<`, `>`), equality (`==`, `!=`), and the conjunction/disjunction (`&&`, `||`). So `1 + 2 shl 3` parses as `(1 + 2) shl 3` because `+` binds tighter than the infix `shl`. And `a == b and c` parses as `a == (b and c)` because infix `and` binds tighter than `==`. This is a frequent gotcha because `shl/shr/and/or/xor` are plain infix functions, not bitwise operators with C-style precedence — in C, `&` is *lower* than `==`, the opposite of Kotlin. Infix calls are also **left-associative** and don't mix with arithmetic operators without parentheses. When in doubt, add explicit parentheses.

code

kotlin · 8 lines
kotlin
val x = 1 + 2 shl 3      // 24  -> (1+2) shl 3
val y = 1 shl 2 + 3      // 32  -> 1 shl (2+3)

val flags = 0b1010
val mask  = 0b0010
// Gotcha: this is `result == (flags and mask)`
val result = 0b0010
val ok = result == flags and mask   // true: flags and mask = 2 == 2

go deeper

for a junior

Knows infix calls have a precedence and that parentheses make grouping explicit when unsure.

for a middle

Places infix below arithmetic/range and above comparison/equality/logical, and predicts (1+2) shl 3.

for a senior

Explains the C-vs-Kotlin bit-op precedence reversal and why it causes real bugs in masks/flags.

for a principal

Advises team conventions (mandatory parens around mixed infix expressions) and weighs infix readability against precedence surprises.

## Where infix sits Kotlin has a fixed operator-precedence table. From **highest** to **lowest**, the relevant slice is: - Postfix: `++`, `--`, `.`, `?.` - Prefix: `-`, `+`, `!` - Multiplicative: `*`, `/`, `%` - Additive: `+`, `-` - Range: `..`, `..<` - **Infix function** (named infix calls) ← all infix functions live here - Elvis: `?:` - Named checks: `in`, `!in`, `is`, `!is` - Comparison: `<`, `>`, `<=`, `>=` - Equality: `==`, `!=` - Conjunction: `&&` - Disjunction: `||` So **infix is below arithmetic and range, but above `?:`, comparison, equality, and the logical operators.** ## Worked examples ```kotlin 1 + 2 shl 3 // (1 + 2) shl 3 = 3 shl 3 = 24, because + binds tighter than shl 1 shl 2 + 3 // 1 shl (2 + 3) = 1 shl 5 = 32, same reason a == b and c // a == (b and c) — infix `and` binds tighter than == ``` `shl`, `shr`, `ushr`, `and`, `or`, `xor`, `inv` are **ordinary infix functions on `Int`/`Long`**, NOT special bitwise operators. This is the #1 surprise for developers coming from C/Java where `&` has *lower* precedence than `==`. In Kotlin it is the reverse. ## Associativity and mixing - All infix calls are **left-associative**: `a foo b foo c` = `(a foo b) foo c`. - You **cannot** chain an infix call with another binary operator without making the grouping explicit; the precedence table resolves it deterministically, but readers often misread it. ## Why `to`/`until`/`downTo` read naturally Because infix sits just below arithmetic and range, expressions like `0 until list.size`, `n downTo 1`, `i to j` group exactly the way you'd read them aloud — the operands are simple values or already-computed sub-expressions, so there's rarely ambiguity. Trouble only appears when you mix infix with `==`/`&&` (config flags, bit masks). ## Practical rule When combining an infix call with `==`, `&&`, `||`, `?:`, or other infix functions, **add parentheses**. It costs nothing and removes the precedence trap entirely.

  • How does Kotlin's bit-op precedence differ from C/Java?
    In C, `&`/`|`/`^` have lower precedence than `==`, so `a == b & c` means `(a == b) & c`. In Kotlin, `and`/`or`/`xor` are infix functions that bind tighter than `==`, so `a == b and c` means `a == (b and c)`.
  • Are infix calls left- or right-associative?
    Left-associative: `a foo b foo c` evaluates as `(a foo b) foo c`.

saying these in an interview costs you the question

  • Assuming bitwise infix functions follow C precedence
  • Saying infix binds tighter than `*`/`+`
  • Claiming infix is right-associative
  • Believing `==` binds tighter than `and`
  • Not reaching for parentheses when mixing infix with logical/equality operators

context