summaryrefslogtreecommitdiff
path: root/testsuite/tests/tcplugins/CtIdPlugin.hs
blob: 2511c902d3831a46baeb80bd14b6b0803196667b (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
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ViewPatterns #-}

module CtIdPlugin where

-- base
import Data.Maybe
import Data.Traversable

-- ghc
import GHC.Core.Class
import GHC.Core.Coercion
import GHC.Core.DataCon
import GHC.Core.Make
import GHC.Core.Predicate
import GHC.Core.TyCo.Rep
import GHC.Plugins
import GHC.Tc.Plugin
import GHC.Tc.Types
import GHC.Tc.Types.Constraint
import GHC.Tc.Types.Evidence

-- common
import Common

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

-- This plugin simplifies Given and Wanted 'CtId' constraints.
-- To do this, we just look through the Givens and Wanteds,
-- find any irreducible constraint whose TyCon matches that of 'CtId',
-- in which case we substitute it for its argument:
-- We create a new Given or Wanted and remove the old one using a cast.

plugin :: Plugin
plugin = mkPlugin solver don'tRewrite

-- Solve "CtId".
solver :: [String]
       -> PluginDefs -> EvBindsVar -> [Ct] -> [Ct]
       -> TcPluginM TcPluginSolveResult
solver _args defs ev givens wanteds = do
  let pluginCo = mkUnivCo (PluginProv "CtIdPlugin") Representational
  let substEvidence ct ct' =
        evCast (ctEvExpr $ ctEvidence ct') $ pluginCo (ctPred ct') (ctPred ct)

  if null wanteds
    then do
      newGivenPredTypes <- traverse (solveCt defs) givens
      newGivens <- for (zip newGivenPredTypes givens) \case
        (Nothing, _) -> return Nothing
        (Just pred, ct) ->
          let EvExpr expr =
                evCast (ctEvExpr $ ctEvidence ct) $ pluginCo (ctPred ct) pred
          in Just . mkNonCanonical <$> newGiven ev (ctLoc ct) pred expr
      let removedGivens =
            [ (substEvidence ct ct', ct)
            | (Just ct', ct) <- zip newGivens givens
            ]
      pure $ TcPluginOk removedGivens (catMaybes newGivens)
    else do
      newWantedPredTypes <- traverse (solveCt defs) wanteds
      newWanteds <- for (zip newWantedPredTypes wanteds) \case
        (Nothing, _) -> return Nothing
        (Just pred, ct) -> do
          evidence <- newWanted (ctLoc ct) pred
          return $ Just (mkNonCanonical evidence)
      let removedWanteds =
            [ (substEvidence ct ct', ct)
            | (Just ct', ct) <- zip newWanteds wanteds
            ]
      pure $ TcPluginOk removedWanteds (catMaybes newWanteds)

solveCt :: PluginDefs -> Ct -> TcPluginM (Maybe PredType)
solveCt (PluginDefs {..}) ct@(classifyPredType . ctPred -> IrredPred pred)
  | Just (tyCon, [arg]) <- splitTyConApp_maybe pred
  , tyCon == ctIdFam
  = pure $ Just arg
solveCt _ ct = pure Nothing