module Language.Haskell.Liquid.Bare.DataType
(
makeDataDecls
, makeConTypes
, makeTyConEmbeds
, makeRecordSelectorSigs
, meetDataConSpec
, makeNumericInfo
, DataConMap
, dataConMap
) where
import DataCon
import Name (getSrcSpan)
import Prelude hiding (error)
import SrcLoc (SrcSpan)
import Text.Parsec
import TyCon hiding (tyConName)
import Var
import InstEnv
import Class
import Data.Maybe
import Language.Haskell.Liquid.GHC.TypeRep
import Control.Monad (when)
import Control.Monad.State (gets)
import qualified Control.Exception as Ex
import qualified Data.List as L
import qualified Data.HashMap.Strict as M
import qualified Language.Fixpoint.Types.Visitor as V
import qualified Language.Fixpoint.Types as F
import qualified Language.Haskell.Liquid.GHC.Misc as GM
import Language.Haskell.Liquid.Types.PredType (dataConPSpecType)
import qualified Language.Haskell.Liquid.Types.RefType as RT
import Language.Haskell.Liquid.Types
import Language.Haskell.Liquid.Types.Meet
import qualified Language.Fixpoint.Misc as Misc
import qualified Language.Haskell.Liquid.Misc as Misc
import Language.Haskell.Liquid.Types.Variance
import Language.Haskell.Liquid.WiredIn
import qualified Language.Haskell.Liquid.Measure as Ms
import qualified Language.Haskell.Liquid.Bare.Misc as GM
import Language.Haskell.Liquid.Bare.Env
import Language.Haskell.Liquid.Bare.Lookup
import Language.Haskell.Liquid.Bare.OfType
makeNumericInfo :: Maybe [ClsInst] -> F.TCEmb TyCon -> F.TCEmb TyCon
makeNumericInfo Nothing x = x
makeNumericInfo (Just is) x = foldl makeNumericInfoOne x is
makeNumericInfoOne :: F.TCEmb TyCon -> ClsInst -> F.TCEmb TyCon
makeNumericInfoOne m is
| isFracCls $ classTyCon $ is_cls is, Just tc <- instanceTyCon is
= M.insertWith (flip F.mappendFTC) tc (ftc tc True True) m
| isNumCls $ classTyCon $ is_cls is, Just tc <- instanceTyCon is
= M.insertWith (flip F.mappendFTC) tc (ftc tc True False) m
| otherwise
= m
where
ftc c = F.symbolNumInfoFTyCon (dummyLoc $ RT.tyConName c)
instanceTyCon :: ClsInst -> Maybe TyCon
instanceTyCon = go . is_tys
where
go [TyConApp c _] = Just c
go _ = Nothing
type DataPropDecl = (DataDecl, Maybe SpecType)
makeDataDecls :: Config -> F.TCEmb TyCon -> [(ModName, TyCon, DataPropDecl)]
-> [(DataCon, Located DataConP)]
-> [F.DataDecl]
makeDataDecls cfg tce tds ds
| makeDecls = [ makeFDataDecls tce tc dd ctors
| (tc, (dd, ctors)) <- groupDataCons tds' ds ]
| otherwise = []
where
makeDecls = exactDC cfg && not (noADT cfg)
tds' = [ (tc, ( d, t)) | (_, tc, (d, t)) <- tds ]
groupDataCons :: [(TyCon, DataPropDecl)] -> [(DataCon, Located DataConP)]
-> [(TyCon, (DataPropDecl, [(DataCon, DataConP)]))]
groupDataCons tds ds = M.toList $ M.intersectionWith (,) declM ctorM
where
declM = M.fromList tds
ctorM = Misc.group [(dataConTyCon d, (d, val dp)) | (d, dp) <- ds]
makeFDataDecls :: F.TCEmb TyCon -> TyCon -> DataPropDecl -> [(DataCon, DataConP)]
-> F.DataDecl
makeFDataDecls tce tc dd ctors = makeDataDecl tce tc (fst dd) ctors
makeDataDecl :: F.TCEmb TyCon -> TyCon -> DataDecl -> [(DataCon, DataConP)]
-> F.DataDecl
makeDataDecl tce tc dd ctors
= F.DDecl
{ F.ddTyCon = ftc
, F.ddVars = length $ tycTyVars dd
, F.ddCtors = makeDataCtor tce ftc <$> ctors
}
where
ftc = F.symbolFTycon (tyConLocSymbol tc dd)
tyConLocSymbol :: TyCon -> DataDecl -> LocSymbol
tyConLocSymbol tc dd = F.atLoc (tycName dd) (F.symbol tc)
makeDataCtor :: F.TCEmb TyCon -> F.FTycon -> (DataCon, DataConP) -> F.DataCtor
makeDataCtor tce c (d, dp) = F.DCtor
{ F.dcName = GM.namedLocSymbol d
, F.dcFields = makeDataFields tce c as xts
}
where
as = freeTyVars dp
xts = [ (fld x, t) | (x, t) <- reverse (tyArgs dp) ]
fld = Loc (dc_loc dp) (dc_locE dp) . fieldName d dp
fieldName :: DataCon -> DataConP -> F.Symbol -> F.Symbol
fieldName d dp x
| dcpIsGadt dp = F.suffixSymbol (F.symbol d) x
| otherwise = x
makeDataFields :: F.TCEmb TyCon -> F.FTycon -> [RTyVar] -> [(F.LocSymbol, SpecType)]
-> [F.DataField]
makeDataFields tce c as xts = [ F.DField x (fSort t) | (x, t) <- xts]
where
su = zip (rtyVarUniqueSymbol <$> as) [0..]
fSort = muSort c (length as) . F.substVars su . RT.rTypeSort tce
muSort :: F.FTycon -> Int -> F.Sort -> F.Sort
muSort c n = V.mapSort tx
where
ct = F.fTyconSort c
me = F.fTyconSelfSort c n
tx t = if t == me then ct else t
makeConTypes
:: (ModName, Ms.Spec ty bndr)
-> BareM ( [(ModName, TyCon, TyConP, Maybe DataPropDecl)]
, [[(DataCon, Located DataConP)]] )
makeConTypes (name, spec) = inModule name $
makeConTypes' name (Ms.dataDecls spec) (Ms.dvariance spec)
makeConTypes' :: ModName -> [DataDecl] -> [(LocSymbol, [Variance])]
-> BareM ( [(ModName, TyCon, TyConP, Maybe DataPropDecl)]
, [[(DataCon, Located DataConP)]])
makeConTypes' name dcs vdcs = do
dcs' <- canonizeDecls dcs
unzip <$> mapM (uncurry (ofBDataDecl name)) (groupVariances dcs' vdcs)
canonizeDecls :: [DataDecl] -> BareM [DataDecl]
canonizeDecls = Misc.nubHashLastM key
where
key = fmap F.symbol . lookupGhcTyCon "canonizeDecls" . tycName
groupVariances :: [DataDecl] -> [(LocSymbol, [Variance])]
-> [(Maybe DataDecl, Maybe (LocSymbol, [Variance]))]
groupVariances dcs vdcs = merge (L.sort dcs) (L.sortBy (\x y -> compare (fst x) (fst y)) vdcs)
where
merge (d:ds) (v:vs)
| tycName d == fst v = (Just d, Just v) : merge ds vs
| tycName d < fst v = (Just d, Nothing) : merge ds (v:vs)
| otherwise = (Nothing, Just v) : merge (d:ds) vs
merge [] vs = ((Nothing,) . Just) <$> vs
merge ds [] = ((,Nothing) . Just) <$> ds
dataConSpec' :: [(DataCon, DataConP)] -> [(Var, (SrcSpan, SpecType))]
dataConSpec' dcs = concatMap tx dcs
where
tx (a, b) = [ (x, (sspan b, t)) | (x, t) <- RT.mkDataConIdsTy (a, dataConPSpecType a b) ]
sspan z = GM.sourcePos2SrcSpan (dc_loc z) (dc_locE z)
meetDataConSpec :: [(Var, SpecType)] -> [(DataCon, DataConP)] -> [(Var, SpecType)]
meetDataConSpec xts dcs = M.toList $ snd <$> L.foldl' upd dcm0 xts
where
dcm0 = M.fromList $ dataConSpec' dcs
meetX x t (sp', t') = meetVarTypes (pprint x) (getSrcSpan x, t) (sp', t')
upd dcm (x, t) = M.insert x (getSrcSpan x, tx') dcm
where
tx' = maybe t (meetX x t) (M.lookup x dcm)
checkDataCtor :: DataCtor -> BareM DataCtor
checkDataCtor d@(DataCtor lc xts _)
| x : _ <- dups = Ex.throw (err lc x :: UserError)
| otherwise = return d
where
dups = [ x | (x, ts) <- Misc.groupList xts, 2 <= length ts ]
err lc x = ErrDupField (GM.sourcePosSrcSpan $ loc lc) (pprint $ val lc) (pprint x)
checkDataDecl :: TyCon -> DataDecl -> Bool
checkDataDecl c d = (cN == dN || null (tycDCons d))
where
cN = length (GM.tyConTyVarsDef c)
dN = length (tycTyVars d)
ofBDataDecl :: ModName
-> Maybe DataDecl
-> (Maybe (LocSymbol, [Variance]))
-> BareM ((ModName, TyCon, TyConP, Maybe DataPropDecl), [(DataCon, Located DataConP)])
ofBDataDecl name (Just dd@(D tc as ps ls cts0 _ sfun pt)) maybe_invariance_info
= do πs <- mapM ofBPVar ps
tc' <- lookupGhcTyCon "ofBDataDecl" tc
when (not $ checkDataDecl tc' dd) (Ex.throw err)
cts <- mapM checkDataCtor cts0
cts' <- mapM (ofBDataCtor name lc lc' tc' αs ps ls πs) cts
pd <- mapM (mkSpecType' lc []) pt
let tys = [t | (_, dcp) <- cts', (_, t) <- tyArgs dcp]
let initmap = zip (RT.uPVar <$> πs) [0..]
let varInfo = L.nub $ concatMap (getPsSig initmap True) tys
let defPs = varSignToVariance varInfo <$> [0 .. (length πs 1)]
let (tvi, pvi) = f defPs
let tcp = TyConP lc αs πs ls tvi pvi sfun
return ((name, tc', tcp, Just (dd { tycDCons = cts }, pd)), (Misc.mapSnd (Loc lc lc') <$> cts'))
where
err = ErrBadData (GM.fSrcSpan tc) (pprint tc) "Mismatch in number of type variables" :: UserError
αs = RTV . GM.symbolTyVar <$> as
n = length αs
lc = loc tc
lc' = locE tc
f defPs = case maybe_invariance_info of
{ Nothing -> ([], defPs);
Just (_,is) -> (take n is, if null (drop n is) then defPs else (drop n is))}
ofBDataDecl name Nothing (Just (tc, is))
= do tc' <- lookupGhcTyCon "ofBDataDecl" tc
return ((name, tc', TyConP srcpos [] [] [] tcov tcontr Nothing, Nothing), [])
where
(tcov, tcontr) = (is, [])
srcpos = F.dummyPos "LH.DataType.Variance"
ofBDataDecl _ Nothing Nothing
= panic Nothing "Bare.DataType.ofBDataDecl called on invalid inputs"
varSignToVariance :: Eq a => [(a, Bool)] -> a -> Variance
varSignToVariance varsigns i = case filter (\p -> fst p == i) varsigns of
[] -> Invariant
[(_, b)] -> if b then Covariant else Contravariant
_ -> Bivariant
getPsSig :: [(UsedPVar, a)] -> Bool -> SpecType -> [(a, Bool)]
getPsSig m pos (RAllT _ t)
= getPsSig m pos t
getPsSig m pos (RApp _ ts rs r)
= addps m pos r ++ concatMap (getPsSig m pos) ts
++ concatMap (getPsSigPs m pos) rs
getPsSig m pos (RVar _ r)
= addps m pos r
getPsSig m pos (RAppTy t1 t2 r)
= addps m pos r ++ getPsSig m pos t1 ++ getPsSig m pos t2
getPsSig m pos (RFun _ t1 t2 r)
= addps m pos r ++ getPsSig m pos t2 ++ getPsSig m (not pos) t1
getPsSig m pos (RHole r)
= addps m pos r
getPsSig _ _ z
= panic Nothing $ "getPsSig" ++ show z
getPsSigPs :: [(UsedPVar, a)] -> Bool -> SpecProp -> [(a, Bool)]
getPsSigPs m pos (RProp _ (RHole r)) = addps m pos r
getPsSigPs m pos (RProp _ t) = getPsSig m pos t
addps :: [(UsedPVar, a)] -> b -> UReft t -> [(a, b)]
addps m pos (MkUReft _ ps _) = (flip (,)) pos . f <$> pvars ps
where f = fromMaybe (panic Nothing "Bare.addPs: notfound") . (`L.lookup` m) . RT.uPVar
ofBDataCtor :: ModName
-> SourcePos
-> SourcePos
-> TyCon
-> [RTyVar]
-> [PVar BSort]
-> [F.Symbol]
-> [PVar RSort]
-> DataCtor
-> BareM (DataCon, DataConP)
ofBDataCtor name l l' tc αs ps ls πs (DataCtor c xts res) = do
c' <- lookupGhcDataCon c
ts' <- mapM (mkSpecType' l ps) ts
res' <- mapM (mkSpecType' l ps) res
let cs = RT.ofType <$> dataConStupidTheta c'
let t0' = fromMaybe t0 res'
cfg <- gets beConfig
let (yts, ot) = qualifyDataCtor (exactDC cfg && not isGadt) name dLoc (zip xs ts', t0')
let zts = zipWith (normalizeField c') [1..] (reverse yts)
return (c', DataConP l αs πs ls cs zts ot isGadt l')
where
(xs, ts) = unzip xts
rs = [RT.rVar α | RTV α <- αs]
t0 = RT.rApp tc rs (rPropP [] . pdVarReft <$> πs) mempty
isGadt = isJust res
dLoc = F.Loc l l' ()
normalizeField :: DataCon -> Int -> (F.Symbol, a) -> (F.Symbol, a)
normalizeField c i (x, t)
| isTmp x = (xi, t)
| otherwise = (x , t)
where
isTmp = F.isPrefixOfSym F.tempPrefix
xi = GM.makeDataConSelector Nothing c i
type CtorType = ([(F.Symbol, SpecType)], SpecType)
qualifyDataCtor :: Bool -> ModName -> F.Located a -> CtorType -> CtorType
qualifyDataCtor qualFlag name l ct@(xts, t)
| qualFlag = (xts', t')
| otherwise = ct
where
t' = F.subst su <$> t
xts' = [ (qx, F.subst su t) | (qx, t, _) <- fields ]
su = F.notracepp "F-ING subst" $ F.mkSubst [ (x, F.eVar qx) | (qx, _, Just x) <- fields ]
fields = [ (qx, t, mbX) | (x, t) <- xts, let (mbX, qx) = qualifyField name (F.atLoc l x) ]
qualifyField :: ModName -> LocSymbol -> (Maybe F.Symbol, F.Symbol)
qualifyField name lx
| needsQual = (Just x, F.notracepp msg $ qualifyName name x)
| otherwise = (Nothing, x)
where
msg = "QUALIFY-NAME: " ++ show x ++ " in module " ++ show (F.symbol name)
x = val lx
needsQual = not (isWiredIn lx)
qualifyName :: ModName -> F.Symbol -> F.Symbol
qualifyName n = GM.qualifySymbol nSym
where
nSym = F.symbol n
makeTyConEmbeds :: (ModName,Ms.Spec ty bndr) -> BareM (F.TCEmb TyCon)
makeTyConEmbeds (mod, spec)
= inModule mod $ makeTyConEmbeds' $ Ms.embeds spec
makeTyConEmbeds' :: F.TCEmb LocSymbol -> BareM (F.TCEmb TyCon)
makeTyConEmbeds' z = M.fromList <$> mapM tx (M.toList z)
where
tx (c, y) = (, y) <$> lookupGhcTyCon "makeTyConEmbeds'" c
makeRecordSelectorSigs :: [(DataCon, Located DataConP)] -> BareM [(Var, LocSpecType)]
makeRecordSelectorSigs dcs = F.notracepp "makeRecordSelectorSigs" <$> (concat <$> mapM makeOne dcs)
where
makeOne (dc, Loc l l' dcp)
| null (dataConFieldLabels dc)
|| any (isFunTy . snd) args
|| dcpIsGadt dcp
= return []
| otherwise = do
fs <- mapM lookupGhcVar (dataConFieldLabels dc)
return $ zip fs ts
where
ts :: [ LocSpecType ]
ts = [ Loc l l' (mkArrow (makeRTVar <$> freeTyVars dcp) [] (freeLabels dcp)
[(z, res, mempty)]
(dropPreds (F.subst su t `RT.strengthen` mt)))
| (x, t) <- reverse args
, let vv = rTypeValueVar t
, let mt = RT.uReft (vv, F.PAtom F.Eq (F.EVar vv) (F.EApp (F.EVar x) (F.EVar z)))
]
su = F.mkSubst [ (x, F.EApp (F.EVar x) (F.EVar z)) | x <- fst <$> args ]
args = tyArgs dcp
z = F.notracepp ("makeRecordSelectorSigs:" ++ show args) "lq$recSel"
res = dropPreds (tyRes dcp)
dropPreds = fmap (\(MkUReft r _ps ss) -> MkUReft r mempty ss)