`Nat.shiftLeft` by 2^32 or more crashes Lean in `rfl`, `decide`, `simp`, `cbv`, `grind` and the compiler

Open
#15,193 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Domain
compilers

Research direction

Run the minimal reproduction examples first to confirm the panic, then read Lean.Meta.reduceNat? in src/Lean/Meta/WHNF.lean and the listed shift call sites in the simproc, evaluator, Grind, and LCNF constant-folder files. Done means oversized nonzero shifts no longer terminate Lean, while ordinary shifts such as 1 << 1000 still evaluate.

Written by the indexing model from the issue text.

Description

Prerequisites
Description

Several elaborator and compiler procedures evaluate Nat.shiftLeft on literals without checking the shift amount. The runtime's lean_nat_shiftl calls lean_internal_panic("Nat.shiftl exponent is too big") when the shifted value is nonzero and the shift amount does not fit in 32 bits, so a one-line file terminates the whole Lean process (and with it the language server or lake build).

The kernel no longer has this problem: it now refuses such shifts with a kernel exception. Nat.pow is also already protected on the elaborator side by checkExponent. Nat.shiftLeft is not guarded at these call sites:

  • Lean.Meta.reduceNat? (src/Lean/Meta/WHNF.lean): reached by rfl, decide, whnf
  • Nat.reduceShiftLeft simproc (src/Lean/Meta/Tactic/Simp/BuiltinSimprocs/Nat.lean): simp
  • evalShift in src/Lean/Meta/Sym/Simp/EvalGround.lean and src/Lean/Meta/Sym/DSimp/EvalGround.lean (Nat, Int and BitVec shifts): cbv, bv_decide normalization
  • propagateNatShiftLeft (src/Lean/Meta/Tactic/Grind/Arith/Propagate.lean): grind
  • the LCNF constant folder for Nat.shiftLeft (src/Lean/Compiler/LCNF/Simp/ConstantFold.lean): compiling a definition
Steps to Reproduce

Each of the following files, on its own, crashes Lean:

example : (1 <<< 4294967296 : Nat) = 0 := by rfl
example : (1 <<< 4294967296 : Nat) = 0 := by decide
example : (1 <<< 4294967296 : Nat) = 0 := by simp
example : (1 <<< 4294967296 : Nat) = 0 := by cbv
example : ((1 : Int) <<< (4294967296 : Nat)) = 0 := by cbv
example : ((1#8) <<< (4294967296 : Nat)) = 0#8 := by cbv
example (x : Nat) (h : x = 4294967296) : (1 <<< x : Nat) = 0 := by grind
def f : Nat := 1 <<< 4294967296

Expected behavior: The shift is left unevaluated (or an error or warning is reported, as for 2 ^ 4294967296), and Lean keeps running.

Actual behavior: The process exits with INTERNAL PANIC: Nat.shiftl exponent is too big.

Versions

4.36.0-nightly-2026-09-16 (also reproduced on v4.35.0-rc2 and v4.32.0-rc1)
Linux 6.17.0 aarch64

Additional Information

2 ^ 4294967296 does not crash: checkExponent logs "exponent 4294967296 exceeds the threshold 256" instead. For shifts, the exponentiation threshold would be too restrictive, since evaluating 1 <<< 1000 is cheap and works today. The runtime's actual limit is the one that matters: skip evaluation when the value is nonzero and the shift amount is at least 2^32.

I plan to open a PR implementing that guard at the call sites above. An AI assistant (Claude) found and reduced this bug and drafted this report; each reproduction above was run on the listed versions.

Impact

Add 👍 to issues you consider important. If others are impacted by this issue, please ask them to add 👍 to it.

Dominant language
Lean
Stars
9.2k
Forks
990
Avg merge
1d 17h
Merged PRs (30d)
175

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from leanprover/lean4

All issues in leanprover/lean4

Similar issues

More Compilers issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.