chore: upstream haveI tactic

chore: `haveI` and `letI` builtin parsers
This commit is contained in:
Scott Morrison 2024-02-15 12:36:01 +11:00 committed by Scott Morrison
parent 9cea1a503e
commit 98085661c7
3 changed files with 66 additions and 0 deletions

View file

@ -27,3 +27,4 @@ import Lean.Elab.Tactic.Guard
import Lean.Elab.Tactic.RCases
import Lean.Elab.Tactic.Repeat
import Lean.Elab.Tactic.Change
import Lean.Elab.Tactic.HaveI

View file

@ -0,0 +1,59 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
import Lean.Elab.ElabRules
open Lean Elab Parser Term Meta Macro
/-!
Defines variants of `have` and `let` syntax which do not produce `let_fun` or `let` bindings,
but instead inline the value instead.
This is useful to declare local instances and proofs in theorem statements
and subgoals, where the extra binding is inconvenient.
-/
#exit
namespace Std.Tactic
macro_rules
| `(haveI $hy:hygieneInfo $bs* $[: $ty]? := $val; $body) =>
`(haveI $(HygieneInfo.mkIdent hy `this (canonical := true)) $bs* $[: $ty]? := $val; $body)
| `(haveI _ $bs* := $val; $body) => `(haveI x $bs* : _ := $val; $body)
| `(haveI _ $bs* : $ty := $val; $body) => `(haveI x $bs* : $ty := $val; $body)
| `(haveI $x:ident $bs* := $val; $body) => `(haveI $x $bs* : _ := $val; $body)
| `(haveI $_:ident $_* : $_ := $_; $_) => throwUnsupported -- handled by elab
macro_rules
| `(letI $hy:hygieneInfo $bs* $[: $ty]? := $val; $body) =>
`(letI $(HygieneInfo.mkIdent hy `this (canonical := true)) $bs* $[: $ty]? := $val; $body)
| `(letI _ $bs* := $val; $body) => `(letI x $bs* : _ := $val; $body)
| `(letI _ $bs* : $ty := $val; $body) => `(letI x $bs* : $ty := $val; $body)
| `(letI $x:ident $bs* := $val; $body) => `(letI $x $bs* : _ := $val; $body)
| `(letI $_:ident $_* : $_ := $_; $_) => throwUnsupported -- handled by elab
elab_rules <= expectedType
| `(haveI $x:ident $bs* : $ty := $val; $body) => do
let (ty, val) ← elabBinders bs fun bs => do
let ty ← elabType ty
let val ← elabTermEnsuringType val ty
pure (← mkForallFVars bs ty, ← mkLambdaFVars bs val)
withLocalDeclD x.getId ty fun x => do
return (← (← elabTerm body expectedType).abstractM #[x]).instantiate #[val]
elab_rules <= expectedType
| `(letI $x:ident $bs* : $ty := $val; $body) => do
let (ty, val) ← elabBinders bs fun bs => do
let ty ← elabType ty
let val ← elabTermEnsuringType val ty
pure (← mkForallFVars bs ty, ← mkLambdaFVars bs val)
withLetDecl x.getId ty val fun x => do
return (← (← elabTerm body expectedType).abstractM #[x]).instantiate #[val]
/-- `haveI` behaves like `have`, but inlines the value instead of producing a `let_fun` term. -/
macro "haveI" d:haveDecl : tactic => `(tactic| refine_lift haveI $d:haveDecl; ?_)
/-- `letI` behaves like `let`, but inlines the value instead of producing a `let_fun` term. -/
macro "letI" d:haveDecl : tactic => `(tactic| refine_lift letI $d:haveDecl; ?_)

View file

@ -569,6 +569,12 @@ def haveDecl := leading_parser (withAnonymousAntiquot := false)
haveIdDecl <|> (ppSpace >> letPatDecl) <|> haveEqnsDecl
@[builtin_term_parser] def «have» := leading_parser:leadPrec
withPosition ("have" >> haveDecl) >> optSemicolon termParser
/-- `haveI` behaves like `have`, but inlines the value instead of producing a `let_fun` term. -/
@[builtin_term_parser] def «haveI» := leading_parser
withPosition ("haveI " >> haveDecl) >> optSemicolon termParser
/-- `letI` behaves like `let`, but inlines the value instead of producing a `let_fun` term. -/
@[builtin_term_parser] def «letI» := leading_parser
withPosition ("letI " >> haveDecl) >> optSemicolon termParser
def «scoped» := leading_parser "scoped "
def «local» := leading_parser "local "