Skip to content

The Mojo compiler hang

github-actions[bot] edited this page Sep 28, 2026 · 4 revisions

The Mojo compiler hang

A Bool variable that is set to True inside a nested for loop and read after the loop hangs mojo build. The coverage instrumenter emits an Int counter instead. Two reproducers are in docs/mojo-compiler-issue/.

Affects

Mojo 1.0.0 (ed45d567), installed with uv pip install mojo, on macOS 25.6 with an Apple M4 Max. Not yet checked on other platforms, and not re-checked on Mojo 1.1.0.

The two reproducers keep the spelling they were written in, @no_inline included. They are evidence of what one toolchain did, so modernizing them would make them evidence of something else. coverage/runtime.mojo, which is live code, uses the current @inline(.never).

What happens

Type-checking is unaffected. mojo doc completes in under a second on the hanging file. Codegen is what hangs.

var ran = False                # hangs the compiler when nested
for i in range(n):
    ran = True
    ...
_ = observe("loop", ran)

var ran = 0                    # builds in about a second
for i in range(n):
    ran += 1
    ...
_ = observe("loop", ran > 0)

Why this project hit it

The coverage tool reports whether a for loop ever ran zero times. It emitted exactly the first pattern: a flag raised in the body and read after the loop. Every module with nested loops then took minutes to build under instrumentation. For a while the Makefile carried a growing exclusion list and blamed "statement-dense arithmetic". That diagnosis was wrong.

How it was found

Bisecting the instrumented geometries/sphere.mojo by removing one kind of emitted code at a time, with each build capped at 25 seconds:

Removed from the instrumented file Build
Nothing, the plain file 1 s
Every line probe Hangs
Every branch probe 2 s
The loop flag machinery 1 s
Only the per-iteration probe inside loops Hangs
Only the after-loop read of the flag 2 s

Candidate fixes on the full instrumented file:

Change Build
Bool flag to Int counter 2 s
Copy the flag to a fresh local before the read Hangs
Track only the outermost loop of each nest 2 s

Two earlier hypotheses were wrong, and are recorded so nobody tests them again. @no_inline on the probe functions does not help. Passing StaticString instead of String does not help. Both changes were kept as improvements.

A second pattern on Mojo 1.1.0

A struct value that is built again in two nested loops, and read after them, also hangs codegen. This was on Mojo 1.1.0 in WSL. postprocessing/ssr_node.mojo hit it: its box blur summed its taps into a FloatColor. The fix was to sum four Float32 values and build the color after the loops. A copy of the function with four floats built in 7 seconds. The same function with the FloatColor did not build in 200 seconds.

Four nested loops in one function hang codegen too, as render/target.mojo notes. Keep at most two loops in a function, and call a function for the inner two.

Reproduce it

Two files, identical except for the flag type. Do not add repro_bool.mojo to any build target. Time-box it:

perl -e 'alarm 25; exec @ARGV' .venv/bin/mojo build -o /tmp/b docs/mojo-compiler-issue/repro_bool.mojo
echo "exit $?"     # 142 when SIGALRM killed it after 25 s
.venv/bin/mojo build -o /tmp/i docs/mojo-compiler-issue/repro_int.mojo   # about 1 s

Upstream

The report belongs on modular/modular. A search of that tracker found no existing report of this pattern. The report text is the section above, with repro_bool.mojo and repro_int.mojo attached.

Home

Tutorials

How-to

Reference

Explanation

Clone this wiki locally