{-# OPTIONS --cubical-compatible --safe #-}
module Tactic.Cong where
open import Function.Base using (_$_)
open import Data.Bool.Base using (true; false; if_then_else_; _∧_)
open import Data.Char.Base as Char using (toℕ)
open import Data.Float.Base as Float using (_≡ᵇ_)
open import Data.List.Base as List using ([]; _∷_)
open import Data.Maybe.Base as Maybe using (Maybe; just; nothing)
open import Data.Nat.Base as ℕ using (ℕ; zero; suc; _≡ᵇ_; _+_)
open import Data.Unit.Base using (⊤)
open import Data.Word64.Base as Word64 using (toℕ)
open import Data.Product.Base using (_×_; map₁; _,_)
open import Function using (flip; case_of_)
open import Relation.Binary.PropositionalEquality.Core using (_≡_; refl; cong)
open import Relation.Nullary.Decidable.Core using (yes; no)
import Agda.Builtin.String as String renaming (primStringEquality to _≡ᵇ_)
open import Reflection
open import Reflection.AST.Abstraction
open import Reflection.AST.AlphaEquality as Alpha
open import Reflection.AST.Argument as Arg
open import Reflection.AST.Argument.Information as ArgInfo
open import Reflection.AST.Argument.Visibility as Visibility
open import Reflection.AST.Literal as Literal
open import Reflection.AST.Meta as Meta
open import Reflection.AST.Name as Name
open import Reflection.AST.Term as Term
import Reflection.AST.Traversal as Traversal
open import Reflection.TCM.Syntax
open import Reflection.TCM.Utilities
⌞_⌟ : ∀ {a} {A : Set a} → A → A
⌞_⌟ x = x
private
varDescend : ℕ → ℕ → ℕ
varDescend ϕ x = if ϕ ℕ.≤ᵇ x then suc x else x
patternDescend : ℕ → Pattern → Pattern × ℕ
patternsDescend : ℕ → Args Pattern → Args Pattern × ℕ
patternDescend ϕ (con c ps) = map₁ (con c) (patternsDescend ϕ ps)
patternDescend ϕ (dot t) = dot t , ϕ
patternDescend ϕ (var x) = var (varDescend ϕ x) , suc ϕ
patternDescend ϕ (lit l) = lit l , ϕ
patternDescend ϕ (proj f) = proj f , ϕ
patternDescend ϕ (absurd x) = absurd (varDescend ϕ x) , suc ϕ
patternsDescend ϕ ((arg i p) ∷ ps) =
let (p' , ϕ') = patternDescend ϕ p
(ps' , ϕ'') = patternsDescend ϕ' ps
in (arg i p ∷ ps' , ϕ'')
patternsDescend ϕ [] =
[] , ϕ
notEqualityError : ∀ {A : Set} Term → TC A
notEqualityError goal = typeError (strErr "Cannot rewrite a goal that is not equality: " ∷ termErr goal ∷ [])
unificationError : ∀ {A : Set} → TC Term → TC Term → TC A
unificationError term symTerm = do
term' ← term
symTerm' ← symTerm
let symErr = case term' Term.≟ symTerm' of λ where
(yes _) → []
(no _) → strErr "\n" ∷ termErr symTerm' ∷ []
typeError (strErr "cong! failed, tried:\n" ∷ termErr term' ∷ symErr)
record EqualityGoal : Set where
constructor equals
field
level : Term
type : Term
lhs : Term
rhs : Term
destructEqualityGoal : Term → TC EqualityGoal
destructEqualityGoal goal@(def (quote _≡_) (lvl ∷ tp ∷ lhs ∷ rhs ∷ [])) =
pure $ equals (unArg lvl) (unArg tp) (unArg lhs) (unArg rhs)
destructEqualityGoal (meta m args) =
blockOnMeta m
destructEqualityGoal goal =
notEqualityError goal
`cong : ∀ {a} {A : Set a} {x y : A} → EqualityGoal → Term → x ≡ y → TC Term
`cong {a = a} {A = A} {x = x} {y = y} eqGoal f x≡y = do
let open EqualityGoal eqGoal
eq ← quoteTC x≡y
`a ← quoteTC a
`A ← quoteTC A
`x ← quoteTC x
`y ← quoteTC y
pure $ def (quote cong) $ `a ⟅∷⟆ `A ⟅∷⟆ level ⟅∷⟆ type ⟅∷⟆ vLam "ϕ" f ⟨∷⟩ `x ⟅∷⟆ `y ⟅∷⟆ eq ⟨∷⟩ []
private
antiUnify : ℕ → Term → Term → Term
antiUnifyArgs : ℕ → Args Term → Args Term → Maybe (Args Term)
antiUnifyClauses : ℕ → Clauses → Clauses → Maybe Clauses
antiUnifyClause : ℕ → Clause → Clause → Maybe Clause
pattern apply-⌞⌟ t = (def (quote ⌞_⌟) (_ ∷ _ ∷ arg _ t ∷ []))
antiUnify ϕ (var x args) (var y args') with x ℕ.≡ᵇ y | antiUnifyArgs ϕ args args'
... | _ | nothing = var ϕ []
... | false | just uargs = var ϕ uargs
... | true | just uargs = var (varDescend ϕ x) uargs
antiUnify ϕ (con c args) (con c' args') with c Name.≡ᵇ c' | antiUnifyArgs ϕ args args'
... | _ | nothing = var ϕ []
... | false | just uargs = var ϕ []
... | true | just uargs = con c uargs
antiUnify ϕ (def f args) (apply-⌞⌟ t) = antiUnify ϕ (def f args) t
antiUnify ϕ (def f args) (def f' args') with f Name.≡ᵇ f' | antiUnifyArgs ϕ args args'
... | _ | nothing = var ϕ []
... | false | just uargs = var ϕ []
... | true | just uargs = def f uargs
antiUnify ϕ (lam v (abs s t)) (lam _ (abs _ t')) =
lam v (abs s (antiUnify (suc ϕ) t t'))
antiUnify ϕ (pat-lam cs args) (pat-lam cs' args') with antiUnifyClauses ϕ cs cs' | antiUnifyArgs ϕ args args'
... | nothing | _ = var ϕ []
... | _ | nothing = var ϕ []
... | just ucs | just uargs = pat-lam ucs uargs
antiUnify ϕ (Π[ s ∶ arg i a ] b) (Π[ _ ∶ arg _ a' ] b') =
Π[ s ∶ arg i (antiUnify ϕ a a') ] antiUnify (suc ϕ) b b'
antiUnify ϕ (sort (set t)) (sort (set t')) =
sort (set (antiUnify ϕ t t'))
antiUnify ϕ (sort (lit n)) (sort (lit n')) with n ℕ.≡ᵇ n'
... | true = sort (lit n)
... | false = var ϕ []
antiUnify ϕ (sort (propLit n)) (sort (propLit n')) with n ℕ.≡ᵇ n'
... | true = sort (propLit n)
... | false = var ϕ []
antiUnify ϕ (sort (inf n)) (sort (inf n')) with n ℕ.≡ᵇ n'
... | true = sort (inf n)
... | false = var ϕ []
antiUnify ϕ (sort unknown) (sort unknown) =
sort unknown
antiUnify ϕ (lit l) (lit l') with l Literal.≡ᵇ l'
... | true = lit l
... | false = var ϕ []
antiUnify ϕ (meta x args) (meta x' args') with x Meta.≡ᵇ x' | antiUnifyArgs ϕ args args'
... | _ | nothing = var ϕ []
... | false | _ = var ϕ []
... | true | just uargs = meta x uargs
antiUnify ϕ unknown unknown = unknown
antiUnify ϕ _ _ = var ϕ []
antiUnifyArgs ϕ (arg i t ∷ args) (arg _ t' ∷ args') =
Maybe.map (arg i (antiUnify ϕ t t') ∷_) (antiUnifyArgs ϕ args args')
antiUnifyArgs ϕ [] [] =
just []
antiUnifyArgs ϕ _ _ =
nothing
antiUnifyClause ϕ (clause Γ pats t) (clause Δ pats' t') =
Maybe.when ((Γ =α= Δ) ∧ (pats =α= pats'))
let (upats , ϕ') = patternsDescend ϕ pats
in clause Γ upats (antiUnify ϕ' t t')
antiUnifyClause ϕ (absurd-clause Γ pats) (absurd-clause Δ pats') =
Maybe.when ((Γ =α= Δ) ∧ (pats =α= pats')) $
absurd-clause Γ pats
antiUnifyClause ϕ _ _ =
nothing
antiUnifyClauses ϕ (c ∷ cs) (c' ∷ cs') =
Maybe.ap (Maybe.map _∷_ (antiUnifyClause ϕ c c')) (antiUnifyClauses ϕ cs cs')
antiUnifyClauses ϕ _ _ =
just []
macro
cong! : ∀ {a} {A : Set a} {x y : A} → x ≡ y → Term → TC ⊤
cong! x≡y hole =
withNormalisation false $ do
goal ← inferType hole
eqGoal ← destructEqualityGoal goal
let makeTerm = λ lhs rhs → `cong eqGoal (antiUnify 0 lhs rhs) x≡y
let lhs = EqualityGoal.lhs eqGoal
let rhs = EqualityGoal.rhs eqGoal
let term = makeTerm lhs rhs
let symTerm = makeTerm rhs lhs
let uni = _>>= flip unify hole
catchTC (uni term) $
catchTC (uni symTerm) $ do
blockOnMetas goal
unificationError term symTerm