aboutsummaryrefslogtreecommitdiff
path: root/src/Haddock/Convert.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Haddock/Convert.hs')
-rw-r--r--src/Haddock/Convert.hs275
1 files changed, 275 insertions, 0 deletions
diff --git a/src/Haddock/Convert.hs b/src/Haddock/Convert.hs
new file mode 100644
index 00000000..f5a200a9
--- /dev/null
+++ b/src/Haddock/Convert.hs
@@ -0,0 +1,275 @@
+
+-- This functionality may be moved into GHC at some point, and then
+-- we can use the GHC version (#if GHC version is new enough).
+module Haddock.Convert ( tyThingToHsSynSig {- :: TyThing -> LHsDecl Name -} )
+ where
+
+import HsSyn
+import TcType ( tcSplitSigmaTy )
+import TypeRep
+import Type ( splitKindFunTys )
+import Name
+import HscTypes
+import Var
+import Class
+import TyCon
+import DataCon
+import Id
+import BasicTypes
+import TysPrim ( alphaTyVars )
+import Bag ( emptyBag )
+import SrcLoc ( Located, noLoc )
+import Maybe
+
+-- the main function here! yay!
+tyThingToHsSynSig :: TyThing -> LHsDecl Name
+-- ids (functions and zero-argument a.k.a. CAFs) get a type signature.
+-- Including built-in functions like seq.
+-- foreign-imported functions could be represented with ForD
+-- instead of SigD if we wanted...
+tyThingToHsSynSig (AnId i) = noLoc $
+ -- in a future code version we could turn idVarDetails = foreign-call
+ -- into a ForD instead of a SigD if we wanted. Haddock doesn't
+ -- need to care.
+ SigD (synifyIdSig ImplicitizeForAll i)
+-- type-constructors (e.g. Maybe) are complicated, put the definition
+-- later in the file (also it's used for class associated-types too.)
+tyThingToHsSynSig (ATyCon tc) = noLoc $
+ TyClD (synifyTyCon tc)
+-- a data-constructor alone just gets rendered as a function:
+tyThingToHsSynSig (ADataCon dc) = noLoc $
+ SigD (TypeSig (synifyName dc)
+ (synifyType ImplicitizeForAll (dataConUserType dc)))
+-- classes are just a little tedious
+tyThingToHsSynSig (AClass cl) = noLoc $
+ TyClD $ ClassDecl
+ (synifyCtx (classSCTheta cl))
+ (synifyName cl)
+ (synifyTyVars (classTyVars cl))
+ (map (\ (l,r) -> noLoc
+ (map getName l, map getName r) ) $
+ snd $ classTvsFds cl)
+ (map (\i -> noLoc $ synifyIdSig DeleteTopLevelQuantification i)
+ (classMethods cl))
+ emptyBag --ignore default method definitions, they don't affect signature
+ (map synifyClassAT (classATs cl))
+ [] --we don't have any docs at this point
+
+-- class associated-types are a subset of TyCon
+-- (mainly only type/data-families)
+synifyClassAT :: TyCon -> LTyClDecl Name
+synifyClassAT tc = noLoc $ synifyTyCon tc
+
+synifyTyCon :: TyCon -> TyClDecl Name
+synifyTyCon tc
+ | isFunTyCon tc || isPrimTyCon tc =
+ TyData
+ -- arbitrary lie, they are neither algebraic data nor newtype:
+ DataType
+ -- no built-in type has any stupidTheta:
+ (noLoc [])
+ (synifyName tc)
+ -- tyConTyVars doesn't work on fun/prim, but we can make them up:
+ (zipWith
+ (\fakeTyVar realKind -> noLoc $
+ KindedTyVar (getName fakeTyVar) realKind)
+ alphaTyVars --a, b, c... which are unfortunately all kind *
+ (fst . splitKindFunTys $ tyConKind tc)
+ )
+ -- assume primitive types aren't members of data/newtype families:
+ Nothing
+ -- we have their kind accurately:
+ (Just (tyConKind tc))
+ -- no algebraic constructors:
+ []
+ -- "deriving" needn't be specified:
+ Nothing
+ | isOpenSynTyCon tc =
+ case synTyConRhs tc of
+ OpenSynTyCon rhs_kind _ ->
+ TyFamily TypeFamily (synifyName tc) (synifyTyVars (tyConTyVars tc))
+ (Just rhs_kind)
+ _ -> error "synifyTyCon: impossible open type synonym?"
+ | isOpenTyCon tc = --(why no "isOpenAlgTyCon"?)
+ case algTyConRhs tc of
+ OpenTyCon _ ->
+ TyFamily DataFamily (synifyName tc) (synifyTyVars (tyConTyVars tc))
+ Nothing --always kind '*'
+ _ -> error "synifyTyCon: impossible open data type?"
+ | otherwise =
+ -- (closed) type, newtype, and data
+ let
+ -- alg_ only applies to newtype/data
+ -- syn_ only applies to type
+ -- others apply to both
+ alg_nd = if isNewTyCon tc then NewType else DataType
+ alg_ctx = synifyCtx (tyConStupidTheta tc)
+ name = synifyName tc
+ tyvars = synifyTyVars (tyConTyVars tc)
+ typats = case tyConFamInst_maybe tc of
+ Nothing -> Nothing
+ Just (_, indexes) -> Just (map (synifyType WithinType) indexes)
+ alg_kindSig = Just (tyConKind tc)
+ -- The data constructors.
+ --
+ -- Any data-constructors not exported from the module that *defines* the
+ -- type will not (cannot) be included.
+ --
+ -- Very simple constructors, Haskell98 with no existentials or anything,
+ -- probably look nicer in non-GADT syntax. In source code, all constructors
+ -- must be declared with the same (GADT vs. not) syntax, and it probably
+ -- is less confusing to follow that principle for the documentation as well.
+ --
+ -- There is no sensible infix-representation for GADT-syntax constructor
+ -- declarations. They cannot be made in source code, but we could end up
+ -- with some here in the case where some constructors use existentials.
+ -- That seems like an acceptable compromise (they'll just be documented
+ -- in prefix position), since, otherwise, the logic (at best) gets much more
+ -- complicated. (would use dataConIsInfix.)
+ alg_use_gadt_syntax = any (not . isVanillaDataCon) (tyConDataCons tc)
+ alg_cons = map (synifyDataCon alg_use_gadt_syntax) (tyConDataCons tc)
+ -- "deriving" doesn't affect the signature, no need to specify any.
+ alg_deriv = Nothing
+ syn_type = synifyType WithinType (synTyConType tc)
+ in if isSynTyCon tc
+ then TySynonym name tyvars typats syn_type
+ else TyData alg_nd alg_ctx name tyvars typats alg_kindSig alg_cons alg_deriv
+
+-- User beware: it is your responsibility to pass True (use_gadt_syntax)
+-- for any constructor that would be misrepresented by omitting its
+-- result-type.
+-- But you might want pass False in simple enough cases,
+-- if you think it looks better.
+synifyDataCon :: Bool -> DataCon -> LConDecl Name
+synifyDataCon use_gadt_syntax dc = noLoc $
+ let
+ -- dataConIsInfix allegedly tells us whether it was declared with
+ -- infix *syntax*.
+ use_infix_syntax = dataConIsInfix dc
+ use_named_field_syntax = not (null field_tys)
+ name = synifyName dc
+ -- con_qvars means a different thing depending on gadt-syntax
+ qvars = if use_gadt_syntax
+ then synifyTyVars (dataConAllTyVars dc)
+ else synifyTyVars (dataConExTyVars dc)
+ -- skip any EqTheta, use 'orig'inal syntax
+ ctx = synifyCtx (dataConDictTheta dc)
+ linear_tys = zipWith (\ty strict ->
+ let tySyn = synifyType WithinType ty
+ in case strict of
+ MarkedStrict -> noLoc $ HsBangTy HsStrict tySyn
+ MarkedUnboxed -> noLoc $ HsBangTy HsUnbox tySyn
+ NotMarkedStrict ->
+ -- HsNoBang never appears, it's implied instead.
+ tySyn
+ )
+ (dataConOrigArgTys dc) (dataConStrictMarks dc)
+ field_tys = zipWith (\field synTy -> ConDeclField
+ (synifyName field) synTy Nothing)
+ (dataConFieldLabels dc) linear_tys
+ tys = case (use_named_field_syntax, use_infix_syntax) of
+ (True,True) -> error "synifyDataCon: contradiction!"
+ (True,False) -> RecCon field_tys
+ (False,False) -> PrefixCon linear_tys
+ (False,True) -> case linear_tys of
+ [a,b] -> InfixCon a b
+ _ -> error "synifyDataCon: infix with non-2 args?"
+ res_ty = if use_gadt_syntax
+ then ResTyGADT (synifyType WithinType (dataConOrigResTy dc))
+ else ResTyH98
+ -- finally we get synifyDataCon's result!
+ in ConDecl name Implicit{-we don't know nor care-}
+ qvars ctx tys res_ty Nothing
+
+synifyName :: NamedThing n => n -> Located Name
+synifyName n = noLoc (getName n)
+
+synifyIdSig :: SynifyTypeState -> Id -> Sig Name
+synifyIdSig s i = TypeSig (synifyName i) (synifyType s (varType i))
+
+
+synifyCtx :: [PredType] -> LHsContext Name
+synifyCtx ps = (\ps' -> noLoc ps') $
+ map synifyPred ps
+ where
+ synifyPred (ClassP cls tys) =
+ let sTys = map (synifyType WithinType) tys
+ in noLoc $
+ HsClassP (getName cls) sTys
+ synifyPred (IParam ip ty) =
+ let sTy = synifyType WithinType ty
+ -- IPName should be in class NamedThing...
+ in noLoc $
+ HsIParam ip sTy
+ synifyPred (EqPred ty1 ty2) =
+ let
+ s1 = synifyType WithinType ty1
+ s2 = synifyType WithinType ty2
+ in noLoc $
+ HsEqualP s1 s2
+
+synifyTyVars :: [TyVar] -> [LHsTyVarBndr Name]
+synifyTyVars = map synifyTyVar
+ where
+ synifyTyVar tv = noLoc $ let
+ kind = tyVarKind tv
+ name = getName tv
+ in if isLiftedTypeKind kind
+ then UserTyVar name
+ else KindedTyVar name kind
+
+--states of what to do with foralls:
+data SynifyTypeState
+ = WithinType
+ -- ^ normal situation. This is the safe one to use if you don't
+ -- quite understand what's going on.
+ | ImplicitizeForAll
+ -- ^ beginning of a function definition, in which, to make it look
+ -- less ugly, those rank-1 foralls are made implicit.
+ | DeleteTopLevelQuantification
+ -- ^ because in class methods the context is added to the type
+ -- (e.g. adding @forall a. Num a =>@ to @(+) :: a -> a -> a@)
+ -- which is rather sensible,
+ -- but we want to restore things to the source-syntax situation where
+ -- the defining class gets to quantify all its functions for free!
+
+synifyType :: SynifyTypeState -> Type -> LHsType Name
+synifyType _ (PredTy{}) = --should never happen.
+ error "synifyType: PredTys are not, in themselves, source-level types."
+synifyType _ (TyVarTy tv) = noLoc $ HsTyVar (getName tv)
+synifyType _ (TyConApp tc tys)
+ -- Use non-prefix tuple syntax where possible, because it looks nicer.
+ | isTupleTyCon tc && tyConArity tc == length tys =
+ let sTys = map (synifyType WithinType) tys
+ in noLoc $
+ HsTupleTy (tupleTyConBoxity tc) sTys
+ -- We could do the same for list types if we knew how to determine
+ -- whether the constructor was the list-constructor....
+ -- Most TyCons:
+ | otherwise =
+ foldl (\t1 t2 -> noLoc (HsAppTy t1 t2))
+ (noLoc $ HsTyVar (getName tc))
+ (map (synifyType WithinType) tys)
+synifyType _ (AppTy t1 t2) = let
+ s1 = synifyType WithinType t1
+ s2 = synifyType WithinType t2
+ in noLoc $ HsAppTy s1 s2
+synifyType _ (FunTy t1 t2) = let
+ s1 = synifyType WithinType t1
+ s2 = synifyType WithinType t2
+ in noLoc $ HsFunTy s1 s2
+synifyType s forallty@(ForAllTy _tv _ty) =
+ let (tvs, ctx, tau) = tcSplitSigmaTy forallty
+ in case s of
+ DeleteTopLevelQuantification -> synifyType ImplicitizeForAll tau
+ _ -> let
+ forallPlicitness = case s of
+ WithinType -> Explicit
+ ImplicitizeForAll -> Implicit
+ _ -> error "synifyType: impossible case!!!"
+ sTvs = synifyTyVars tvs
+ sCtx = synifyCtx ctx
+ sTau = synifyType WithinType tau
+ in noLoc $
+ HsForAllTy forallPlicitness sTvs sCtx sTau
+