aboutsummaryrefslogtreecommitdiff
path: root/haddock-api/src/Haddock/Convert.hs
diff options
context:
space:
mode:
Diffstat (limited to 'haddock-api/src/Haddock/Convert.hs')
-rw-r--r--haddock-api/src/Haddock/Convert.hs413
1 files changed, 413 insertions, 0 deletions
diff --git a/haddock-api/src/Haddock/Convert.hs b/haddock-api/src/Haddock/Convert.hs
new file mode 100644
index 00000000..1b1a8a88
--- /dev/null
+++ b/haddock-api/src/Haddock/Convert.hs
@@ -0,0 +1,413 @@
+{-# LANGUAGE CPP, PatternGuards #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : Haddock.Convert
+-- Copyright : (c) Isaac Dupree 2009,
+-- License : BSD-like
+--
+-- Maintainer : haddock@projects.haskell.org
+-- Stability : experimental
+-- Portability : portable
+--
+-- Conversion between TyThing and HsDecl. This functionality may be moved into
+-- GHC at some point.
+-----------------------------------------------------------------------------
+module Haddock.Convert where
+-- Some other functions turned out to be useful for converting
+-- instance heads, which aren't TyThings, so just export everything.
+
+import Bag ( emptyBag )
+import BasicTypes ( TupleSort(..) )
+import Class
+import CoAxiom
+import ConLike
+import Data.Either (lefts, rights)
+import Data.List( partition )
+import Data.Monoid (mempty)
+import DataCon
+import FamInstEnv
+import Haddock.Types
+import HsSyn
+import Kind ( splitKindFunTys, synTyConResKind, isKind )
+import Name
+import PatSyn
+import PrelNames (ipClassName)
+import SrcLoc ( Located, noLoc, unLoc )
+import TcType ( tcSplitSigmaTy )
+import TyCon
+import Type (isStrLitTy, mkFunTys)
+import TypeRep
+import TysPrim ( alphaTyVars )
+import TysWiredIn ( listTyConName, eqTyCon )
+import Unique ( getUnique )
+import Var
+
+
+
+-- the main function here! yay!
+tyThingToLHsDecl :: TyThing -> Either ErrMsg ([ErrMsg], (HsDecl Name))
+tyThingToLHsDecl t = case t of
+ -- 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...
+ --
+ -- 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.
+ AnId i -> allOK $ 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.)
+ ATyCon tc
+ | Just cl <- tyConClass_maybe tc -- classes are just a little tedious
+ -> let extractFamilyDecl :: TyClDecl a -> Either ErrMsg (LFamilyDecl a)
+ extractFamilyDecl (FamDecl d) = return $ noLoc d
+ extractFamilyDecl _ =
+ Left "tyThingToLHsDecl: impossible associated tycon"
+
+ atTyClDecls = [synifyTyCon Nothing at_tc | ATI at_tc _ <- classATItems cl]
+ atFamDecls = map extractFamilyDecl (rights atTyClDecls)
+ tyClErrors = lefts atTyClDecls
+ famDeclErrors = lefts atFamDecls
+ in withErrs (tyClErrors ++ famDeclErrors) . TyClD $ ClassDecl
+ { tcdCtxt = synifyCtx (classSCTheta cl)
+ , tcdLName = synifyName cl
+ , tcdTyVars = synifyTyVars (classTyVars cl)
+ , tcdFDs = map (\ (l,r) -> noLoc
+ (map getName l, map getName r) ) $
+ snd $ classTvsFds cl
+ , tcdSigs = noLoc (MinimalSig . fmap noLoc $ classMinimalDef cl) :
+ map (noLoc . synifyIdSig DeleteTopLevelQuantification)
+ (classMethods cl)
+ , tcdMeths = emptyBag --ignore default method definitions, they don't affect signature
+ -- class associated-types are a subset of TyCon:
+ , tcdATs = rights atFamDecls
+ , tcdATDefs = [] --ignore associated type defaults
+ , tcdDocs = [] --we don't have any docs at this point
+ , tcdFVs = placeHolderNamesTc }
+ | otherwise
+ -> synifyTyCon Nothing tc >>= allOK . TyClD
+
+ -- type-constructors (e.g. Maybe) are complicated, put the definition
+ -- later in the file (also it's used for class associated-types too.)
+ ACoAxiom ax -> synifyAxiom ax >>= allOK
+
+ -- a data-constructor alone just gets rendered as a function:
+ AConLike (RealDataCon dc) -> allOK $ SigD (TypeSig [synifyName dc]
+ (synifyType ImplicitizeForAll (dataConUserType dc)) [])
+
+ AConLike (PatSynCon ps) ->
+ let (univ_tvs, ex_tvs, req_theta, prov_theta, arg_tys, res_ty) = patSynSig ps
+ qtvs = univ_tvs ++ ex_tvs
+ ty = mkFunTys arg_tys res_ty
+ in allOK . SigD $ PatSynSig (synifyName ps)
+ (Implicit, synifyTyVars qtvs)
+ (synifyCtx req_theta)
+ (synifyCtx prov_theta)
+ (synifyType WithinType ty)
+ where
+ withErrs e x = return (e, x)
+ allOK x = return (mempty, x)
+
+synifyAxBranch :: TyCon -> CoAxBranch -> TyFamInstEqn Name
+synifyAxBranch tc (CoAxBranch { cab_tvs = tkvs, cab_lhs = args, cab_rhs = rhs })
+ = let name = synifyName tc
+ typats = map (synifyType WithinType) args
+ hs_rhs = synifyType WithinType rhs
+ (kvs, tvs) = partition isKindVar tkvs
+ in TyFamEqn { tfe_tycon = name
+ , tfe_pats = HsWB { hswb_cts = typats
+ , hswb_kvs = map tyVarName kvs
+ , hswb_tvs = map tyVarName tvs
+ , hswb_wcs = [] }
+ , tfe_rhs = hs_rhs }
+
+synifyAxiom :: CoAxiom br -> Either ErrMsg (HsDecl Name)
+synifyAxiom ax@(CoAxiom { co_ax_tc = tc })
+ | isOpenTypeFamilyTyCon tc
+ , Just branch <- coAxiomSingleBranch_maybe ax
+ = return $ InstD (TyFamInstD
+ (TyFamInstDecl { tfid_eqn = noLoc $ synifyAxBranch tc branch
+ , tfid_fvs = placeHolderNamesTc }))
+
+ | Just ax' <- isClosedSynFamilyTyCon_maybe tc
+ , getUnique ax' == getUnique ax -- without the getUniques, type error
+ = synifyTyCon (Just ax) tc >>= return . TyClD
+
+ | otherwise
+ = Left "synifyAxiom: closed/open family confusion"
+
+-- | Turn type constructors into type class declarations
+synifyTyCon :: Maybe (CoAxiom br) -> TyCon -> Either ErrMsg (TyClDecl Name)
+synifyTyCon coax tc
+ | isFunTyCon tc || isPrimTyCon tc
+ = return $
+ DataDecl { tcdLName = synifyName tc
+ , tcdTyVars = -- tyConTyVars doesn't work on fun/prim, but we can make them up:
+ let mk_hs_tv realKind fakeTyVar
+ = noLoc $ KindedTyVar (getName fakeTyVar)
+ (synifyKindSig realKind)
+ in HsQTvs { hsq_kvs = [] -- No kind polymorphism
+ , hsq_tvs = zipWith mk_hs_tv (fst (splitKindFunTys (tyConKind tc)))
+ alphaTyVars --a, b, c... which are unfortunately all kind *
+ }
+
+ , tcdDataDefn = HsDataDefn { dd_ND = DataType -- arbitrary lie, they are neither
+ -- algebraic data nor newtype:
+ , dd_ctxt = noLoc []
+ , dd_cType = Nothing
+ , dd_kindSig = Just (synifyKindSig (tyConKind tc))
+ -- we have their kind accurately:
+ , dd_cons = [] -- No constructors
+ , dd_derivs = Nothing }
+ , tcdFVs = placeHolderNamesTc }
+
+ | isTypeFamilyTyCon tc
+ = case famTyConFlav_maybe tc of
+ Just rhs ->
+ let info = case rhs of
+ OpenSynFamilyTyCon -> return OpenTypeFamily
+ ClosedSynFamilyTyCon (CoAxiom { co_ax_branches = branches }) ->
+ return $ ClosedTypeFamily
+ (brListMap (noLoc . synifyAxBranch tc) branches)
+ BuiltInSynFamTyCon {} -> return $ ClosedTypeFamily []
+ AbstractClosedSynFamilyTyCon {} -> return $ ClosedTypeFamily []
+ in info >>= \i ->
+ return (FamDecl
+ (FamilyDecl { fdInfo = i
+ , fdLName = synifyName tc
+ , fdTyVars = synifyTyVars (tyConTyVars tc)
+ , fdKindSig =
+ Just (synifyKindSig (synTyConResKind tc))
+ }))
+ Nothing -> Left "synifyTyCon: impossible open type synonym?"
+
+ | isDataFamilyTyCon tc
+ = --(why no "isOpenAlgTyCon"?)
+ case algTyConRhs tc of
+ DataFamilyTyCon -> return $
+ FamDecl (FamilyDecl DataFamily (synifyName tc) (synifyTyVars (tyConTyVars tc))
+ Nothing) --always kind '*'
+ _ -> Left "synifyTyCon: impossible open data type?"
+ | Just ty <- synTyConRhs_maybe tc
+ = return $ SynDecl { tcdLName = synifyName tc
+ , tcdTyVars = synifyTyVars (tyConTyVars tc)
+ , tcdRhs = synifyType WithinType ty
+ , tcdFVs = placeHolderNamesTc }
+ | otherwise =
+ -- (closed) newtype and data
+ let
+ alg_nd = if isNewTyCon tc then NewType else DataType
+ alg_ctx = synifyCtx (tyConStupidTheta tc)
+ name = case coax of
+ Just a -> synifyName a -- Data families are named according to their
+ -- CoAxioms, not their TyCons
+ _ -> synifyName tc
+ tyvars = synifyTyVars (tyConTyVars tc)
+ 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.)
+ use_gadt_syntax = any (not . isVanillaDataCon) (tyConDataCons tc)
+ consRaw = map (synifyDataCon use_gadt_syntax) (tyConDataCons tc)
+ cons = rights consRaw
+ -- "deriving" doesn't affect the signature, no need to specify any.
+ alg_deriv = Nothing
+ defn = HsDataDefn { dd_ND = alg_nd
+ , dd_ctxt = alg_ctx
+ , dd_cType = Nothing
+ , dd_kindSig = fmap synifyKindSig kindSig
+ , dd_cons = cons
+ , dd_derivs = alg_deriv }
+ in case lefts consRaw of
+ [] -> return $
+ DataDecl { tcdLName = name, tcdTyVars = tyvars, tcdDataDefn = defn
+ , tcdFVs = placeHolderNamesTc }
+ dataConErrs -> Left $ unlines dataConErrs
+
+-- 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 -> Either ErrMsg (LConDecl Name)
+synifyDataCon use_gadt_syntax dc =
+ 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
+ (univ_tvs, ex_tvs, _eq_spec, theta, arg_tys, res_ty) = dataConFullSig dc
+
+ qvars = if use_gadt_syntax
+ then synifyTyVars (univ_tvs ++ ex_tvs)
+ else synifyTyVars ex_tvs
+
+ -- skip any EqTheta, use 'orig'inal syntax
+ ctx = synifyCtx theta
+
+ linear_tys = zipWith (\ty bang ->
+ let tySyn = synifyType WithinType ty
+ src_bang = case bang of
+ HsUnpack {} -> HsUserBang (Just True) True
+ HsStrict -> HsUserBang (Just False) True
+ _ -> bang
+ in case src_bang of
+ HsNoBang -> tySyn
+ _ -> noLoc $ HsBangTy bang tySyn
+ -- HsNoBang never appears, it's implied instead.
+ )
+ arg_tys (dataConStrictMarks dc)
+ field_tys = zipWith (\field synTy -> noLoc $ ConDeclField
+ [synifyName field] synTy Nothing)
+ (dataConFieldLabels dc) linear_tys
+ hs_arg_tys = case (use_named_field_syntax, use_infix_syntax) of
+ (True,True) -> Left "synifyDataCon: contradiction!"
+ (True,False) -> return $ RecCon field_tys
+ (False,False) -> return $ PrefixCon linear_tys
+ (False,True) -> case linear_tys of
+ [a,b] -> return $ InfixCon a b
+ _ -> Left "synifyDataCon: infix with non-2 args?"
+ hs_res_ty = if use_gadt_syntax
+ then ResTyGADT (synifyType WithinType res_ty)
+ else ResTyH98
+ -- finally we get synifyDataCon's result!
+ in hs_arg_tys >>=
+ \hat -> return . noLoc $ ConDecl [name] Implicit -- we don't know nor care
+ qvars ctx hat hs_res_ty Nothing
+ -- we don't want any "deprecated GADT syntax" warnings!
+ False
+
+synifyName :: NamedThing n => n -> Located Name
+synifyName = noLoc . getName
+
+
+synifyIdSig :: SynifyTypeState -> Id -> Sig Name
+synifyIdSig s i = TypeSig [synifyName i] (synifyType s (varType i)) []
+
+
+synifyCtx :: [PredType] -> LHsContext Name
+synifyCtx = noLoc . map (synifyType WithinType)
+
+
+synifyTyVars :: [TyVar] -> LHsTyVarBndrs Name
+synifyTyVars ktvs = HsQTvs { hsq_kvs = map tyVarName kvs
+ , hsq_tvs = map synifyTyVar tvs }
+ where
+ (kvs, tvs) = partition isKindVar ktvs
+ synifyTyVar tv
+ | isLiftedTypeKind kind = noLoc (UserTyVar name)
+ | otherwise = noLoc (KindedTyVar name (synifyKindSig kind))
+ where
+ kind = tyVarKind tv
+ name = getName tv
+
+--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 _ (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 =
+ noLoc $ HsTupleTy (case tupleTyConSort tc of
+ BoxedTuple -> HsBoxedTuple
+ ConstraintTuple -> HsConstraintTuple
+ UnboxedTuple -> HsUnboxedTuple)
+ (map (synifyType WithinType) tys)
+ -- ditto for lists
+ | getName tc == listTyConName, [ty] <- tys =
+ noLoc $ HsListTy (synifyType WithinType ty)
+ -- ditto for implicit parameter tycons
+ | tyConName tc == ipClassName
+ , [name, ty] <- tys
+ , Just x <- isStrLitTy name
+ = noLoc $ HsIParamTy (HsIPName x) (synifyType WithinType ty)
+ -- and equalities
+ | tc == eqTyCon
+ , [ty1, ty2] <- tys
+ = noLoc $ HsEqTy (synifyType WithinType ty1) (synifyType WithinType ty2)
+ -- 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
+ sTvs = synifyTyVars tvs
+ sCtx = synifyCtx ctx
+ sTau = synifyType WithinType tau
+ mkHsForAllTy forallPlicitness =
+ noLoc $ HsForAllTy forallPlicitness Nothing sTvs sCtx sTau
+ in case s of
+ DeleteTopLevelQuantification -> synifyType ImplicitizeForAll tau
+ WithinType -> mkHsForAllTy Explicit
+ ImplicitizeForAll -> mkHsForAllTy Implicit
+
+synifyType _ (LitTy t) = noLoc $ HsTyLit $ synifyTyLit t
+
+synifyTyLit :: TyLit -> HsTyLit
+synifyTyLit (NumTyLit n) = HsNumTy n
+synifyTyLit (StrTyLit s) = HsStrTy s
+
+synifyKindSig :: Kind -> LHsKind Name
+synifyKindSig k = synifyType WithinType k
+
+synifyInstHead :: ([TyVar], [PredType], Class, [Type]) -> InstHead Name
+synifyInstHead (_, preds, cls, types) =
+ ( getName cls
+ , map (unLoc . synifyType WithinType) ks
+ , map (unLoc . synifyType WithinType) ts
+ , ClassInst $ map (unLoc . synifyType WithinType) preds
+ )
+ where (ks,ts) = break (not . isKind) types
+
+-- Convert a family instance, this could be a type family or data family
+synifyFamInst :: FamInst -> Bool -> Either ErrMsg (InstHead Name)
+synifyFamInst fi opaque =
+ let fff = case fi_flavor fi of
+ SynFamilyInst | opaque -> return $ TypeInst Nothing
+ SynFamilyInst ->
+ return . TypeInst . Just . unLoc . synifyType WithinType $ fi_rhs fi
+ DataFamilyInst c ->
+ synifyTyCon (Just $ famInstAxiom fi) c >>= return . DataInst
+ in fff >>= \f' -> return (fi_fam fi , map (unLoc . synifyType WithinType) ks,
+ map (unLoc . synifyType WithinType) ts , f')
+ where (ks,ts) = break (not . isKind) $ fi_tys fi