feat: store pending contraints during dependent pattern matching

It is a better solution for issues #1361 and #1279, and it was on the
to-do list for a while.
This commit is contained in:
Leonardo de Moura 2022-08-03 17:38:36 -07:00
parent f2921993bb
commit 84ff8d4a04
5 changed files with 258 additions and 238 deletions

View file

@ -127,27 +127,52 @@ def instantiateAltLHSMVars (altLHS : AltLHS) : MetaM AltLHS :=
patterns := (← altLHS.patterns.mapM instantiatePatternMVars)
}
/-- `Match` alternative -/
structure Alt where
/-- `Syntax` object for providing position information -/
ref : Syntax
idx : Nat -- for generating error messages
/--
Orginal alternative index. Alternatives can be split, this index is the original
position of the alternative that generated this one.
-/
idx : Nat
/--
Right-hand-side of the alternative.
-/
rhs : Expr
/--
Alternative pattern variables.
-/
fvarDecls : List LocalDecl
/--
Alternative patterns.
-/
patterns : List Pattern
/--
Pending constraints `lhs ≋ rhs` that need to be solved before the alternative
is considered acceptable. We generate them when processing inaccessible patterns.
Note that `lhs` and `rhs` often have different types.
After we perform additional case analysis, their types become definitionally equal.
-/
cnstrs : List (Expr × Expr)
deriving Inhabited
namespace Alt
partial def toMessageData (alt : Alt) : MetaM MessageData := do
withExistingLocalDecls alt.fvarDecls do
let msg : List MessageData := alt.fvarDecls.map fun d => m!"{d.toExpr}:({d.type})"
let msg : MessageData := m!"{msg} |- {alt.patterns.map Pattern.toMessageData} => {alt.rhs}"
let msg := alt.fvarDecls.map fun d => m!"{d.toExpr}:({d.type})"
let mut msg := m!"{msg} |- {alt.patterns.map Pattern.toMessageData} => {alt.rhs}"
for (lhs, rhs) in alt.cnstrs do
msg := m!"{msg}\n | {lhs} ≋ {rhs}"
addMessageContext msg
def applyFVarSubst (s : FVarSubst) (alt : Alt) : Alt :=
{ alt with
patterns := alt.patterns.map fun p => p.applyFVarSubst s,
fvarDecls := alt.fvarDecls.map fun d => d.applyFVarSubst s,
rhs := alt.rhs.applyFVarSubst s }
rhs := alt.rhs.applyFVarSubst s
cnstrs := alt.cnstrs.map fun (lhs, rhs) => (lhs.applyFVarSubst s, rhs.applyFVarSubst s) }
def replaceFVarId (fvarId : FVarId) (v : Expr) (alt : Alt) : Alt :=
{ alt with
@ -156,7 +181,11 @@ def replaceFVarId (fvarId : FVarId) (v : Expr) (alt : Alt) : Alt :=
fvarDecls :=
let decls := alt.fvarDecls.filter fun d => d.fvarId != fvarId
decls.map (·.replaceFVarId fvarId v)
}
cnstrs := alt.cnstrs.map fun (lhs, rhs) => (lhs.replaceFVarId fvarId v, rhs.replaceFVarId fvarId v) }
/-- Return `true` if `fvarId` is one of the alternative pattern variables -/
def isLocalDecl (fvarId : FVarId) (alt : Alt) : Bool :=
alt.fvarDecls.any fun d => d.fvarId == fvarId
/--
Similar to `checkAndReplaceFVarId`, but ensures type of `v` is definitionally equal to type of `fvarId`.
@ -209,7 +238,7 @@ def checkAndReplaceFVarId (fvarId : FVarId) (v : Expr) (alt : Alt) : MetaM Alt :
withExistingLocalDecls alt.fvarDecls do
let (expectedType, givenType) ← addPPExplicitToExposeDiff vType fvarDecl.type
throwErrorAt alt.ref "type mismatch during dependent match-elimination at pattern variable '{mkFVar fvarDecl.fvarId}' with type{indentExpr givenType}\nexpected type{indentExpr expectedType}"
pure $ replaceFVarId fvarId v alt
return replaceFVarId fvarId v alt
end Alt

