/-! # Tests of universe constraint testing in the `inductive` command -/ set_option pp.mvars false set_option pp.universes true /-! Infers the resulting universe `Sort ?u`. Here, `x : Nat : Type`, so `1 ≤ ?u` gives `Type` as least universe. -/ inductive ExResultUni1 where | mk (x : Nat) /-- info: inductive ExResultUni1 : Type number of parameters: 0 constructors: ExResultUni1.mk : Nat → ExResultUni1 -/ #guard_msgs in #print ExResultUni1 /-! Infers the resulting universe `Sort ?u`. Here, `x : α : Sort v`, so `v ≤ ?u` and the additional `1 ≤ ?u` gives `Sort (max 1 v)`. -/ inductive ExResultUni2 (α : Sort v) where | mk (x : α) /-- info: inductive ExResultUni2.{v} : Sort v → Sort (max 1 v) number of parameters: 1 constructors: ExResultUni2.mk : {α : Sort v} → α → ExResultUni2.{v} α -/ #guard_msgs in #print ExResultUni2 /-! Infers the resulting universe `Sort ?u`. Here, `x : α : Type v`, so `v + 1 ≤ ?u`. The `1 ≤ ?u` isn't necessary. Get `Type v`. -/ inductive ExResultUni3 (α : Type v) where | mk (x : α) /-- info: inductive ExResultUni3.{v} : Type v → Type v number of parameters: 1 constructors: ExResultUni3.mk : {α : Type v} → α → ExResultUni3.{v} α -/ #guard_msgs in #print ExResultUni3 /-! Infers the resulting universe `Sort ?u`. Even though this *could* be a syntactic subsingleton with `?u = 0`, it is recursive so, heuristically, we don't use that solution. From `x : α : Sort v` and `y : ExResultUni4 α : Sort ?u` we get `v ≤ ?u` and `?u ≤ ?u`. With the additional `1 ≤ ?u`, we get `Sort (max 1 v) -/ inductive ExResultUni4 (α : Sort v) where | mk (x : α) (y : ExResultUni4 α) /-- info: inductive ExResultUni4.{v} : Sort v → Sort (max 1 v) number of parameters: 1 constructors: ExResultUni4.mk : {α : Sort v} → α → ExResultUni4.{v} α → ExResultUni4.{v} α -/ #guard_msgs in #print ExResultUni4 /-! An "unnecessarily high" universe warning. (Though erroneous!) The use of `List` causes the resulting universe to be `Type ?u`. From `x : α : Sort v` we get `v ≤ ?u + 1`, and from `y : List (ExResultUni5 α) : Type ?u` we get `?u ≤ ?u`. There's a potential universe bump here, since ideally we would create `Type (v - 1)` as the level, if there were such a thing as level subtraction, but instead the only solution is `Type v`. Unfortunately, this check does not realize that `List (ExResultUni5 α)` forces `Type v`. It's a warning, and it can be silenced with an explicit universe level. -/ /-- warning: Inferred universe level for type may be unnecessarily high. The inferred resulting universe is Type v but it possibly could be Sort (max 1 v) Explicitly providing a resulting universe with no metavariables will silence this warning. Note: The elaborated resulting universe after constructor elaboration is Type _ The inference algorithm attempts to compute the smallest level for `_` such that all universe constraints for all constructor fields are satisfied, with some approximations. The following derived constraint(s) are the cause of this possible unnecessarily high universe: v ≤ _ + 1 For example, if the resulting universe is of the form `Sort (?r + 1)` and a constructor field is in universe `Sort u`, the constraint `u ≤ ?r + 1` leads to the unnecessarily high resulting universe `Sort (u + 1)`. Using `Sort (max 1 u)` avoids this universe bump, if using it is possible. -/ #guard_msgs in inductive ExResultUni5 (α : Sort v) where | mk (x : α) (y : List (ExResultUni5 α)) -- Silenced: #guard_msgs in inductive ExResultUni5' (α : Sort v) : Type v where | mk (x : α) (y : List (ExResultUni5' α)) /-- Inference can handle `Sort (max _ 1)` as the resulting universe. -/ inductive ExResultUni6 (α : Sort v) (β : Sort w) : Sort (max _ 1) where | mk (x : α) (y : β) /-- info: ExResultUni6.{v, w} (α : Sort v) (β : Sort w) : Sort (max (max v w) 1) -/ #guard_msgs in #check ExResultUni6 /-- Inference can also handle parameters already existing in the resulting universe. -/ inductive ExResultUni7 (α : Sort v) : Sort (max v _) where | mk (x : α)-- (n : Nat) /-- info: ExResultUni7.{v} (α : Sort v) : Sort (max v 1) -/ #guard_msgs in #check ExResultUni7 /-- The universe of `α` is inferrable here, since it's known to be a `Type _` (from the use of `List`), and inference will use level constraints to assign level metavariables if they imply a unique solution, ignoring constant solutions if there's a non-constant solution. -/ inductive ExParamUni1 (x : α) : Type v where | mk (ys : List α) (h : [x] = ys) /-- info: inductive ExParamUni1.{v} : {α : Type v} → α → Type v number of parameters: 2 constructors: ExParamUni1.mk : {α : Type v} → {x : α} → (ys : List.{v} α) → Eq.{v + 1} (List.cons.{v} x List.nil.{v}) ys → ExParamUni1.{v} x -/ #guard_msgs in #print ExParamUni1 /-! Uses `Type v` for `α` and `β`, since these are the unique non-constant solutions. -/ inductive ExParamUni2 {α β : Type _} (x : α) (y : β) : Type v where | mk (fs : List (α → β)) /-- info: inductive ExParamUni2.{v} : {α β : Type v} → α → β → Type v number of parameters: 4 constructors: ExParamUni2.mk : {α β : Type v} → {x : α} → {y : β} → List.{v} (α → β) → ExParamUni2.{v} x y -/ #guard_msgs in #print ExParamUni2 /-! The resulting type after resulting type inference is `Type ?u`, with a metavariable. Constructor field universe propagation treats this metavariable as a parameter, inferring `PUnit : Type ?u`. -/ inductive ExParamUni3 {α : Type _} where | mk (x : α) (a : PUnit) -- use Unit if it should be in `Type 0` /-- info: inductive ExParamUni3.{u_1} : {α : Type u_1} → Type u_1 number of parameters: 1 constructors: ExParamUni3.mk : {α : Type u_1} → α → PUnit.{u_1 + 1} → ExParamUni3.{u_1} -/ #guard_msgs in #print ExParamUni3 /-! Given the resultant type, infer that the `x` parameter is `Type`. -/ inductive T0 : Type where | mk (x : PUnit.{_+1}) /-- info: T0.mk (x : PUnit.{1}) : T0 -/ #guard_msgs in #check T0.mk /-! Given the resultant type, infer that the `x` parameter is `Type` as well. -/ inductive T1 : Type where | mk (x : PUnit.{_+1} × PUnit.{_+1}) /-- info: T1.mk (x : Prod.{0, 0} PUnit.{1} PUnit.{1}) : T1 -/ #guard_msgs in #check T1.mk /-! Given the resultant type is `Prop`, do not do this inference. -/ /-- error: Failed to infer universe levels in type of binder `x` Prod.{_, _} PUnit.{_ + 1} PUnit.{_ + 1} -/ #guard_msgs in inductive T3 : Prop where | mk (x : PUnit.{_+1} × PUnit.{_+1}) /-! Given the resultant type, fail to infer a level for `PUnit` if there's not a unique solution. -/ /-- error: Failed to infer universe levels in type of binder `x` PUnit.{_} -/ #guard_msgs in inductive E0 : Type 1 where | mk (x : PUnit) /-! Inference goes for `Type _` fields, so this succeeds. -/ inductive E0' : Type where | mk (x : PUnit) /-! Given the resultant type, fail to infer a level for `PUnit` if there's not a unique solution. -/ /-- error: Failed to infer universe levels in type of binder `x` Prod.{_, _} PUnit.{_ + 1} PUnit.{_ + 1} -/ #guard_msgs in inductive E1 : Type 1 where | mk (x : PUnit × PUnit) /-! Even though `α : Prop` is the unique solution, reject it for being unintuitive. -/ /-- error: Failed to infer universe levels in type of binder `α` Sort _ -/ #guard_msgs in inductive E2 : Type where | mk {α} (x : α) /-! `Sort` polymorphism is not allowed. -/ /-- error: Invalid universe polymorphic resulting type: The resulting universe is not `Prop`, but it may be `Prop` for some parameter values: Sort u Hint: A possible solution is to use levels of the form `max 1 _` or `_ + 1` to ensure the universe is of the form `Type _` -/ #guard_msgs in inductive P (α : Sort u) : Sort u where | mk (x : α) /-! Errors for `structure` are specialized to talking about fields. -/ /-- error: Invalid universe level for field `α`: Field has type Type at universe level 2 which is not less than or equal to the structure's resulting universe level 1 -/ #guard_msgs in structure A : Type where α : Type /-! Errors for `structure` talk about parent projection fields too. (Note: it could easily point to `A'` and say the error is field `α`.) -/ structure A' where α : Type /-- error: Invalid universe level for field `α`: Field has type Type at universe level 2 which is not less than or equal to the structure's resulting universe level 1 -/ #guard_msgs in structure B : Type extends A' /-! Basic test of resulting type for recursive type. Least universe is `Type`. -/ inductive Bar3 | foobar {Foo : Prop} : Foo → Bar3 | somelist : List Bar3 → Bar3 /-- info: Bar3 : Type -/ #guard_msgs in #check Bar3 /-! Test of resulting type for recursive type. Least universe here is `Type 1`. -/ inductive Bar4 | foobar {Foo : Type} : Foo → Bar4 | somelist : List Bar4 → Bar4 /-- info: Bar4 : Type 1 -/ #guard_msgs in #check Bar4 /-! Like `Bar4`, but this time infers the type of the `Foo` field as `Type`. -/ inductive Bar4' : Type 1 | foobar {Foo} : Foo → Bar4' | somelist : List Bar4' → Bar4' /-- info: Bar4' : Type 1 -/ #guard_msgs in #check Bar4' /-- info: Bar4'.foobar {Foo : Type} : Foo → Bar4' -/ #guard_msgs in #check Bar4'.foobar /-! Type error at `List Bar5`, stops elaboration. -/ /-- error: Application type mismatch: The argument Bar5 has type Prop of sort `Type` but is expected to have type Type _ of sort `Type (_ + 1)` in the application List.{_} Bar5 -/ #guard_msgs in inductive Bar5 : Prop where | foobar {Foo : Prop} : Foo → Bar5 | somelist : List Bar5 → Bar5 /-! Type error on `foobar` constructor. Resulting universe is too small. -/ /-- error: Invalid universe level in constructor `Bar6.foobar`: Parameter `Foo` has type Type at universe level 2 which is not less than or equal to the inductive type's resulting universe level 1 -/ #guard_msgs in inductive Bar6 : Type where | foobar {Foo : Type} : Foo → Bar6 | somelist : List Bar6 → Bar6 /-! With `Foo : Prop`, the `Bar6` example would be ok. -/ inductive Bar7 : Type where | foobar {Foo : Prop} : Foo → Bar7 | somelist : List Bar7 → Bar7 /-! While `Foo : Sort ?u` gives the constraint `?u + 1 ≤ 1`, which has `?u = 0` as the unique solution, the `x : Foo : Sort ?u` gives the additional constraint `1 ≤ ?u`, so it doesn't assign `?u = 0`. -/ /-- error: Constructor field `Foo` of `Bar8.foobar` contains universe level metavariables at the expression Sort _ in its type Sort _ -/ #guard_msgs in inductive Bar8 : Type where | foobar : (x : Foo) → Bar8 | somelist : List Bar8 → Bar8 /-! This gives `Foo : Type u`. This comes from `Foo : Sort ?u : Sort (?u + 1)`, so `?u + 1 ≤ u + 2` has two non-constant solutions: `?u = u` and `?u = u + 1`. Additionally, `x : Foo : Sort ?u` gives `1 ≤ ?u` since it's a field. This excludes `?u = u` since `1 ≤ u` isn't always true. Therefore it chooses `?u = u + 1`. -/ inductive Bar9 : Type (u + 1) where | foobar : (x : Foo) → Bar9 | somelist : List Bar9 → Bar9 /-- info: Bar9.foobar.{u} {Foo : Type u} (x : Foo) : Bar9.{u} -/ #guard_msgs in #check Bar9.foobar /-! Can infer imax field, constant resulting type. -/ inductive Bar10 : Type 1 where | mk (f : α → β) /-- info: Bar10.mk {α β : Type} (f : α → β) : Bar10 -/ #guard_msgs in #check Bar10.mk /-! Can infer imax field, non-constant resulting type. -/ inductive Bar11 : Type (u + 1) where | mk (f : α → β) /-- info: Bar11.mk.{u} {α β : Type u} (f : α → β) : Bar11.{u} -/ #guard_msgs in #check Bar11.mk