packages feed

ghc-magic-dict-compat-0.0.0.0: src/GHC/Magic/Dict/Plugin/Old.hs

{-# 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 {..}