diff options
Diffstat (limited to 'haddock-api/src/Haddock/Convert.hs')
| -rw-r--r-- | haddock-api/src/Haddock/Convert.hs | 413 | 
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 | 
