lean4-htt/tests/elab/doForMutUniv.lean
Sebastian Graf f9b2f6b597
fix: use getDecLevel/isLevelDefEq for for loop mut var universe constraints (#13332)
This PR fixes universe unification for `for` loops with `mut` variables
whose types span multiple implicit universes. The old approach used
`ensureHasType (mkSort mi.u.succ)` per variable, which generated
constraints like `max (?u+1) (?v+1) =?= ?u+1` that the universe solver
cannot decompose. The new approach uses `getDecLevel`/`isLevelDefEq` on
the decremented level, producing `max ?u ?v =?= ?u` which `solveSelfMax`
handles directly.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 16:34:51 +00:00

45 lines
1.6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Std.Data.HashSet
/-!
Test that `for` loops with `mut` variables whose type spans multiple implicit universes
do not get stuck on `max (?u+1) (?v+1) =?= ?u+1`. This is solved by instead solving the decremented
constraint `max ?u ?v =?= ?u` eagerly, which solves by `solveSelfMax`.
-/
-- Works with explicit universe variables
example {α : Type (max u v)} {β : Type v} [Inhabited α] [Inhabited β] (as : Array α) : Array α := Id.run do
let mut m : α × β := default
for a in as do
m := (a, m.2)
return #[m.1]
-- Works with legacy do elaborator
set_option backward.do.legacy true in
example [Inhabited α] [Inhabited β] (as : Array α) : Array α := Id.run do
let mut m : α × β := default
for a in as do
m := (a, m.2)
return #[m.1]
-- Also works with implicit universe variables (the actual regression case)
example [Inhabited α] [Inhabited β] (as : Array α) : Array α := Id.run do
let mut m : α × β := default
for a in as do
m := (a, m.2)
return #[m.1]
-- Closer to the real world reproducer from aesop's UnionFind data structure:
def cluster [BEq α] [Hashable α] [BEq β] [Hashable β] (f : α → Array β)
(as : Array α) : Array (Array α) := Id.run do
let mut clusters := Std.HashSet.ofArray as
let mut aOccs : Std.HashMap β (Array α) := {}
for a in as do
for b in f a do
match aOccs[b]? with
| some as' =>
for a' in as' do
clusters := clusters.insert a'
aOccs := aOccs.insert b (as'.push a)
| none =>
aOccs := aOccs.insert b #[a]
return #[clusters.toArray]