lean4-htt/tests/lean/PPRoundtrip.lean
Leonardo de Moura 06ad52575a feat: force users to use discard when action result is not being bound and it is not PUnit
After this commit, we have to use an explicit `discard` in code such as
```
def g (x : Nat) : IO Nat := ...
def f (x : Nat) : IO Unit := do
  discard <| g x   -- type error without the `discard`
  IO.println x
```

Motivation: prevent users from making mistakes such as
```
def f (xs : Array Nat) : IO Unit := do
  xs.set! 0 1
  IO.println xs
```
when they meant to write
```
def f (xs : Array Nat) : IO Unit := do
  let xs := xs.set! 0 1
  IO.println xs
```
2020-12-08 06:14:48 -08:00

99 lines
2.7 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 Lean
open Lean
open Lean.Elab
open Lean.Elab.Term
open Lean.Elab.Command
open Lean.Format
open Lean.PrettyPrinter
open Lean.Meta
def checkM (stx : TermElabM Syntax) (optionsPerPos : OptionsPerPos := {}) : TermElabM Unit := do
let opts ← getOptions
let stx ← stx
let e ← elabTermAndSynthesize stx none <* throwErrorIfErrors
let stx' ← delab Name.anonymous [] e optionsPerPos
let f' ← PrettyPrinter.ppTerm stx'
let s := f'.pretty opts
IO.println s
let env ← getEnv
match Parser.runParserCategory env `term s "<input>" with
| Except.error e => throwErrorAt stx e
| Except.ok stx'' => do
let e' ← elabTermAndSynthesize stx'' none <* throwErrorIfErrors
unless (← isDefEq e e') do
throwErrorAt stx (m!"failed to round-trip" ++ line ++ fmt e ++ line ++ fmt e')
-- set_option trace.PrettyPrinter.parenthesize true
set_option format.width 20
-- #eval checkM `(?m) -- fails round-trip
#eval checkM `(Sort)
#eval checkM `(Type)
#eval checkM `(Type 0)
#eval checkM `(Type 1)
-- can't add a new universe variable inside a term...
#eval checkM `(Type _)
#eval checkM `(Type (_ + 2))
#eval checkM `(Nat)
#eval checkM `(List Nat)
#eval checkM `(id Nat)
#eval checkM `(id (id (id Nat)))
section
set_option pp.explicit true
#eval checkM `(List Nat)
#eval checkM `(id Nat)
end
section
set_option pp.universes true
#eval checkM `(List Nat)
#eval checkM `(id Nat)
#eval checkM `(Sum Nat Nat)
end
#eval checkM `(id (id Nat)) (Std.RBMap.empty.insert 4 $ KVMap.empty.insert `pp.explicit true)
-- specify the expected type of `a` in a way that is not erased by the delaborator
def typeAs.{u} (α : Type u) (a : α) := ()
#eval checkM `(fun (a : Nat) => a)
#eval checkM `(fun (a b : Nat) => a)
#eval checkM `(fun (a : Nat) (b : Bool) => a)
#eval checkM `(fun {a b : Nat} => a)
-- implicit lambdas work as long as the expected type is preserved
#eval checkM `(typeAs ({α : Type} → (a : α) → α) fun a => a)
section
set_option pp.explicit true
#eval checkM `(fun {α : Type} [ToString α] (a : α) => toString a)
end
#eval checkM `((α : Type) → α)
#eval checkM `((α β : Type) → α) -- group
#eval checkM `((α β : Type) → Type) -- don't group
#eval checkM `((α : Type) → (a : α) → α)
#eval checkM `((α : Type) → (a : α) → a = a)
#eval checkM `({α : Type} → α)
#eval checkM `({α : Type} → [ToString α] → α)
-- TODO: hide `ofNat`
#eval checkM `(0)
#eval checkM `(1)
#eval checkM `(42)
#eval checkM `("hi")
set_option pp.structure_instance_type true in
#eval checkM `({ type := Nat, val := 0 : PointedType })
#eval checkM `((1,2,3))
#eval checkM `((1,2).fst)
#eval checkM `(1 < 2 || true)
#eval checkM `(id (fun a => a) 0)
#eval checkM `(typeAs Nat (do
let x ← pure 1
discard <| pure 2
let y := 3
return x + y))