View file

@ -66,18 +66,9 @@ where
let rhs := if hasParams then mkAppN minor xs else mkApp minor (mkConst `Unit.unit)
let minors := minors.push (minor, minorNumParams)
let fvarDecls ← lhs.fvarDecls.mapM instantiateLocalDeclMVars
let alts := { ref := lhs.ref, idx := idx, rhs := rhs, fvarDecls := fvarDecls, patterns := lhs.patterns : Alt } :: alts
let alts := { ref := lhs.ref, idx := idx, rhs := rhs, fvarDecls := fvarDecls, patterns := lhs.patterns, cnstrs := [] } :: alts
loop lhss alts minors
def assignGoalOf (p : Problem) (e : Expr) : MetaM Unit :=
withGoalOf p do
let mvar := mkMVar p.mvarId
let mvarType ← inferType mvar
let eType ← inferType e
unless (← isDefEq mvarType eType) do
throwError "dependent elimination failed, type mismatch when solving alternative with type{indentExpr eType}\nbut expected{indentExpr mvarType}"
p.mvarId.assign e
structure State where
used : Std.HashSet Nat := {} -- used alternatives
counterExamples : List (List Example) := []
@ -159,24 +150,109 @@ private def isNatValueTransition (p : Problem) : Bool :=
| _ => false)
private def processSkipInaccessible (p : Problem) : Problem := Id.run do
let _ :: xs := p.vars | unreachable!
let x :: xs := p.vars | unreachable!
let alts := p.alts.map fun alt => Id.run do
let .inaccessible _ :: ps := alt.patterns | unreachable!
{ alt with patterns := ps }
let .inaccessible e :: ps := alt.patterns | unreachable!
{ alt with patterns := ps, cnstrs := (x, e) :: alt.cnstrs }
{ p with alts := alts, vars := xs }
private def processLeaf (p : Problem) : StateRefT State MetaM Unit := do
match p.alts with
| [] =>
/- TODO: allow users to configure which tactic is used to close leaves. -/
unless (← p.mvarId.contradictionCore {}) do
trace[Meta.Match.match] "missing alternative"
p.mvarId.admit
modify fun s => { s with counterExamples := p.examples :: s.counterExamples }
| alt :: _ =>
-- TODO: check whether we have unassigned metavars in rhs
liftM <| assignGoalOf p alt.rhs
modify fun s => { s with used := s.used.insert alt.idx }
/--
If contraint is of the form `e ≋ x` where `x` is a free variable, reorient it
as `x ≋ e` If
- `x` is an `alt`-local declaration
- `e` is not a free variable.
-/
private def reorientCnstrs (alt : Alt) : Alt :=
let cnstrs := alt.cnstrs.map fun (lhs, rhs) =>
if rhs.isFVar && alt.isLocalDecl rhs.fvarId! then
(rhs, lhs)
else if !lhs.isFVar && rhs.isFVar then
(rhs, lhs)
else
(lhs, rhs)
{ alt with cnstrs }
/--
Remove constraints of the form `lhs ≋ rhs` where `lhs` and `rhs` are definitionally equal,
or `lhs` is a free variable.
-/
private def filterTrivialCnstrs (alt : Alt) : MetaM Alt := do
let cnstrs ← withExistingLocalDecls alt.fvarDecls do
alt.cnstrs.filterM fun (lhs, rhs) => do
if (← isDefEqGuarded lhs rhs) then
return false
else if lhs.isFVar then
return false
else
return true
return { alt with cnstrs }
/--
Find an alternative constraint of the form `x ≋ e` where `x` is an alternative
local declarations, and `x` and `e` have definitionally equal types.
Then, replace `x` with `e` in the alternative, and return it.
Return `none` if the alternative does not contain a constraint of this form.
-/
private def solveSomeLocalFVarIdCnstr? (alt : Alt) : MetaM (Option Alt) :=
withExistingLocalDecls alt.fvarDecls do
let (some (fvarId, val), cnstrs) ← go alt.cnstrs | return none
trace[Meta.Match.match] "found cnstr to solve {mkFVar fvarId} ↦ {val}"
return some <| { alt with cnstrs }.replaceFVarId fvarId val
where
go (cnstrs : List (Expr × Expr)) := do
match cnstrs with
| [] => return (none, [])
| (lhs, rhs) :: cnstrs =>
if lhs.isFVar && alt.isLocalDecl lhs.fvarId! then
if !(← dependsOn rhs lhs.fvarId!) && (← isDefEqGuarded (← inferType lhs) (← inferType rhs)) then
return (some (lhs.fvarId!, rhs), cnstrs)
let (p, cnstrs) ← go cnstrs
return (p, (lhs, rhs) :: cnstrs)
/--
Solve pending alternative constraints. If all constraints can be solved perform assignment
`mvarId := alt.rhs`, and return true.
-/
private partial def solveCnstrs (mvarId : MVarId) (alt : Alt) : StateRefT State MetaM Bool := do
go (reorientCnstrs alt)
where
go (alt : Alt) : StateRefT State MetaM Bool := do
match (← solveSomeLocalFVarIdCnstr? alt) with
| some alt => go alt
| none =>
let alt ← filterTrivialCnstrs alt
if alt.cnstrs.isEmpty then
let eType ← inferType alt.rhs
let targetType ← mvarId.getType
unless (← isDefEqGuarded targetType eType) do
trace[Meta.Match.match] "assignGoalOf failed {eType} =?= {targetType}"
throwError "dependent elimination failed, type mismatch when solving alternative with type{indentExpr eType}\nbut expected{indentExpr targetType}"
mvarId.assign alt.rhs
modify fun s => { s with used := s.used.insert alt.idx }
return true
else
trace[Meta.Match.match] "alt has unsolved cnstrs:\n{← alt.toMessageData}"
return false
/--
Try to solve the problem by using the first alternative whose pending constraints can be resolved.
-/
private def processLeaf (p : Problem) : StateRefT State MetaM Unit :=
p.mvarId.withContext do
withPPForTacticGoal do trace[Meta.Match.match] "local context at processLeaf:\n{(← mkFreshTypeMVar).mvarId!}"
go p.alts
where
go (alts : List Alt) : StateRefT State MetaM Unit := do
match alts with
| [] =>
/- TODO: allow users to configure which tactic is used to close leaves. -/
unless (← p.mvarId.contradictionCore {}) do
trace[Meta.Match.match] "missing alternative"
p.mvarId.admit
modify fun s => { s with counterExamples := p.examples :: s.counterExamples }
| alt :: alts =>
unless (← solveCnstrs p.mvarId alt) do
go alts
private def processAsPattern (p : Problem) : MetaM Problem := withGoalOf p do
let x :: _ := p.vars | unreachable!
@ -216,62 +292,43 @@ private def processVariable (p : Problem) : MetaM Problem := withGoalOf p do
let x :: xs := p.vars | unreachable!
let alts ← p.alts.mapM fun alt => do
match alt.patterns with
| .inaccessible _ :: ps => return { alt with patterns := ps }
| .var fvarId :: ps => ({ alt with patterns := ps }).checkAndReplaceFVarId fvarId x
| _ => unreachable!
| .inaccessible e :: ps => return { alt with patterns := ps, cnstrs := (x, e) :: alt.cnstrs }
| .var fvarId :: ps =>
withExistingLocalDecls alt.fvarDecls do
if (← isDefEqGuarded (← fvarId.getType) (← inferType x)) then
return { alt with patterns := ps }.replaceFVarId fvarId x
else
return { alt with patterns := ps, cnstrs := (mkFVar fvarId, x) :: alt.cnstrs }
| _ => unreachable!
return { p with alts := alts, vars := xs }
/-
TODO: FIX the following issue.
/-!
Note that we decided to store pending constraints to address issues exposed by #1279 and #1361.
Here is a simplified version of the example on this issue (see test: `1279_simplified.lean`)
```lean
inductive Arrow : Type → Type → Type 1
| id : Arrow a a
| unit : Arrow Unit Unit
| comp : Arrow β γ → Arrow α β → Arrow α γ
deriving Repr
`fvarId` is not an alternative variable, and we used to return `false` here, but it is incorrect, and may
incorrectly discard applicable alternatives. It was buggy because of the way we handle inaccessible patterns
in variable transitions. The bug was exposed by issue #1279
Here is a simplified version of the example on this issue (see test: `1279_simplified.lean`)
```lean
inductive Arrow : Type → Type → Type 1
| id : Arrow a a
| unit : Arrow Unit Unit
| comp : Arrow β γ → Arrow α β → Arrow α γ
deriving Repr
def Arrow.compose (f : Arrow β γ) (g : Arrow α β) : Arrow α γ :=
match f, g with
| id, g => g
| f, id => f
| f, g => comp f g
```
The initial state for the `match`-expression above is
```lean
[Meta.Match.match] remaining variables: [β✝:(Type), γ✝:(Type), f✝:(Arrow β✝ γ✝), g✝:(Arrow α β✝)]
alternatives:
[β:(Type), g:(Arrow α β)] |- [β, .(β), (Arrow.id .(β)), g] => h_1 β g
[γ:(Type), f:(Arrow α γ)] |- [.(α), γ, f, (Arrow.id .(α))] => h_2 γ f
[β:(Type), γ:(Type), f:(Arrow β γ), g:(Arrow α β)] |- [β, γ, f, g] => h_3 β γ f g
```
The first step is a variable-transition which replaces `β` with `β✝` in the first and third alternatives.
The constraint `β✝ === α` in the second alternative is lost. Note that `α` is not an alternative variable.
After applying the variable-transition step twice, we reach the following state
```lean
[Meta.Match.match] remaining variables: [f✝:(Arrow β✝ γ✝), g✝:(Arrow α β✝)]
alternatives:
[g:(Arrow α β✝)] |- [(Arrow.id .(β✝)), g] => h_1 β✝ g
[f:(Arrow α γ✝)] |- [f, (Arrow.id .(α))] => h_2 γ✝ f
[f:(Arrow β✝ γ✝), g:(Arrow α β✝)] |- [f, g] => h_3 β✝ γ✝ f g
```
A constructor-transition should be used, and the functions `expandVarIntoCtor?` is required for the second and
third alternatives. There are 3 constructors, in the `Arrow.id` case, we use unify to solve
```
Arrow a a =?= Arrow α β✝
```
Where `a` is new alternative variable corresponding to the `Arrow.id` field.
The first assignment is fine `a := α`.
In the second assignment we have `α := β✝` where both `α` and `β✝` are not alternative variables.
We did not store information that `β✝ === α` in the first step, and the alternative was being incorrectly discarded.
Returning `true` here "solves" the problem, but it is a bit hackish. We see two possible improvements:
- We store the constraint `β✝ === α`.
- We postpone variable-transition steps.
It is unclear at this point what is the best solution. We should keep accumulating problematic examples.
def Arrow.compose (f : Arrow β γ) (g : Arrow α β) : Arrow α γ :=
match f, g with
| id, g => g
| f, id => f
| f, g => comp f g
```
The initial state for the `match`-expression above is
```lean
[Meta.Match.match] remaining variables: [β✝:(Type), γ✝:(Type), f✝:(Arrow β✝ γ✝), g✝:(Arrow α β✝)]
alternatives:
[β:(Type), g:(Arrow α β)] |- [β, .(β), (Arrow.id .(β)), g] => h_1 β g
[γ:(Type), f:(Arrow α γ)] |- [.(α), γ, f, (Arrow.id .(α))] => h_2 γ f
[β:(Type), γ:(Type), f:(Arrow β γ), g:(Arrow α β)] |- [β, γ, f, g] => h_3 β γ f g
```
The first step is a variable-transition which replaces `β` with `β✝` in the first and third alternatives.
The constraint `β✝ ≋ α` in the second alternative used to be discarded. We now store it at the
alternative `cnstrs` field.
-/
private def inLocalDecls (localDecls : List LocalDecl) (fvarId : FVarId) : Bool :=
@ -285,23 +342,17 @@ private def expandVarIntoCtor? (alt : Alt) (fvarId : FVarId) (ctorName : Name) :
let (ctorLevels, ctorParams) ← getInductiveUniverseAndParams expectedType
let ctor := mkAppN (mkConst ctorName ctorLevels) ctorParams
let ctorType ← inferType ctor
let ctorAbst? ← withNewMCtxDepth do
let (ctorFields, _, resultType) ← forallMetaTelescopeReducing ctorType
unless (← isDefEq resultType expectedType) do
return none
return some (← abstractMVars (mkAppN ctor ctorFields))
let some ctorAbst := ctorAbst? | return none
lambdaTelescope ctorAbst.expr fun newFVars ctor => do
let ctorArgs := ctor.getAppArgs
let ctorFields := ctorArgs[ctorParams.size:].toArray
forallTelescopeReducing ctorType fun ctorFields resultType => do
let ctor := mkAppN ctor ctorFields
let alt := alt.replaceFVarId fvarId ctor
let newAltDecls ← newFVars.mapM fun newFVar => newFVar.fvarId!.getDecl
let newAltDecls := newAltDecls.toList ++ alt.fvarDecls
let ctorFieldDecls ← ctorFields.mapM fun ctorField => ctorField.fvarId!.getDecl
let newAltDecls := ctorFieldDecls.toList ++ alt.fvarDecls
let mut cnstrs := alt.cnstrs
unless (← isDefEqGuarded resultType expectedType) do
cnstrs := (resultType, expectedType) :: cnstrs
trace[Meta.Match.unify] "expandVarIntoCtor? {mkFVar fvarId} : {expectedType}, ctor: {ctor}"
let ctorFieldPatterns := ctorFields.toList.map fun ctorField => match ctorField with
| .fvar fvarId => if inLocalDecls newAltDecls fvarId then Pattern.var fvarId else Pattern.inaccessible ctorField
| _ => Pattern.inaccessible ctorField
return some { alt with fvarDecls := newAltDecls, patterns := ctorFieldPatterns ++ alt.patterns }
let ctorFieldPatterns := ctorFieldDecls.toList.map fun decl => Pattern.var decl.fvarId
return some { alt with fvarDecls := newAltDecls, patterns := ctorFieldPatterns ++ alt.patterns, cnstrs }
private def getInductiveVal? (x : Expr) : MetaM (Option InductiveVal) := do
let xType ← inferType x
@ -411,34 +462,32 @@ private def processConstructor (p : Problem) : MetaM (Array Problem) := do
| _ => unreachable!
return { mvarId := subgoal.mvarId, vars := newVars, alts := newAlts, examples := examples }
private def altsAreCtorLike (p : Problem) : MetaM Bool := withGoalOf p do
p.alts.allM fun alt => do match alt.patterns with
| .ctor .. :: _ => return true
| .inaccessible e :: _ => return (← whnfD e).isConstructorApp (← getEnv)
| _ => return false
private def processNonVariable (p : Problem) : MetaM Problem := withGoalOf p do
let x :: xs := p.vars | unreachable!
let x ← whnfD x
let env ← getEnv
match x.constructorApp? env with
| some (ctorVal, xArgs) =>
let alts ← p.alts.filterMapM fun alt => do
match alt.patterns with
| .ctor n _ _ fields :: ps =>
if n != ctorVal.name then
return none
else
return some { alt with patterns := fields ++ ps }
| .inaccessible _ :: _ => processInaccessibleAsCtor alt ctorVal.name
| p :: _ => throwError "failed to compile pattern matching, inaccessible pattern or constructor expected{indentD p.toMessageData}"
| _ => unreachable!
let xFields := xArgs.extract ctorVal.numParams xArgs.size
return { p with alts := alts, vars := xFields.toList ++ xs }
| none =>
let alts ← p.alts.filterMapM fun alt => match alt.patterns with
| .inaccessible e :: ps => do
if (← isDefEq x e) then
return some { alt with patterns := ps }
else
return none
| p :: _ => throwError "failed to compile pattern matching, unexpected pattern{indentD p.toMessageData}\ndiscriminant{indentExpr x}"
| _ => unreachable!
return { p with alts := alts, vars := xs }
if let some (ctorVal, xArgs) := (← whnfD x).constructorApp? (← getEnv) then
if (← altsAreCtorLike p) then
let alts ← p.alts.filterMapM fun alt => do
match alt.patterns with
| .ctor ctorName _ _ fields :: ps =>
if ctorName != ctorVal.name then
return none
else
return some { alt with patterns := fields ++ ps }
| .inaccessible _ :: _ => processInaccessibleAsCtor alt ctorVal.name
| _ => unreachable!
let xFields := xArgs.extract ctorVal.numParams xArgs.size
return { p with alts := alts, vars := xFields.toList ++ xs }
let alts ← p.alts.mapM fun alt => do
match alt.patterns with
| p :: ps => return { alt with patterns := ps, cnstrs := (x, ← p.toExpr) :: alt.cnstrs }
| _ => unreachable!
return { p with alts := alts, vars := xs }
private def collectValues (p : Problem) : Array Expr :=
p.alts.foldl (init := #[]) fun values alt =>
@ -601,91 +650,44 @@ private def moveToFront (p : Problem) (i : Nat) : Problem :=
else
p
private partial def process (p : Problem) : StateRefT State MetaM Unit :=
search 0
where
/-- If `p.vars` is empty, then we are done. Otherwise, we process `p.vars[0]`. -/
tryToProcess (p : Problem) : StateRefT State MetaM Unit := withIncRecDepth do
traceState p
let isInductive ← isCurrVarInductive p
if isDone p then
processLeaf p
else if hasAsPattern p then
traceStep ("as-pattern")
let p ← processAsPattern p
process p
else if isNatValueTransition p then
traceStep ("nat value to constructor")
process (expandNatValuePattern p)
else if !isNextVar p then
traceStep ("non variable")
let p ← processNonVariable p
process p
else if isInductive && isConstructorTransition p then
let ps ← processConstructor p
ps.forM process
else if isVariableTransition p then
traceStep ("variable")
let p ← processVariable p
process p
else if isValueTransition p then
let ps ← processValue p
ps.forM process
else if isArrayLitTransition p then
let ps ← processArrayLit p
ps.forM process
else if hasNatValPattern p then
-- This branch is reachable when `p`, for example, is just values without an else-alternative.
-- We added it just to get better error messages.
traceStep ("nat value to constructor")
process (expandNatValuePattern p)
else
checkNextPatternTypes p
throwNonSupported p
/-- Return `true` if `type` does not depend on the first `i` elements in `xs` -/
checkVarDeps (xs : List Expr) (i : Nat) (type : Expr) : MetaM Bool := do
match i, xs with
| 0, _ => return true
| _, [] => unreachable!
| i+1, x::xs =>
if x.isFVar then
if (← dependsOn type x.fvarId!) then
return false
checkVarDeps xs i type
/--
Auxiliary method for `search`. Find next variable to "try".
`i` is the position of the variable s.t. `tryToProcess` failed.
We only consider variables that do not depend on other variables at `p.vars`. -/
findNext (i : Nat) : MetaM (Option Nat) := do
if h : i < p.vars.length then
let x := p.vars.get ⟨i, h⟩
if (← checkVarDeps p.vars i (← inferType x)) then
return i
else
findNext (i+1)
else
return none
/--
Auxiliary method for trying the next variable to process.
It moves variable `#i` to the front of the to-do list and invokes `tryToProcess`.
Note that for non-dependent elimination, variable `#0` always work.
If variable `#i` fails, we use `findNext` to select the next variable to try.
Remark: the "missing cases" error is not considered a failure. -/
search (i : Nat) : StateRefT State MetaM Unit := do
let s₁ ← getThe Meta.State
let s₂ ← get
let p' := moveToFront p i
try
tryToProcess p'
catch ex =>
match (← withGoalOf p <| findNext (i+1)) with
| none => throw ex
| some j =>
trace[Meta.Match.match] "failed with #{i}, trying #{j}{indentD ex.toMessageData}"
set s₁; set s₂; search j
private partial def process (p : Problem) : StateRefT State MetaM Unit := do
traceState p
let isInductive ← isCurrVarInductive p
if isDone p then
traceStep ("leaf")
processLeaf p
else if hasAsPattern p then
traceStep ("as-pattern")
let p ← processAsPattern p
process p
else if isNatValueTransition p then
traceStep ("nat value to constructor")
process (expandNatValuePattern p)
else if !isNextVar p then
traceStep ("non variable")
let p ← processNonVariable p
process p
else if isInductive && isConstructorTransition p then
let ps ← processConstructor p
ps.forM process
else if isVariableTransition p then
traceStep ("variable")
let p ← processVariable p
process p
else if isValueTransition p then
let ps ← processValue p
ps.forM process
else if isArrayLitTransition p then
let ps ← processArrayLit p
ps.forM process
else if hasNatValPattern p then
-- This branch is reachable when `p`, for example, is just values without an else-alternative.
-- We added it just to get better error messages.
traceStep ("nat value to constructor")
process (expandNatValuePattern p)
else
checkNextPatternTypes p
throwNonSupported p
private def getUElimPos? (matcherLevels : List Level) (uElim : Level) : MetaM (Option Nat) :=
if uElim == levelZero then

View file

@ -10,10 +10,10 @@
4
---- inv
10
match1.lean:82:0-82:73: error: type mismatch during dependent match-elimination at pattern variable 'w' with type
VecPred P Vec.nil
expected type
VecPred P tail✝
match1.lean:82:0-82:73: error: dependent elimination failed, type mismatch when solving alternative with type
motive 0 (Vec.cons head✝ Vec.nil) (_ : VecPred P (Vec.cons head✝ Vec.nil))
but expected
motive _fun_discr✝ (Vec.cons head✝ tail✝) (_ : VecPred P (Vec.cons head✝ tail✝))
[false, true, true]
match1.lean:119:0-119:41: error: dependent match elimination failed, inaccessible pattern found
.(j + j)

13
tests/lean/run/1361b.lean Normal file
View file

@ -0,0 +1,13 @@
def Multiset (α: Type) : Type := sorry
def Multiset.ndinsert [DecidableEq α](a : α): Multiset α → Multiset α := sorry
def Finset (α : Type _) : Type := @Subtype (Multiset α) sorry
def Finset.insert [DecidableEq α](a : α): Finset α → Finset α
| ⟨ms, prop⟩ => ⟨ms.ndinsert a, sorry⟩
inductive Bar : Finset Nat → Type
| insert : Bar (Finset.insert n Γ)
| empty : Bar Γ
example {Γ: Finset Nat}: ∀ (p: Bar Γ), Nat
| Bar.empty => 1 -- missing cases: (@Bar.insert _ (Subtype.mk _ _))
| _ => 2 -- redundant alternative

View file

@ -216,27 +216,3 @@ def ex3 (α : Type u) (β : Type v) (n : Nat) (x : List α) (y : List β) :
#eval test `ex3 2 `elimTest3
#print elimTest3
def ex4 (α : Type u) (n : Nat) (xs : Vec α n) :
LHS (Pat (inaccessible 0) × Pat (Vec.nil : Vec α 0))
× LHS (forall (n : Nat) (xs : Vec α (n+1)), Pat (inaccessible (n+1)) × Pat xs) :=
default
#eval test `ex4 2 `elimTest4
#print elimTest4
def ex5 (α : Type u) (n : Nat) (xs : Vec α n) :
LHS (Pat Nat.zero × Pat (Vec.nil : Vec α 0))
× LHS (forall (n : Nat) (xs : Vec α (n+1)), Pat (Nat.succ n) × Pat xs) :=
default
#eval test `ex5 2 `elimTest5
#print elimTest5
def ex6 (α : Type u) (n : Nat) (xs : Vec α n) :
LHS (Pat (inaccessible Nat.zero) × Pat (Vec.nil : Vec α 0))
× LHS (forall (N : Nat) (XS : Vec α N), Pat (inaccessible N) × Pat XS) :=
default
-- set_option trace.Meta.Match.match true
-- set_option trace.Meta.Match.debug true