refactor: TerminationArgument → TerminationMeasure (#6727)

this PR aligns the terminology of the code with the one use in the
reference manual, as developed with and refined by @david-christiansen.
This commit is contained in:
Joachim Breitner 2025-01-23 11:41:38 +01:00 committed by GitHub
parent c073da20ce
commit 56733b953e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 215 additions and 215 deletions

View file

@ -590,9 +590,9 @@ This table should be read as follows:
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
Lean will print the termination argument it found if ``set_option showInferredTerminationBy true`` is set.
Lean will print the termination measure it found if ``set_option showInferredTerminationBy true`` is set.
If Lean does not find the termination argument, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
If Lean does not find the termination measure, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
```
termination_by e
```
@ -672,7 +672,7 @@ def num_consts_lst : List Term → Nat
end
```
In a set of mutually recursive function, either all or no functions must have an explicit termination argument (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
In a set of mutually recursive function, either all or no functions must have an explicit termination measure (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
```
mutual

View file

@ -219,14 +219,14 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
m!"`nontermination_partialFixpointursive`, so this one cannot be either.")
/--
Elaborates the `TerminationHint` in the clique to `TerminationArguments`
Elaborates the `TerminationHint` in the clique to `TerminationMeasures`
-/
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationArgument)) := do
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationMeasure)) := do
preDefs.mapM fun preDef => do
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
let hints := preDef.termination
hints.terminationBy?.mapM
(TerminationArgument.elab preDef.declName preDef.type arity hints.extraParams ·)
(TerminationMeasure.elab preDef.declName preDef.type arity hints.extraParams ·)
def shouldUseStructural (preDefs : Array PreDefinition) : Bool :=
preDefs.any fun preDef =>
@ -279,18 +279,18 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
try
checkCodomainsLevel preDefs
checkTerminationByHints preDefs
let termArg?s ← elabTerminationByHints preDefs
let termMeasures?s ← elabTerminationByHints preDefs
if shouldUseStructural preDefs then
structuralRecursion preDefs termArg?s
structuralRecursion preDefs termMeasures?s
else if shouldUsepartialFixpoint preDefs then
partialFixpoint preDefs
else if shouldUseWF preDefs then
wfRecursion preDefs termArg?s
wfRecursion preDefs termMeasures?s
else
withRef (preDefs[0]!.ref) <| mapError
(orelseMergeErrors
(structuralRecursion preDefs termArg?s)
(wfRecursion preDefs termArg?s))
(structuralRecursion preDefs termMeasures?s)
(wfRecursion preDefs termMeasures?s))
(fun msg =>
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")

View file

@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.RecArgInfo
@ -56,7 +56,7 @@ private def hasBadParamDep? (ys : Array Expr) (indParams : Array Expr) : MetaM (
/--
Assemble the `RecArgInfo` for the `i`th parameter in the parameter list `xs`. This performs
various sanity checks on the argument (is it even an inductive type etc).
various sanity checks on the parameter (is it even of inductive type etc).
-/
def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) : MetaM RecArgInfo := do
if h : i < xs.size then
@ -112,17 +112,17 @@ considered.
The `xs` are the fixed parameters, `value` the body with the fixed prefix instantiated.
Takes the optional user annotations into account (`termArg?`). If this is given and the argument
Takes the optional user annotation into account (`termMeasure?`). If this is given and the measure
is unsuitable, throw an error.
-/
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
(termArg? : Option TerminationArgument) : MetaM (Array RecArgInfo × MessageData) := do
(termMeasure? : Option TerminationMeasure) : MetaM (Array RecArgInfo × MessageData) := do
lambdaTelescope value fun ys _ => do
if let .some termArg := termArg? then
-- User explicitly asked to use a certain argument, so throw errors eagerly
let recArgInfo ← withRef termArg.ref do
mapError (f := (m!"cannot use specified parameter for structural recursion:{indentD ·}")) do
getRecArgInfo fnName xs.size (xs ++ ys) (← termArg.structuralArg)
if let .some termMeasure := termMeasure? then
-- User explicitly asked to use a certain measure, so throw errors eagerly
let recArgInfo ← withRef termMeasure.ref do
mapError (f := (m!"cannot use specified measure for structural recursion:{indentD ·}")) do
getRecArgInfo fnName xs.size (xs ++ ys) (← termMeasure.structuralArg)
return (#[recArgInfo], m!"")
else
let mut recArgInfos := #[]
@ -233,12 +233,12 @@ def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
(termArg?s : Array (Option TerminationArgument)) (k : Array RecArgInfo → M α) : M α := do
(termMeasure?s : Array (Option TerminationMeasure)) (k : Array RecArgInfo → M α) : M α := do
let mut report := m!""
-- Gather information on all possible recursive arguments
let mut recArgInfoss := #[]
for fnName in fnNames, value in values, termArg? in termArg?s do
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termArg?
for fnName in fnNames, value in values, termMeasure? in termMeasure?s do
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termMeasure?
report := report ++ thisReport
recArgInfoss := recArgInfoss.push recArgInfos
-- Put non-indices first

View file

@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.FindRecArg
import Lean.Elab.PreDefinition.Structural.Preprocess
@ -127,7 +127,7 @@ private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) :
private def inferRecArgPos (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) :
M (Array Nat × (Array PreDefinition) × Nat) := do
withoutModifyingEnv do
preDefs.forM (addAsAxiom ·)
@ -142,7 +142,7 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (O
assert! xs.size = maxNumFixed
let values ← preDefs.mapM (instantiateLambda ·.value xs)
tryAllArgs fnNames xs values termArg?s fun recArgInfos => do
tryAllArgs fnNames xs values termMeasure?s fun recArgInfos => do
let recArgPoss := recArgInfos.map (·.recArgPos)
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
@ -156,20 +156,20 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (O
let preDefs' ← elimMutualRecursion preDefs xs recArgInfos
return (recArgPoss, preDefs', numFixed)
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
def reporttermMeasure (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
if let some ref := preDef.termination.terminationBy?? then
let fn ← lambdaTelescope preDef.value fun xs _ => mkLambdaFVars xs xs[recArgPos]!
let termArg : TerminationArgument:= {ref := .missing, structural := true, fn}
let termMeasure : TerminationMeasure:= {ref := .missing, structural := true, fn}
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
let stx ← termArg.delab arity (extraParams := preDef.termination.extraParams)
let stx ← termMeasure.delab arity (extraParams := preDef.termination.extraParams)
Tactic.TryThis.addSuggestion ref stx
def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) : TermElabM Unit := do
let names := preDefs.map (·.declName)
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termArg?s
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termMeasure?s
for recArgPos in recArgPoss, preDef in preDefs do
reportTermArg preDef recArgPos
reporttermMeasure preDef recArgPos
state.addMatchers.forM liftM
preDefsNonRec.forM fun preDefNonRec => do
let preDefNonRec ← eraseRecAppSyntax preDefNonRec

View file

@ -54,7 +54,7 @@ structure TerminationHints where
Here we record the number of parameters past the `:`. It is set by
`TerminationHints.rememberExtraParams` and used as follows:
* When we guess the termination argument in `GuessLex` and want to print it in surface-syntax
* When we guess the termination measure in `GuessLex` and want to print it in surface-syntax
compatible form.
* If there are fewer variables in the `termination_by` annotation than there are extra
parameters, we know which parameters they should apply to (`TerminationBy.checkVars`).

View file

@ -14,8 +14,8 @@ import Lean.PrettyPrinter.Delaborator.Basic
/-!
This module contains
* the data type `TerminationArgument`, the elaborated form of a `TerminationBy` clause,
* the `TerminationArguments` type for a clique, and
* the data type `TerminationMeasure`, the elaborated form of a `TerminationBy` clause,
* the `TerminationMeasures` type for a clique, and
* elaboration and deelaboration functions.
-/
@ -29,28 +29,28 @@ open Lean Meta Elab Term
Elaborated form for a `termination_by` clause.
The `fn` has the same (value) arity as the recursive functions (stored in
`arity`), and maps its arguments (including fixed prefix, in unpacked form) to
the termination argument.
`arity`), and maps its measures (including fixed prefix, in unpacked form) to
the termination measure.
If `structural := Bool`, then the `fn` is a lambda picking out exactly one argument.
If `structural := Bool`, then the `fn` is a lambda picking out exactly one measure.
-/
structure TerminationArgument where
structure TerminationMeasure where
ref : Syntax
structural : Bool
fn : Expr
deriving Inhabited
/-- A complete set of `TerminationArgument`s, as applicable to a single clique. -/
abbrev TerminationArguments := Array TerminationArgument
/-- A complete set of `TerminationMeasure`s, as applicable to a single clique. -/
abbrev TerminationMeasures := Array TerminationMeasure
/--
Elaborates a `TerminationBy` to an `TerminationArgument`.
Elaborates a `TerminationBy` to an `TerminationMeasure`.
* `type` is the full type of the original recursive function, including fixed prefix.
* `hint : TerminationBy` is the syntactic `TerminationBy`.
-/
def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams : Nat)
(hint : TerminationBy) : TermElabM TerminationArgument := withDeclName funName do
def TerminationMeasure.elab (funName : Name) (type : Expr) (arity extraParams : Nat)
(hint : TerminationBy) : TermElabM TerminationMeasure := withDeclName funName do
assert! extraParams ≤ arity
if h : hint.vars.size > extraParams then
@ -73,7 +73,7 @@ def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams :
-- Structural recursion: The body has to be a single parameter, whose index we return
if hint.structural then unless (ys ++ xs).contains body do
let params := MessageData.andList ((ys ++ xs).toList.map (m!"'{·}'"))
throwErrorAt hint.ref m!"The termination argument of a structurally recursive " ++
throwErrorAt hint.ref m!"The termination measure of a structurally recursive " ++
m!"function must be one of the parameters {params}, but{indentExpr body}\nisn't " ++
m!"one of these."
@ -87,24 +87,24 @@ def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams :
| 1 => "one parameter"
| n => m!"{n} parameters"
def TerminationArgument.structuralArg (termArg : TerminationArgument) : MetaM Nat := do
assert! termArg.structural
lambdaTelescope termArg.fn fun ys e => do
def TerminationMeasure.structuralArg (measure : TerminationMeasure) : MetaM Nat := do
assert! measure.structural
lambdaTelescope measure.fn fun ys e => do
let .some idx := ys.indexOf? e
| panic! "TerminationArgument.structuralArg: body not one of the parameters"
| panic! "TerminationMeasure.structuralArg: body not one of the parameters"
return idx
open PrettyPrinter Delaborator SubExpr Parser.Termination Parser.Term in
/--
Delaborates a `TerminationArgument` back to a `TerminationHint`, e.g. for `termination_by?`.
Delaborates a `TerminationMeasure` back to a `TerminationHint`, e.g. for `termination_by?`.
This needs extra information:
* `arity` is the value arity of the recursive function
* `extraParams` indicates how many of the functions arguments are bound “after the colon”.
* `extraParams` indicates how many of the function's parameters are bound “after the colon”.
-/
def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : TerminationArgument) : MetaM (TSyntax ``terminationBy) := do
lambdaBoundedTelescope termArg.fn (arity - extraParams) fun _ys e => do
def TerminationMeasure.delab (arity : Nat) (extraParams : Nat) (measure : TerminationMeasure) : MetaM (TSyntax ``terminationBy) := do
lambdaBoundedTelescope measure.fn (arity - extraParams) fun _ys e => do
pure (← delabCore e (delab := go extraParams #[])).1
where
go : Nat → TSyntaxArray `ident → DelabM (TSyntax ``terminationBy)
@ -119,7 +119,7 @@ def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : Termi
-- drop trailing underscores
let mut vars := vars
while ! vars.isEmpty && vars.back!.raw.isOfKind ``hole do vars := vars.pop
if termArg.structural then
if measure.structural then
if vars.isEmpty then
`(terminationBy|termination_by structural $stxBody)
else

View file

@ -14,13 +14,13 @@ import Lean.Elab.Quotation
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.WF.Basic
import Lean.Data.Array
/-!
This module finds lexicographic termination arguments for well-founded recursion.
This module finds lexicographic termination measures for well-founded recursion.
Starting with basic measures (`sizeOf xᵢ` for all parameters `xᵢ`), and complex measures
(e.g. `e₂ - e₁` if `e₁ < e₂` is found in the context of a recursive call) it tries all combinations
@ -42,7 +42,7 @@ guessed lexicographic order.
The following optimizations are applied to make this feasible:
1. The crucial optimization is to look at each argument of each recursive call
1. The crucial optimization is to look at each measure of each recursive call
_once_, try to prove `<` and (if that fails `≤`), and then look at that table to
pick a suitable measure.
@ -50,7 +50,7 @@ The following optimizations are applied to make this feasible:
expensive) tactics as few times as possible, while still being able to consider a possibly
large number of combinations.
3. Before we even try to prove `<`, we check if the arguments are equal (`=`). No well-founded
3. Before we even try to prove `<`, we check if the measures are equal (`=`). No well-founded
measure will relate equal terms, likely this check is faster than firing up the tactic engine,
and it adds more signal to the output.
@ -91,7 +91,7 @@ def originalVarNames (preDef : PreDefinition) : MetaM (Array Name) := do
/--
Given the original parameter names from `originalVarNames`, find
good variable names to be used when talking about termination arguments:
good variable names to be used when talking about termination measures:
Use user-given parameter names if present; use x1...xn otherwise.
The names ought to accessible (no macro scopes) and fresh wrt to the current environment,
@ -121,7 +121,7 @@ def naryVarNames (xs : Array Name) : MetaM (Array Name) := do
freshen ns (n.appendAfter "'")
/-- A termination measure with extra fields for use within GuessLex -/
structure Measure extends TerminationArgument where
structure BasicMeasure extends TerminationMeasure where
/--
Like `.fn`, but unconditionally with `sizeOf` at the right type.
We use this one when in `evalRecCall`
@ -130,7 +130,7 @@ structure Measure extends TerminationArgument where
deriving Inhabited
/-- String description of this measure -/
def Measure.toString (measure : Measure) : MetaM String := do
def BasicMeasure.toString (measure : BasicMeasure) : MetaM String := do
lambdaTelescope measure.fn fun _xs e => do
-- This is a bit slopping if `measure.fn` takes more parameters than the `PreDefinition`
return (← ppExpr e).pretty
@ -138,10 +138,10 @@ def Measure.toString (measure : Measure) : MetaM String := do
/--
Determine if the measure for parameter `x` should be `sizeOf x` or just `x`.
For non-mutual definitions, we omit `sizeOf` when the argument does not depend on
For non-mutual definitions, we omit `sizeOf` when the measure does not depend on
the other varying parameters, and its `WellFoundedRelation` instance goes via `SizeOf`.
For mutual definitions, we omit `sizeOf` only when the argument is (at reducible transparency!) of
For mutual definitions, we omit `sizeOf` only when the measure is (at reducible transparency!) of
type `Nat` (else we'd have to worry about differently-typed measures from different functions to
line up).
-/
@ -170,12 +170,12 @@ def withUserNames {α} (xs : Array Expr) (ns : Array Name) (k : MetaM α) : Meta
/-- Create one measure for each (eligible) parameter of the given predefintion. -/
def simpleMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
(userVarNamess : Array (Array Name)) : MetaM (Array (Array Measure)) := do
(userVarNamess : Array (Array Name)) : MetaM (Array (Array BasicMeasure)) := do
let is_mutual : Bool := preDefs.size > 1
preDefs.mapIdxM fun funIdx preDef => do
lambdaTelescope preDef.value fun xs _ => do
withUserNames xs[fixedPrefixSize:] userVarNamess[funIdx]! do
let mut ret : Array Measure := #[]
let mut ret : Array BasicMeasure := #[]
for x in xs[fixedPrefixSize:] do
-- If the `SizeOf` instance produces a constant (e.g. because it's type is a `Prop` or
-- `Type`), then ignore this parameter
@ -369,7 +369,7 @@ def isNatCmp (e : Expr) : Option (Expr × Expr) :=
def complexMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
(userVarNamess : Array (Array Name)) (recCalls : Array RecCallWithContext) :
MetaM (Array (Array Measure)) := do
MetaM (Array (Array BasicMeasure)) := do
preDefs.mapIdxM fun funIdx _preDef => do
let mut measures := #[]
for rc in recCalls do
@ -426,7 +426,7 @@ def GuessLexRel.toNatRel : GuessLexRel → Expr
For a given recursive call, and a choice of parameter and argument index,
try to prove equality, < or ≤.
-/
def evalRecCall (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasures : Array Measure)
def evalRecCall (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasures : Array BasicMeasure)
(rcc : RecCallWithContext) (callerMeasureIdx calleeMeasureIdx : Nat) : MetaM GuessLexRel := do
rcc.ctxt.run do
let callerMeasure := callerMeasures[callerMeasureIdx]!
@ -467,13 +467,13 @@ def evalRecCall (decrTactic? : Option DecreasingBy) (callerMeasures calleeMeasur
/- A cache for `evalRecCall` -/
structure RecCallCache where mk'' ::
decrTactic? : Option DecreasingBy
callerMeasures : Array Measure
calleeMeasures : Array Measure
callerMeasures : Array BasicMeasure
calleeMeasures : Array BasicMeasure
rcc : RecCallWithContext
cache : IO.Ref (Array (Array (Option GuessLexRel)))
/-- Create a cache to memoize calls to `evalRecCall descTactic? rcc` -/
def RecCallCache.mk (decrTactics : Array (Option DecreasingBy)) (measuress : Array (Array Measure))
def RecCallCache.mk (decrTactics : Array (Option DecreasingBy)) (measuress : Array (Array BasicMeasure))
(rcc : RecCallWithContext) :
BaseIO RecCallCache := do
let decrTactic? := decrTactics[rcc.caller]!
@ -499,7 +499,7 @@ def RecCallCache.prettyEntry (rcc : RecCallCache) (callerMeasureIdx calleeMeasur
| .some rel => toString rel
| .none => "_"
/-- The measures that we order lexicographically can be comparing arguments,
/-- The measures that we order lexicographically can be comparing basic measures,
or numbering the functions -/
inductive MutualMeasure where
/-- For every function, the given argument index -/
@ -509,9 +509,9 @@ inductive MutualMeasure where
/-- Evaluate a recursive call at a given `MutualMeasure` -/
def inspectCall (rc : RecCallCache) : MutualMeasure → MetaM GuessLexRel
| .args taIdxs => do
let callerMeasureIdx := taIdxs[rc.rcc.caller]!
let calleeMeasureIdx := taIdxs[rc.rcc.callee]!
| .args tmIdxs => do
let callerMeasureIdx := tmIdxs[rc.rcc.caller]!
let calleeMeasureIdx := tmIdxs[rc.rcc.callee]!
rc.eval callerMeasureIdx calleeMeasureIdx
| .func funIdx => do
if rc.rcc.caller == funIdx && rc.rcc.callee != funIdx then
@ -554,16 +554,16 @@ where
/--
Enumerate all measures we want to try.
All arguments (resp. combinations thereof) and
All measures (resp. combinations thereof) and
possible orderings of functions (if more than one)
-/
def generateMeasures (numTermArgs : Array Nat) : MetaM (Array MutualMeasure) := do
let some arg_measures := generateCombinations? numTermArgs
def generateMeasures (numMeasures : Array Nat) : MetaM (Array MutualMeasure) := do
let some arg_measures := generateCombinations? numMeasures
| throwError "Too many combinations"
let func_measures :=
if numTermArgs.size > 1 then
(List.range numTermArgs.size).toArray
if numMeasures.size > 1 then
(List.range numMeasures.size).toArray
else
#[]
@ -652,8 +652,8 @@ def RecCallWithContext.posString (rcc : RecCallWithContext) : MetaM String := do
return s!"{position.line}:{position.column}{endPosStr}"
/-- How to present the measure in the table header, possibly abbreviated. -/
def measureHeader (measure : Measure) : StateT (Nat × String) MetaM String := do
/-- How to present the basic measure in the table header, possibly abbreviated. -/
def measureHeader (measure : BasicMeasure) : StateT (Nat × String) MetaM String := do
let s ← measure.toString
if s.length > 5 then
let (i, footer) ← get
@ -670,7 +670,7 @@ def collectHeaders {α} (a : StateT (Nat × String) MetaM α) : MetaM (α × Str
/-- Explain what we found out about the recursive calls (non-mutual case) -/
def explainNonMutualFailure (measures : Array Measure) (rcs : Array RecCallCache) : MetaM Format := do
def explainNonMutualFailure (measures : Array BasicMeasure) (rcs : Array RecCallCache) : MetaM Format := do
let (header, footer) ← collectHeaders (measures.mapM measureHeader)
let mut table : Array (Array String) := #[#[""] ++ header]
for i in [:rcs.size], rc in rcs do
@ -685,7 +685,7 @@ def explainNonMutualFailure (measures : Array Measure) (rcs : Array RecCallCache
return out ++ "\n\n" ++ footer
/-- Explain what we found out about the recursive calls (mutual case) -/
def explainMutualFailure (declNames : Array Name) (measuress : Array (Array Measure))
def explainMutualFailure (declNames : Array Name) (measuress : Array (Array BasicMeasure))
(rcs : Array RecCallCache) : MetaM Format := do
let (headerss, footer) ← collectHeaders (measuress.mapM (·.mapM measureHeader))
@ -718,9 +718,9 @@ def explainMutualFailure (declNames : Array Name) (measuress : Array (Array Meas
return r
def explainFailure (declNames : Array Name) (measuress : Array (Array Measure))
def explainFailure (declNames : Array Name) (measuress : Array (Array BasicMeasure))
(rcs : Array RecCallCache) : MetaM Format := do
let mut r : Format := "The arguments relate at each recursive call as follows:\n" ++
let mut r : Format := "The basic measures relate at each recursive call as follows:\n" ++
"(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)\n"
if declNames.size = 1 then
r := r ++ (← explainNonMutualFailure measuress[0]! rcs)
@ -739,29 +739,29 @@ def mkProdElem (xs : Array Expr) : MetaM Expr := do
let n := xs.size
xs[0:n-1].foldrM (init:=xs[n-1]!) fun x p => mkAppM ``Prod.mk #[x,p]
def toTerminationArguments (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
(userVarNamess : Array (Array Name)) (measuress : Array (Array Measure))
(solution : Array MutualMeasure) : MetaM TerminationArguments := do
def toTerminationMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
(userVarNamess : Array (Array Name)) (measuress : Array (Array BasicMeasure))
(solution : Array MutualMeasure) : MetaM TerminationMeasures := do
preDefs.mapIdxM fun funIdx preDef => do
let measures := measuress[funIdx]!
lambdaTelescope preDef.value fun xs _ => do
withUserNames xs[fixedPrefixSize:] userVarNamess[funIdx]! do
let args := solution.map fun
| .args taIdxs => measures[taIdxs[funIdx]!]!.fn.beta xs
| .args tmIdxs => measures[tmIdxs[funIdx]!]!.fn.beta xs
| .func funIdx' => mkNatLit <| if funIdx' == funIdx then 1 else 0
let fn ← mkLambdaFVars xs (← mkProdElem args)
return { ref := .missing, structural := false, fn}
/--
Shows the inferred termination argument to the user, and implements `termination_by?`
Shows the inferred termination measure to the user, and implements `termination_by?`
-/
def reportTermArgs (preDefs : Array PreDefinition) (termArgs : TerminationArguments) : MetaM Unit := do
for preDef in preDefs, termArg in termArgs do
def reportTerminationMeasures (preDefs : Array PreDefinition) (termMeasures : TerminationMeasures) : MetaM Unit := do
for preDef in preDefs, termMeasure in termMeasures do
let stx := do
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
termArg.delab arity (extraParams := preDef.termination.extraParams)
termMeasure.delab arity (extraParams := preDef.termination.extraParams)
if showInferredTerminationBy.get (← getOptions) then
logInfoAt preDef.ref m!"Inferred termination argument:\n{← stx}"
logInfoAt preDef.ref m!"Inferred termination measure:\n{← stx}"
if let some ref := preDef.termination.terminationBy?? then
Tactic.TryThis.addSuggestion ref (← stx)
@ -771,14 +771,14 @@ open GuessLex
/--
Main entry point of this module:
Try to find a lexicographic ordering of the arguments for which the recursive definition
Try to find a lexicographic ordering of the basic measures for which the recursive definition
terminates. See the module doc string for a high-level overview.
The `preDefs` are used to determine arity and types of arguments; the bodies are ignored.
The `preDefs` are used to determine arity and types of parameters; the bodies are ignored.
-/
def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
(fixedPrefixSize : Nat) (argsPacker : ArgsPacker) :
MetaM TerminationArguments := do
MetaM TerminationMeasures := do
let userVarNamess ← argsPacker.varNamess.mapM (naryVarNames ·)
trace[Elab.definition.wf] "varNames is: {userVarNamess}"
@ -788,30 +788,30 @@ def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
-- For every function, the measures we want to use
-- (One for each non-forbiddend arg)
let meassures₁ ← simpleMeasures preDefs fixedPrefixSize userVarNamess
let meassures₂ ← complexMeasures preDefs fixedPrefixSize userVarNamess recCalls
let measuress := Array.zipWith meassures₁ meassures₂ (· ++ ·)
let basicMeassures₁ ← simpleMeasures preDefs fixedPrefixSize userVarNamess
let basicMeassures₂ ← complexMeasures preDefs fixedPrefixSize userVarNamess recCalls
let basicMeasures := Array.zipWith basicMeassures₁ basicMeassures₂ (· ++ ·)
-- The list of measures, including the measures that order functions.
-- The function ordering measures come last
let measures ← generateMeasures (measuress.map (·.size))
let mutualMeasures ← generateMeasures (basicMeasures.map (·.size))
-- If there is only one plausible measure, use that
if let #[solution] := measures then
let termArgs ← toTerminationArguments preDefs fixedPrefixSize userVarNamess measuress #[solution]
reportTermArgs preDefs termArgs
return termArgs
if let #[solution] := mutualMeasures then
let termMeasures ← toTerminationMeasures preDefs fixedPrefixSize userVarNamess basicMeasures #[solution]
reportTerminationMeasures preDefs termMeasures
return termMeasures
let rcs ← recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasingBy?)) measuress ·)
let rcs ← recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasingBy?)) basicMeasures ·)
let callMatrix := rcs.map (inspectCall ·)
match ← liftMetaM <| solve measures callMatrix with
match ← liftMetaM <| solve mutualMeasures callMatrix with
| .some solution => do
let termArgs ← toTerminationArguments preDefs fixedPrefixSize userVarNamess measuress solution
reportTermArgs preDefs termArgs
return termArgs
let termMeasures ← toTerminationMeasures preDefs fixedPrefixSize userVarNamess basicMeasures solution
reportTerminationMeasures preDefs termMeasures
return termMeasures
| .none =>
let explanation ← explainFailure (preDefs.map (·.declName)) measuress rcs
let explanation ← explainFailure (preDefs.map (·.declName)) basicMeasures rcs
Lean.throwError <| "Could not find a decreasing measure.\n" ++
explanation ++ "\n" ++
"Please use `termination_by` to specify a decreasing measure."

View file

@ -5,7 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Elab.PreDefinition.Mutual
import Lean.Elab.PreDefinition.WF.PackMutual
import Lean.Elab.PreDefinition.WF.Preprocess
@ -19,8 +19,8 @@ namespace Lean.Elab
open WF
open Meta
def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
let termArgs? := termArg?s.mapM id -- Either all or none, checked by `elabTerminationByHints`
def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) : TermElabM Unit := do
let termMeasures? := termMeasure?s.mapM id -- Either all or none, checked by `elabTerminationByHints`
let preDefs ← preDefs.mapM fun preDef =>
return { preDef with value := (← preprocess preDef.value) }
let (fixedPrefixSize, argsPacker, unaryPreDef) ← withoutModifyingEnv do
@ -36,8 +36,8 @@ def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option Termi
let preDefsDIte ← preDefs.mapM fun preDef => return { preDef with value := (← iteToDIte preDef.value) }
return (fixedPrefixSize, argsPacker, ← packMutual fixedPrefixSize argsPacker preDefsDIte)
let wf : TerminationArguments ← do
if let some tas := termArgs? then pure tas else
let wf : TerminationMeasures ← do
if let some tms := termMeasures? then pure tms else
-- No termination_by here, so use GuessLex to infer one
guessLex preDefs unaryPreDef fixedPrefixSize argsPacker

View file

@ -9,7 +9,7 @@ import Lean.Meta.Tactic.Cases
import Lean.Meta.Tactic.Rename
import Lean.Elab.SyntheticMVars
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.TerminationMeasure
import Lean.Meta.ArgsPacker
namespace Lean.Elab.WF
@ -17,21 +17,21 @@ open Meta
open Term
/--
The termination arguments must not depend on the varying parameters of the function, and in
The termination measures must not depend on the varying parameters of the function, and in
a mutual clique, they must be the same for all functions.
This ensures the preconditions for `ArgsPacker.uncurryND`.
-/
def checkCodomains (names : Array Name) (prefixArgs : Array Expr) (arities : Array Nat)
(termArgs : TerminationArguments) : TermElabM Expr := do
(termMeasures : TerminationMeasures) : TermElabM Expr := do
let mut codomains := #[]
for name in names, arity in arities, termArg in termArgs do
let type ← inferType (termArg.fn.beta prefixArgs)
for name in names, arity in arities, termMeasure in termMeasures do
let type ← inferType (termMeasure.fn.beta prefixArgs)
let codomain ← forallBoundedTelescope type arity fun xs codomain => do
let fvars := xs.map (·.fvarId!)
if codomain.hasAnyFVar (fvars.contains ·) then
throwErrorAt termArg.ref m!"The termination argument's type must not depend on the " ++
m!"function's varying parameters, but {name}'s termination argument does:{indentExpr type}\n" ++
throwErrorAt termMeasure.ref m!"The termination measure's type must not depend on the " ++
m!"function's varying parameters, but {name}'s termination measure does:{indentExpr type}\n" ++
"Try using `sizeOf` explicitly"
pure codomain
codomains := codomains.push codomain
@ -39,26 +39,26 @@ def checkCodomains (names : Array Name) (prefixArgs : Array Expr) (arities : Arr
let codomain0 := codomains[0]!
for h : i in [1 : codomains.size] do
unless ← isDefEqGuarded codomain0 codomains[i] do
throwErrorAt termArgs[i]!.ref m!"The termination arguments of mutually recursive functions " ++
m!"must have the same return type, but the termination argument of {names[0]!} has type" ++
throwErrorAt termMeasures[i]!.ref m!"The termination measures of mutually recursive functions " ++
m!"must have the same return type, but the termination measure of {names[0]!} has type" ++
m!"{indentExpr codomain0}\n" ++
m!"while the termination argument of {names[i]!} has type{indentExpr codomains[i]}\n" ++
m!"while the termination measure of {names[i]!} has type{indentExpr codomains[i]}\n" ++
"Try using `sizeOf` explicitly"
return codomain0
/--
If the `termArgs` map the packed argument `argType` to `β`, then this function passes to the
If the `termMeasures` map the packed argument `argType` to `β`, then this function passes to the
continuation a value of type `WellFoundedRelation argType` that is derived from the instance
for `WellFoundedRelation β` using `invImage`.
-/
def elabWFRel (declNames : Array Name) (unaryPreDefName : Name) (prefixArgs : Array Expr)
(argsPacker : ArgsPacker) (argType : Expr) (termArgs : TerminationArguments)
(argsPacker : ArgsPacker) (argType : Expr) (termMeasures : TerminationMeasures)
(k : Expr → TermElabM α) : TermElabM α := withDeclName unaryPreDefName do
let α := argType
let u ← getLevel α
let β ← checkCodomains declNames prefixArgs argsPacker.arities termArgs
let β ← checkCodomains declNames prefixArgs argsPacker.arities termMeasures
let v ← getLevel β
let packedF ← argsPacker.uncurryND (termArgs.map (·.fn.beta prefixArgs))
let packedF ← argsPacker.uncurryND (termMeasures.map (·.fn.beta prefixArgs))
let inst ← synthInstance (.app (.const ``WellFoundedRelation [v]) β)
let rel ← instantiateMVars (mkApp4 (.const ``invImage [u,v]) α β packedF inst)
k rel

View file

@ -50,8 +50,8 @@ private partial def updateAlts (unrefinedArgType : Expr) (typeNew : Expr) (altNu
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
This is used in `Lean.Elab.PreDefinition.WF.Fix` when replacing recursive calls with calls to
the argument provided by `fix` to refine the termination argument, which may mention `major`.
See there for how to use this function.
the argument provided by `fix` to refine type of the local variable used for recursive calls,
which may mention `major`. See there for how to use this function.
-/
def addArg (matcherApp : MatcherApp) (e : Expr) : MetaM MatcherApp :=
lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do

View file

@ -692,7 +692,7 @@ letrec we need them here already.
-/
/--
Specify a termination argument for recursive functions.
Specify a termination measure for recursive functions.
```
termination_by a - b
```
@ -710,8 +710,8 @@ recursion. The syntax `termination_by structural a` (or `termination_by structur
indicates the function is expected to be structural recursive on the argument. In this case
the body of the `termination_by` clause must be one of the function's parameters.
If omitted, a termination argument will be inferred. If written as `termination_by?`,
the inferrred termination argument will be suggested.
If omitted, a termination measure will be inferred. If written as `termination_by?`,
the inferrred termination measure will be suggested.
-/
@[builtin_doc] def terminationBy := leading_parser
@ -751,7 +751,7 @@ the syntax `partial_fixpoint monotonicity by $tac` the proof can be done manuall
checkColGt "indented monotonicity proof" >> termParser))
/--
Manually prove that the termination argument (as specified with `termination_by` or inferred)
Manually prove that the termination measure (as specified with `termination_by` or inferred)
decreases at each recursive call.
By default, the tactic `decreasing_tactic` is used.

View file

@ -1,12 +1,12 @@
decreasing_by.lean:34:0-39:17: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 34:29-43 = ?
2) 34:46-62 ? _
Please use `termination_by` to specify a decreasing measure.
decreasing_by.lean:61:0-67:19: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 61:29-43 = ?
@ -27,7 +27,7 @@ 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:93:0-94:22: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 93:29-43 = ?
@ -37,7 +37,7 @@ decreasing_by.lean:104:0-106:17: error: unsolved goals
n m : Nat
⊢ Prod.Lex (fun a₁ a₂ => a₁ < a₂) (fun a₁ a₂ => a₁ < a₂) (dec1 n, 100) (n, m)
decreasing_by.lean:114:0-117:17: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 114:29-43 = ?

View file

@ -90,7 +90,7 @@ def confuseLex2 : @PSigma Nat (fun _ => Nat) → Nat
| ⟨0,_⟩ => 0
| ⟨.succ y,.succ n⟩ => confuseLex2 ⟨y,n⟩
-- NB: uses sizeOf to make the termination argument non-dependent
-- NB: uses sizeOf to make the termination measure non-dependent
def dependent : (n : Nat) → (m : Fin n) → Nat
| 0, i => Fin.elim0 i
| .succ 0, 0 => 0
@ -137,7 +137,7 @@ def shadow2 (some_n : Nat) : Nat → Nat
| .succ n => shadow2 (some_n + 1) n
decreasing_by decreasing_tactic
-- Tests that the inferred termination argument is shown without extra underscores
-- Tests that the inferred termination measure is shown without extra underscores
def foo : Nat → Nat → Nat → Nat
| _, 0, acc => acc
| k, n+1, acc => foo (k+1) n (acc + k)
@ -181,7 +181,7 @@ end VarNames
namespace MutualNotNat1
-- A type that isn't Nat, checking that the inferred argument uses `sizeOf` so that
-- the types of the termination argument aligns.
-- the types of the termination measure aligns.
structure OddNat2 where nat : Nat
instance : SizeOf OddNat2 := ⟨fun n => n.nat⟩
@[simp] theorem OddNat2.sizeOf_eq (n : OddNat2) : sizeOf n = n.nat := rfl
@ -197,7 +197,7 @@ end MutualNotNat1
namespace MutualNotNat2
-- A type that is defeq to Nat, but with a different `sizeOf`, checking that the
-- inferred argument uses `sizeOf` so that the types of the termination argument aligns.
-- inferred argument uses `sizeOf` so that the types of the termination measure aligns.
def OddNat3 := Nat
instance : SizeOf OddNat3 := ⟨fun n => 42 - @id Nat n⟩
@[simp] theorem OddNat3.sizeOf_eq (n : OddNat3) : sizeOf n = 42 - @id Nat n := rfl

View file

@ -1,62 +1,62 @@
Inferred termination argument:
Inferred termination measure:
termination_by (n, m)
Inferred termination argument:
Inferred termination measure:
termination_by (m, n)
Inferred termination argument:
Inferred termination measure:
termination_by (n, m)
Inferred termination argument:
Inferred termination measure:
termination_by x1 x2 => (x2, x1)
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => (x1, 0)
Inferred termination argument:
Inferred termination measure:
termination_by (n, 1)
Inferred termination argument:
Inferred termination measure:
termination_by (m, n)
Inferred termination argument:
Inferred termination measure:
termination_by x1 x2 x3 x4 x5 x6 x7 x8 => (x8, x7, x6, x5, x4, x3, x2, x1)
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => sizeOf x1
Inferred termination argument:
Inferred termination measure:
termination_by n m => (n, sizeOf m)
Inferred termination argument:
Inferred termination measure:
termination_by m => m
Inferred termination argument:
Inferred termination measure:
termination_by (sizeOf a, 1)
Inferred termination argument:
Inferred termination measure:
termination_by (sizeOf a, 0)
Inferred termination argument:
Inferred termination measure:
termination_by x2' => x2'
Inferred termination argument:
Inferred termination measure:
termination_by x2 => x2
Inferred termination argument:
Inferred termination measure:
termination_by _ x2 => x2
Inferred termination argument:
Inferred termination measure:
termination_by x1 => sizeOf x1
Inferred termination argument:
Inferred termination measure:
termination_by x2 => SizeOf.sizeOf x2
Inferred termination argument:
Inferred termination measure:
termination_by x1 => SizeOf.sizeOf x1
Inferred termination argument:
Inferred termination measure:
termination_by x2 => SizeOf.sizeOf x2
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => sizeOf x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by sizeOf o
Inferred termination argument:
Inferred termination measure:
termination_by x1 => x1
Inferred termination argument:
Inferred termination measure:
termination_by x1 => sizeOf x1

View file

@ -1,14 +1,14 @@
Inferred termination argument:
Inferred termination measure:
termination_by n - i
Inferred termination argument:
Inferred termination measure:
termination_by xs.size - i
Inferred termination argument:
Inferred termination measure:
termination_by xs.size - i
Inferred termination argument:
Inferred termination measure:
termination_by (xs.size - i, ys.size - j)
Inferred termination argument:
Inferred termination measure:
termination_by (xs.size - i, i - j)
Inferred termination argument:
Inferred termination measure:
termination_by (xs.size - i, i)
guessLexDiff.lean:84:4-84:11: error: fail to show termination for
failure
@ -22,7 +22,7 @@ Cannot use parameter i:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
i #1 #2 i + i
1) 85:26-38 = = = =
@ -54,7 +54,7 @@ Cannot use parameters i of mutual_failure and i of mutual_failure2:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
Call from mutual_failure to mutual_failure2 at 104:4-24:
i #1 #2

View file

@ -11,13 +11,13 @@ Cannot use parameter #2:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
x1 x2
1) 11:12-46 ? ?
Please use `termination_by` to specify a decreasing measure.
guessLexFailures.lean:15:0-18:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
x1 x2
1) 17:12-47 ? ?
@ -39,7 +39,7 @@ no parameters suitable for structural recursion
well-founded recursion cannot be used, 'noNonFixedArguments' does not take any (non-fixed) arguments
guessLexFailures.lean:27:0-33:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m l
1) 29:6-25 = = =
@ -47,7 +47,7 @@ The arguments relate at each recursive call as follows:
3) 31:6-23 < _ _
Please use `termination_by` to specify a decreasing measure.
guessLexFailures.lean:37:0-43:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m x4
1) 39:6-27 = = =
@ -55,7 +55,7 @@ The arguments relate at each recursive call as follows:
3) 41:6-25 < _ _
Please use `termination_by` to specify a decreasing measure.
guessLexFailures.lean:48:0-54:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m l
1) 50:6-25 = = =
@ -79,7 +79,7 @@ Skipping arguments of type True, as Mutual.f has no compatible argument.
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
Call from Mutual.f to Mutual.f at 61:8-33:
n m l
@ -128,13 +128,13 @@ Cannot use parameter #2:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 89:19-32 ? ?
Please use `termination_by` to specify a decreasing measure.
guessLexFailures.lean:100:0-103:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 101:83-105 ? ?
@ -146,7 +146,7 @@ guessLexFailures.lean:113:14-113:31: error: failed to prove termination, possibl
n✝ m n : Nat
⊢ n < n✝
guessLexFailures.lean:119:0-123:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
n m
1) 121:31-54 ? ?

View file

@ -1,6 +1,6 @@
Inferred termination argument:
Inferred termination measure:
termination_by (y, 1, 0)
Inferred termination argument:
Inferred termination measure:
termination_by (y, 0, 1)
Inferred termination argument:
Inferred termination measure:
termination_by (y, 0, 0)

View file

@ -1,4 +1,4 @@
Inferred termination argument:
Inferred termination measure:
termination_by x1 x2 => (x1, x2, 0)
Inferred termination argument:
Inferred termination measure:
termination_by x1 x2 => (x1, x2, 1)

View file

@ -1,9 +1,9 @@
issue2260.lean:8:0-8:21: error: The termination argument's type must not depend on the function's varying parameters, but foo's termination argument does:
issue2260.lean:8:0-8:21: error: The termination measure's type must not depend on the function's varying parameters, but foo's termination measure does:
{i : Nat} → DNat i → DNat i
Try using `sizeOf` explicitly
issue2260.lean:15:0-15:21: error: The termination argument's type must not depend on the function's varying parameters, but bar1's termination argument does:
issue2260.lean:15:0-15:21: error: The termination measure's type must not depend on the function's varying parameters, but bar1's termination measure does:
{i : Nat} → DNat i → DNat i
Try using `sizeOf` explicitly
issue2260.lean:31:0-31:21: error: The termination argument's type must not depend on the function's varying parameters, but baz2's termination argument does:
issue2260.lean:31:0-31:21: error: The termination measure's type must not depend on the function's varying parameters, but baz2's termination measure does:
{i : Nat} → DNat i → DNat i
Try using `sizeOf` explicitly

View file

@ -1,10 +1,10 @@
mutwftypemismatch.lean:11:0-11:25: error: The termination arguments of mutually recursive functions must have the same return type, but the termination argument of Ex1.foo has type
mutwftypemismatch.lean:11:0-11:25: error: The termination measures of mutually recursive functions must have the same return type, but the termination measure of Ex1.foo has type
Nat
while the termination argument of Ex1.bar has type
while the termination measure of Ex1.bar has type
Prop
Try using `sizeOf` explicitly
mutwftypemismatch.lean:26:0-26:44: error: The termination arguments of mutually recursive functions must have the same return type, but the termination argument of Ex2.foo has type
mutwftypemismatch.lean:26:0-26:44: error: The termination measures of mutually recursive functions must have the same return type, but the termination measure of Ex2.foo has type
Nat
while the termination argument of Ex2.bar has type
while the termination measure of Ex2.bar has type
Fin fixed
Try using `sizeOf` explicitly

View file

@ -5,7 +5,7 @@ inductive A (n : Nat) : Type
| b : A n → A n
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type is an inductive datatype
A n
and the datatype parameter
@ -28,7 +28,7 @@ inductive Xn (e : Sigma.{0} (· → Type)) (α : Type) : Nat → Type where
| mk2 {n} (s : e.1) (p : e.2 s → Xn e α n) : Xn e α n
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type is an inductive datatype
Xn e (Xn e α n) m
and the datatype parameter

View file

@ -415,7 +415,7 @@ inductive B (n : Nat) : Type
end
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type is an inductive datatype
A n
and the datatype parameter
@ -439,7 +439,7 @@ end
end Mutual3
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type FixedIndex.T is an inductive family and indices are not variables
T 37
-/
@ -460,7 +460,7 @@ inductive T (n : Nat) : Nat → Type where
| n : T n n → T n n
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type is an inductive datatype
T n n
and the datatype parameter
@ -523,7 +523,7 @@ Too many possible combinations of parameters of type Nattish (or please indicate
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
Call from ManyCombinations.f to ManyCombinations.g at 557:15-29:
#1 #2 #3 #4

View file

@ -19,7 +19,7 @@ namespace Errors
-- A few error conditions
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
it is unchanged in the recursive calls
-/
#guard_msgs in
@ -27,7 +27,7 @@ def foo1 (n : Nat) : Nat := foo1 n
termination_by structural n
/--
error: cannot use specified parameter for structural recursion:
error: cannot use specified measure for structural recursion:
its type Nat.le is an inductive family and indices are not variables
n.succ.le 100
-/
@ -73,7 +73,7 @@ def ackermann2 (n m : Nat) := match n, m with
termination_by structural m
/--
error: The termination argument of a structurally recursive function must be one of the parameters 'n', but
error: The termination measure of a structurally recursive function must be one of the parameters 'n', but
id n + 1
isn't one of these.
-/

View file

@ -9,7 +9,7 @@ Cannot use parameters #1 of f.g and x of f:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
Call from f.g to f at 9:9-12:
x1

View file

@ -11,7 +11,7 @@ Cannot use parameter y:
Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
The basic measures relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
x y y - x
1) 3:12-19 ≤ ? ?