singletons-presburger-0.7.4.1: src/Data/Singletons/TypeNats/Presburger.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module Data.Singletons.TypeNats.Presburger (
plugin,
singletonTranslation,
) where
import Control.Monad
import Control.Monad.Trans (MonadTrans (lift))
import Control.Monad.Trans.Maybe
import Data.Maybe (fromMaybe)
import Data.Reflection (Given, give, given)
import qualified Debug.Trace as DT
import GHC.Plugins (Outputable, (<+>))
import GHC.TypeLits.Presburger.Compat
import GHC.TypeLits.Presburger.Types
plugin :: Plugin
plugin =
pluginWith $
(<>) <$> defaultTranslation <*> singletonTranslation
data SingletonCons = SingletonCons
{ singApply :: TyCon
, singNatLeq :: TyCon
, singNatGeq :: TyCon
, singNatLt :: TyCon
, singNatGt :: TyCon
, singNatPlus :: TyCon
, singNatMinus :: TyCon
, singNatTimes :: TyCon
, singNatCompare :: TyCon
, singTrueSym0 :: TyCon
, singFalseSym0 :: TyCon
, caseNameForSingLeq :: [ComparisonMethod]
, caseNameForSingGeq :: [ComparisonMethod]
, caseNameForSingLt :: [ComparisonMethod]
, caseNameForSingGt :: [ComparisonMethod]
, singMin :: TyCon
, singMax :: TyCon
, caseNameForMin :: [ComparisonMethod]
, caseNameForMax :: [ComparisonMethod]
}
data ComparisonMethod = CaseFun TyCon | DirectTFHelper TyCon
deriving (Eq)
instance Outputable ComparisonMethod where
ppr (CaseFun con) = text "CaseFun" <+> ppr con
ppr (DirectTFHelper con) = text "DirectTFHelper" <+> ppr con
singletonTranslation ::
TcPluginM Translation
singletonTranslation = toTranslation <$> genSingletonCons
toTranslation ::
SingletonCons -> Translation
toTranslation scs@SingletonCons {..} =
give scs $
mempty
{ natLeqBool = [singNatLeq]
, natGeqBool = [singNatGeq]
, natLtBool = [singNatLt]
, natGtBool = [singNatGt]
, natCompare = [singNatCompare]
, natPlus = [singNatPlus]
, natMinus = [singNatMinus]
, natTimes = [singNatTimes]
, parsePred = parseSingPred
, parseExpr = parseSingExpr
, trueData = [singTrueSym0]
, natMin = [singMin]
, natMax = [singMax]
, falseData = [singFalseSym0]
}
singBasePackage :: FastString
singBasePackage = "singletons-base"
singTopPackage :: FastString
singTopPackage = "singletons"
ordModName, numModName, prelInstName, dataSingletonsName :: ModuleName
ordModName = mkModuleName "Data.Ord.Singletons"
numModName = mkModuleName "GHC.Num.Singletons"
prelInstName = mkModuleName "Data.Singletons.Base.Instances"
dataSingletonsName = mkModuleName "Data.Singletons"
genSingletonCons :: TcPluginM SingletonCons
genSingletonCons = do
tcPluginTrace "sing: Start singleton consing" ""
dataSingletons <- lookupModule dataSingletonsName singTopPackage
singletonOrd <- lookupModule ordModName singBasePackage
tcPluginTrace "sing: singletonOrd" $ ppr singletonOrd
let singUnit = moduleUnit' singletonOrd
prel = mkModule singUnit prelInstName
singletonsNum = mkModule singUnit numModName
singApply <- tcLookupTyCon =<< lookupOrig dataSingletons (mkTcOcc "Apply")
singTrueSym0 <- tcLookupTyCon =<< lookupOrig prel (mkTcOcc "TrueSym0")
tcPluginTrace "sing: singTrueSym0" $ ppr singTrueSym0
singFalseSym0 <- tcLookupTyCon =<< lookupOrig prel (mkTcOcc "FalseSym0")
tcPluginTrace "sing: singFalseSym0" $ ppr singFalseSym0
singNatLeq <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc "<=")
tcPluginTrace "sing: singNatLeq" $ ppr singNatLeq
singNatLt <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc "<")
tcPluginTrace "sing: singNatLt" $ ppr singNatLt
singNatGeq <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc ">=")
tcPluginTrace "sing: singNatGeq" $ ppr singNatGeq
singNatGt <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc ">")
tcPluginTrace "sing: singNatGt" $ ppr singNatGt
singNatPlus <- tcLookupTyCon =<< lookupOrig singletonsNum (mkTcOcc "+")
tcPluginTrace "sing: singNatPlus" $ ppr singNatPlus
singNatTimes <- tcLookupTyCon =<< lookupOrig singletonsNum (mkTcOcc "*")
tcPluginTrace "sing: singNatTimes" $ ppr singNatTimes
singNatMinus <- tcLookupTyCon =<< lookupOrig singletonsNum (mkTcOcc "-")
tcPluginTrace "sing: singNatMinus" $ ppr singNatMinus
singMin <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc "Min")
tcPluginTrace "sing: singNatMin" $ ppr singMin
singMax <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc "Max")
tcPluginTrace "sing: singNatMax" $ ppr singMax
caseNameForSingLeq <- getCaseNameForSingletonBinRel singNatLeq
tcPluginTrace "sing: caseNameForSingLeq" $ ppr caseNameForSingLeq
caseNameForSingLt <- getCaseNameForSingletonBinRel singNatLt
caseNameForSingGeq <- getCaseNameForSingletonBinRel singNatGeq
caseNameForSingGt <- getCaseNameForSingletonBinRel singNatGt
caseNameForMin <- getCaseNameForSingletonBinOp singMin
caseNameForMax <- getCaseNameForSingletonBinOp singMax
singNatCompare <- tcLookupTyCon =<< lookupOrig singletonOrd (mkTcOcc "Compare")
tcPluginTrace "pres: minMaxes" $
ppr (singMin, singMax, caseNameForMin, caseNameForMax)
return SingletonCons {..}
getCaseNameForSingletonBinOp :: TyCon -> TcPluginM [ComparisonMethod]
#if MIN_VERSION_singletons_base(3,5,0)
getCaseNameForSingletonBinOp = getCaseNameForSingletonBinOpSingleton35
#else
getCaseNameForSingletonBinOp = getCaseNameForSingletonBinOpOldSingleton
#endif
getCaseNameForSingletonBinOpSingleton35 :: TyCon -> TcPluginM [ComparisonMethod]
getCaseNameForSingletonBinOpSingleton35 con = do
let vars = [typeNatKind, LitTy (NumTyLit 0), LitTy (NumTyLit 0)]
tcPluginTrace "matching... for " (ppr con)
matched <- fmap splitTyConApp <$> matchFam' con vars
tcPluginTrace "matched. " (ppr matched)
Just (appTy0, [n, l, r]) <- fmap splitTyConApp <$> matchFam' con vars
applied <- matchFam' appTy0 [n, l, r]
tcPluginTrace "Double matched. " (ppr $ splitTyConApp_maybe =<< applied)
rest <-
fromMaybe [] <$> runMaybeT do
(app, args@[_bool, _nat, _body, _comps]) <- hoistMaybe $ splitTyConApp_maybe =<< applied
lift $ tcPluginTrace "app, args" (ppr (app, args))
agains <- MaybeT (matchFam' app args)
lift $ tcPluginTrace "Final match" (ppr agains)
(caseName, [_N, _0, _02, _]) <-
hoistMaybe $ splitTyConApp_maybe agains
pure [CaseFun caseName]
return $ DirectTFHelper appTy0 : rest
getCaseNameForSingletonBinOpOldSingleton :: TyCon -> TcPluginM [ComparisonMethod]
getCaseNameForSingletonBinOpOldSingleton con = do
let vars = [typeNatKind, LitTy (NumTyLit 0), LitTy (NumTyLit 0)]
tcPluginTrace "matching... for " (ppr con)
Just (appTy0, [n, b, bdy, r]) <- fmap splitTyConApp <$> matchFam' con vars
let (appTy, args) = splitTyConApp bdy
Just innermost <- matchFam' appTy args
Just dat <- matchFam' appTy0 [n, b, innermost, r]
Just dat' <- uncurry matchFam' (splitTyConApp dat)
tcPluginTrace "matched. (orig, inner) = " (ppr (con, fst $ splitTyConApp dat'))
return [CaseFun $ fst $ splitTyConApp dat']
getCaseNameForSingletonBinRel :: TyCon -> TcPluginM [ComparisonMethod]
#if MIN_VERSION_singletons_base(3,5,0)
getCaseNameForSingletonBinRel = getCaseNameForSingletonBinRelSingleton35
#else
getCaseNameForSingletonBinRel =getCaseNameForSingletonBinRelOldSingleton
#endif
getCaseNameForSingletonBinRelSingleton35 :: TyCon -> TcPluginM [ComparisonMethod]
getCaseNameForSingletonBinRelSingleton35 con = do
let vars = [typeNatKind, LitTy (NumTyLit 0), LitTy (NumTyLit 0)]
tcPluginTrace "matching... for " (ppr con)
matched <- fmap splitTyConApp <$> matchFam' con vars
tcPluginTrace "matched. " (ppr matched)
Just (appTy0, [n, l, r]) <- fmap splitTyConApp <$> matchFam' con vars
applied <- matchFam' appTy0 [n, l, r]
tcPluginTrace "Double matched. " (ppr $ splitTyConApp_maybe =<< applied)
rest <-
fromMaybe [] <$> runMaybeT do
(app, args@[_ordering, _bool, _body, _comps]) <- hoistMaybe $ splitTyConApp_maybe =<< applied
lift $ tcPluginTrace "app, args" (ppr (app, args))
agains <- MaybeT (matchFam' app args)
lift $ tcPluginTrace "Final match" (ppr agains)
(caseName, [_N, _0, _02, _]) <-
hoistMaybe $ splitTyConApp_maybe agains
pure [CaseFun caseName]
return $ DirectTFHelper appTy0 : rest
getCaseNameForSingletonBinRelOldSingleton :: TyCon -> TcPluginM [ComparisonMethod]
getCaseNameForSingletonBinRelOldSingleton con = do
let vars = [typeNatKind, LitTy (NumTyLit 0), LitTy (NumTyLit 0)]
tcPluginTrace "matching... for " (ppr con)
Just (appTy0, [n, b, bdy, r]) <- fmap splitTyConApp <$> matchFam' con vars
let (appTy, args) = splitTyConApp bdy
Just innermost <- matchFam' appTy args
Just dat <- matchFam' appTy0 [n, b, innermost, r]
Just dat' <- uncurry matchFam' (splitTyConApp dat)
tcPluginTrace "matched. (orig, inner)<= = " (ppr (con, fst $ splitTyConApp dat'))
return [CaseFun $ fst $ splitTyConApp dat']
lastTwo :: [a] -> [a]
lastTwo = drop <$> subtract 2 . length <*> id
parseSingExpr ::
(Given SingletonCons) =>
(Type -> Machine Expr) ->
Type ->
Machine Expr
parseSingExpr toE ty
-- singletons >=3.5
| Just (con, [l, r]) <- splitTyConApp_maybe ty
, Just bin <- lookup (DirectTFHelper con) minLikeCaseDic = do
lift $ lift $ tcPluginTrace "hit!" $ ppr (ty, con)
bin <$> toE l <*> toE r
-- singletons <3.5
| Just (con, [_, l, r, _]) <- splitTyConApp_maybe ty
, Just bin <- lookup (CaseFun con) minLikeCaseDic = do
lift $ lift $ tcPluginTrace "hit!" $ ppr (ty, con)
bin <$> toE l <*> toE r
| otherwise = do
lift $ lift $ tcPluginTrace "I don't know how to read:" $ ppr (ty, splitTyConApp_maybe ty)
mzero
minLikeCaseDic :: (Given SingletonCons) => [(ComparisonMethod, Expr -> Expr -> Expr)]
minLikeCaseDic =
concatMap
(\(methods, op) -> [(method, op) | method <- methods])
[ (caseNameForMin given, Min)
, (caseNameForMax given, Max)
]
parseSingPred ::
(Given SingletonCons) =>
(Type -> Machine Expr) ->
Type ->
Machine Prop
parseSingPred toExp ty
| isEqPred ty = parseSingPredTree toExp $ classifyPredType ty
| Just (con, [l, r]) <- splitTyConApp_maybe ty
, Just bin <- lookup (DirectTFHelper con) compCaseDic =
bin <$> toExp l <*> toExp r
| Just (con, [_, _, l, r, cmpTy]) <- splitTyConApp_maybe ty
, Just bin <- lookup (CaseFun con) compCaseDic
, isCompTy cmpTy =
bin <$> toExp l <*> toExp r
| Just (con, [_, l, r, cmpTy]) <- splitTyConApp_maybe ty
, Just bin <- lookup (CaseFun con) compCaseDic
, isCompTy cmpTy =
bin <$> toExp l <*> toExp r
| otherwise = do
lift $ lift $ tcPluginTrace "pres: Miokuring" (ppr ty)
mzero
isCompTy :: (Given SingletonCons) => Type -> Bool
isCompTy cmpTy =
case splitTyConApp_maybe cmpTy of
Just (app, [_dom, _cod, f, _r])
| app == singApply given
, Just (app', [_dom', _cod', cmp, _l]) <- splitTyConApp_maybe f
, app' == singApply given
, Just (cmp', []) <- splitTyConApp_maybe cmp ->
cmp' `elem` comps
Just (cmp, _) ->
cmp `elem` comps
_ -> False
where
comps = [singNatCompare given, typeNatCmpTyCon]
compCaseDic :: (Given SingletonCons) => [(ComparisonMethod, Expr -> Expr -> Prop)]
compCaseDic =
concatMap
(\(methods, op) -> [(method, op) | method <- methods])
[ (caseNameForSingLeq given, (:<=))
, (caseNameForSingLt given, (:<))
, (caseNameForSingGeq given, (:>=))
, (caseNameForSingGt given, (:>))
]
parseSingPredTree ::
(Given SingletonCons) =>
(Type -> Machine Expr) ->
PredTree ->
Machine Prop
parseSingPredTree toExp (EqPred NomEq p b) -- (n :<=? m) ~ 'True
-- singletons >=3.5
| Just promotedTrueDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [l, r]) <- splitTyConApp_maybe p
, Just bin <- lookup (DirectTFHelper con) compCaseDic =
bin <$> toExp l <*> toExp r
| Just promotedFalseDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [l, r]) <- splitTyConApp_maybe p
, Just bin <- lookup (DirectTFHelper con) compCaseDic =
fmap Not . bin <$> toExp l <*> toExp r
-- singletons <3.5
| Just promotedTrueDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [_, _, _, _, cmpTy]) <- splitTyConApp_maybe p
, Just bin <- lookup (CaseFun con) compCaseDic
, Just (cmp, lastTwo -> [l, r]) <- splitTyConApp_maybe cmpTy
, cmp `elem` [singNatCompare given, typeNatCmpTyCon] =
bin <$> toExp l <*> toExp r
| Just promotedFalseDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [_, _, _, _, cmpTy]) <- splitTyConApp_maybe p
, Just bin <- lookup (CaseFun con) compCaseDic
, Just (cmp, lastTwo -> [l, r]) <- splitTyConApp_maybe cmpTy
, cmp `elem` [singNatCompare given, typeNatCmpTyCon] =
fmap Not . bin <$> toExp l <*> toExp r
| Just promotedTrueDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [_nat, l, r, cmpTy]) <- splitTyConApp_maybe p
, Just bin <- lookup (CaseFun con) compCaseDic
, DT.trace ("bin: " <> showSDocUnsafe (ppr con)) True
, Just (cmp, _) <- splitTyConApp_maybe cmpTy
, cmp `elem` [singNatCompare given, typeNatCmpTyCon] =
bin <$> toExp l <*> toExp r
| Just promotedFalseDataCon == tyConAppTyCon_maybe b -- Singleton's <=...
, Just (con, [_nat, l, r, cmpTy]) <- splitTyConApp_maybe p
, Just bin <- lookup (CaseFun con) compCaseDic
, Just (cmp, _) <- splitTyConApp_maybe cmpTy
, cmp `elem` [singNatCompare given, typeNatCmpTyCon] =
fmap Not . bin <$> toExp l <*> toExp r
parseSingPredTree _ _ = mzero