176 lines
7.2 KiB
Text
176 lines
7.2 KiB
Text
/-
|
||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Lean.Meta.Basic
|
||
import Lean.Meta.Tactic.Refl
|
||
import Lean.Meta.Tactic.Cases
|
||
import Lean.Meta.Tactic.Assumption
|
||
import Lean.Meta.Tactic.Simp.Main
|
||
import Lean.Meta.SameCtorUtils
|
||
|
||
public section
|
||
|
||
namespace Lean.Meta
|
||
|
||
private def mkAnd? (args : Array Expr) : Option Expr := Id.run do
|
||
if args.isEmpty then
|
||
return none
|
||
else
|
||
let mut result := args.back!
|
||
for arg in args.reverse[1...*] do
|
||
result := mkApp2 (mkConst ``And) arg result
|
||
return result
|
||
|
||
def elimOptParam (type : Expr) : CoreM Expr := do
|
||
Core.transform type fun e =>
|
||
if e.isAppOfArity ``optParam 2 then
|
||
return TransformStep.visit (e.getArg! 0)
|
||
else
|
||
return .continue
|
||
|
||
private partial def mkInjectiveTheoremTypeCore? (ctorVal : ConstructorVal) (useEq : Bool) : MetaM (Option Expr) := do
|
||
let us := ctorVal.levelParams.map mkLevelParam
|
||
let type ← elimOptParam ctorVal.type
|
||
forallBoundedTelescope type ctorVal.numParams fun params type =>
|
||
forallTelescope type fun args1 resultType => do
|
||
let jp (args2 args2New : Array Expr) : MetaM (Option Expr) := do
|
||
let lhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args1
|
||
let rhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args2
|
||
let eq ← mkEq lhs rhs
|
||
let mut eqs := #[]
|
||
for arg1 in args1, arg2 in args2 do
|
||
let arg1Type ← inferType arg1
|
||
if !(← isProp arg1Type) && arg1 != arg2 then
|
||
eqs := eqs.push (← mkEqHEq arg1 arg2)
|
||
if let some andEqs := mkAnd? eqs then
|
||
let result ← if useEq then
|
||
mkEq eq andEqs
|
||
else
|
||
mkArrow eq andEqs
|
||
mkForallFVars params (← mkForallFVars args1 (← mkForallFVars args2New result))
|
||
else
|
||
return none
|
||
let rec mkArgs2 (i : Nat) (type : Expr) (args2 args2New : Array Expr) : MetaM (Option Expr) := do
|
||
if h : i < args1.size then
|
||
match (← whnf type) with
|
||
| Expr.forallE n d b _ =>
|
||
let arg1 := args1[i]
|
||
if occursOrInType (← getLCtx) arg1 resultType then
|
||
mkArgs2 (i + 1) (b.instantiate1 arg1) (args2.push arg1) args2New
|
||
else
|
||
withLocalDecl n (if useEq then BinderInfo.default else BinderInfo.implicit) d fun arg2 =>
|
||
mkArgs2 (i + 1) (b.instantiate1 arg2) (args2.push arg2) (args2New.push arg2)
|
||
| _ => throwError "unexpected constructor type for `{ctorVal.name}`"
|
||
else
|
||
jp args2 args2New
|
||
if useEq then
|
||
mkArgs2 0 type #[] #[]
|
||
else
|
||
withNewBinderInfos (params.map fun param => (param.fvarId!, BinderInfo.implicit)) <|
|
||
withNewBinderInfos (args1.map fun arg1 => (arg1.fvarId!, BinderInfo.implicit)) <|
|
||
mkArgs2 0 type #[] #[]
|
||
|
||
private def mkInjectiveTheoremType? (ctorVal : ConstructorVal) : MetaM (Option Expr) :=
|
||
mkInjectiveTheoremTypeCore? ctorVal false
|
||
|
||
private def injTheoremFailureHeader (ctorName : Name) : MessageData :=
|
||
m!"failed to prove injectivity theorem for constructor `{ctorName}`, use 'set_option genInjectivity false' to disable the generation"
|
||
|
||
private def throwInjectiveTheoremFailure {α} (ctorName : Name) (mvarId : MVarId) : MetaM α :=
|
||
throwError "{injTheoremFailureHeader ctorName}{indentD <| MessageData.ofGoal mvarId}"
|
||
|
||
private def solveEqOfCtorEq (ctorName : Name) (mvarId : MVarId) (h : FVarId) : MetaM Unit := do
|
||
match (← injection mvarId h) with
|
||
| InjectionResult.solved => unreachable!
|
||
| InjectionResult.subgoal mvarId .. =>
|
||
(← mvarId.splitAnd).forM fun mvarId =>
|
||
unless (← mvarId.assumptionCore) do
|
||
throwInjectiveTheoremFailure ctorName mvarId
|
||
|
||
private def mkInjectiveTheoremValue (ctorName : Name) (targetType : Expr) : MetaM Expr :=
|
||
forallTelescopeReducing targetType fun xs type => do
|
||
let mvar ← mkFreshExprSyntheticOpaqueMVar type
|
||
solveEqOfCtorEq ctorName mvar.mvarId! xs.back!.fvarId!
|
||
mkLambdaFVars xs mvar
|
||
|
||
def mkInjectiveTheoremNameFor (ctorName : Name) : Name :=
|
||
ctorName ++ `inj
|
||
|
||
private def mkInjectiveTheorem (ctorVal : ConstructorVal) : MetaM Unit := do
|
||
let some type ← mkInjectiveTheoremType? ctorVal
|
||
| return ()
|
||
let value ← mkInjectiveTheoremValue ctorVal.name type
|
||
let name := mkInjectiveTheoremNameFor ctorVal.name
|
||
addDecl <| Declaration.thmDecl {
|
||
name
|
||
levelParams := ctorVal.levelParams
|
||
type := (← instantiateMVars type)
|
||
value := (← instantiateMVars value)
|
||
}
|
||
|
||
def mkInjectiveEqTheoremNameFor (ctorName : Name) : Name :=
|
||
ctorName ++ `injEq
|
||
|
||
private def mkInjectiveEqTheoremType? (ctorVal : ConstructorVal) : MetaM (Option Expr) :=
|
||
mkInjectiveTheoremTypeCore? ctorVal true
|
||
|
||
private def mkInjectiveEqTheoremValue (ctorName : Name) (targetType : Expr) : MetaM Expr := do
|
||
forallTelescopeReducing targetType fun xs type => do
|
||
let mvar ← mkFreshExprSyntheticOpaqueMVar type
|
||
let [mvarId₁, mvarId₂] ← mvar.mvarId!.apply (mkConst ``Eq.propIntro)
|
||
| throwError "unexpected number of subgoals when proving injective theorem for constructor `{ctorName}`"
|
||
let (h, mvarId₁) ← mvarId₁.intro1
|
||
let (_, mvarId₂) ← mvarId₂.intro1
|
||
solveEqOfCtorEq ctorName mvarId₁ h
|
||
let mvarId₂ ← mvarId₂.casesAnd
|
||
if let some mvarId₂ ← mvarId₂.substEqs then
|
||
try mvarId₂.refl catch _ => throwError (injTheoremFailureHeader ctorName)
|
||
mkLambdaFVars xs mvar
|
||
|
||
private def mkInjectiveEqTheorem (ctorVal : ConstructorVal) : MetaM Unit := do
|
||
let some type ← mkInjectiveEqTheoremType? ctorVal
|
||
| return ()
|
||
let value ← mkInjectiveEqTheoremValue ctorVal.name type
|
||
let name := mkInjectiveEqTheoremNameFor ctorVal.name
|
||
addDecl <| Declaration.thmDecl {
|
||
name
|
||
levelParams := ctorVal.levelParams
|
||
type := (← instantiateMVars type)
|
||
value := (← instantiateMVars value)
|
||
}
|
||
addSimpTheorem (ext := simpExtension) name (post := true) (inv := false) AttributeKind.global (prio := eval_prio default)
|
||
|
||
register_builtin_option genInjectivity : Bool := {
|
||
defValue := true
|
||
descr := "generate injectivity theorems for inductive datatype constructors. \
|
||
Temporarily (for bootstrapping reasons) also controls the generation of the
|
||
`ctorIdx` definition."
|
||
}
|
||
|
||
def mkInjectiveTheorems (declName : Name) : MetaM Unit := do
|
||
if (← getEnv).contains ``Eq.propIntro && genInjectivity.get (← getOptions) && !(← isInductivePredicate declName) then
|
||
withTraceNode `Meta.injective (fun _ => return m!"{declName}") do
|
||
let info ← getConstInfoInduct declName
|
||
unless info.isUnsafe do
|
||
-- We need to reset the local context here because `solveEqOfCtorEq` uses
|
||
-- `assumptionCore`, which can reference "outside" free variables that
|
||
-- were not introduced by `mkInjective(Eq)Theorem` and are not abstracted
|
||
-- by `mkLambdaFVars`, thus adding a declaration with free variables.
|
||
-- See https://github.com/leanprover/lean4/issues/2188
|
||
withLCtx {} {} do
|
||
for ctor in info.ctors do
|
||
withExporting (isExporting := !isPrivateName ctor) do
|
||
let ctorVal ← getConstInfoCtor ctor
|
||
if ctorVal.numFields > 0 then
|
||
mkInjectiveTheorem ctorVal
|
||
mkInjectiveEqTheorem ctorVal
|
||
|
||
builtin_initialize
|
||
registerTraceClass `Meta.injective
|
||
|
||
end Lean.Meta
|