google / google/heir

Comparison operators (<, >, ==, !=) are broken in heir-py for every operand type

Open Beginner friendly
#3,384 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
MLIR
Stars
906
Forks
171
Avg merge
4d 12h
Merged PRs (30d)
32

Description

I've been fuzzing the heir-py Python frontend and ran into something that looks like a bug.

Comparison operators are actually implemented. `emit_binop()` in `mlir_emitter.py` has a `match binop.fn:` block that handles `operator.lt`, `.ge`, `.eq`, `.ne` and correctly emits `arith.cmpf` / `arith.cmpi` with the right predicate. But there's a bitwidth matching check that runs before that code, and it rejects every comparison before the actual handling logic is ever reached.

Minimal repro:

```python
from heir import compile
from heir.backends.cleartext import CleartextBackend
from heir.mlir.types import Secret, F64, I32

def cmp_float(x: Secret[F64], y: F64) -> F64:
return x < y

compile(scheme="ckks", backend=CleartextBackend())(cmp_float)
# InternalCompilerError: encountered internal error: "Encountered unexpected
# type float64 of type "

def cmp_int(x: Secret[I32], y: I32) -> I32:
return x < y

compile(scheme="bgv", backend=CleartextBackend())(cmp_int)
# InternalCompilerError: encountered internal error: "Result type bool is
# narrower than operands int32."
```

The root cause looks like this block in `emit_binop`:

```python
if result_type is not None and isIntegerLike(result_type):
ty_bw = getBitwidth(ty)
result_bw = getBitwidth(result_type)
if result_bw < ty_bw:
raise InternalCompilerError(
f"Result type {result_type} is narrower than operands {ty}."
)
```

This looks like it's meant to catch arithmetic ops where the operands and the declared result type have mismatched bitwidths, for example adding an i8 and an i32 where the result needs to be widened. It's gated on `isIntegerLike(result_type)`, and a comparison's result type is `bool`, which counts as integer-like, so this block runs for comparisons too. But a comparison's result being narrower than its operands is the whole point of a comparison, not an error condition.

For integer operands this hits the "narrower than operands" check directly. For float operands, `getBitwidth(ty)` gets called on the operand type before that check even happens, and `getBitwidth` was only ever written to handle integer-like types, so it just raises right away on a float operand.

The comparison handling code further down in the same function looks correct on its own and should work fine once it's actually reached. A fix would be to skip this bitwidth check when `binop.fn` is one of the comparison operators, since a comparison's result type doesn't need to match operand width the way an arithmetic op's result does.

Environment: heir-py 2026.8.1 from PyPI, Python 3.12, numba 0.63.0. Also confirmed this reproduces on numba 0.62.0 (the version declared in heir-py's own python extra), so it isn't a numba version mismatch on my end.

Let me know if a fuller repro script or anything else would help track this down.

Contributor guide

Open the contributing guide

Research direction

Start in mlir_emitter.py at emit_binop(), reading the bitwidth check before the comparison match block. Run the two minimal reproducers from the issue for float and integer operands, then verify that comparisons compile without the reported errors and that existing comparison behavior remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
compilers
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.