Skip to content

Commit 81b8520

Browse files
leodemourakim-em
authored andcommitted
fix: exponential compilation times due to inlined instances (#8254)
This PR fixes unintended inlining of `ToJson`, `FromJson`, and `Repr` instances, which was causing exponential compilation times in `deriving` clauses for large structures.
1 parent e65f5f1 commit 81b8520

File tree

8 files changed

+275
-78
lines changed

8 files changed

+275
-78
lines changed

src/Init/Data/Array/Basic.lean

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,13 +2158,15 @@ Examples:
21582158

21592159
/-! ### Repr and ToString -/
21602160

2161+
protected def Array.repr {α : Type u} [Repr α] (xs : Array α) : Std.Format :=
2162+
let _ : Std.ToFormat α := ⟨repr⟩
2163+
if xs.size == 0 then
2164+
"#[]"
2165+
else
2166+
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
2167+
21612168
instance {α : Type u} [Repr α] : Repr (Array α) where
2162-
reprPrec xs _ :=
2163-
let _ : Std.ToFormat α := ⟨repr⟩
2164-
if xs.size == 0 then
2165-
"#[]"
2166-
else
2167-
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
2169+
reprPrec xs _ := Array.repr xs
21682170

21692171
instance [ToString α] : ToString (Array α) where
21702172
toString xs := "#" ++ toString xs.toList

src/Init/Data/Array/Subarray.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,12 @@ instance : Append (Subarray α) where
464464
let a := x.toArray ++ y.toArray
465465
a.toSubarray 0 a.size
466466

467+
/-- `Subarray` representation. -/
468+
protected def Subarray.repr [Repr α] (s : Subarray α) : Std.Format :=
469+
repr s.toArray ++ ".toSubarray"
470+
467471
instance [Repr α] : Repr (Subarray α) where
468-
reprPrec s _ := repr s.toArray ++ ".toSubarray"
472+
reprPrec s _ := Subarray.repr s
469473

470474
instance [ToString α] : ToString (Subarray α) where
471475
toString s := toString s.toArray

src/Init/Data/BitVec/Basic.lean

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@ protected def toHex {n : Nat} (x : BitVec n) : String :=
199199
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
200200
t ++ s
201201

202-
instance : Repr (BitVec n) where reprPrec a _ := "0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
202+
/-- `BitVec` representation. -/
203+
protected def BitVec.repr (a : BitVec n) : Std.Format :=
204+
"0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
205+
206+
instance : Repr (BitVec n) where
207+
reprPrec a _ := BitVec.repr a
208+
203209
instance : ToString (BitVec n) where toString a := toString (repr a)
204210

205211
end repr_toString

src/Init/Data/Float.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,11 @@ implementation.
291291
instance : Inhabited Float where
292292
default := UInt64.toFloat 0
293293

294+
protected def Float.repr (n : Float) (prec : Nat) : Std.Format :=
295+
if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
296+
294297
instance : Repr Float where
295-
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
298+
reprPrec := Float.repr
296299

297300
instance : ReprAtom Float := ⟨⟩
298301

src/Init/Data/Float32.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,11 @@ implementation.
292292
instance : Inhabited Float32 where
293293
default := UInt64.toFloat32 0
294294

295+
protected def Float32.repr (n : Float32) (prec : Nat) : Std.Format :=
296+
if n < UInt64.toFloat32 0 then Repr.addAppParen (toString n) prec else toString n
297+
295298
instance : Repr Float32 where
296-
reprPrec n prec := if n < UInt64.toFloat32 0 then Repr.addAppParen (toString n) prec else toString n
299+
reprPrec := Float32.repr
297300

298301
instance : ReprAtom Float32 := ⟨⟩
299302

src/Init/Data/Repr.lean

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,25 @@ This instance allows us to use `Empty` as a type parameter without causing insta
5555
instance : Repr Empty where
5656
reprPrec := nofun
5757

58+
protected def Bool.repr : Bool → Nat → Format
59+
| true, _ => "true"
60+
| false, _ => "false"
61+
5862
instance : Repr Bool where
59-
reprPrec
60-
| true, _ => "true"
61-
| false, _ => "false"
63+
reprPrec := Bool.repr
6264

6365
def Repr.addAppParen (f : Format) (prec : Nat) : Format :=
6466
if prec >= max_prec then
6567
Format.paren f
6668
else
6769
f
6870

71+
protected def Decidable.repr : Decidable p → Nat → Format
72+
| .isTrue _, prec => Repr.addAppParen "isTrue _" prec
73+
| .isFalse _, prec => Repr.addAppParen "isFalse _" prec
74+
6975
instance : Repr (Decidable p) where
70-
reprPrec
71-
| Decidable.isTrue _, prec => Repr.addAppParen "isTrue _" prec
72-
| Decidable.isFalse _, prec => Repr.addAppParen "isFalse _" prec
76+
reprPrec := Decidable.repr
7377

7478
instance : Repr PUnit.{u+1} where
7579
reprPrec _ _ := "PUnit.unit"
@@ -109,17 +113,23 @@ export ReprTuple (reprTuple)
109113
instance [Repr α] : ReprTuple α where
110114
reprTuple a xs := repr a :: xs
111115

116+
protected def Prod.reprTuple [Repr α] [ReprTuple β] : α × β → List Format → List Format
117+
| (a, b), xs => reprTuple b (repr a :: xs)
118+
112119
instance [Repr α] [ReprTuple β] : ReprTuple (α × β) where
113-
reprTuple | (a, b), xs => reprTuple b (repr a :: xs)
120+
reprTuple := Prod.reprTuple
114121

115122
protected def Prod.repr [Repr α] [ReprTuple β] : α × β → Nat → Format
116123
| (a, b), _ => Format.bracket "(" (Format.joinSep (reprTuple b [repr a]).reverse ("," ++ Format.line)) ")"
117124

118125
instance [Repr α] [ReprTuple β] : Repr (α × β) where
119126
reprPrec := Prod.repr
120127

128+
protected def Sigma.repr {β : α → Type v} [Repr α] [(x : α) → Repr (β x)] : Sigma β → Nat → Format
129+
| ⟨a, b⟩, _ => Format.bracket "⟨" (repr a ++ ", " ++ repr b) "⟩"
130+
121131
instance {β : α → Type v} [Repr α] [(x : α) → Repr (β x)] : Repr (Sigma β) where
122-
reprPrec | ⟨a, b⟩, _ => Format.bracket "⟨" (repr a ++ ", " ++ repr b) "⟩"
132+
reprPrec := Sigma.repr
123133

124134
instance {p : α → Prop} [Repr α] : Repr (Subtype p) where
125135
reprPrec s prec := reprPrec s.val prec

src/Lean/Data/Json/FromToJson.lean

Lines changed: 122 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -47,54 +47,97 @@ instance : ToJson String := ⟨fun s => s⟩
4747
instance : FromJson System.FilePath := ⟨fun j => System.FilePath.mk <$> Json.getStr? j⟩
4848
instance : ToJson System.FilePath := ⟨fun p => p.toString⟩
4949

50+
protected def _root_.Array.fromJson? [FromJson α] : Json → Except String (Array α)
51+
| Json.arr a => a.mapM fromJson?
52+
| j => throw s!"expected JSON array, got '{j}'"
53+
5054
instance [FromJson α] : FromJson (Array α) where
51-
fromJson?
52-
| Json.arr a => a.mapM fromJson?
53-
| j => throw s!"expected JSON array, got '{j}'"
55+
fromJson? := Array.fromJson?
56+
57+
protected def _root_.Array.toJson [ToJson α] (a : Array α) : Json :=
58+
Json.arr (a.map toJson)
5459

55-
instance [ToJson α] : ToJson (Array α) :=
56-
fun a => Json.arr (a.map toJson)⟩
60+
instance [ToJson α] : ToJson (Array α) where
61+
toJson := Array.toJson
62+
63+
protected def _root_.List.fromJson? [FromJson α] (j : Json) : Except String (List α) :=
64+
(fromJson? j (α := Array α)).map Array.toList
5765

5866
instance [FromJson α] : FromJson (List α) where
59-
fromJson? j := (fromJson? j (α := Array α)).map Array.toList
67+
fromJson? := List.fromJson?
68+
69+
protected def _root_.List.toJson [ToJson α] (a : List α) : Json :=
70+
toJson a.toArray
6071

6172
instance [ToJson α] : ToJson (List α) where
62-
toJson xs := toJson xs.toArray
73+
toJson := List.toJson
74+
75+
protected def _root_.Option.fromJson? [FromJson α] : Json → Except String (Option α)
76+
| Json.null => Except.ok none
77+
| j => some <$> fromJson? j
6378

6479
instance [FromJson α] : FromJson (Option α) where
65-
fromJson?
66-
| Json.null => Except.ok none
67-
| j => some <$> fromJson? j
80+
fromJson? := Option.fromJson?
81+
82+
protected def _root_.Option.toJson [ToJson α] : Option α → Json
83+
| none => Json.null
84+
| some a => toJson a
85+
86+
instance [ToJson α] : ToJson (Option α) where
87+
toJson := Option.toJson
6888

69-
instance [ToJson α] : ToJson (Option α) :=
70-
fun
71-
| none => Json.null
72-
| some a => toJson a⟩
89+
protected def _root_.Prod.fromJson? {α : Type u} {β : Type v} [FromJson α] [FromJson β] : Json → Except String (α × β)
90+
| Json.arr #[ja, jb] => do
91+
let ⟨a⟩ : ULift.{v} α := ← (fromJson? ja).map ULift.up
92+
let ⟨b⟩ : ULift.{u} β := ← (fromJson? jb).map ULift.up
93+
return (a, b)
94+
| j => throw s!"expected pair, got '{j}'"
7395

7496
instance {α : Type u} {β : Type v} [FromJson α] [FromJson β] : FromJson (α × β) where
75-
fromJson?
76-
| Json.arr #[ja, jb] => do
77-
let ⟨a⟩ : ULift.{v} α := ← (fromJson? ja).map ULift.up
78-
let ⟨b⟩ : ULift.{u} β := ← (fromJson? jb).map ULift.up
79-
return (a, b)
80-
| j => throw s!"expected pair, got '{j}'"
97+
fromJson? := Prod.fromJson?
98+
99+
protected def _root_.Prod.toJson [ToJson α] [ToJson β] : α × β → Json
100+
| (a, b) => Json.arr #[toJson a, toJson b]
81101

82102
instance [ToJson α] [ToJson β] : ToJson (α × β) where
83-
toJson := fun (a, b) => Json.arr #[toJson a, toJson b]
103+
toJson := Prod.toJson
104+
105+
protected def Name.fromJson? (j : Json) : Except String Name := do
106+
let s ← j.getStr?
107+
if s == "[anonymous]" then
108+
return Name.anonymous
109+
else
110+
let n := s.toName
111+
if n.isAnonymous then throw s!"expected a `Name`, got '{j}'"
112+
return n
84113

85114
instance : FromJson Name where
86-
fromJson? j := do
87-
let s ← j.getStr?
88-
if s == "[anonymous]" then
89-
return Name.anonymous
90-
else
91-
let n := s.toName
92-
if n.isAnonymous then throw s!"expected a `Name`, got '{j}'"
93-
return n
115+
fromJson? := Name.fromJson?
94116

95117
instance : ToJson Name where
96118
toJson n := toString n
97119

120+
protected def NameMap.fromJson? [FromJson α] : Json → Except String (NameMap α)
121+
| .obj obj => obj.foldM (init := {}) fun m k v => do
122+
if k == "[anonymous]" then
123+
return m.insert .anonymous (← fromJson? v)
124+
else
125+
let n := k.toName
126+
if n.isAnonymous then
127+
throw s!"expected a `Name`, got '{k}'"
128+
else
129+
return m.insert n (← fromJson? v)
130+
| j => throw s!"expected a `NameMap`, got '{j}'"
131+
132+
instance [FromJson α] : FromJson (NameMap α) where
133+
fromJson? := NameMap.fromJson?
134+
135+
protected def NameMap.toJson [ToJson α] (m : NameMap α) : Json :=
136+
Json.obj <| m.fold (fun n k v => n.insert compare k.toString (toJson v)) .leaf
137+
138+
instance [ToJson α] : ToJson (NameMap α) where
139+
toJson := NameMap.toJson
140+
98141
/-- Note that `USize`s and `UInt64`s are stored as strings because JavaScript
99142
cannot represent 64-bit numbers. -/
100143
def bignumFromJson? (j : Json) : Except String Nat := do
@@ -106,58 +149,77 @@ def bignumFromJson? (j : Json) : Except String Nat := do
106149
def bignumToJson (n : Nat) : Json :=
107150
toString n
108151

152+
protected def _root_.USize.fromJson? (j : Json) : Except String USize := do
153+
let n ← bignumFromJson? j
154+
if n ≥ USize.size then
155+
throw "value '{j}' is too large for `USize`"
156+
return USize.ofNat n
157+
109158
instance : FromJson USize where
110-
fromJson? j := do
111-
let n ← bignumFromJson? j
112-
if n ≥ USize.size then
113-
throw "value '{j}' is too large for `USize`"
114-
return USize.ofNat n
159+
fromJson? := USize.fromJson?
115160

116161
instance : ToJson USize where
117162
toJson v := bignumToJson (USize.toNat v)
118163

164+
protected def _root_.UInt64.fromJson? (j : Json) : Except String UInt64 := do
165+
let n ← bignumFromJson? j
166+
if n ≥ UInt64.size then
167+
throw "value '{j}' is too large for `UInt64`"
168+
return UInt64.ofNat n
169+
119170
instance : FromJson UInt64 where
120-
fromJson? j := do
121-
let n ← bignumFromJson? j
122-
if n ≥ UInt64.size then
123-
throw "value '{j}' is too large for `UInt64`"
124-
return UInt64.ofNat n
171+
fromJson? := UInt64.fromJson?
125172

126173
instance : ToJson UInt64 where
127174
toJson v := bignumToJson (UInt64.toNat v)
128175

176+
protected def _root_.Float.toJson (x : Float) : Json :=
177+
match JsonNumber.fromFloat? x with
178+
| Sum.inl e => Json.str e
179+
| Sum.inr n => Json.num n
180+
129181
instance : ToJson Float where
130-
toJson x :=
131-
match JsonNumber.fromFloat? x with
132-
| Sum.inl e => Json.str e
133-
| Sum.inr n => Json.num n
182+
toJson := Float.toJson
183+
184+
protected def _root_.Float.fromJson? : Json → Except String Float
185+
| (Json.str "Infinity") => Except.ok (1.0 / 0.0)
186+
| (Json.str "-Infinity") => Except.ok (-1.0 / 0.0)
187+
| (Json.str "NaN") => Except.ok (0.0 / 0.0)
188+
| (Json.num jn) => Except.ok jn.toFloat
189+
| _ => Except.error "Expected a number or a string 'Infinity', '-Infinity', 'NaN'."
134190

135191
instance : FromJson Float where
136-
fromJson? := fun
137-
| (Json.str "Infinity") => Except.ok (1.0 / 0.0)
138-
| (Json.str "-Infinity") => Except.ok (-1.0 / 0.0)
139-
| (Json.str "NaN") => Except.ok (0.0 / 0.0)
140-
| (Json.num jn) => Except.ok jn.toFloat
141-
| _ => Except.error "Expected a number or a string 'Infinity', '-Infinity', 'NaN'."
192+
fromJson? := Float.fromJson?
193+
194+
protected def RBMap.toJson [ToJson α] (m : RBMap String α cmp) : Json :=
195+
Json.obj <| RBNode.map (fun _ => toJson) <| m.val
142196

143197
instance [ToJson α] : ToJson (RBMap String α cmp) where
144-
toJson m := Json.obj <| RBNode.map (fun _ => toJson) <| m.val
198+
toJson := RBMap.toJson
199+
200+
protected def RBMap.fromJson? [FromJson α] (j : Json) : Except String (RBMap String α cmp) := do
201+
let o ← j.getObj?
202+
o.foldM (fun x k v => x.insert k <$> fromJson? v) ∅
145203

146204
instance {cmp} [FromJson α] : FromJson (RBMap String α cmp) where
147-
fromJson? j := do
148-
let o ← j.getObj?
149-
o.foldM (fun x k v => x.insert k <$> fromJson? v) ∅
205+
fromJson? := RBMap.fromJson?
150206

151207
namespace Json
152208

153-
instance : FromJson Structured := ⟨fun
154-
| arr a => return Structured.arr a
155-
| obj o => return Structured.obj o
156-
| j => throw s!"expected structured object, got '{j}'"
209+
protected def Structured.fromJson? : Json → Except String Structured
210+
| .arr a => return Structured.arr a
211+
| .obj o => return Structured.obj o
212+
| j => throw s!"expected structured object, got '{j}'"
213+
214+
instance : FromJson Structured where
215+
fromJson? := Structured.fromJson?
216+
217+
protected def Structured.toJson : Structured → Json
218+
| .arr a => .arr a
219+
| .obj o => .obj o
157220

158-
instance : ToJson Structured := ⟨fun
159-
| Structured.arr a => arr a
160-
| Structured.obj o => obj o⟩
221+
instance : ToJson Structured where
222+
toJson := Structured.toJson
161223

162224
def toStructured? [ToJson α] (v : α) : Except String Structured :=
163225
fromJson? (toJson v)

0 commit comments

Comments
 (0)