chore: add elaborator for let_fun and let_delayed

This commit is contained in:
Leonardo de Moura 2021-03-11 10:40:25 -08:00
parent 90abace21a
commit e7140959c4
6 changed files with 18 additions and 14 deletions

View file

@ -269,7 +269,6 @@ syntax (name := «have») "have " haveDecl : tactic
syntax (name := «suffices») "suffices " sufficesDecl : tactic
syntax (name := «show») "show " term : tactic
syntax (name := «let») "let " letDecl : tactic
syntax (name := «let!») "let! " letDecl : tactic
syntax (name := letrec) withPosition(atomic(group("let " &"rec ")) letRecDecls) : tactic
syntax inductionAlt := "| " (group("@"? ident) <|> "_") (ident <|> "_")* " => " (hole <|> syntheticHole <|> tacticSeq)

View file

@ -578,8 +578,8 @@ def elabLetDeclCore (stx : Syntax) (expectedType? : Option Expr) (useLetExpr : B
let stxNew ← `(let x : $type := $val; match x with | $pat => $body)
let stxNew := match useLetExpr, elabBodyFirst with
| true, false => stxNew
| true, true => stxNew.setKind `Lean.Parser.Term.«let*»
| false, true => stxNew.setKind `Lean.Parser.Term.«let!»
| true, true => stxNew.setKind `Lean.Parser.Term.«let_delayed»
| false, true => stxNew.setKind `Lean.Parser.Term.«let_fun»
| false, false => unreachable!
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
else if letDecl.getKind == `Lean.Parser.Term.letEqnsDecl then
@ -599,6 +599,12 @@ def elabLetDeclCore (stx : Syntax) (expectedType? : Option Expr) (useLetExpr : B
@[builtinTermElab «let*»] def elabLetStarDecl : TermElab :=
fun stx expectedType? => elabLetDeclCore stx expectedType? true true
@[builtinTermElab «let_fun»] def elabLetFunDecl : TermElab :=
fun stx expectedType? => elabLetDeclCore stx expectedType? false false
@[builtinTermElab «let_delayed»] def elabLetDelayedDecl : TermElab :=
fun stx expectedType? => elabLetDeclCore stx expectedType? true true
builtin_initialize registerTraceClass `Elab.let
end Lean.Elab.Term

View file

@ -38,7 +38,7 @@ open Meta
@[builtinMacro Lean.Parser.Term.show] def expandShow : Macro := fun stx =>
match stx with
| `(show $type from $val) => let thisId := mkIdentFrom stx `this; `(let! $thisId : $type := $val; $thisId)
| `(show $type from $val) => let thisId := mkIdentFrom stx `this; `(let_fun $thisId : $type := $val; $thisId)
| `(show $type by $tac:tacticSeq) => `(show $type from by $tac:tacticSeq)
| _ => Macro.throwUnsupported
@ -46,8 +46,8 @@ open Meta
let mkId (x? : Option Syntax) : Syntax :=
x?.getD <| mkIdentFrom stx `this
match stx with
| `(have $[$x :]? $type from $val $[;]? $body) => let x := mkId x; `(let! $x : $type := $val; $body)
| `(have $[$x :]? $type := $val $[;]? $body) => let x := mkId x; `(let! $x : $type := $val; $body)
| `(have $[$x :]? $type from $val $[;]? $body) => let x := mkId x; `(let_fun $x : $type := $val; $body)
| `(have $[$x :]? $type := $val $[;]? $body) => let x := mkId x; `(let_fun $x : $type := $val; $body)
| `(have $[$x :]? $type by $tac:tacticSeq $[;]? $body) => `(have $[$x :]? $type from by $tac:tacticSeq; $body)
| _ => Macro.throwUnsupported

View file

@ -950,7 +950,7 @@ def mkJoinPoint (j : Name) (ps : Array (Name × Bool)) (body : Syntax) (k : Synt
let pTypes ← ps.mapM fun ⟨id, useTypeOf⟩ => do if useTypeOf then `(typeOf! $(← mkIdentFromRef id)) else `(_)
let ps ← ps.mapM fun ⟨id, useTypeOf⟩ => mkIdentFromRef id
/-
We use `let*` instead of `let` for joinpoints to make sure `$k` is elaborated before `$body`.
We use `let_delayed` instead of `let` for joinpoints to make sure `$k` is elaborated before `$body`.
By elaborating `$k` first, we "learn" more about `$body`'s type.
For example, consider the following example `do` expression
```
@ -970,10 +970,10 @@ def mkJoinPoint (j : Name) (ps : Array (Name × Bool)) (body : Syntax) (k : Synt
else
jp ()
```
If we use the regular `let` instead of `let*`, the joinpoint `jp` will be elaborated and its type will be inferred to be `Unit → IO (IO.Ref Bool)`.
Then, we get a typing error at `jp ()`. By using `let*`, we first elaborate `if x > 0 ...` and learn that `jp` has type `Unit → IO Unit`.
If we use the regular `let` instead of `let_delayed`, the joinpoint `jp` will be elaborated and its type will be inferred to be `Unit → IO (IO.Ref Bool)`.
Then, we get a typing error at `jp ()`. By using `let_delayed`, we first elaborate `if x > 0 ...` and learn that `jp` has type `Unit → IO Unit`.
Then, we get the expected type mismatch error at `IO.mkRef true`. -/
`(let* $(← mkIdentFromRef j):ident $[($ps : $pTypes)]* : $((← read).m) _ := $body; $k)
`(let_delayed $(← mkIdentFromRef j):ident $[($ps : $pTypes)]* : $((← read).m) _ := $body; $k)
def mkJmp (ref : Syntax) (j : Name) (args : Array Syntax) : Syntax :=
Syntax.mkApp (mkIdentFrom ref j) args

View file

@ -301,7 +301,7 @@ private partial def getHeadInfo (alt : Alt) : TermElabM HeadInfo :=
match k with
| `optional =>
let nones := mkArray ids.size (← `(none))
`(let* yes _ $ids* := $yes;
`(let_delayed yes _ $ids* := $yes;
if discr.isNone then yes () $[ $nones]*
else match discr with
| `($(mkNullNode contents)) => yes () $[ (some $ids)]*
@ -385,7 +385,7 @@ private partial def getHeadInfo (alt : Alt) : TermElabM HeadInfo :=
| r => r }
| _ => throwErrorAt! pat "match_syntax: unexpected pattern kind {pat}"
-- Bind right-hand side to new `let*` decl in order to prevent code duplication
-- Bind right-hand side to new `let_delayed` decl in order to prevent code duplication
private def deduplicate (floatedLetDecls : Array Syntax) : Alt → TermElabM (Array Syntax × Alt)
-- NOTE: new macro scope so that introduced bindings do not collide
| (pats, rhs) => do
@ -448,7 +448,7 @@ private partial def compileStxMatch (discrs : List Syntax) (alts : List Alt) : T
withFreshMacroScope $ compileStxMatch (newDiscrs ++ discrs) yesAlts.toList)
(no := withFreshMacroScope $ compileStxMatch (discr::discrs) nonExhaustiveAlts.toList)
for d in floatedLetDecls do
stx ← `(let* $d:letDecl; $stx)
stx ← `(let_delayed $d:letDecl; $stx)
`(let discr := $discr; $stx)
| _, _ => unreachable!

View file

@ -17,7 +17,6 @@ private def liftTermBinderSyntax : Macro := fun stx => do
@[builtinMacro Lean.Parser.Tactic.have] def expandHaveTactic : Macro := liftTermBinderSyntax
@[builtinMacro Lean.Parser.Tactic.let] def expandLetTactic : Macro := liftTermBinderSyntax
@[builtinMacro Lean.Parser.Tactic.«let!»] def expandLetBangTactic : Macro := liftTermBinderSyntax
@[builtinMacro Lean.Parser.Tactic.suffices] def expandSufficesTactic : Macro := liftTermBinderSyntax
@[builtinMacro Lean.Parser.Tactic.letrec] def expandLetRecTactic : Macro := liftTermBinderSyntax