%
% (c) The University of Glasgow 2011
%
\begin{code}
module Generics ( canDoGenerics,
mkBindsRep, tc_mkRepTyCon, mkBindsMetaD,
MetaTyCons(..), metaTyCons2TyCons
) where
import HsSyn
import Type
import TcType
import DataCon
import TyCon
import Name hiding (varName)
import Module (moduleName, moduleNameString)
import RdrName
import BasicTypes
import TysWiredIn
import PrelNames
import TcEnv (tcLookupTyCon)
import TcRnMonad
import HscTypes
import BuildTyCl
import SrcLoc
import Bag
import Outputable
import FastString
#include "HsVersions.h"
\end{code}
%************************************************************************
%* *
\subsection{Generating representation types}
%* *
%************************************************************************
\begin{code}
canDoGenerics :: TyCon -> Maybe SDoc
canDoGenerics tycon
= mergeErrors (
(if (not (null (tyConStupidTheta tycon)))
then (Just (ppr tycon <+> text "must not have a datatype context"))
else Nothing)
: (if (isFamilyTyCon tycon)
then (Just (ppr tycon <+> text "must not be a family instance"))
else Nothing)
: (map bad_con (tyConDataCons tycon)))
where
bad_con dc = if (any bad_arg_type (dataConOrigArgTys dc))
then (Just (ppr dc <+> text "must not have unlifted or polymorphic arguments"))
else (if (not (isVanillaDataCon dc))
then (Just (ppr dc <+> text "must be a vanilla data constructor"))
else Nothing)
bad_arg_type ty = isUnLiftedType ty || not (isTauTy ty)
mergeErrors :: [Maybe SDoc] -> Maybe SDoc
mergeErrors [] = Nothing
mergeErrors ((Just s):t) = case mergeErrors t of
Nothing -> Just s
Just s' -> Just (s <> text ", and" $$ s')
mergeErrors (Nothing :t) = mergeErrors t
\end{code}
%************************************************************************
%* *
\subsection{Generating the RHS of a generic default method}
%* *
%************************************************************************
\begin{code}
type US = Int
type Alt = (LPat RdrName, LHsExpr RdrName)
mkBindsRep :: TyCon -> LHsBinds RdrName
mkBindsRep tycon =
unitBag (L loc (mkFunBind (L loc from_RDR) from_matches))
`unionBags`
unitBag (L loc (mkFunBind (L loc to_RDR) to_matches))
where
from_matches = [mkSimpleHsAlt pat rhs | (pat,rhs) <- from_alts]
to_matches = [mkSimpleHsAlt pat rhs | (pat,rhs) <- to_alts ]
loc = srcLocSpan (getSrcLoc tycon)
datacons = tyConDataCons tycon
from_alts, to_alts :: [Alt]
(from_alts, to_alts) = mkSum (1 :: US) tycon datacons
tc_mkRepTyCon :: TyCon
-> MetaTyCons
-> TcM TyCon
tc_mkRepTyCon tycon metaDts =
do {
rep0 <- tcLookupTyCon repTyConName
; rep0Ty <- tc_mkRepTy tycon metaDts
; rep_name <- newImplicitBinder (tyConName tycon) mkGenR
; let
tyvars = tyConTyVars tycon
rep_kind = liftedTypeKind `mkArrowKind` liftedTypeKind
appT = [mkTyConApp tycon (mkTyVarTys tyvars)]
; buildSynTyCon rep_name tyvars (SynonymTyCon rep0Ty) rep_kind
NoParentTyCon (Just (rep0, appT)) }
tc_mkRepTy ::
TyCon
-> MetaTyCons
-> TcM Type
tc_mkRepTy tycon metaDts =
do
d1 <- tcLookupTyCon d1TyConName
c1 <- tcLookupTyCon c1TyConName
s1 <- tcLookupTyCon s1TyConName
nS1 <- tcLookupTyCon noSelTyConName
rec0 <- tcLookupTyCon rec0TyConName
par0 <- tcLookupTyCon par0TyConName
u1 <- tcLookupTyCon u1TyConName
v1 <- tcLookupTyCon v1TyConName
plus <- tcLookupTyCon sumTyConName
times <- tcLookupTyCon prodTyConName
let mkSum' a b = mkTyConApp plus [a,b]
mkProd a b = mkTyConApp times [a,b]
mkRec0 a = mkTyConApp rec0 [a]
mkPar0 a = mkTyConApp par0 [a]
mkD a = mkTyConApp d1 [metaDTyCon, sumP (tyConDataCons a)]
mkC i d a = mkTyConApp c1 [d, prod i (dataConOrigArgTys a)
(null (dataConFieldLabels a))]
mkS True _ a = mkTyConApp s1 [mkTyConTy nS1, a]
mkS False d a = mkTyConApp s1 [d, a]
sumP [] = mkTyConTy v1
sumP l = ASSERT (length metaCTyCons == length l)
foldBal mkSum' [ mkC i d a
| (d,(a,i)) <- zip metaCTyCons (zip l [0..])]
prod :: Int -> [Type] -> Bool -> Type
prod i [] _ = ASSERT (length metaSTyCons > i)
ASSERT (length (metaSTyCons !! i) == 0)
mkTyConTy u1
prod i l b = ASSERT (length metaSTyCons > i)
ASSERT (length l == length (metaSTyCons !! i))
foldBal mkProd [ arg d t b
| (d,t) <- zip (metaSTyCons !! i) l ]
arg :: Type -> Type -> Bool -> Type
arg d t b = mkS b d (recOrPar t (getTyVar_maybe t))
recOrPar t Nothing = mkRec0 t
recOrPar t (Just _) = mkPar0 t
metaDTyCon = mkTyConTy (metaD metaDts)
metaCTyCons = map mkTyConTy (metaC metaDts)
metaSTyCons = map (map mkTyConTy) (metaS metaDts)
return (mkD tycon)
data MetaTyCons = MetaTyCons {
metaD :: TyCon
, metaC :: [TyCon]
, metaS :: [[TyCon]] }
instance Outputable MetaTyCons where
ppr (MetaTyCons d c s) = ppr d $$ vcat (map ppr c) $$ vcat (map ppr (concat s))
metaTyCons2TyCons :: MetaTyCons -> [TyCon]
metaTyCons2TyCons (MetaTyCons d c s) = d : c ++ concat s
mkBindsMetaD :: FixityEnv -> TyCon
-> ( LHsBinds RdrName
, [LHsBinds RdrName]
, [[LHsBinds RdrName]])
mkBindsMetaD fix_env tycon = (dtBinds, allConBinds, allSelBinds)
where
mkBag l = foldr1 unionBags
[ unitBag (L loc (mkFunBind (L loc name) matches))
| (name, matches) <- l ]
dtBinds = mkBag [ (datatypeName_RDR, dtName_matches)
, (moduleName_RDR, moduleName_matches)]
allConBinds = map conBinds datacons
conBinds c = mkBag ( [ (conName_RDR, conName_matches c)]
++ ifElseEmpty (dataConIsInfix c)
[ (conFixity_RDR, conFixity_matches c) ]
++ ifElseEmpty (length (dataConFieldLabels c) > 0)
[ (conIsRecord_RDR, conIsRecord_matches c) ]
)
ifElseEmpty p x = if p then x else []
fixity c = case lookupFixity fix_env (dataConName c) of
Fixity n InfixL -> buildFix n leftAssocDataCon_RDR
Fixity n InfixR -> buildFix n rightAssocDataCon_RDR
Fixity n InfixN -> buildFix n notAssocDataCon_RDR
buildFix n assoc = nlHsApps infixDataCon_RDR [nlHsVar assoc
, nlHsIntLit (toInteger n)]
allSelBinds = map (map selBinds) datasels
selBinds s = mkBag [(selName_RDR, selName_matches s)]
loc = srcLocSpan (getSrcLoc tycon)
mkStringLHS s = [mkSimpleHsAlt nlWildPat (nlHsLit (mkHsString s))]
datacons = tyConDataCons tycon
datasels = map dataConFieldLabels datacons
dtName_matches = mkStringLHS . showPpr . nameOccName . tyConName
$ tycon
moduleName_matches = mkStringLHS . moduleNameString . moduleName
. nameModule . tyConName $ tycon
conName_matches c = mkStringLHS . showPpr . nameOccName
. dataConName $ c
conFixity_matches c = [mkSimpleHsAlt nlWildPat (fixity c)]
conIsRecord_matches _ = [mkSimpleHsAlt nlWildPat (nlHsVar true_RDR)]
selName_matches s = mkStringLHS (showPpr (nameOccName s))
mkSum :: US
-> TyCon
-> [DataCon]
-> ([Alt],
[Alt])
mkSum _us tycon [] = ([from_alt], [to_alt])
where
from_alt = (nlWildPat, mkM1_E (makeError errMsgFrom))
to_alt = (mkM1_P nlWildPat, makeError errMsgTo)
makeError s = nlHsApp (nlHsVar error_RDR) (nlHsLit (mkHsString s))
errMsgFrom = "No generic representation for empty datatype " ++ showPpr tycon
errMsgTo = "No values for empty datatype " ++ showPpr tycon
mkSum us _tycon datacons =
unzip [ mk1Sum us i (length datacons) d | (d,i) <- zip datacons [1..] ]
mk1Sum :: US
-> Int
-> Int
-> DataCon
-> (Alt,
Alt)
mk1Sum us i n datacon = (from_alt, to_alt)
where
n_args = dataConSourceArity datacon
datacon_vars = map mkGenericLocal [us .. us+n_args1]
us' = us + n_args
datacon_rdr = getRdrName datacon
app_exp = nlHsVarApps datacon_rdr datacon_vars
from_alt = (nlConVarPat datacon_rdr datacon_vars, from_alt_rhs)
from_alt_rhs = mkM1_E (genLR_E i n (mkProd_E us' datacon_vars))
to_alt = (mkM1_P (genLR_P i n (mkProd_P us' datacon_vars)), to_alt_rhs)
to_alt_rhs = app_exp
genLR_P :: Int -> Int -> LPat RdrName -> LPat RdrName
genLR_P i n p
| n == 0 = error "impossible"
| n == 1 = p
| i <= div n 2 = nlConPat l1DataCon_RDR [genLR_P i (div n 2) p]
| otherwise = nlConPat r1DataCon_RDR [genLR_P (im) (nm) p]
where m = div n 2
genLR_E :: Int -> Int -> LHsExpr RdrName -> LHsExpr RdrName
genLR_E i n e
| n == 0 = error "impossible"
| n == 1 = e
| i <= div n 2 = nlHsVar l1DataCon_RDR `nlHsApp` genLR_E i (div n 2) e
| otherwise = nlHsVar r1DataCon_RDR `nlHsApp` genLR_E (im) (nm) e
where m = div n 2
mkProd_E :: US
-> [RdrName]
-> LHsExpr RdrName
mkProd_E _ [] = mkM1_E (nlHsVar u1DataCon_RDR)
mkProd_E _ vars = mkM1_E (foldBal prod appVars)
where
appVars = map wrapArg_E vars
prod a b = prodDataCon_RDR `nlHsApps` [a,b]
wrapArg_E :: RdrName -> LHsExpr RdrName
wrapArg_E v = mkM1_E (k1DataCon_RDR `nlHsVarApps` [v])
mkProd_P :: US
-> [RdrName]
-> LPat RdrName
mkProd_P _ [] = mkM1_P (nlNullaryConPat u1DataCon_RDR)
mkProd_P _ vars = mkM1_P (foldBal prod appVars)
where
appVars = map wrapArg_P vars
prod a b = prodDataCon_RDR `nlConPat` [a,b]
wrapArg_P :: RdrName -> LPat RdrName
wrapArg_P v = mkM1_P (k1DataCon_RDR `nlConVarPat` [v])
mkGenericLocal :: US -> RdrName
mkGenericLocal u = mkVarUnqual (mkFastString ("g" ++ show u))
mkM1_E :: LHsExpr RdrName -> LHsExpr RdrName
mkM1_E e = nlHsVar m1DataCon_RDR `nlHsApp` e
mkM1_P :: LPat RdrName -> LPat RdrName
mkM1_P p = m1DataCon_RDR `nlConPat` [p]
foldBal :: (a -> a -> a) -> [a] -> a
foldBal op = foldBal' op (error "foldBal: empty list")
foldBal' :: (a -> a -> a) -> a -> [a] -> a
foldBal' _ x [] = x
foldBal' _ _ [y] = y
foldBal' op x l = let (a,b) = splitAt (length l `div` 2) l
in foldBal' op x a `op` foldBal' op x b
\end{code}