{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module GHC.Magic.Dict.Plugin.Old (plugin) where
import Control.Applicative (liftA2)
import Data.Bitraversable (bitraverse)
import qualified Data.DList as DL
import Data.Maybe (mapMaybe)
import GHC.Builtin.Types.Prim (openAlphaTy, openAlphaTyVar, runtimeRep1TyVar)
import qualified GHC.Core as Core
import GHC.Core.Class
import GHC.Core.Coercion (mkSubCo, mkSymCo, mkTransCo)
import GHC.Core.DataCon
import GHC.Core.Make (mkCoreLams)
import GHC.Core.Predicate
import GHC.Core.TyCon
import GHC.Core.Type
import GHC.Data.FastString
import GHC.Plugins (Plugin (..), defaultPlugin, mkModuleName, purePlugin)
import GHC.Tc.Instance.Family (tcInstNewTyCon_maybe)
import GHC.Tc.Plugin hiding (newWanted)
import GHC.Tc.Types
import GHC.Tc.Types.Constraint
import GHC.Tc.Types.Evidence
import GHC.TcPluginM.Extra
import GHC.Types.Id
import GHC.Types.Name
import GHC.Utils.Outputable
plugin :: Plugin
plugin =
defaultPlugin
{ tcPlugin = const $ Just withDictPlugin
, pluginRecompile = purePlugin
}
withDictPlugin :: TcPlugin
withDictPlugin =
tracePlugin
"WithDictPlugin"
TcPlugin
{ tcPluginStop = const $ pure ()
, tcPluginSolve = const solveWithDict
, tcPluginInit = pure ()
}
data Info = Info
{ _WithDict :: !Class
, _WithDictDataCon :: !DataCon
}
solveWithDict :: TcPluginSolver
solveWithDict _ _ [] = pure $ TcPluginOk [] []
solveWithDict gs _ wanteds = do
let subs = map fst $ mkSubst' gs
info <- lookupInfo
let withDicts =
mapMaybe
( liftA2 (,)
<$> (liftA2 (,) <$> pure . ctLoc <*> decodeWithDictPred info . ctPred . substCt subs)
<*> pure
)
wanteds
(contrs, solved, wants) <-
foldMap
( \case
(Nothing, ct) -> (DL.singleton ct, mempty, mempty)
(Just (pf, newWants), ct) -> (mempty, DL.singleton (pf, ct), DL.fromList newWants)
)
<$> mapM (bitraverse (uncurry $ solveWithDictPred info) pure) withDicts
tcPluginTrace
"solveWithDict/contradictions"
(ppr $ DL.toList contrs)
tcPluginTrace "solveWithDict/solveds" $ ppr $ DL.toList solved
tcPluginTrace "solveWithDict/newWanteds" $ ppr $ DL.toList wants
pure $
if null contrs
then TcPluginOk (DL.toList solved) (DL.toList wants)
else TcPluginContradiction $ DL.toList contrs
mkNonCanonical' ::
CtLoc -> CtEvidence -> Ct
mkNonCanonical' origCtl ev =
let ct_ls = ctLocSpan origCtl
ctl = ctEvLoc ev
wanted = mkNonCanonical ev
in setCtLoc wanted (setCtLocSpan ctl ct_ls)
solveWithDictPred :: Info -> CtLoc -> DecodedPred -> TcPluginM (Maybe (EvTerm, [Ct]))
solveWithDictPred Info {..} loc DecodedPred {..} = do
tcPluginTrace "solveWithDictPred" (ppr (constraint, argType))
case tcInstNewTyCon_maybe constrTyCon constrArgs of
Nothing -> do
tcPluginTrace "solveWithDictPred: Failed!" (ppr (constraint, argType))
pure Nothing
Just (onlyMethodType, co) -> do
tcPluginTrace "solveWithDictPred: singleton class found" (ppr (constraint, argType, onlyMethodType, co))
let nomEq = mkPrimEqPred argType onlyMethodType
hole <- newCoercionHole nomEq
let want = CtWanted nomEq (HoleDest hole) WDeriv loc
sv <- unsafeTcPluginTcM $ mkSysLocalM (fsLit "withDict_s") Many argType
k <- unsafeTcPluginTcM $ mkSysLocalM (fsLit "withDict_k") Many (mkInvisFunTy Many constraint openAlphaTy)
-- Given co2 : mty ~N# inst_meth_ty, construct the method of
-- the WithDict dictionary:
--
-- \@(r :: RuntimeRep) @(a :: TYPE r) (sv :: mty) (k :: cls => a) ->
-- k (sv |> (sub co ; sym co2))
let proof =
evDataConApp
_WithDictDataCon
[constraint, argType]
[ mkCoreLams [runtimeRep1TyVar, openAlphaTyVar, sv, k] $
Core.Var k
`Core.App` (Core.Var sv `Core.Cast` mkTransCo (mkSubCo (ctEvCoercion want)) (mkSymCo co))
]
pure $ Just (proof, [mkNonCanonical' loc want])
data DecodedPred = DecodedPred
{ constraint :: !PredType
, constrTyCon :: !TyCon
, constrArgs :: ![Type]
, argType :: !Type
}
decodeWithDictPred :: Info -> PredType -> Maybe DecodedPred
decodeWithDictPred Info {..} pt
| ClassPred withDic [cls, argType] <- classifyPredType pt
, withDic == _WithDict
, Just (dict_tc, dict_args) <- tcSplitTyConApp_maybe cls =
pure
DecodedPred
{ constraint = cls
, constrTyCon = dict_tc
, constrArgs = dict_args
, ..
}
| otherwise = Nothing
lookupInfo :: TcPluginM Info
lookupInfo = do
theMod <-
lookupModule
(mkModuleName "GHC.Magic.Dict.Compat")
(fsLit "ghc-magic-dict-compat")
_WithDict <- tcLookupClass =<< lookupOrig theMod (mkTcOcc "WithDict")
let _WithDictDataCon = classDataCon _WithDict
pure Info {..}