summaryrefslogtreecommitdiff
path: root/testsuite/tests/tcplugins/TyFamPlugin.hs
blob: 523bdc10c19343ff8e825111ee170bf5b155ac72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}

module TyFamPlugin where
-- Solving an equality constraint involving type families.

-- base
import Data.Maybe
  ( catMaybes )

-- ghc
import GHC.Builtin.Types
  ( unitTy )
import GHC.Core
  ( Expr(Coercion) )
import GHC.Core.Coercion
  ( mkUnivCo )
import GHC.Core.Predicate
  ( EqRel(NomEq), Pred(EqPred)
  , classifyPredType
  )
import GHC.Core.TyCo.Rep
  ( Type, UnivCoProvenance(PluginProv) )
import GHC.Core.Type
  ( eqType, mkTyConApp, splitTyConApp_maybe )
import GHC.Plugins
  ( Plugin )
import GHC.Tc.Plugin
  ( TcPluginM
  , unsafeTcPluginTcM
  )
import GHC.Tc.Types
  ( TcPluginResult(..) )
import GHC.Tc.Types.Constraint
  ( Ct(..), CanEqLHS(..)
  , ctPred
  )
import GHC.Tc.Types.Evidence
  ( EvTerm(EvExpr), Role(Nominal) )

-- common
import Common
  ( PluginDefs(..)
  , mkPlugin
  )

--------------------------------------------------------------------------------

-- This plugin solves Wanted constraints of the form "MyTyFam a a ~ ()".
--
-- It does so by looking through the Wanted constraints for equality constraints
-- whose left hand side is a type-family application "MyTyFam arg1 arg2",
-- such that "arg1 `eqType` arg2" returns "True",
-- and whose left hand side is the unit type "()".
--
-- In such a case, the plugin creates a universal coercion
-- with Plugin provenance to prove the equality constraint.

plugin :: Plugin
plugin = mkPlugin solver

solver :: [String]
       -> PluginDefs -> [Ct] -> [Ct] -> [Ct]
       -> TcPluginM TcPluginResult
solver _args defs _gs _ds ws = do
  solved <- catMaybes <$> traverse ( solveCt defs ) ws
  pure $ TcPluginOk solved []

-- Solve `MyTyFam a a ~ ()`.
solveCt :: PluginDefs -> Ct -> TcPluginM ( Maybe (EvTerm, Ct) )
solveCt ( PluginDefs {..} ) ct@( classifyPredType . ctPred -> EqPred NomEq lhs rhs )
  | Just ( tyFam, [arg1, arg2] ) <- splitTyConApp_maybe lhs
  , tyFam == myTyFam
  , arg1 `eqType` arg2
  , rhs `eqType` unitTy
  , let
      evTerm :: EvTerm
      evTerm = EvExpr . Coercion
             $ mkUnivCo ( PluginProv "TyFamPlugin" ) Nominal lhs rhs
  = pure $ Just ( evTerm, ct )
solveCt _ ct = pure Nothing