{-| Copyright : (C) 2015-2016, University of Twente License : BSD2 (see the file LICENSE) Maintainer : Christiaan Baaij -} {-# LANGUAGE CPP #-} module GHC.TypeLits.Extra.Solver.Unify ( ExtraDefs (..) , UnifyResult (..) , normaliseNat , unifyExtra ) where -- external import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT (..)) import Data.Function (on) -- GHC API import Outputable (Outputable (..), ($$), text) import TcPluginM (TcPluginM, matchFam, tcPluginTrace) import TcRnMonad (Ct) import TcTypeNats (typeNatExpTyCon) import Type (TyVar, coreView, mkNumLitTy, mkTyConApp, mkTyVarTy) import TyCon (TyCon) #if __GLASGOW_HASKELL__ >= 711 import TyCoRep (Type (..), TyLit (..)) #else import TypeRep (Type (..), TyLit (..)) #endif import UniqSet (UniqSet, emptyUniqSet, unionUniqSets, unitUniqSet) -- internal import GHC.TypeLits.Extra.Solver.Operations data ExtraDefs = ExtraDefs { gcdTyCon :: TyCon , clogTyCon :: TyCon } normaliseNat :: ExtraDefs -> Type -> MaybeT TcPluginM ExtraOp normaliseNat defs ty | Just ty1 <- coreView ty = normaliseNat defs ty1 normaliseNat _ (TyVarTy v) = pure (V v) normaliseNat _ (LitTy (NumTyLit i)) = pure (I i) normaliseNat defs (TyConApp tc [x,y]) | tc == gcdTyCon defs = mergeGCD <$> normaliseNat defs x <*> normaliseNat defs y | tc == clogTyCon defs = do x' <- normaliseNat defs x y' <- normaliseNat defs y MaybeT (return (mergeCLog x' y')) | tc == typeNatExpTyCon = mergeExp <$> normaliseNat defs x <*> normaliseNat defs y normaliseNat defs (TyConApp tc tys) = do tys' <- mapM (fmap (reifyEOP defs) . normaliseNat defs) tys tyM <- lift (matchFam tc tys') case tyM of Just (_,ty) -> normaliseNat defs ty _ -> return (C (EType (TyConApp tc tys))) normaliseNat _ t = return (C (EType t)) -- | Result of comparing two 'SOP' terms, returning a potential substitution -- list under which the two terms are equal. data UnifyResult = Win -- ^ Two terms are equal | Lose -- ^ Two terms are /not/ equal | Draw -- ^ We don't know if the two terms are equal instance Outputable UnifyResult where ppr Win = text "Win" ppr Lose = text "Lose" ppr Draw = text "Draw" unifyExtra :: Ct -> ExtraOp -> ExtraOp -> TcPluginM UnifyResult unifyExtra ct u v = do tcPluginTrace "unifyExtra" (ppr ct $$ ppr u $$ ppr v) return (unifyExtra' ct u v) unifyExtra' :: Ct -> ExtraOp -> ExtraOp -> UnifyResult unifyExtra' _ u v | eqFV u v = if u == v then Win else if containsConstants u || containsConstants v then Draw else Lose | otherwise = Draw fvOP :: ExtraOp -> UniqSet TyVar fvOP (I _) = emptyUniqSet fvOP (V v) = unitUniqSet v fvOP (C _) = emptyUniqSet fvOP (GCD x y) = fvOP x `unionUniqSets` fvOP y fvOP (CLog x y) = fvOP x `unionUniqSets` fvOP y fvOP (Exp x y) = fvOP x `unionUniqSets` fvOP y eqFV :: ExtraOp -> ExtraOp -> Bool eqFV = (==) `on` fvOP reifyEOP :: ExtraDefs -> ExtraOp -> Type reifyEOP _ (I i) = mkNumLitTy i reifyEOP _ (V v) = mkTyVarTy v reifyEOP _ (C (EType c)) = c reifyEOP defs (GCD x y) = mkTyConApp (gcdTyCon defs) [reifyEOP defs x ,reifyEOP defs y] reifyEOP defs (CLog x y) = mkTyConApp (clogTyCon defs) [reifyEOP defs x ,reifyEOP defs y] reifyEOP defs (Exp x y) = mkTyConApp typeNatExpTyCon [reifyEOP defs x ,reifyEOP defs y] containsConstants :: ExtraOp -> Bool containsConstants (I _) = False containsConstants (V _) = False containsConstants (C _) = True containsConstants (GCD x y) = containsConstants x || containsConstants y containsConstants (CLog x y) = containsConstants x || containsConstants y containsConstants (Exp x y) = containsConstants x || containsConstants y