Compare commits

...

9 Commits

Author SHA1 Message Date
Lean stage0 autoupdater
cc9a217df8 chore: update stage0 2026-04-19 21:43:33 +00:00
Sebastian Graf
81f559b0e4 chore: remove repeat/while macro_rules bootstrap from Init.While (#13479)
This PR removes the transitional `macro_rules` for `repeat`, `while`,
and `repeat ... until` from `Init.While`. After the latest stage0
update, the `@[builtin_macro]` and `@[builtin_doElem_elab]` definitions
in `Lean.Elab.BuiltinDo.Repeat` are picked up directly, so the bootstrap
duplicates in `Init.While` are no longer needed. `Init.While` now only
provides the `Loop` type and its `ForIn` instance.

This PR also adjusts `repeat`'s `ControlInfo` to match `for ... in`: its
`numRegularExits` is now unconditionally `1` rather than `if info.breaks
then 1 else 0`. Reporting `0` when the body has no `break` causes
`inferControlInfoSeq` (in any enclosing sequence whose `ControlInfo` is
inferred — e.g. a surrounding `for`/`if`/`match`/`try` body) to stop
aggregating after the `repeat` and miss any `return`/`break`/`continue`
that follows. The corresponding elaborator then sees the actual control
flow disagree with the inferred info and throws errors like `Early
returning ... but the info said there is no early return`. The new test
in `tests/elab/newdo.lean` pins down the regression. See
[#13437](https://github.com/leanprover/lean4/pull/13437) for further
discussion.
2026-04-19 21:01:14 +00:00
Joachim Breitner
7cc3a4cc0b perf: use .local asyncMode for eqnOptionsExt (#13477)
This PR fixes a benchmark regression introduced in #13475:
`eqnOptionsExt`
was using `.async .asyncEnv` asyncMode, which accumulates state in the
`checked` environment and can block. Switching to `.local` — consistent
with the neighbouring `eqnsExt` and the other declaration caches in
`src/Lean/Meta` — restores performance (the
`build/profile/blocked (unaccounted) wall-clock` bench moves from +33%
back to baseline). `.local` is safe here because
`saveEqnAffectingOptions`
is only called during top-level `def` elaboration and downstream readers
see the imported state; modifications on non-main branches are merged
into the main branch on completion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:49:00 +00:00
Leonardo de Moura
e82cd9b62c fix: filter assigned metavariables before computing apply subgoal tags (#13476)
This PR refines how the `apply` tactic (and related tactics like
`rewrite`) name and tag the remaining subgoals. Assigned metavariables
are now filtered out *before* computing subgoal tags. As a consequence,
when only one unassigned subgoal remains, it inherits the tag of the
input goal instead of being given a fresh suffixed tag.

User-visible effect: proof states that previously displayed tags like
`case h`, `case a`, or `case upper.h` for a single remaining goal now
display the input goal's tag directly (e.g. no tag at all, or `case
upper`). This removes noise from `funext`, `rfl`-style, and
`induction`-alternative goals when the applied lemma introduces only one
non-assigned metavariable. Multi-goal applications are unaffected —
their subgoals continue to receive distinguishing suffixes.

This may affect users whose proofs rely on the previous tag names (for
example, `case h => ...` after `funext`). Such scripts need to be
updated to use the input goal's tag instead.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:31:49 +00:00
Joachim Breitner
1d2cfb47e7 feat: store eqn-affecting options at definition time instead of eager generation (#13475)
This PR replaces the eager equation realization that was triggered by
non-default values of equation-affecting options (like
`backward.eqns.nonrecursive`) with a `MapDeclarationExtension` that
stores non-default option values at definition time. These values are
then restored when equations are lazily realized, so the same equations
are produced regardless of when generation occurs.

Restoring the options is done via a new `withEqnOptions` helper in
`Lean.Meta.Eqns`. Because `realizeConst` overrides the caller's options
with the options saved in its `RealizationContext` — which are empty
for imported constants — the helper must also be applied inside the
`realizeConst` callbacks in `mkSimpleEqThm`, `mkEqns` (in
`Elab/PreDefinition/Eqns.lean`), `getConstUnfoldEqnFor?`, and
`Structural.mkUnfoldEq`. Without that, equation generation code that
reads eqn-affecting options inside the realize callback would see the
caller-independent defaults rather than the values stored in
`eqnOptionsExt` — so the store-at-definition-time behavior would not
carry across module boundaries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:30:08 +00:00
Leonardo de Moura
439e6a85d3 fix: prune goals assigned by isDefEq in sym => mode (#13474)
This PR fixes a bug in `sym =>` interactive mode where goals whose
metavariable was assigned by `isDefEq` (e.g. via `apply Eq.refl`) were
not pruned. `pruneSolvedGoals` previously only filtered out goals
flagged as inconsistent, so an already-assigned goal would linger as an
unsolved goal. It now also removes goals whose metavariable is already
assigned.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:55:11 +00:00
Leonardo de Moura
2d38a70d1c fix: auto-introduce in sym => mode when goal closes during preprocessing (#13472)
This PR fixes a bug in `sym =>` interactive mode where satellite solvers
(`lia`, `ring`, `linarith`) would throw an internal error if their
automatic `intros + assertAll` preprocessing step already closed the
goal. Previously, `evalCheck` used `liftAction` which discarded the
closure result, so the subsequent `liftGoalM` call failed due to the
absence of a main goal. `liftAction` is now split so the caller can
distinguish the closed and subgoals cases and skip the solver body when
preprocessing already finished the job.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 08:32:49 +00:00
Sebastian Ullrich
80cbab1642 chore: don't fail on running build bench on built stage3 (#13467) 2026-04-18 22:07:21 +00:00
Leonardo de Moura
c0a53ffe97 chore: minor tweaks to Sym.simp test and benchmark (#13468)
This PR applies two minor tweaks:
- `tests/bench/sym/simp_1.lean`: share-common the proof term before
counting objects in `getProofSize`, so the reported size reflects the
shared representation.
- `tests/elab/sym_simp_3.lean`: use `>>` instead of `.andThen` when
composing `Sym.Simp` methods.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:11:30 +00:00
61 changed files with 161 additions and 87 deletions

View File

@@ -279,7 +279,8 @@ jobs:
"os": large ? "nscloud-ubuntu-24.04-amd64-8x16-with-cache" : "ubuntu-latest",
"enabled": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
// Done as part of test-bench
//"check-stage3": level >= 2,
"test": true,
// NOTE: `test-bench` currently seems to be broken on `ubuntu-latest`
"test-bench": large && level >= 2,
@@ -291,7 +292,8 @@ jobs:
"os": large ? "nscloud-ubuntu-24.04-amd64-8x16-with-cache" : "ubuntu-latest",
"enabled": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
// Done as part of test-bench
//"check-stage3": level >= 2,
"test": true,
"secondary": true,
// NOTE: `test-bench` currently seems to be broken on `ubuntu-latest`

View File

@@ -13,10 +13,11 @@ public section
namespace Lean
/-!
# `while` and `repeat` loop support
# `Loop` type backing `repeat`/`while`/`repeat ... until`
The parsers for `repeat`, `while`, and `repeat ... until` are
`@[builtin_doElem_parser]` definitions in `Lean.Parser.Do`.
The parsers and elaborators for `repeat`, `while`, and `repeat ... until` live in
`Lean.Parser.Do` and `Lean.Elab.BuiltinDo.Repeat`. This module only provides the
`Loop` type (and `ForIn` instance) that those elaborators expand to.
-/
inductive Loop where
@@ -33,23 +34,4 @@ partial def Loop.forIn {β : Type u} {m : Type u → Type v} [Monad m] (_ : Loop
instance [Monad m] : ForIn m Loop Unit where
forIn := Loop.forIn
-- The canonical parsers for `repeat`/`while`/`repeat ... until` live in `Lean.Parser.Do`
-- as `@[builtin_doElem_parser]` definitions. We register the expansion macros here so
-- they are available to `prelude` files in `Init`, which do not import `Lean.Elab`.
macro_rules
| `(doElem| repeat%$tk $seq) => `(doElem| for%$tk _ in Loop.mk do $seq)
macro_rules
| `(doElem| while%$tk $h : $cond do $seq) =>
`(doElem| repeat%$tk if $h:ident : $cond then $seq else break)
macro_rules
| `(doElem| while%$tk $cond do $seq) =>
`(doElem| repeat%$tk if $cond then $seq else break)
macro_rules
| `(doElem| repeat%$tk $seq until $cond) =>
`(doElem| repeat%$tk do $seq:doSeq; if $cond then break)
end Lean

View File

@@ -139,8 +139,14 @@ partial def ofElem (stx : TSyntax `doElem) : TermElabM ControlInfo := do
}
| `(doRepeat| repeat $bodySeq) =>
let info ofSeq bodySeq
-- Match `for ... in` and report `numRegularExits := 1` unconditionally. Reporting `0` when
-- the body has no `break` causes `ofSeq` (in any enclosing sequence whose `ControlInfo` is
-- inferred, e.g. a surrounding `for`/`if`/`match`/`try` body) to stop aggregating after this
-- element and miss any `return`/`break`/`continue` that follows. The corresponding elaborator
-- then sees the actual control flow disagree with the inferred info and throws errors like
-- "Early returning ... but the info said there is no early return". See #13437 for details.
return { info with
numRegularExits := if info.breaks then 1 else 0,
numRegularExits := 1,
continues := false,
breaks := false
}

View File

@@ -222,8 +222,8 @@ private def addNonRecAux (docCtx : LocalContext × LocalInstances) (preDef : Pre
if compile && shouldGenCodeFor preDef then
compileDecl decl
if applyAttrAfterCompilation then
saveEqnAffectingOptions preDef.declName
enableRealizationsForConst preDef.declName
generateEagerEqns preDef.declName
addPreDefDocs docCtx preDef
if applyAttrAfterCompilation then
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation

View File

@@ -28,7 +28,7 @@ def getConstUnfoldEqnFor? (declName : Name) : MetaM (Option Name) := do
trace[ReservedNameAction] "getConstUnfoldEqnFor? {declName} failed, no unfold theorem available"
return none
let name := mkEqLikeNameFor ( getEnv) declName eqUnfoldThmSuffix
realizeConst declName name do
realizeConst declName name <| withEqnOptions declName do
-- we have to call `getUnfoldEqnFor?` again to make `unfoldEqnName` available in this context
let some unfoldEqnName getUnfoldEqnFor? (nonRec := true) declName | unreachable!
let info getConstInfo unfoldEqnName

View File

@@ -367,7 +367,7 @@ def mkEqns (declName : Name) (declNames : Array Name) : MetaM (Array Name) := do
thmNames := thmNames.push name
-- determinism: `type` should be independent of the environment changes since `baseName` was
-- added
realizeConst declName name (doRealize name info type)
realizeConst declName name (withEqnOptions declName (doRealize name info type))
return thmNames
where
doRealize name info type := withOptions (tactic.hygienic.set · false) do

View File

@@ -69,8 +69,10 @@ def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
a.name = `instance_reducible || a.name = `implicit_reducible do
setIrreducibleAttribute preDef.declName
for preDef in preDefs do
saveEqnAffectingOptions preDef.declName
/-
`enableRealizationsForConst` must happen before `generateEagerEqns`
It must happen in reverse order so that constants realized as part of the first decl
have realizations for the other ones enabled
-/
@@ -78,7 +80,6 @@ def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
enableRealizationsForConst preDef.declName
for preDef in preDefs do
generateEagerEqns preDef.declName
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
end Lean.Elab.Mutual

View File

@@ -163,7 +163,7 @@ public def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (r
/-- Generate the "unfold" lemma for `declName`. -/
def mkUnfoldEq (declName : Name) (info : EqnInfo) : MetaM Name := do
let name := mkEqLikeNameFor ( getEnv) info.declName unfoldThmSuffix
realizeConst info.declNames[0]! name (doRealize name)
realizeConst info.declNames[0]! name (withEqnOptions declName (doRealize name))
return name
where
doRealize name := withOptions (tactic.hygienic.set · false) do

View File

@@ -208,11 +208,11 @@ def structuralRecursion
-/
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos fixedParamPerms
addSmartUnfoldingDef docCtx preDef recArgPos
for preDef in preDefs do
saveEqnAffectingOptions preDef.declName
for preDef in preDefs do
-- must happen in separate loop so realizations can see eqnInfos of all other preDefs
enableRealizationsForConst preDef.declName
-- must happen after `enableRealizationsForConst`
generateEagerEqns preDef.declName
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation

View File

@@ -497,14 +497,21 @@ def forEachVar (hs : Array Syntax) (tac : MVarId → FVarId → MetaM MVarId) :
/--
Searches for a metavariable `g` s.t. `tag` is its exact name.
If none then searches for a metavariable `g` s.t. `tag` is a suffix of its name.
If none, then it searches for a metavariable `g` s.t. `tag` is a prefix of its name. -/
If none, then it searches for a metavariable `g` s.t. `tag` is a prefix of its name.
We erase macro scopes from the metavariable's user name before comparing, so that
user-written tags match even when a previous tactic left hygienic macro scopes at
the end of the tag (e.g. `e_a.yield._@._internal._hyg.0`, where `yield` is not the
literal last component of the name). Case tags written by the user are never
macro-scoped, so erasing scopes on the mvar side is sufficient.
-/
private def findTag? (mvarIds : List MVarId) (tag : Name) : TacticM (Option MVarId) := do
match ( mvarIds.findM? fun mvarId => return tag == ( mvarId.getDecl).userName) with
match ( mvarIds.findM? fun mvarId => return tag == ( mvarId.getDecl).userName.eraseMacroScopes) with
| some mvarId => return mvarId
| none =>
match ( mvarIds.findM? fun mvarId => return tag.isSuffixOf ( mvarId.getDecl).userName) with
match ( mvarIds.findM? fun mvarId => return tag.isSuffixOf ( mvarId.getDecl).userName.eraseMacroScopes) with
| some mvarId => return mvarId
| none => mvarIds.findM? fun mvarId => return tag.isPrefixOf ( mvarId.getDecl).userName
| none => mvarIds.findM? fun mvarId => return tag.isPrefixOf ( mvarId.getDecl).userName.eraseMacroScopes
private def getCaseGoals (tag : TSyntax ``binderIdent) : TacticM (MVarId × List MVarId) := do
let gs getUnsolvedGoals

View File

@@ -68,7 +68,10 @@ def setGoals (goals : List Goal) : GrindTacticM Unit :=
def pruneSolvedGoals : GrindTacticM Unit := do
let gs getGoals
let gs := gs.filter fun g => !g.inconsistent
let gs gs.filterM fun g => do
if g.inconsistent then return false
-- The metavariable may have been assigned by `isDefEq`
return !( g.mvarId.isAssigned)
setGoals gs
def getUnsolvedGoals : GrindTacticM (List Goal) := do
@@ -329,13 +332,19 @@ def liftGoalM (k : GoalM α) : GrindTacticM α := do
replaceMainGoal [goal]
return a
def liftAction (a : Action) : GrindTacticM Unit := do
inductive LiftActionCoreResult where
| closed | subgoals
def liftActionCore (a : Action) : GrindTacticM LiftActionCoreResult := do
let goal getMainGoal
let ka := fun _ => throwError "tactic is not applicable"
let kp := fun goal => return .stuck [goal]
match ( liftGrindM <| a goal ka kp) with
| .closed _ => replaceMainGoal []
| .stuck gs => replaceMainGoal gs
| .closed _ => replaceMainGoal []; return .closed
| .stuck gs => replaceMainGoal gs; return .subgoals
def liftAction (a : Action) : GrindTacticM Unit := do
discard <| liftActionCore a
def done : GrindTacticM Unit := do
pruneSolvedGoals

View File

@@ -111,7 +111,9 @@ def evalCheck (tacticName : Name) (k : GoalM Bool)
This matches the behavior of these tactics in default tactic mode
where `lia` can close `x > 1 → x + y + z > 0` directly. -/
if ( read).sym then
liftAction <| Action.intros 0 >> Action.assertAll
match ( liftActionCore <| Action.intros 0 >> Action.assertAll) with
| .closed => return () -- closed the goal
| .subgoals => pure () -- continue
let recover := ( read).recover
liftGoalM do
let progress k

View File

@@ -37,12 +37,17 @@ register_builtin_option backward.eqns.deepRecursiveSplit : Bool := {
These options affect the generation of equational theorems in a significant way. For these, their
value at definition time, not realization time, should matter.
This is implemented by
* eagerly realizing the equations when they are set to a non-default value
* when realizing them lazily, reset the options to their default
This is implemented by storing their values at definition time (when non-default) in an environment
extension, and restoring them when the equations are lazily realized.
-/
def eqnAffectingOptions : Array (Lean.Option Bool) := #[backward.eqns.nonrecursive, backward.eqns.deepRecursiveSplit]
/-- Environment extension that stores the values of `eqnAffectingOptions` at definition time,
keyed by declaration name. Only populated when at least one option has a non-default value.
Stores an association list of (option name, value) pairs for options that differ from defaults. -/
builtin_initialize eqnOptionsExt : MapDeclarationExtension (Array (Name × DataValue))
mkMapDeclarationExtension (asyncMode := .local)
def eqnThmSuffixBase := "eq"
def eqnThmSuffixBasePrefix := eqnThmSuffixBase ++ "_"
def eqn1ThmSuffix := eqnThmSuffixBasePrefix ++ "1"
@@ -153,12 +158,30 @@ structure EqnsExtState where
builtin_initialize eqnsExt : EnvExtension EqnsExtState
registerEnvExtension (pure {}) (asyncMode := .local)
/--
Runs `act` with the equation-affecting options restored to the values stored for `declName`
at definition time (or reset to their defaults if none were stored). Use this inside
`realizeConst` callbacks, which otherwise see the caller-independent `ctx.opts` rather than
the outer `getEqnsFor?` context. -/
def withEqnOptions (declName : Name) (act : MetaM α) : MetaM α := do
let env getEnv
let setOpts : Options Options :=
if let some values := eqnOptionsExt.find? env declName then
fun os => Id.run do
let mut os := eqnAffectingOptions.foldl (fun os o => o.set os o.defValue) os
for (name, v) in values do
os := os.insert name v
return os
else
fun os => eqnAffectingOptions.foldl (fun os o => o.set os o.defValue) os
withOptions setOpts act
/--
Simple equation theorem for nonrecursive definitions.
-/
def mkSimpleEqThm (declName : Name) (name : Name) : MetaM (Option Name) := do
if let some (.defnInfo info) := ( getEnv).find? declName then
realizeConst declName name (doRealize name info)
realizeConst declName name (withEqnOptions declName (doRealize name info))
return some name
else
return none
@@ -229,19 +252,22 @@ private def getEqnsFor?Core (declName : Name) : MetaM (Option (Array Name)) := w
Returns equation theorems for the given declaration.
-/
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := withLCtx {} {} do
-- This is the entry point for lazy equation generation. Ignore the current value
-- of the options, and revert to the default.
withOptions (eqnAffectingOptions.foldl fun os o => o.set os o.defValue) do
withEqnOptions declName do
getEqnsFor?Core declName
/--
If any equation theorem affecting option is not the default value, create the equations now.
If any equation theorem affecting option is not the default value, store the option values
for later use during lazy equation generation.
-/
def generateEagerEqns (declName : Name) : MetaM Unit := do
def saveEqnAffectingOptions (declName : Name) : MetaM Unit := do
let opts getOptions
if eqnAffectingOptions.any fun o => o.get opts != o.defValue then
trace[Elab.definition.eqns] "generating eager equations for {declName}"
let _ getEqnsFor?Core declName
let mut nonDefaults : Array (Name × DataValue) := #[]
for o in eqnAffectingOptions do
if o.get opts != o.defValue then
nonDefaults := nonDefaults.push (o.name, KVMap.Value.toDataValue (o.get opts))
unless nonDefaults.isEmpty do
trace[Elab.definition.eqns] "saving equation-affecting options for {declName}"
modifyEnv (eqnOptionsExt.insert · declName nonDefaults)
@[expose] def GetUnfoldEqnFn := Name MetaM (Option Name)

View File

@@ -128,7 +128,6 @@ def postprocessAppMVars (tacticName : Name) (mvarId : MVarId) (newMVars : Array
(synthAssignedInstances := true) (allowSynthFailures := false) : MetaM Unit := do
synthAppInstances tacticName mvarId newMVars binderInfos synthAssignedInstances allowSynthFailures
-- TODO: default and auto params
appendParentTag mvarId newMVars binderInfos
private def dependsOnOthers (mvar : Expr) (otherMVars : Array Expr) : MetaM Bool :=
otherMVars.anyM fun otherMVar => do
@@ -223,6 +222,7 @@ def _root_.Lean.MVarId.apply (mvarId : MVarId) (e : Expr) (cfg : ApplyConfig :=
let e instantiateMVars e
mvarId.assign (mkAppN e newMVars)
let newMVars newMVars.filterM fun mvar => not <$> mvar.mvarId!.isAssigned
appendParentTag mvarId newMVars binderInfos
let otherMVarIds getMVarsNoDelayed e
let newMVarIds reorderGoals newMVars cfg.newGoals
let otherMVarIds := otherMVarIds.filter fun mvarId => !newMVarIds.contains mvarId

View File

@@ -82,6 +82,7 @@ def _root_.Lean.MVarId.rewrite (mvarId : MVarId) (e : Expr) (heq : Expr)
postprocessAppMVars `rewrite mvarId newMVars binderInfos
(synthAssignedInstances := !tactic.skipAssignedInstances.get ( getOptions))
let newMVarIds newMVars.map Expr.mvarId! |>.filterM fun mvarId => not <$> mvarId.isAssigned
appendParentTag mvarId newMVars binderInfos
let otherMVarIds getMVarsNoDelayed heqIn
let otherMVarIds := otherMVarIds.filter (!newMVarIds.contains ·)
let newMVarIds := newMVarIds ++ otherMVarIds

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -23,6 +23,8 @@ echo ">"
echo "> Building $STAGE_NEXT..."
echo ">"
make -C "$BUILD_NEXT" clean-stdlib
LAKE_OVERRIDE_LEAN=true LEAN="$(realpath fake_root/bin/lean)" \
WRAPPER_PREFIX="$(realpath fake_root)" WRAPPER_OUT="$OUT" \
lakeprof record -- \

View File

@@ -10,7 +10,9 @@ namespace SimpBench
def getProofSize (r : Sym.Simp.Result) : MetaM Nat :=
match r with
| .rfl _ _ => return 0
| .step _ p _ _ => p.numObjs
| .step _ p _ _ =>
let p := ShareCommon.shareCommon' p
p.numObjs
def checkWithKernel (r : Sym.Simp.Result) : MetaM Float := do
match r with

View File

@@ -1,4 +1,3 @@
case h
α : Type ?u
inst✝ : DecidableEq α
i : α

View File

@@ -2,8 +2,7 @@
2 * a
/--
trace: case h
x : Nat
trace: x : Nat
⊢ 2 * x = x + x
-/
#guard_msgs in
@@ -19,8 +18,7 @@ by
| a => 2 * a
/--
trace: case h
x : Nat
trace: x : Nat
⊢ 2 * x = x + x
-/
#guard_msgs in

View File

@@ -0,0 +1,18 @@
/-!
Regression test: `case <ctor>` must keep working after `congr; ext _; cases _`
even though the subgoal tags produced by `apply`-family tactics now inherit
the parent tag (without appending the binder name) when only one subgoal is
left. The case-tag matcher erases macro scopes so that `case yield` still
matches tags like `e_a.yield._@._internal._hyg.0`.
-/
example {δ : Type} {m : Type Type} [Monad m] [LawfulMonad m]
(x : m (ForInStep δ))
(f g : ForInStep δ m (ForInStep δ))
(h : a, f a = g a) :
(x >>= f) = (x >>= g) := by
congr
ext step
cases step
case yield => apply h
case done => apply h

View File

@@ -1,5 +1,4 @@
consumePPHint.lean:8:8-8:14: warning: declaration uses `sorry`
case a
⊢ q
(have x := 0;
x + 1)

View File

@@ -10,7 +10,6 @@ y : Nat
| (fun x => x + y = 0) = fun x => False
y : Nat
| fun x => x + y = 0
case h
y x : Nat
| y + x = 0
y : Nat

View File

@@ -10,7 +10,7 @@ axiom elimEx (motive : Nat → Nat → Sort u) (x y : Nat)
error: Invalid alternative name `lower2`: Expected `lower`
---
error: unsolved goals
case upper.h
case upper
q d : Nat
⊢ q + d.succ > q
---
@@ -62,7 +62,7 @@ theorem invalidWildCard (p: Nat) : p ≤ q p > q := by
error: Invalid alternative name `lower2`: There are no unhandled alternatives
---
error: unsolved goals
case lower.h
case lower
p delta✝ : Nat
⊢ p > p + delta✝.succ
-/

View File

@@ -100,7 +100,6 @@ x y : Nat
h : x = y
⊢ y = x
-----
case h
x y : Nat
h : x = y
⊢ x = y

View File

@@ -489,4 +489,18 @@ example : IO Bool := do
for (x, y) in ([] : List (Nat × Nat)) do count := count + 1
return Float.abs count > 0
-- A `repeat` (without `break`) followed by an early `return` inside an enclosing `for`. The for
-- elaborator computes the loop body's `ControlInfo` via `inferControlInfoSeq`, which stops
-- aggregating once it encounters an element with `numRegularExits := 0`. If `repeat` reported
-- `numRegularExits := 0` for break-less bodies, the trailing `return 2` would be skipped during
-- inference and the for elaborator would later throw "Early returning ... but the info said there
-- is no early return". This test pins down that `repeat` reports `numRegularExits := 1`, matching
-- `for ... in`.
example : IO Nat := do
for _ in [1, 2] do
repeat
pure ()
return 2
return 1
end Repros

View File

@@ -505,28 +505,24 @@ is not definitionally equal to the right-hand side
example : S true false := by with_reducible apply_rfl -- Error
/--
error: unsolved goals
case a
⊢ true = true
-/
#guard_msgs in
example : S true true := by apply_rfl -- Error (left-over goal)
/--
error: unsolved goals
case a
⊢ true = true
-/
#guard_msgs in
example : S true true := by with_reducible apply_rfl -- Error (left-over goal)
/--
error: unsolved goals
case a
⊢ false = true
-/
#guard_msgs in
example : S false false := by apply_rfl -- Error (left-over goal)
/--
error: unsolved goals
case a
⊢ false = true
-/
#guard_msgs in

View File

@@ -0,0 +1,21 @@
example (p q : Prop) : p q p q := by
sym =>
intro hp hq
apply And.intro
apply hp
apply hq
register_sym_simp chainSimp where
post := ground >> rewrite [Nat.add_zero, Nat.zero_add]
example (x y : Nat) (h : x y) : (1 - 1) + x y + (1 + 0) := by
sym =>
simp chainSimp
-- In the following tactic the goal is closed while preprocessing the target
lia
example : x, x = a := by
sym =>
apply Exists.intro
apply Eq.refl

View File

@@ -5,7 +5,7 @@ elab "sym_simp" "[" declNames:ident,* "]" : tactic => do
let rewrite Sym.mkSimprocFor ( declNames.getElems.mapM fun s => realizeGlobalConstNoOverload s.raw) Sym.Simp.dischargeSimpSelf
let methods : Sym.Simp.Methods := {
pre := Sym.Simp.simpControl
post := Sym.Simp.evalGround.andThen rewrite
post := Sym.Simp.evalGround >> rewrite
}
liftMetaTactic1 fun mvarId => Sym.SymM.run do
let mvarId Sym.preprocessMVar mvarId

View File

@@ -9,14 +9,13 @@ x y : Nat
case x
x y : Nat
⊢ x + y = y.add x
case a
a b : Nat
| foo (0 + a) (b + 0)
case a.x
case x
a b : Nat
| 0 + a
case a.y
case y
a b : Nat
| b + 0
a b : Nat
@@ -55,13 +54,10 @@ x y : Nat
⊢ f x (x.add y) y = y + x
x y : Nat
| x + y
case h.h
a b : Nat
| 0 + a + b
case h.h
a b : Nat
| a + b
case h.h
a b : Nat
| 0 + a + b
p : Nat → Prop
@@ -83,7 +79,6 @@ x : Nat
p : Prop
x : Nat
⊢ (True → p) → p
case h
x : Nat
| 0 + x
p : Prop

View File

@@ -19,7 +19,6 @@ n m : Nat
n m : Nat
⊢ Prod.Lex (fun a₁ a₂ => a₁ < a₂) (fun a₁ a₂ => a₁ < a₂) (dec1 n, 100) (n, m)
decreasing_by.lean:85:0-85:22: error: unsolved goals
case a
n m : Nat
⊢ Prod.Lex (fun a₁ a₂ => a₁ < a₂) (fun a₁ a₂ => a₁ < a₂) (n, dec2 m) (n, m)

View File

@@ -1,9 +1,9 @@
inductionErrors.lean:11:12-11:27: error: unsolved goals
case lower.h
case lower
p d : Nat
⊢ p ≤ p + d.succ
inductionErrors.lean:12:12-12:27: error: unsolved goals
case upper.h
case upper
q d : Nat
⊢ q + d.succ > q
inductionErrors.lean:16:19-16:26: error(lean.unknownIdentifier): Unknown identifier `elimEx2`

View File

@@ -1,5 +1,4 @@
mutwf1.lean:21:2-21:6: error: unsolved goals
case h.a
n : Nat
h : n ≠ 0
⊢ n.sub 0 ≠ 0
@@ -7,6 +6,5 @@ mutwf1.lean:31:22-31:29: error: failed to prove termination, possible solutions:
- Use `have`-expressions to prove the remaining goals
- Use `termination_by` to specify a different well-founded relation
- Use `decreasing_by` to specify your own tactic for discharging this kind of goal
case h
n : Nat
⊢ n + 1 < n

View File

@@ -4,12 +4,11 @@
"goals": ["case right\n⊢ True"]}
{"textDocument": {"uri": "file:///6594.lean"},
"position": {"line": 9, "character": 2}}
{"rendered": "```lean\ncase a\n⊢ True ∧ True\n```",
"goals": ["case a\n⊢ True ∧ True"]}
{"rendered": "```lean\n⊢ True ∧ True\n```", "goals": ["⊢ True ∧ True"]}
{"textDocument": {"uri": "file:///6594.lean"},
"position": {"line": 13, "character": 2}}
{"rendered": "```lean\ncase a.right\n⊢ True\n```",
"goals": ["case a.right\n⊢ True"]}
{"rendered": "```lean\ncase right\n⊢ True\n```",
"goals": ["case right\n⊢ True"]}
{"textDocument": {"uri": "file:///6594.lean"},
"position": {"line": 20, "character": 2}}
{"rendered": "```lean\ncase right\n⊢ True\n```",