diff --git a/src/Lean/Elab/MutualDef.lean b/src/Lean/Elab/MutualDef.lean index d22482c7fdab..36818838867e 100644 --- a/src/Lean/Elab/MutualDef.lean +++ b/src/Lean/Elab/MutualDef.lean @@ -1098,6 +1098,7 @@ where finishElab headers processDeriving headers elabAsync header view declId := do + assert! view.kind.isTheorem let env ← getEnv let async ← env.addConstAsync declId.declName .thm (exportedKind := .axiom) setEnv async.mainEnv diff --git a/src/Lean/Elab/PreDefinition/Basic.lean b/src/Lean/Elab/PreDefinition/Basic.lean index c6d598f1d044..397190983e46 100644 --- a/src/Lean/Elab/PreDefinition/Basic.lean +++ b/src/Lean/Elab/PreDefinition/Basic.lean @@ -12,6 +12,7 @@ import Lean.Util.NumApps import Lean.Meta.AbstractNestedProofs import Lean.Meta.ForEachExpr import Lean.Meta.Eqns +import Lean.Meta.LetToHave import Lean.Elab.RecAppSyntax import Lean.Elab.DefView import Lean.Elab.PreDefinition.TerminationHint @@ -20,6 +21,11 @@ namespace Lean.Elab open Meta open Term +register_builtin_option cleanup.letToHave : Bool := { + defValue := true + descr := "Enables transforming `let`s to `have`s when elaborating a declaration." +} + /-- A (potentially recursive) definition. The elaborator converts it into Kernel definitions using many different strategies. @@ -87,6 +93,21 @@ def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : Attribu for preDef in preDefs do applyAttributesAt preDef.declName preDef.modifiers.attrs applicationTime +/-- +Applies `Meta.letToHave` to the values of defs, instances, and abbrevs. +Does not apply the transformation to values that are proofs, or to unsafe definitions. +-/ +def letToHave (preDef : PreDefinition) : MetaM PreDefinition := withRef preDef.ref do + if !cleanup.letToHave.get (← getOptions) || preDef.modifiers.isUnsafe then + return preDef + else if preDef.kind matches .theorem | .example | .opaque then + return preDef + else if ← Meta.isProp preDef.type then + return preDef + else + let value ← Meta.letToHave preDef.value + return { preDef with value } + def abstractNestedProofs (preDef : PreDefinition) (cache := true) : MetaM PreDefinition := withRef preDef.ref do if preDef.kind.isTheorem || preDef.kind.isExample then pure preDef @@ -124,6 +145,7 @@ private def reportTheoremDiag (d : TheoremVal) : TermElabM Unit := do private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List Name) (applyAttrAfterCompilation := true) (cacheProofs := true) : TermElabM Unit := withRef preDef.ref do let preDef ← abstractNestedProofs (cache := cacheProofs) preDef + let preDef ← letToHave preDef let mkDefDecl : TermElabM Declaration := return Declaration.defnDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value diff --git a/src/Lean/Elab/PreDefinition/Mutual.lean b/src/Lean/Elab/PreDefinition/Mutual.lean index 6b150ff01176..a269ee8167da 100644 --- a/src/Lean/Elab/PreDefinition/Mutual.lean +++ b/src/Lean/Elab/PreDefinition/Mutual.lean @@ -55,6 +55,7 @@ Cleans the right-hand-sides of the predefinitions, to prepare for inclusion in t def cleanPreDef (preDef : PreDefinition) (cacheProofs := true) : MetaM PreDefinition := do let preDef ← eraseRecAppSyntax preDef let preDef ← abstractNestedProofs (cache := cacheProofs) preDef + let preDef ← letToHave preDef return preDef /-- diff --git a/src/Lean/Elab/PreDefinition/Structural/Main.lean b/src/Lean/Elab/PreDefinition/Structural/Main.lean index 733864d2d2bd..c4f2d3b1326a 100644 --- a/src/Lean/Elab/PreDefinition/Structural/Main.lean +++ b/src/Lean/Elab/PreDefinition/Structural/Main.lean @@ -187,6 +187,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array ( unless preDef.kind.isTheorem do unless (← isProp preDef.type) do preDef ← abstractNestedProofs preDef + preDef ← letToHave preDef /- Don't save predefinition info for equation generator for theorems and definitions that are propositions. diff --git a/src/Lean/Meta.lean b/src/Lean/Meta.lean index 619a4ad64d13..f53aec60eb36 100644 --- a/src/Lean/Meta.lean +++ b/src/Lean/Meta.lean @@ -26,6 +26,7 @@ import Lean.Meta.Match import Lean.Meta.ReduceEval import Lean.Meta.Closure import Lean.Meta.AbstractNestedProofs +import Lean.Meta.LetToHave import Lean.Meta.ForEachExpr import Lean.Meta.Transform import Lean.Meta.PPGoal diff --git a/src/Lean/Meta/AppBuilder.lean b/src/Lean/Meta/AppBuilder.lean index 35024b6b2b16..fea10e1a3d2b 100644 --- a/src/Lean/Meta/AppBuilder.lean +++ b/src/Lean/Meta/AppBuilder.lean @@ -36,14 +36,18 @@ def mkExpectedTypeHint (e : Expr) (expectedType : Expr) : MetaM Expr := do /-- `mkLetFun x v e` creates the encoding for the `let_fun x := v; e` expression. The expression `x` can either be a free variable or a metavariable, and the function suitably abstracts `x` in `e`. + +If `usedLetOnly` is `true`, then returns `e` if `x` is unused in `e`. -/ -def mkLetFun (x : Expr) (v : Expr) (e : Expr) : MetaM Expr := do +def mkLetFun (x : Expr) (v : Expr) (e : Expr) (usedLetOnly : Bool := false) : MetaM Expr := do -- If `x` is an `ldecl`, then the result of `mkLambdaFVars` is a let expression. let ensureLambda : Expr → Expr | .letE n t _ b _ => .lam n t b .default | e@(.lam ..) => e | _ => unreachable! let f ← ensureLambda <$> mkLambdaFVars (usedLetOnly := false) #[x] e + if usedLetOnly && !f.bindingBody!.hasLooseBVars then + return f.bindingBody! let ety ← inferType e let α ← inferType x let β ← ensureLambda <$> mkLambdaFVars (usedLetOnly := false) #[x] ety diff --git a/src/Lean/Meta/LetToHave.lean b/src/Lean/Meta/LetToHave.lean new file mode 100644 index 000000000000..d37db2fa0f79 --- /dev/null +++ b/src/Lean/Meta/LetToHave.lean @@ -0,0 +1,408 @@ +/- +Copyright (c) 2025 Lean FRO, LLC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kyle Miller +-/ +prelude +import Lean.Meta.InferType + +/-! +# Transforming non-dependent `let`s into `have`s + +A `let` expression `let x := v; b` is *dependent* if `(fun x => b) v` is not type correct; +which is to say if `have x := v; b` is not type correct. +This module has a procedure that transforms non-dependent `let`s into equivalent `have` expressions. + +Dependence checking is approximated using the `withTrackingZetaDelta` trick: +we take `let x := v; b`, add a `x := v` declaration to the local context, +and then type check `b` using `withTrackingZetaDelta`. +If `x` is not unfolded, then we know that `b` does not depend on `v`. +This is a conservative check, since `isDefEq` may unfold local definitions unnecessarily. + +We do not use `Lean.Meta.check` directly. A naive algorithm would be to do `Meta.check (b.instantiate1 x)` +for each `let` body, which would involve rechecking subexpressions multiple times when there are nested `let`s. +Instead, we re-implement a type checking algorithm here to be able to interleave checking and transformation. + +The trace class `trace.Meta.letToHave` reports any errors that arise. +Given that this transformation might be applied before elaborator type checking, we take the approach that if there +are type errors, the transformation is a no-op and errors are traced. +That way, only `Lean.Meta.check`/`Lean.Meta.inferType` have sole responsibility for elaborator typechecking error messages. + +The transformation has very limited support for metavariables. +Any `let` that contains a metavariable remains a `let`. +-/ + +namespace Lean.Meta + +namespace LetToHave + +/-- +An expression along with its type. +-/ +private structure Expr' where + val : Expr + /-- The type of `e`. -/ + type : Expr + deriving Inhabited + +@[inline] private def Expr'.abstract (e : Expr') (fvars : Array Expr) : Expr' where + val := e.val.abstract fvars + type := e.type.abstract fvars + +@[inline] private def Expr'.instantiate1 (e : Expr') (subst : Expr) : Expr' where + val := e.val.instantiate1 subst + type := e.type.instantiate1 subst + +/-- +Creates a `have` expression. +- `u` is the universe level for the type of the value `val` +- `v` is the universe level for the type of the body `b` +- The `b` type and value may have loose bvars. +-/ +private def Expr'.mkHave (u v : Level) (n : Name) (val : Expr') (b : Expr') : Expr' where + val := + mkApp4 (mkConst ``letFun [u, v]) + val.type (Expr.lam n val.type b.type .default) + val.val + (Expr.lam n val.type b.val .default) + type := + if b.type.hasLooseBVar 0 then + mkApp4 (mkConst ``letFun [u, v.succ]) + val.type (Expr.lam n val.type (Expr.sort v) .default) + val.val + (Expr.lam n val.type b.type .default) + else + b.type.lowerLooseBVars 1 1 + +/-- +Creates a `let` expression. The `b` type and value may have loose bvars. +-/ +private def Expr'.mkLet (n : Name) (val : Expr') (b : Expr') (nonDep : Bool) : Expr' where + val := Expr.letE n val.type val.val b.val nonDep + type := + if b.type.hasLooseBVar 0 then + Expr.letE n val.type val.val b.type nonDep + else + b.type.lowerLooseBVars 1 1 + +private local instance : CoeHead Expr' Expr where coe := Expr'.val + +private structure State where + count : Nat := 0 + +private abbrev M := MonadCacheT ExprStructEq Expr' (StateRefT State MetaM) + +/-- Increments the count of the number of `let`s transformed into `have`s. -/ +private def incCount : M Unit := + modify fun s => { s with count := s.count + 1 } + +private def checkMCache (e : Expr) (m : M Expr') : M Expr' := + checkCache { val := e : ExprStructEq } fun _ => m + +private def findMCache? (e : Expr) : M (Option Expr') := + MonadCache.findCached? { val := e : ExprStructEq } + +private def visitFVar (e : Expr) : MetaM Expr' := do + let some decl ← e.fvarId!.findDecl? | throwError "fvar: unknown" + -- We did `instantiateLCtxMVars`, so no need to `instantiateMVars` the type. + return { val := e, type := decl.type } + +private def visitMVar (e : Expr) : MetaM Expr' := do + let some decl ← e.mvarId!.findDecl? | throwError "mvar: unknown" + return { val := e, type := (← instantiateMVars decl.type) } + +private def visitConst (e : Expr) (constName : Name) (us : List Level) : M Expr' := do + let cinfo ← getConstVal constName + if cinfo.levelParams.length == us.length then + if us.isEmpty then + return { val := e, type := cinfo.type } + else + let type ← instantiateTypeLevelParams cinfo us + return { val := e, type } + else + throwError "const: invalid" + +private def visitApp (f : Expr') (args : Array Expr') : M Expr' := do + let argVals := args.map Expr'.val + let mut type := f.type + let mut j := 0 + for i in [:args.size] do + unless type.isForall do + type ← whnf <| type.instantiateRevRange j i argVals + j := i + let .forallE _ dom b _ := type | throwError "app: function expected" + type := b + let dom := dom.instantiateRevRange j i argVals + unless ← isDefEq dom args[i]!.type do throwError "app: dom not defeq" + type := type.instantiateRevRange j argVals.size argVals + return { val := mkAppN f argVals, type } + +private def getSortLevel (e : Expr) : MetaM Level := do + let .sort u ← whnf e | throwError "not Sort" + return u + +private inductive BoundKind + /-- `t` is the visited binding domain *with* annotations. -/ + | forall (t : Expr) + /-- `t` is the visited binding domain *with* annotations. -/ + | lambda (t : Expr) + | let + /-- `v` is the value of the `have`. This is stored here because the value + is not put into the local context. -/ + | have (v : Expr) + deriving Inhabited + +/-- +A conservative check for whether to keep a `let` dependent. +The `body` may have loose bvars. +-/ +def isDepLet (fvarId : FVarId) (body : Expr) : M Bool := do + let some decl ← fvarId.findDecl? | throwError "isDepLet: unknown" + if (← getZetaDeltaFVarIds).contains fvarId then + /- + If an fvar is not zeta delta reduced during typechecking, then it is safe + to assume it is non-dependent. However, if it *is* zeta delta reduced, + then we conservatively assume it is a dependent `let` binding. + -/ + return true + else if body.hasExprMVar then + /- + We do not try to analyze whether the metavariable depends on the local definition. + This is rather subtle, and instead we conservatively assume that the + metavariable *does* depend on it. + -/ + return true + else if decl.userName.eraseMacroScopes == `__do_jp then + /- + Join points for `do` notation are represented using `let`s, + and we should leave them as `let`s for the compiler's sake. + TODO(kmill) Remove this hack once the new compiler is ready to be told about `have`s. + -/ + return true + else + return false + +mutual + +private partial def visitApp' (f : Expr) (args : Array Expr) : M Expr' := do + visitApp (← visit f) (← args.mapM visit) + +private partial def visitApp'' (e : Expr) : M Expr' := do + let arity := e.getAppNumArgs + if e.isAppOf ``letFun && arity ≥ 4 then + if arity == 4 then + visitBound e + else + visitApp' (e.getBoundedAppFn (arity - 4)) (e.getBoundedAppArgs (arity - 4)) + else + visitApp' e.getAppFn e.getAppArgs + +/-- +Visits foralls, lambdas, lets, and haves. + +Enters the entire telescope at once. +Checking the cache is more limited because there can be loose bvars. +The tradeoff is that we save time instantiating bvars by doing it all at once, +at the expense of possibly not sharing terms. +-/ +private partial def visitBound (e : Expr) : M Expr' := do + visitBody (← getLCtx) #[] #[] #[] e +where + /-- + Enters a forall/lambda/let/have telescope, checking that each domain type is a type. + For let/have, checks that each value has a type defeq to the domain type. + Calls `finalize` once the telescope is constructed. + -/ + visitBody (lctx : LocalContext) (fvars : Array Expr) (kinds : Array BoundKind) (levels : Array Level) (e : Expr) : M Expr' := do + if !e.hasLooseBVars then + if let some e' ← findMCache? e then + return ← withLCtx lctx {} do finalize fvars kinds levels e' + let visitAndInstantiate (e : Expr) : M Expr' := withLCtx lctx {} do + let e := e.instantiateRev fvars + -- trace[Meta.letToHave.debug] "visit{indentExpr e}" + visit e + -- trace[Meta.letToHave.debug] "visitBody{indentExpr e}" + match e with + | .forallE n t b bi => + let t ← visitAndInstantiate t + let fvarId ← mkFreshFVarId + let lctx := lctx.mkLocalDecl fvarId n t.val.cleanupAnnotations bi + let fvars := fvars.push (mkFVar fvarId) + let kinds := kinds.push (.forall t) + let levels := levels.push (← withLCtx lctx {} <| getSortLevel t.type) + visitBody lctx fvars kinds levels b + | .lam n t b bi => + let t ← visitAndInstantiate t + let fvarId ← mkFreshFVarId + let lctx := lctx.mkLocalDecl fvarId n t.val.cleanupAnnotations bi + let fvars := fvars.push (mkFVar fvarId) + let kinds := kinds.push (.lambda t) + let levels := levels.push (← withLCtx lctx {} <| getSortLevel t.type) + visitBody lctx fvars kinds levels b + | .letE n t v b _ => + -- trace[Meta.letToHave.debug] "let" + let t ← visitAndInstantiate t + let v ← visitAndInstantiate v + -- trace[Meta.letToHave.debug] "let2" + unless ← withLCtx lctx {} (isDefEq t v.type) do throwError "let: value type not defeq to type" + -- trace[Meta.letToHave.debug] "let3" + let fvarId ← mkFreshFVarId + let lctx := lctx.mkLetDecl fvarId n t v + let fvars := fvars.push (mkFVar fvarId) + let kinds := kinds.push .let + let levels := levels.push (← withLCtx lctx {} <| getSortLevel t.type) + visitBody lctx fvars kinds levels b + | _ => + if let some (n, t, v, b) := e.letFun? then + let t ← visitAndInstantiate t + let v ← visitAndInstantiate v + unless ← withLCtx lctx {} (isDefEq t v.type) do throwError "have: value type not defeq to type" + let fvarId ← mkFreshFVarId + let lctx := lctx.mkLocalDecl fvarId n t + let fvars := fvars.push (mkFVar fvarId) + let kinds := kinds.push (.have v) + let levels := levels.push (← withLCtx lctx {} <| getSortLevel t.type) + visitBody lctx fvars kinds levels b + else + withLCtx lctx {} do + let e' ← visit (e.instantiateRev fvars) + finalize fvars kinds levels e' + /-- + Assumption: the telescope domains and values are well-typed and the innermost body is well-typed. + This function checks that the telescope is well-constructed (the bodies of foralls are types) + and converts `let`s to `have`s when possible. + -/ + finalize (fvars : Array Expr) (kinds : Array BoundKind) (levels : Array Level) (body : Expr') : M Expr' := do + let mut body := body + trace[Meta.letToHave.debug] "finalize {fvars},{indentD m!"{body.val} : {body.type}"}" + -- When building `have`s, we need to know the type of `type`. + -- We must compute this before we abstract the fvars. + let mut typeLevel ← getSortLevel (← inferType body.type) + -- If there is a forall, then there cannot be any lambdas after it, and `body` must be a type. + -- This is necessary and sufficient for all the foralls to be well-constructed. + if let some i := kinds.findIdx? (· matches .forall _) then + let u ← getSortLevel body.type + -- Invariant: the `type` will always be of the form `Sort _` when visiting forall. + body := { body with type := mkSort u } + if kinds.any (start := i + 1) (· matches .lambda _) then + throwError "forall: body is lambda" + -- Now reconstruct the binding expressions + body := Expr'.abstract body fvars + for j in [0:fvars.size] do + let i := fvars.size - j - 1 + let fvar := fvars[i]! + let some decl ← fvar.fvarId!.findDecl? | throwError "fvar: unknown in visitBound" + let kind := kinds[i]! + let level := levels[i]! + match kind with + | .forall t => + let t := t.abstractRange i fvars + let Expr.sort u := body.type | throwError "forall: invariant failure" + let u' := mkLevelIMax' level u + body := { val := Expr.forallE decl.userName t body.val decl.binderInfo, + type := Expr.sort u' } + typeLevel := u'.succ + | .lambda t => + let t := t.abstractRange i fvars + body := { val := Expr.lam decl.userName t body.val decl.binderInfo, + type := Expr.forallE decl.userName t body.type decl.binderInfo } + typeLevel := mkLevelIMax' level typeLevel + | .let => + -- typeLevel remains the same + let t := decl.type.abstractRange i fvars + let v := decl.value.abstractRange i fvars + -- if v.isBVar || v.isFVar then + -- body := Expr'.mkLet decl.userName { val := v, type := t } body false + -- else + if ← isDepLet fvar.fvarId! body.val then + body := Expr'.mkLet decl.userName { val := v, type := t } body false + else + incCount + body := Expr'.mkHave level.normalize typeLevel.normalize decl.userName { val := v, type := t } body + | .have v => + -- typeLevel remains the same + let t := decl.type.abstractRange i fvars + let v := v.abstractRange i fvars + body := Expr'.mkHave level.normalize typeLevel.normalize decl.userName { val := v, type := t } body + return body + +private partial def visitProj (structName : Name) (idx : Nat) (struct : Expr') : M Expr' := do + let structType ← whnf struct.type + let prop ← isProp structType + let failed {α} (_ : Unit) : M α := throwError "proj: invalid" + matchConstStructure structType.getAppFn failed fun structVal structLvls ctorVal => do + unless structVal.name == structName do failed () + let structTypeArgs := structType.getAppArgs + if structVal.numParams + structVal.numIndices != structTypeArgs.size then + failed () + let ctor ← visitApp' (mkConst ctorVal.name structLvls) structTypeArgs[:structVal.numParams] + let mut ctorType := ctor.type + let mut args := #[] + let mut j := 0 + let mut lastFieldTy : Expr := default + for i in [:idx+1] do + unless ctorType.isForall do + ctorType ← whnf <| ctorType.instantiateRevRange j i args + j := i + let .forallE _ dom body _ := ctorType | failed () + let dom := dom.instantiateRevRange j i args + if prop then + unless ← isProp dom do + throwError "proj: large elim" + args := args.push <| Expr.proj structName i struct + ctorType := body + lastFieldTy := dom + lastFieldTy := lastFieldTy.cleanupAnnotations + return { val := Expr.proj structName idx struct, type := lastFieldTy } + +private partial def visit (e : Expr) : M Expr' := do + withTraceNode `Meta.letToHave.debug (fun res => + return m!"{if res.isOk then checkEmoji else crossEmoji} visit{indentExpr e}") do + match e with + | .bvar .. => throwError "bvar: unexpected" + | .fvar .. => visitFVar e + | .mvar .. => visitMVar e + | .sort u => return { val := e, type := .sort u.succ } + | .const constName [] => visitConst e constName [] + | .const constName us => checkMCache e do visitConst e constName us + | .app .. => checkMCache e do visitApp'' e + | .lam .. | .forallE .. | .letE .. => checkMCache e do visitBound e + | .lit v => return { val := e, type := v.type } + | .mdata _ e' => let e' ← visit e'; return { e' with val := e.updateMData! e' } + | .proj structName idx struct => checkMCache e do visitProj structName idx (← visit struct) + +end + +private def main (e : Expr) : MetaM Expr := do + Prod.fst <$> withTraceNode `Meta.letToHave (fun + | .ok (_, msg) => pure m!"{checkEmoji} {msg}" + | .error ex => pure m!"{crossEmoji} {ex.toMessageData}") do + resetZetaDeltaFVarIds + withTrackingZetaDelta <| + withTransparency TransparencyMode.all <| + withInferTypeConfig do + let lctx ← instantiateLCtxMVars (← getLCtx) + withLCtx lctx {} do + let (e', s) ← visit e |>.run |>.run {} + if s.count == 0 then + trace[Met.letToHave] "result: (no change)" + else + trace[Meta.letToHave] "result:{indentExpr e'.val}" + return (e'.val, m!"transformed {s.count} `let` expressions into `have` expressions") + +end LetToHave + +/-- +Transforms non-dependent `let` expressions into `have` expressions. +If `e` is not type correct, returns `e`. +The `Meta.letToHave` trace class logs errors and messages. +-/ +def letToHave (e : Expr) : MetaM Expr := do + let e ← instantiateMVars e + LetToHave.main e + +builtin_initialize + registerTraceClass `Meta.letToHave + registerTraceClass `Meta.letToHave.debug + +end Lean.Meta diff --git a/src/Lean/Meta/Tactic/ElimInfo.lean b/src/Lean/Meta/Tactic/ElimInfo.lean index b645b592cf14..d19576a24c1e 100644 --- a/src/Lean/Meta/Tactic/ElimInfo.lean +++ b/src/Lean/Meta/Tactic/ElimInfo.lean @@ -45,10 +45,14 @@ structure ElimInfo where /-- Given the type `t` of an alternative, determines the number of parameters (.forall and .let)-bound, and whether the conclusion is a `motive`-application. -/ -def altArity (motive : Expr) (n : Nat) : Expr → Nat × Bool +partial def altArity (motive : Expr) (n : Nat) : Expr → Nat × Bool | .forallE _ _ b _ => altArity motive (n+1) b | .letE _ _ _ b _ => altArity motive (n+1) b - | conclusion => (n, conclusion.getAppFn == motive) + | conclusion => + if let some (_, _, _, b) := conclusion.letFun? then + altArity motive (n+1) b + else + (n, conclusion.getAppFn == motive) def getElimExprInfo (elimExpr : Expr) (baseDeclName? : Option Name := none) : MetaM ElimInfo := do diff --git a/src/Lean/Meta/Tactic/FunInd.lean b/src/Lean/Meta/Tactic/FunInd.lean index 6767d7845e1c..9ba9b8709a01 100644 --- a/src/Lean/Meta/Tactic/FunInd.lean +++ b/src/Lean/Meta/Tactic/FunInd.lean @@ -296,9 +296,9 @@ partial def foldAndCollect (oldIH newIH : FVarId) (isRecCall : Expr → Option E let t' ← foldAndCollect oldIH newIH isRecCall t let v' ← foldAndCollect oldIH newIH isRecCall v return ← withLocalDeclD n t' fun x => do - M.localMapM (mkLetFun x v' ·) do + M.localMapM (mkLetFun x v' · (usedLetOnly := true)) do let b' ← foldAndCollect oldIH newIH isRecCall (b.instantiate1 x) - mkLetFun x v' b' + mkLetFun x v' b' (usedLetOnly := true) if let some matcherApp ← matchMatcherApp? e (alsoCasesOn := true) then if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then @@ -879,10 +879,10 @@ partial def buildInductionBody (toErase toClear : Array FVarId) (goal : Expr) if let some (n, t, v, b) := e.letFun? then let t' ← foldAndCollect oldIH newIH isRecCall t let v' ← foldAndCollect oldIH newIH isRecCall v - return ← withLocalDeclD n t' fun x => M2.branch do + return ← withLetDecl n t' v' fun x => M2.branch do let b' ← withRewrittenMotiveArg goal (rwHaveWith x) fun goal' => buildInductionBody toErase toClear goal' oldIH newIH isRecCall (b.instantiate1 x) - mkLetFun x v' b' + mkLetFVars #[x] b' -- Special case for traversing the PProd’ed bodies in our encoding of structural mutual recursion if let .lam n t b bi := e then diff --git a/src/Lean/PrettyPrinter/Delaborator/Builtins.lean b/src/Lean/PrettyPrinter/Delaborator/Builtins.lean index cc002d944040..305f72a70c6f 100644 --- a/src/Lean/PrettyPrinter/Delaborator/Builtins.lean +++ b/src/Lean/PrettyPrinter/Delaborator/Builtins.lean @@ -844,7 +844,7 @@ where x /-- -Delaborates applications of the form `letFun v (fun x => b)` as `let_fun x := v; b`. +Delaborates applications of the form `letFun v (fun x => b)` as `have x := v; b`. -/ @[builtin_delab app.letFun] def delabLetFun : Delab := whenPPOption getPPNotation <| withOverApp 4 do @@ -856,9 +856,9 @@ def delabLetFun : Delab := whenPPOption getPPNotation <| withOverApp 4 do let (stxN, stxB) ← withAppArg <| withBindingBody' n (mkAnnotatedIdent n) fun stxN => return (stxN, ← delab) if ← getPPOption getPPLetVarTypes <||> getPPOption getPPAnalysisLetVarType then let stxT ← SubExpr.withNaryArg 0 delab - `(let_fun $stxN : $stxT := $stxV; $stxB) + `(have $stxN : $stxT := $stxV; $stxB) else - `(let_fun $stxN := $stxV; $stxB) + `(have $stxN := $stxV; $stxB) @[builtin_delab mdata] def delabMData : Delab := do diff --git a/tests/lean/445.lean.expected.out b/tests/lean/445.lean.expected.out index 816015d5d61d..512da23bf880 100644 --- a/tests/lean/445.lean.expected.out +++ b/tests/lean/445.lean.expected.out @@ -11,5 +11,5 @@ i + 1 i + i * 2 i + i * r i j i + i * r i j -let z := s i j; +have z := s i j; z + z diff --git a/tests/lean/commandPrefix.lean.expected.out b/tests/lean/commandPrefix.lean.expected.out index b9a65fc994d3..d3e22115f15b 100644 --- a/tests/lean/commandPrefix.lean.expected.out +++ b/tests/lean/commandPrefix.lean.expected.out @@ -1,3 +1,3 @@ -let_fun this := (); +have this := (); this : Id Unit commandPrefix.lean:3:0-3:4: error: unexpected identifier; expected command diff --git a/tests/lean/consumePPHint.lean.expected.out b/tests/lean/consumePPHint.lean.expected.out index ff65105109bf..5c253a21760f 100644 --- a/tests/lean/consumePPHint.lean.expected.out +++ b/tests/lean/consumePPHint.lean.expected.out @@ -1,6 +1,6 @@ consumePPHint.lean:4:8-4:14: warning: declaration uses 'sorry' case a ⊢ q - (let_fun x := 0; + (have x := 0; x + 1) consumePPHint.lean:6:8-6:15: warning: declaration uses 'sorry' diff --git a/tests/lean/doubleReset.lean.expected.out b/tests/lean/doubleReset.lean.expected.out index 25937761cd2e..9385be5fdd67 100644 --- a/tests/lean/doubleReset.lean.expected.out +++ b/tests/lean/doubleReset.lean.expected.out @@ -12,18 +12,16 @@ def Array.mapMUnsafe.map._at.applyProjectionRules._spec_1._rarg (x_1 : obj) (x_2 let x_9 : obj := proj[0] x_6; case x_9 : obj of Prod.mk → - case x_9 : obj of - Prod.mk → - let x_10 : obj := proj[0] x_9; - let x_11 : obj := proj[1] x_9; - let x_18 : obj := reset[2] x_9; - let x_12 : obj := reuse x_18 in ctor_0[Prod.mk] x_10 x_11; - let x_13 : obj := ctor_0[Prod.mk] x_12 x_1; - let x_14 : usize := 1; - let x_15 : usize := USize.add x_3 x_14; - let x_16 : obj := Array.uset ◾ x_8 x_3 x_13 ◾; - let x_17 : obj := Array.mapMUnsafe.map._at.applyProjectionRules._spec_1._rarg x_1 x_2 x_15 x_16; - ret x_17 + let x_10 : obj := proj[0] x_9; + let x_11 : obj := proj[1] x_9; + let x_18 : obj := reset[2] x_9; + let x_12 : obj := reuse x_18 in ctor_0[Prod.mk] x_10 x_11; + let x_13 : obj := ctor_0[Prod.mk] x_12 x_1; + let x_14 : usize := 1; + let x_15 : usize := USize.add x_3 x_14; + let x_16 : obj := Array.uset ◾ x_8 x_3 x_13 ◾; + let x_17 : obj := Array.mapMUnsafe.map._at.applyProjectionRules._spec_1._rarg x_1 x_2 x_15 x_16; + ret x_17 def Array.mapMUnsafe.map._at.applyProjectionRules._spec_1 (x_1 : ◾) (x_2 : ◾) (x_3 : ◾) : obj := let x_4 : obj := pap Array.mapMUnsafe.map._at.applyProjectionRules._spec_1._rarg; ret x_4 diff --git a/tests/lean/letFun.lean b/tests/lean/letFun.lean index c7bede1899d6..98a2c494f6d7 100644 --- a/tests/lean/letFun.lean +++ b/tests/lean/letFun.lean @@ -1,34 +1,34 @@ import Lean.Elab.Command /-! -# Tests for `let_fun x := v; b` notation +# Tests for `have x := v; b` notation -/ /-! -Checks that types can be inferred and that default instances work with `let_fun`. +Checks that types can be inferred and that default instances work with `have`. -/ #check - let_fun f x := x * 2 - let_fun x := 1 - let_fun y := x + 1 + have f x := x * 2 + have x := 1 + have y := x + 1 f (y + x) /-! -Checks that `simp` can do zeta reduction of `let_fun`s +Checks that `simp` can do zeta reduction of `have`s -/ -example (a b : Nat) (h1 : a = 0) (h2 : b = 0) : (let_fun x := a + 1; x + x) > b := by +example (a b : Nat) (h1 : a = 0) (h2 : b = 0) : (have x := a + 1; x + x) > b := by simp (config := { zeta := false }) [h1] trace_state simp (config := { decide := true }) [h2] /-! -Checks that the underlying encoding for `let_fun` can be overapplied. -This still pretty prints with `let_fun` notation. +Checks that the underlying encoding for `have` can be overapplied. +This still pretty prints with `have` notation. -/ #check (show Nat → Nat from id) 1 /-! -Checks that zeta reduction still occurs even if the `let_fun` is applied to an argument. +Checks that zeta reduction still occurs even if the `have` is applied to an argument. -/ example (a b : Nat) (h : a > b) : (show Nat → Nat from id) a > b := by simp @@ -36,23 +36,23 @@ example (a b : Nat) (h : a > b) : (show Nat → Nat from id) a > b := by exact h /-! -Checks that the type of a `let_fun` can depend on the value. +Checks that the type of a `have` can depend on the value. -/ -#check let_fun n := 5 +#check have n := 5 (⟨[], Nat.zero_le n⟩ : { as : List Bool // as.length ≤ n }) /-! -Check that `let_fun` is reducible. +Check that `have` is reducible. -/ -#check (rfl : (let_fun n := 5; n) = 5) +#check (rfl : (have n := 5; n) = 5) /-! -Exercise `isDefEqQuick` for `let_fun`. +Exercise `isDefEqQuick` for `have`. -/ -#check (rfl : (let_fun _n := 5; 2) = 2) +#check (rfl : (have _n := 5; 2) = 2) /-! -Check that `let_fun` responds to WHNF's `zeta` option. +Check that `have` responds to WHNF's `zeta` option. -/ open Lean Meta Elab Term in @@ -61,5 +61,5 @@ elab "#whnfCore " z?:(&"noZeta")? t:term : command => Command.runTermElabM fun _ let e ← withConfig (fun c => { c with zeta := z?.isNone }) <| Meta.whnfCore e logInfo m!"{e}" -#whnfCore let_fun n := 5; n -#whnfCore noZeta let_fun n := 5; n +#whnfCore have n := 5; n +#whnfCore noZeta have n := 5; n diff --git a/tests/lean/letFun.lean.expected.out b/tests/lean/letFun.lean.expected.out index 45b30806e3ee..a55c0573d38a 100644 --- a/tests/lean/letFun.lean.expected.out +++ b/tests/lean/letFun.lean.expected.out @@ -1,26 +1,26 @@ -let_fun f := fun x => x * 2; -let_fun x := 1; -let_fun y := x + 1; +have f := fun x => x * 2; +have x := 1; +have y := x + 1; f (y + x) : Nat a b : Nat h1 : a = 0 h2 : b = 0 ⊢ b < - let_fun x := 1; + have x := 1; x + x -(let_fun this := id; +(have this := id; this) 1 : Nat a b : Nat h : a > b ⊢ b < a -let_fun n := 5; +have n := 5; ⟨[], ⋯⟩ : { as // as.length ≤ 5 } -rfl : (let_fun n := 5; +rfl : (have n := 5; n) = - let_fun n := 5; + have n := 5; n rfl : 2 = 2 5 -let_fun n := 5; +have n := 5; n diff --git a/tests/lean/ppBeta.lean.expected.out b/tests/lean/ppBeta.lean.expected.out index 351e60c62d57..c06c79f34c26 100644 --- a/tests/lean/ppBeta.lean.expected.out +++ b/tests/lean/ppBeta.lean.expected.out @@ -1,7 +1,7 @@ (fun x => x) Nat : Type -let_fun x := Nat; +have x := Nat; x : Type Nat : Type -let_fun x := Nat; +have x := Nat; x : Type Nat : Type diff --git a/tests/lean/ppNotationCode.lean.expected.out b/tests/lean/ppNotationCode.lean.expected.out index b8b379014794..63694dba908b 100644 --- a/tests/lean/ppNotationCode.lean.expected.out +++ b/tests/lean/ppNotationCode.lean.expected.out @@ -3,13 +3,13 @@ (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol "+++") (Lean.ParserDescr.cat `term 46)) [Elab.definition.body] «_aux_ppNotationCode___macroRules_term_+++__1» : Lean.Macro := fun x => - let_fun __discr := x; + have __discr := x; if __discr.isOfKind `«term_+++_» = true then - let_fun __discr_1 := __discr.getArg 0; - let_fun __discr_2 := __discr.getArg 1; - let_fun __discr := __discr.getArg 2; - let_fun rhs := { raw := __discr }; - let_fun lhs := { raw := __discr_1 }; + have __discr_1 := __discr.getArg 0; + have __discr_2 := __discr.getArg 1; + have __discr := __discr.getArg 2; + have rhs := { raw := __discr }; + have lhs := { raw := __discr_1 }; do let info ← Lean.MonadRef.mkInfoFromRefPos let scp ← Lean.getCurrMacroScope @@ -22,33 +22,33 @@ [Lean.Syntax.Preresolved.decl `Nat.add [], Lean.Syntax.Preresolved.namespace `Nat.add]) (Lean.Syntax.node2 info `null lhs.raw rhs.raw) }.raw else - let_fun __discr := x; + have __discr := x; throw Lean.Macro.Exception.unsupportedSyntax [Elab.definition.body] _aux_ppNotationCode___unexpand_Nat_add_1 : Lean.PrettyPrinter.Unexpander := fun x => - let_fun __discr := x; + have __discr := x; if __discr.isOfKind `Lean.Parser.Term.app = true then - let_fun __discr_1 := __discr.getArg 0; + have __discr_1 := __discr.getArg 0; bif false || __discr_1.isOfKind `ident then - let_fun __discr_2 := __discr.getArg 1; + have __discr_2 := __discr.getArg 1; if __discr_2.matchesNull 2 = true then - let_fun __discr := __discr_2.getArg 0; - let_fun __discr_3 := __discr_2.getArg 1; - let_fun rhs := { raw := __discr_3 }; - let_fun lhs := { raw := __discr }; - let_fun f := { raw := __discr_1 }; + have __discr := __discr_2.getArg 0; + have __discr_3 := __discr_2.getArg 1; + have rhs := { raw := __discr_3 }; + have lhs := { raw := __discr }; + have f := { raw := __discr_1 }; Lean.withRef f.raw do let info ← Lean.MonadRef.mkInfoFromRefPos let _ ← Lean.getCurrMacroScope let _ ← Lean.getMainModule pure { raw := Lean.Syntax.node3 info `«term_+++_» lhs.raw (Lean.Syntax.atom info "+++") rhs.raw }.raw else - let_fun __discr := __discr.getArg 1; + have __discr := __discr.getArg 1; throw () else - let_fun __discr_2 := __discr.getArg 0; - let_fun __discr := __discr.getArg 1; + have __discr_2 := __discr.getArg 0; + have __discr := __discr.getArg 1; throw () else - let_fun __discr := x; + have __discr := x; throw () diff --git a/tests/lean/precissues.lean.expected.out b/tests/lean/precissues.lean.expected.out index c922b85fd4f8..64f05baa34e1 100644 --- a/tests/lean/precissues.lean.expected.out +++ b/tests/lean/precissues.lean.expected.out @@ -9,10 +9,10 @@ id.{u} {α : Sort u} (a : α) : α precissues.lean:17:10-17:13: error: unexpected token 'let'; expected command 1 : Nat id - (let_fun this := True.intro; + (have this := True.intro; this) : True 0 = - let_fun this := 1; + have this := 1; this : Prop 0 = let x := 0; diff --git a/tests/lean/run/1026.lean b/tests/lean/run/1026.lean index d9bd5ab9782e..e47ba997bbb6 100644 --- a/tests/lean/run/1026.lean +++ b/tests/lean/run/1026.lean @@ -15,8 +15,8 @@ info: foo.eq_def (n : Nat) : foo n = if n = 0 then 0 else - let x := n - 1; - let_fun this := foo._proof_4; + have x := n - 1; + have this := foo._proof_4; foo x -/ #guard_msgs in diff --git a/tests/lean/run/2058.lean b/tests/lean/run/2058.lean index c86998069138..dc645cf234d8 100644 --- a/tests/lean/run/2058.lean +++ b/tests/lean/run/2058.lean @@ -39,7 +39,7 @@ def foo : IO Unit := do -- specializes to 0 on error /-- info: def foo : IO Unit := -let x := PUnit.unit.{0}; +have x := PUnit.unit.{0}; pure.{0, 0} Unit.unit -/ #guard_msgs in set_option pp.universes true in #print foo diff --git a/tests/lean/run/821.lean b/tests/lean/run/821.lean index af127115b259..ea9b92034fd3 100644 --- a/tests/lean/run/821.lean +++ b/tests/lean/run/821.lean @@ -1,7 +1,7 @@ macro "foo" : term => `(have := 1; this) /-- -info: let_fun this := 1; +info: have this := 1; this : Nat -/ #guard_msgs in diff --git a/tests/lean/run/decideNative.lean b/tests/lean/run/decideNative.lean index 86023ab3324a..a9d89f45e2fc 100644 --- a/tests/lean/run/decideNative.lean +++ b/tests/lean/run/decideNative.lean @@ -82,7 +82,7 @@ error: tactic 'native_decide' evaluated that the proposition False is false --- -info: let_fun this := sorry; +info: have this := sorry; this : False -/ #guard_msgs in #check show False by native_decide diff --git a/tests/lean/run/delabApp.lean b/tests/lean/run/delabApp.lean index 4d640dedf054..8b676f4b5f99 100644 --- a/tests/lean/run/delabApp.lean +++ b/tests/lean/run/delabApp.lean @@ -43,7 +43,7 @@ structure Foo where Overapplied `letFun` -/ /-- -info: (let_fun f := id; +info: (have f := id; f) 1 : Nat -/ diff --git a/tests/lean/run/delabMatch.lean b/tests/lean/run/delabMatch.lean index 56902fbce76e..a03f180b4d78 100644 --- a/tests/lean/run/delabMatch.lean +++ b/tests/lean/run/delabMatch.lean @@ -32,7 +32,7 @@ fun {α} x => -/ /-- info: fun x => - let_fun this := + have this := match h : ↑x + 1 with | 0 => Nat.noConfusion h | n.succ => Exists.intro n (Nat.succ.inj h); @@ -50,7 +50,7 @@ Shadowing with `h :` annotations -/ /-- info: fun h => - let_fun this := + have this := match h_1 : ↑h + 1 with | 0 => Nat.noConfusion h_1 | n.succ => Exists.intro n (Nat.succ.inj h_1); @@ -69,7 +69,7 @@ Even more shadowing with `h :` annotations set_option linter.unusedVariables false in /-- info: fun h => - let_fun this := + have this := match h_1 : ↑h + 1 with | 0 => Nat.noConfusion h_1 | h_2.succ => Exists.intro h_2 (Nat.succ.inj h_1); diff --git a/tests/lean/run/extract_lets.lean b/tests/lean/run/extract_lets.lean index 1a815edf9e5f..0e9d54ca5e37 100644 --- a/tests/lean/run/extract_lets.lean +++ b/tests/lean/run/extract_lets.lean @@ -287,9 +287,9 @@ example : let x := 2; let y := 3; let z := 3; x = 2 + y - z := by -/ /-- trace: a✝ : Nat := 2 -⊢ (let_fun x := a✝; +⊢ (have x := a✝; x) = - let_fun y := a✝ + 0; + have y := a✝ + 0; y -/ #guard_msgs in @@ -655,7 +655,7 @@ Same example, but testing `letFun`. -/ /-- trace: ⊢ ∀ (n : Nat), - let_fun x := n; + have x := n; n = x -/ #guard_msgs in @@ -672,7 +672,7 @@ and whether the second is a `have` or `let`. -/ /-- trace: ⊢ ∀ (n : Nat), - let_fun x := n; + have x := n; x = x -/ #guard_msgs in @@ -724,8 +724,8 @@ Without merging -/ /-- trace: ⊢ ∀ (n : Nat), - let_fun x := n; - let_fun x' := n; + have x := n; + have x' := n; x = x' -/ #guard_msgs in diff --git a/tests/lean/run/funind_tests.lean b/tests/lean/run/funind_tests.lean index 30c0438744e5..d8d3d2cf0b15 100644 --- a/tests/lean/run/funind_tests.lean +++ b/tests/lean/run/funind_tests.lean @@ -76,9 +76,9 @@ def have_tailrec : Nat → Nat have h2 : n < n+1 := Nat.lt_succ_self n have_tailrec n termination_by n => n - +-- TODO(kmill) n < n + 1 parameter is gone /-- -info: have_tailrec.induct (motive : Nat → Prop) (case1 : motive 0) (case2 : ∀ (n : Nat), n < n + 1 → motive n → motive n.succ) +info: have_tailrec.induct (motive : Nat → Prop) (case1 : motive 0) (case2 : ∀ (n : Nat), motive n → motive n.succ) (a✝ : Nat) : motive a✝ -/ #guard_msgs in @@ -542,10 +542,10 @@ def bar {α} (x : α) : List α → Nat have this := bar x ys this termination_by xs => xs - +-- TODO(kmill) the `(this : Nat)` parameter is gone /-- info: LetFun.bar.induct.{u_1} {α : Type u_1} (x : α) (motive : List α → Prop) (case1 : motive []) - (case2 : ∀ (_y : α) (ys : List α) (this : Nat), motive ys → motive (_y :: ys)) (a✝ : List α) : motive a✝ + (case2 : ∀ (_y : α) (ys : List α), motive ys → motive (_y :: ys)) (a✝ : List α) : motive a✝ -/ #guard_msgs in #check bar.induct diff --git a/tests/lean/run/funind_unfolding.lean b/tests/lean/run/funind_unfolding.lean index 0a35764e4676..013185498f66 100644 --- a/tests/lean/run/funind_unfolding.lean +++ b/tests/lean/run/funind_unfolding.lean @@ -426,17 +426,19 @@ def withHave (n : Nat): Bool := termination_by n decreasing_by exact testSorry +-- TODO(kmill) no more 0 < n and 0 < 42 parameters /-- -info: withHave.induct_unfolding (motive : Nat → Bool → Prop) (case1 : 0 < 42 → motive 42 true) - (case2 : ∀ (x : Nat), 0 < x → ¬x = 42 → motive (x - 1) (withHave (x - 1)) → motive x (withHave (x - 1))) (n : Nat) : +info: withHave.induct_unfolding (motive : Nat → Bool → Prop) (case1 : motive 42 true) + (case2 : ∀ (x : Nat), ¬x = 42 → motive (x - 1) (withHave (x - 1)) → motive x (withHave (x - 1))) (n : Nat) : motive n (withHave n) -/ #guard_msgs(pass trace, all) in #check withHave.induct_unfolding +-- -- TODO(kmill) no more 0 < n and 0 < 42 parameters /-- -info: withHave.fun_cases_unfolding (motive : Nat → Bool → Prop) (case1 : 0 < 42 → motive 42 true) - (case2 : ∀ (n : Nat), 0 < n → ¬n = 42 → motive n (withHave (n - 1))) (n : Nat) : motive n (withHave n) +info: withHave.fun_cases_unfolding (motive : Nat → Bool → Prop) (case1 : motive 42 true) + (case2 : ∀ (n : Nat), ¬n = 42 → motive n (withHave (n - 1))) (n : Nat) : motive n (withHave n) -/ #guard_msgs(pass trace, all) in #check withHave.fun_cases_unfolding diff --git a/tests/lean/run/heapSort.lean b/tests/lean/run/heapSort.lean index b053ac9780b0..d7ed55aaa385 100644 --- a/tests/lean/run/heapSort.lean +++ b/tests/lean/run/heapSort.lean @@ -184,7 +184,7 @@ info: Array.heapSort.loop.eq_1 fun a b => match e : a.max with | none => out | some x => - let_fun this := ⋯; + have this := ⋯; Array.heapSort.loop (fun a b => decide (a < b)) a.popMax (out.push x) -/ #guard_msgs in diff --git a/tests/lean/run/issue5767.lean b/tests/lean/run/issue5767.lean index c7ced93d71f5..b46fd68ab026 100644 --- a/tests/lean/run/issue5767.lean +++ b/tests/lean/run/issue5767.lean @@ -20,12 +20,13 @@ def go1 (ss : Int) (st0 : St) : Bool := termination_by st0 decreasing_by sorry +-- TODO(kmill) lost `P st1` parameters in case1 /-- info: go1.induct (ss : Int) (motive : St → Prop) (case1 : ∀ (x : St), let st1 := { m := x.m, map := x.map.insert }; - ∀ (val : Unit), st1.map.get? ss = some val → P st1 → P st1 → motive st1 → motive x) + ∀ (val : Unit), st1.map.get? ss = some val → motive st1 → motive x) (case2 : ∀ (x : St), let st1 := { m := x.m, map := x.map.insert }; diff --git a/tests/lean/run/issue7550.lean b/tests/lean/run/issue7550.lean index 0fdafaf907cd..38c75bc4af0a 100644 --- a/tests/lean/run/issue7550.lean +++ b/tests/lean/run/issue7550.lean @@ -27,13 +27,13 @@ termination_by structural fuel -- let some x ← Lean.Meta.getFunIndInfo? false ``Bug.divCore | return -- Lean.logInfo m!"{repr x}" +-- TODO(kmill) `this✝ : x✝ - y < x✝` hypothesis no longer present /-- error: tactic 'fail' failed case case1 x y fuel x✝ fuel✝ : Nat hfuel✝ : x✝ < fuel✝.succ h✝ : 0 < y ∧ y ≤ x✝ -this✝ : x✝ - y < x✝ ih1✝ : Bug.divCore (x✝ - y) y fuel✝ ⋯ = 42 ⊢ Bug.divCore (x✝ - y) y fuel✝ ⋯ + 1 = 42 @@ -48,13 +48,13 @@ protected theorem divCore_eq_div : Bug.divCore x y fuel h = 42 := by fun_induction Bug.divCore fail +-- TODO(kmill) `this✝ : x✝ - y < x✝` hypotheses no longer present /-- error: tactic 'fail' failed case case1 x y fuel x✝ fuel✝ : Nat hfuel✝ : x✝ < fuel✝.succ h✝ : 0 < y ∧ y ≤ x✝ -this✝ : x✝ - y < x✝ ih1✝ : Bug.divCore (x✝ - y) y fuel✝ ⋯ = 42 ⊢ Bug.divCore (x✝ - y) y fuel✝ ⋯ + 1 = 42 diff --git a/tests/lean/run/lift_lets.lean b/tests/lean/run/lift_lets.lean index 00849d10b1c0..126c33be24c4 100644 --- a/tests/lean/run/lift_lets.lean +++ b/tests/lean/run/lift_lets.lean @@ -139,7 +139,7 @@ Lifting `letFun` under a binder, dependency. -/ /-- trace: ⊢ ∀ (n : Nat), - let_fun x := n; + have x := n; n = x -/ #guard_msgs in @@ -153,7 +153,7 @@ example : ∀ n : Nat, n = (have x := n; x) := by Lifting `letFun` under a binder, no dependency. -/ /-- -trace: ⊢ let_fun x := 0; +trace: ⊢ have x := 0; ∀ (n : Nat), n = n + x -/ #guard_msgs in @@ -212,7 +212,7 @@ and whether the second is a `have` or `let`. -/ /-- trace: ⊢ ∀ (n : Nat), - let_fun x := n; + have x := n; x = x -/ #guard_msgs in diff --git a/tests/lean/run/prefixTableStep.lean b/tests/lean/run/prefixTableStep.lean index 27daa19025f4..8959ad202dc8 100644 --- a/tests/lean/run/prefixTableStep.lean +++ b/tests/lean/run/prefixTableStep.lean @@ -27,13 +27,13 @@ info: PrefixTable.step.eq_def.{u_1} {α : Type u_1} [BEq α] (t : PrefixTable α t.step x kf = match kf with | ⟨k, hk⟩ => - let cont := fun x_1 => + have cont := fun x_1 => match k, hk with | 0, hk => ⟨0, ⋯⟩ | k.succ, hk => - let_fun h2 := ⋯; + have h2 := ⋯; let k' := t.toArray[k].snd; - let_fun hk' := ⋯; + have hk' := ⋯; t.step x ⟨k', ⋯⟩; if hsz : k < t.size then if (x == t.toArray[k].fst) = true then ⟨k + 1, ⋯⟩ else cont () else cont () -/ diff --git a/tests/lean/run/rflTacticErrors.lean b/tests/lean/run/rflTacticErrors.lean index c9cfb84725ff..56f2a14b70e0 100644 --- a/tests/lean/run/rflTacticErrors.lean +++ b/tests/lean/run/rflTacticErrors.lean @@ -6,6 +6,8 @@ This file tests the `rfl` tactic: * Effect of `with_reducible` -/ +set_option pp.mvars.levels false + -- First, let's see what `rfl` does: /-- @@ -203,7 +205,7 @@ with the goal @HEq Bool true'' Bool true Note: The full type of 'HEq.refl' is - ∀ {α : Sort ?u.601} (a : α), HEq a a + ∀ {α : Sort _} (a : α), HEq a a ⊢ HEq true'' true -/ #guard_msgs in @@ -271,7 +273,7 @@ with the goal HEq false true Note: The full type of 'HEq.refl' is - ∀ {α : Sort ?u.653} (a : α), HEq a a + ∀ {α : Sort _} (a : α), HEq a a ⊢ HEq false true -/ #guard_msgs in @@ -338,7 +340,7 @@ with the goal HEq false true Note: The full type of 'HEq.refl' is - ∀ {α : Sort ?u.705} (a : α), HEq a a + ∀ {α : Sort _} (a : α), HEq a a ⊢ HEq false true -/ #guard_msgs in @@ -398,7 +400,7 @@ with the goal HEq true 1 Note: The full type of 'HEq.refl' is - ∀ {α : Sort ?u.774} (a : α), HEq a a + ∀ {α : Sort _} (a : α), HEq a a ⊢ HEq true 1 -/ #guard_msgs in @@ -410,7 +412,7 @@ with the goal HEq true 1 Note: The full type of 'HEq.refl' is - ∀ {α : Sort ?u.815} (a : α), HEq a a + ∀ {α : Sort _} (a : α), HEq a a ⊢ HEq true 1 -/ #guard_msgs in diff --git a/tests/lean/run/structInst3.lean b/tests/lean/run/structInst3.lean index 2dacbf5c0b6b..91d4c62f136a 100644 --- a/tests/lean/run/structInst3.lean +++ b/tests/lean/run/structInst3.lean @@ -71,7 +71,7 @@ info: let __src := c2; #guard_msgs in #check { c2 with x.1 := 3 } /-- -info: let_fun this := +info: have this := let __src := c2.toB; { toB := __src, z := __src.g __src.x __src.y : C (Nat × Nat) }; this : C (Nat × Nat) diff --git a/tests/lean/run/structInstExtraEta.lean b/tests/lean/run/structInstExtraEta.lean index 4bdfc01fd2c2..d9377deda0a6 100644 --- a/tests/lean/run/structInstExtraEta.lean +++ b/tests/lean/run/structInstExtraEta.lean @@ -20,7 +20,7 @@ def a : A := ⟨ 0 ⟩ def b : B := { a with } /-- info: def b : B := -let __src := a; +have __src := a; { toA := __src } -/ #guard_msgs in #print b @@ -28,7 +28,7 @@ let __src := a; def c : C := { a with } /-- info: def c : C := -let __src := a; +have __src := a; { toB := { toA := __src } } -/ #guard_msgs in #print c @@ -36,7 +36,7 @@ let __src := a; def d : D := { c with } /-- info: def d : D := -let __src := c; +have __src := c; { toB := __src.toB } -/ #guard_msgs in #print d diff --git a/tests/lean/run/wf_preprocess.lean b/tests/lean/run/wf_preprocess.lean index f9c34a993990..91b1fbb63e82 100644 --- a/tests/lean/run/wf_preprocess.lean +++ b/tests/lean/run/wf_preprocess.lean @@ -185,23 +185,23 @@ decreasing_by info: equations: theorem MTree.size.eq_1.{u_1} : ∀ {α : Type u_1} (t : MTree α), t.size = - (let s := 1; + (have s := 1; do let r ← forIn t.cs s fun css r => - let s := r; + have s := r; do let r ← forIn css s fun c r => - let s := r; - let s := s + c.size; + have s := r; + have s := s + c.size; do pure PUnit.unit pure (ForInStep.yield s) - let s : Nat := r + have s : Nat := r pure PUnit.unit pure (ForInStep.yield s) - let s : Nat := r + have s : Nat := r pure s).run -/ #guard_msgs in diff --git a/tests/lean/run/zetaUnused.lean b/tests/lean/run/zetaUnused.lean index cdf0dca1e65b..1779bda5dcc7 100644 --- a/tests/lean/run/zetaUnused.lean +++ b/tests/lean/run/zetaUnused.lean @@ -2,7 +2,7 @@ /-- trace: b : Bool ⊢ if b = true then - let_fun unused := (); + have unused := (); True else False --- @@ -25,7 +25,7 @@ example (b : Bool) : if b then have unused := (); True else False := by /-- trace: b : Bool ⊢ b = true ∧ - let_fun unused := (); + have unused := (); True --- warning: declaration uses 'sorry' @@ -68,7 +68,7 @@ example (b : Bool) : if b then have unused := (); True else False := by trace: case isTrue b : Bool h✝ : b = true -⊢ let_fun unused := (); +⊢ have unused := (); True --- warning: declaration uses 'sorry' diff --git a/tests/lean/simp_trace.lean.expected.out b/tests/lean/simp_trace.lean.expected.out index d8552c4d0d2c..fab1a45e91c0 100644 --- a/tests/lean/simp_trace.lean.expected.out +++ b/tests/lean/simp_trace.lean.expected.out @@ -25,7 +25,7 @@ Try this: simp only [foo] [Meta.Tactic.simp.rewrite] unfold foo, foo ==> 10 [Meta.Tactic.simp.rewrite] eq_self:1000: 10 + x = 10 + x ==> True Try this: simp only [g, pure] -[Meta.Tactic.simp.rewrite] unfold g, g x ==> (let x := x; +[Meta.Tactic.simp.rewrite] unfold g, g x ==> (have x := x; pure x).run Try this: simp (config := { unfoldPartialApp := true }) only [f1, modify, modifyGet, MonadStateOf.modifyGet, StateT.modifyGet, pure, f2, bind, StateT.bind, get, getThe, MonadStateOf.get, StateT.get, set, StateT.set] diff --git a/tests/lean/unusedLet.lean.expected.out b/tests/lean/unusedLet.lean.expected.out index 8966ef27fc23..977006f25ebb 100644 --- a/tests/lean/unusedLet.lean.expected.out +++ b/tests/lean/unusedLet.lean.expected.out @@ -1,5 +1,5 @@ def pro : Bool := -let x := 42; +have x := 42; false def f : Nat → Nat := fun x => @@ -7,6 +7,6 @@ fun x => (match (motive := (x : Nat) → Nat.below x → Nat) x with | 0 => fun x => 1 | n.succ => fun x => - let y := 42; + have y := 42; 2 * x.1) f diff --git a/tests/lean/wfrecUnusedLet.lean.expected.out b/tests/lean/wfrecUnusedLet.lean.expected.out index df3e7ff0de46..cc81bcb1ecae 100644 --- a/tests/lean/wfrecUnusedLet.lean.expected.out +++ b/tests/lean/wfrecUnusedLet.lean.expected.out @@ -2,5 +2,5 @@ f._proof_1.fix fun n a => if h : n = 0 then 1 else - let y := 42; + have y := 42; 2 * a (n - 1) ⋯