Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit fefdcf5

Browse files
fpvandoornFloris van Doorn
andcommitted
feat(tactic/lint): add universe linter (#8685)
* The linter checks that there are no bad `max u v` occurrences in declarations (bad means that neither `u` nor `v` occur by themselves). See documentation for more details. * `meta/expr` now imports `meta/rb_map` (but this doesn't give any new transitive imports, so it barely matters). I also removed a transitive import from `meta/rb_map`. Co-authored-by: Floris van Doorn <fpv@andrew.cmu.edu>
1 parent 3453f7a commit fefdcf5

File tree

6 files changed

+88
-7
lines changed

6 files changed

+88
-7
lines changed

src/meta/expr.lean

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Mario Carneiro, Simon Hudon, Scott Morrison, Keeley Hoek, Robert Y. Lewis, Floris van Doorn
55
-/
66
import data.string.defs
7-
import data.option.defs
7+
import meta.rb_map
88
import tactic.derive_inhabited
99
/-!
1010
# Additional operations on expr and related types
@@ -247,6 +247,17 @@ meta def fold_mvar {α} : level → (name → α → α) → α → α
247247
| (max a b) f := fold_mvar a f ∘ fold_mvar b f
248248
| (imax a b) f := fold_mvar a f ∘ fold_mvar b f
249249

250+
/--
251+
`l.params` is the set of parameters occuring in `l`.
252+
For example if `l = max 1 (max (u+1) (max v w))` then `l.params = {u, v, w}`.
253+
-/
254+
protected meta def params (u : level) : name_set :=
255+
u.fold mk_name_set $ λ v l,
256+
match v with
257+
| (param nm) := l.insert nm
258+
| _ := l
259+
end
260+
250261
end level
251262

252263
/-! ### Declarations about `binder` -/
@@ -882,6 +893,21 @@ protected meta def apply_replacement_fun (f : name → name) (test : expr → bo
882893
| _ := none
883894
end
884895

896+
open native
897+
/--
898+
`univ_params_grouped e` computes for each `level` `u` of `e` the parameters that occur in `u`,
899+
and returns the corresponding set of lists of parameters.
900+
In pseudo-mathematical form, this returns `{ { p : parameter | p ∈ u } | (u : level) ∈ e }`
901+
We use `list name` instead of `name_set`, since `name_set` does not have an order.
902+
-/
903+
meta def univ_params_grouped (e : expr) : rb_set (list name) :=
904+
e.fold mk_rb_set $ λ e n l,
905+
match e with
906+
| e@(sort u) := l.insert u.params.to_list
907+
| e@(const nm us) := l.union $ rb_set.of_list $ us.map $ λ u : level, u.params.to_list
908+
| _ := l
909+
end
910+
885911
end expr
886912

887913
/-! ### Declarations about `environment` -/

src/meta/rb_map.lean

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Robert Y. Lewis
55
-/
6-
import data.option.defs
76
import data.list.defs
87

98
/-!

src/model_theory/basic.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace first_order
4343

4444
/-- A first-order language consists of a type of functions of every natural-number arity and a
4545
type of relations of every natural-number arity. -/
46+
@[nolint check_univs] -- false positive
4647
structure language :=
4748
(functions : ℕ → Type u) (relations : ℕ → Type v)
4849

src/tactic/lint/default.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The following linters are run by default:
5050
18. `has_coe_to_fun` checks that every type that coerces to a function has a direct
5151
`has_coe_to_fun` instance.
5252
19. `check_type` checks that the statement of a declaration is well-typed.
53+
20. `check_univs` checks that that there are no bad `max u v` universe levels.
5354
5455
Another linter, `doc_blame_thm`, checks for missing doc strings on lemmas and theorems.
5556
This is not run by default.

src/tactic/lint/misc.lean

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This file defines several small linters:
1616
- `doc_blame_thm` checks that every theorem has a documentation string (not enabled by default).
1717
- `def_lemma` checks that a declaration is a lemma iff its type is a proposition.
1818
- `check_type` checks that the statement of a declaration is well-typed.
19+
- `check_univs` checks that that there are no bad `max u v` universe levels.
1920
-/
2021

2122
open tactic expr
@@ -212,8 +213,6 @@ meta def linter.doc_blame_thm : linter :=
212213
errors_found := "THEOREMS ARE MISSING DOCUMENTATION STRINGS:",
213214
is_fast := ff }
214215

215-
216-
217216
/-!
218217
## Linter for correct usage of `lemma`/`def`
219218
-/
@@ -249,6 +248,10 @@ has been used. -/
249248

250249
attribute [nolint def_lemma] classical.dec classical.dec_pred classical.dec_rel classical.dec_eq
251250

251+
/-!
252+
## Linter that checks whether declarations are well-typed
253+
-/
254+
252255
/-- Checks whether the statement of a declaration is well-typed. -/
253256
meta def check_type (d : declaration) : tactic (option string) :=
254257
(type_check d.type >> return none) <|> return "The statement doesn't type-check"
@@ -266,3 +269,53 @@ Some definitions in the statement are marked `@[irreducible]`, which means that
266269
"or `@[semireducible]`. This can especially cause problems with type class inference or " ++
267270
"`@[simps]`.",
268271
is_fast := tt }
272+
273+
/-!
274+
## Linter for universe parameters
275+
-/
276+
277+
open native
278+
279+
/--
280+
The good parameters are the parameters that occur somewhere in the `rb_set` as a singleton or
281+
(recursively) with only other good parameters.
282+
All other parameters in the `rb_set` are bad.
283+
-/
284+
meta def bad_params : rb_set (list name) → list name | l :=
285+
let good_levels : name_set :=
286+
l.fold mk_name_set $ λ us prev, if us.length = 1 then prev.insert us.head else prev in
287+
if good_levels.empty then
288+
l.fold [] list.union
289+
else bad_params $ rb_set.of_list $ l.to_list.map $ λ us, us.filter $ λ nm, !good_levels.contains nm
290+
291+
/--
292+
Checks whether all universe levels `u` in `d` are "good".
293+
This means that `u` either occurs in a `level` of `d` by itself, or (recursively)
294+
with only other good levels.
295+
When this fails, usually this means that there is a level `max u v`, where neither `u` nor `v`
296+
occur by themselves in a level. It is ok if *one* of `u` or `v` never occurs alone. For example,
297+
`(α : Type u) (β : Type (max u v))` is a occasionally useful method of saying that `β` lives in
298+
a higher universe level than `α`.
299+
-/
300+
meta def check_univs (d : declaration) : tactic (option string) := do
301+
let l := d.type.univ_params_grouped.union d.value.univ_params_grouped,
302+
let bad := bad_params l,
303+
if bad.empty then return none else return $ some $ "universes " ++ to_string bad ++
304+
" only occur together."
305+
306+
/-- A linter for checking that there are no bad `max u v` universe levels. -/
307+
@[linter]
308+
meta def linter.check_univs : linter :=
309+
{ test := check_univs,
310+
auto_decls := tt,
311+
no_errors_found :=
312+
"All declaratations have good universe levels.",
313+
errors_found := "THE STATEMENTS OF THE FOLLOWING DECLARATIONS HAVE BAD UNIVERSE LEVELS. " ++
314+
"This usually means that there is a `max u v` in the declaration where neither `u` nor `v` " ++
315+
"occur by themselves. Solution: Find the type (or type bundled with data) that has this " ++
316+
"universe argument and provide the universe level explicitly. If this happens in an implicit " ++
317+
"argument of the declaration, a better solution is to move this argument to a `variables` " ++
318+
"command (where the universe level can be kept implicit).
319+
Note: if the linter flags an automatically generated declaration `xyz._proof_i`, it means that
320+
the universe problem is with `xyz` itself (even if the linter doesn't flag `xyz`)",
321+
is_fast := tt }

src/topology/category/Profinite/projective.lean

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ Let `X` be a profinite set.
2525

2626
noncomputable theory
2727

28+
universe variables u v w
2829
open category_theory function
2930

3031
namespace Profinite
3132

32-
instance projective_ultrafilter (X : Type*) :
33+
instance projective_ultrafilter (X : Type u) :
3334
projective (of $ ultrafilter X) :=
3435
{ factors := λ Y Z f g hg,
3536
begin
@@ -46,14 +47,14 @@ instance projective_ultrafilter (X : Type*) :
4647
end }
4748

4849
/-- For any profinite `X`, the natural map `ultrafilter X → X` is a projective presentation. -/
49-
def projective_presentation (X : Profinite) : projective_presentation X :=
50+
def projective_presentation (X : Profinite.{u}) : projective_presentation X :=
5051
{ P := of $ ultrafilter X,
5152
f := ⟨_, continuous_ultrafilter_extend id⟩,
5253
projective := Profinite.projective_ultrafilter X,
5354
epi := concrete_category.epi_of_surjective _ $
5455
λ x, ⟨(pure x : ultrafilter X), congr_fun (ultrafilter_extend_extends (𝟙 X)) x⟩ }
5556

56-
instance : enough_projectives Profinite :=
57+
instance : enough_projectives Profinite.{u} :=
5758
{ presentation := λ X, ⟨projective_presentation X⟩ }
5859

5960
end Profinite

0 commit comments

Comments
 (0)