[project @ 2000-10-26 07:19:52 by simonpj]
[ghc-hetmet.git] / ghc / compiler / main / MkIface.lhs
index 7b1123c..1172df3 100644 (file)
@@ -4,59 +4,60 @@
 \section[MkIface]{Print an interface for a module}
 
 \begin{code}
-module MkIface ( completeIface ) where
+module MkIface ( 
+       mkModDetails, mkModDetailsFromIface, completeIface, writeIface
+  ) where
 
 #include "HsVersions.h"
 
 import HsSyn
-import HsCore          ( HsIdInfo(..), toUfExpr, ifaceSigName )
+import HsCore          ( HsIdInfo(..), UfExpr(..), toUfExpr, toUfBndr )
 import HsTypes         ( toHsTyVars )
 import BasicTypes      ( Fixity(..), NewOrData(..),
-                         Version, bumpVersion, isLoopBreaker
+                         Version, initialVersion, bumpVersion, isLoopBreaker
                        )
 import RnMonad
-import RnHsSyn         ( RenamedInstDecl, RenamedTyClDecl, RenamedRuleDecl, RenamedIfaceSig )
+import RnHsSyn         ( RenamedInstDecl, RenamedTyClDecl )
+import TcHsSyn         ( TypecheckedRuleDecl )
 import HscTypes                ( VersionInfo(..), IfaceDecls(..), ModIface(..), ModDetails(..),
-                         TyThing(..), DFunId )
+                         TyThing(..), DFunId, TypeEnv, isTyClThing, Avails,
+                         WhatsImported(..), GenAvailInfo(..), 
+                         ImportVersion, AvailInfo, Deprecations(..), 
+                         Finder, ModuleLocation(..)
+                       )
 
 import CmdLineOpts
 import Id              ( Id, idType, idInfo, omitIfaceSigForId, isUserExportedId, hasNoBinding,
-                         idSpecialisation
+                         idSpecialisation, idName, setIdInfo
                        )
 import Var             ( isId )
 import VarSet
 import DataCon         ( StrictnessMark(..), dataConSig, dataConFieldLabels, dataConStrictMarks )
-import IdInfo          ( IdInfo, StrictnessInfo(..), ArityInfo(..), 
-                         CprInfo(..), CafInfo(..),
-                         inlinePragInfo, arityInfo, arityLowerBound,
-                         strictnessInfo, isBottomingStrictness,
-                         cafInfo, specInfo, cprInfo, 
-                         occInfo, isNeverInlinePrag,
-                         workerInfo, WorkerInfo(..)
+import IdInfo          -- Lots
+import CoreSyn         ( CoreExpr, CoreBind, Bind(..), CoreRule(..), IdCoreRule, 
+                         isBuiltinRule, rulesRules, rulesRhsFreeVars, emptyCoreRules,
+                         bindersOfBinds
                        )
-import CoreSyn         ( CoreExpr, CoreBind, Bind(..), isBuiltinRule, rulesRules, rulesRhsFreeVars )
 import CoreFVs         ( exprSomeFreeVars, ruleSomeLhsFreeVars, ruleSomeFreeVars )
-import CoreUnfold      ( okToUnfoldInHiFile, mkTopUnfolding, neverUnfold )
-import Name            ( isLocallyDefined, getName, nameModule,
+import CoreUnfold      ( okToUnfoldInHiFile, mkTopUnfolding, neverUnfold, unfoldingTemplate, noUnfolding )
+import Name            ( isLocallyDefined, getName, 
                          Name, NamedThing(..),
-                         plusNameEnv, lookupNameEnv, emptyNameEnv, extendNameEnv, lookupNameEnv_NF, nameEnvElts
+                         plusNameEnv, lookupNameEnv, emptyNameEnv, mkNameEnv,
+                         extendNameEnv, lookupNameEnv_NF, nameEnvElts
                        )
+import OccName         ( pprOccName )
 import TyCon           ( TyCon, getSynTyConDefn, isSynTyCon, isNewTyCon, isAlgTyCon,
                          tyConTheta, tyConTyVars, tyConDataCons, tyConFamilySize
                        )
 import Class           ( classExtraBigSig, DefMeth(..) )
 import FieldLabel      ( fieldLabelType )
 import Type            ( splitSigmaTy, tidyTopType, deNoteType )
-
-import Rules           ( ProtoCoreRule(..) )
-
-import Bag             ( bagToList )
-import UniqFM          ( lookupUFM, listToUFM )
 import SrcLoc          ( noSrcLoc )
-import Bag
 import Outputable
+import Module          ( ModuleName, moduleName )
 
 import List            ( partition )
+import IO              ( IOMode(..), openFile, hClose )
 \end{code}
 
 
@@ -67,26 +68,52 @@ import List         ( partition )
 %************************************************************************
 
 \begin{code}
-completeModDetails :: ModDetails
-                  -> [CoreBind] -> [Id]        -- Final bindings, plus the top-level Ids from the
-                                               -- code generator; they have authoritative arity info
-                  -> [IdCoreRule]              -- Tidy orphan rules
-                  -> ModDetails
-completeModDetails mds tidy_binds stg_ids orphan_rules
-  = ModDetails { 
-
+mkModDetails :: TypeEnv -> [DFunId]    -- From typechecker
+            -> [CoreBind] -> [Id]      -- Final bindings, plus the top-level Ids from the
+                                       -- code generator; they have authoritative arity info
+            -> [IdCoreRule]            -- Tidy orphan rules
+            -> ModDetails
+mkModDetails type_env dfun_ids tidy_binds stg_ids orphan_rules
+  = ModDetails { md_types = new_type_env,
+                md_rules = rule_dcls,
+                md_insts = dfun_ids }
   where
-    dfun_ids = md_insts mds
-    
+       -- The competed type environment is gotten from
+       --      a) keeping the types and classes
+       --      b) removing all Ids, and Ids with correct IdInfo
+       --              gotten from the bindings
+    new_type_env = mkNameEnv [(getName tycl, tycl) | tycl <- orig_type_env, isTyClThing tycl]
+                       `plusNameEnv`
+                  mkNameEnv [(idName id, AnId id) | id <- final_ids]
+
+    orig_type_env = nameEnvElts type_env
+
     final_ids = bindsToIds (mkVarSet dfun_ids `unionVarSet` orphan_rule_ids)
                           (mkVarSet stg_ids)
                           tidy_binds
 
-     rule_dcls | opt_OmitInterfacePragmas = []
-              | otherwise                = getRules orphan_rules tidy_binds (mkVarSet final_ids)
-
-     orphan_rule_ids = unionVarSets [ ruleSomeFreeVars interestingId rule 
-                                   | (_, rule) <- tidy_orphan_rules]
+       -- The complete rules are gotten by combining
+       --      a) the orphan rules
+       --      b) rules embedded in the top-level Ids
+    rule_dcls | opt_OmitInterfacePragmas = []
+             | otherwise                 = getRules orphan_rules tidy_binds (mkVarSet final_ids)
+
+    orphan_rule_ids = unionVarSets [ ruleSomeFreeVars interestingId rule 
+                                  | (_, rule) <- orphan_rules]
+
+
+-- This version is used when we are re-linking a module
+-- so we've only run the type checker on its previous interface 
+mkModDetailsFromIface :: TypeEnv -> [DFunId]   -- From typechecker
+                     -> [TypecheckedRuleDecl]
+                     -> ModDetails
+mkModDetailsFromIface type_env dfun_ids rules
+  = ModDetails { md_types = type_env,
+                md_rules = rule_dcls,
+                md_insts = dfun_ids }
+  where
+    rule_dcls = [(id,rule) | IfaceRuleOut id rule <- rules]
+       -- All the rules from an interface are of the IfaceRuleOut form
 
 
 completeIface :: Maybe ModIface                -- The old interface, if we have it
@@ -109,9 +136,9 @@ completeIface maybe_old_iface new_iface mod_details
                              dcl_insts = inst_dcls,
                              dcl_rules = rule_dcls }
 
-     inst_dcls   = map ifaceInstance (mk_insts mds)
-     ty_cls_dcls = map ifaceTyCls (nameEnvElts (md_types details))
-     rule_dcls   = map ifaceRule (md_rules details)
+     inst_dcls   = map ifaceInstance (md_insts mod_details)
+     ty_cls_dcls = map ifaceTyCls (nameEnvElts (md_types mod_details))
+     rule_dcls   = map ifaceRule (md_rules mod_details)
 \end{code}
 
 
@@ -220,14 +247,14 @@ ifaceTyCls (AnId id)
 
 
     ------------  Worker  --------------
-    wkr_hsinfo = case workerInfo id_info of
+    wrkr_hsinfo = case workerInfo id_info of
                    HasWorker work_id wrap_arity -> [HsWorker (getName work_id)]
                    NoWorker                     -> []
 
     ------------  Unfolding  --------------
-    unfold_info = unfoldInfo id_info
-    inine_prag  = inlinePragInfo id_info
-    rhs                = unfoldingTempate unfold_info
+    unfold_info = unfoldingInfo id_info
+    inline_prag = inlinePragInfo id_info
+    rhs                = unfoldingTemplate unfold_info
     unfold_hsinfo | neverUnfold unfold_info = []
                  | otherwise               = [HsUnfold inline_prag (toUfExpr rhs)]
 \end{code}
@@ -293,7 +320,7 @@ bindsToIds needed_ids codegen_ids binds
        -- exported, there's no need to emit anything
     need_id needed_set id = id `elemVarSet` needed_set || isUserExportedId id 
 
-    go needed [] decls emitted
+    go needed [] emitted
        | not (isEmptyVarSet needed) = pprTrace "ifaceBinds: free vars:" 
                                          (sep (map ppr (varSetElems needed)))
                                       emitted
@@ -308,7 +335,7 @@ bindsToIds needed_ids codegen_ids binds
               binds
               (new_id:emitted)
        | otherwise
-       = go needed binds decls emitted
+       = go needed binds emitted
        where
          (new_id, extras) = mkFinalId codegen_ids False id rhs
 
@@ -317,7 +344,7 @@ bindsToIds needed_ids codegen_ids binds
        -- have to look for a fixed point.  We don't want necessarily them all, 
        -- because without -O we may only need the first one (if we don't emit
        -- its unfolding)
-    go needed (Rec pairs : binds) decls emitted
+    go needed (Rec pairs : binds) emitted
        = go needed' binds emitted' 
        where
          (new_emitted, extras) = go_rec needed pairs
@@ -351,7 +378,6 @@ mkFinalId :: IdSet          -- The Ids with arity info from the code generator
 mkFinalId codegen_ids is_rec id rhs
   = (id `setIdInfo` new_idinfo, new_needed_ids)
   where
-    id_type     = idType id
     core_idinfo = idInfo id
     stg_idinfo  = case lookupVarSet codegen_ids id of
                        Just id' -> idInfo id'
@@ -361,7 +387,7 @@ mkFinalId codegen_ids is_rec id rhs
     new_idinfo | opt_OmitInterfacePragmas
               = vanillaIdInfo
               | otherwise                
-              = core_idinfo `setArityInfo`      stg_arity_info
+              = core_idinfo `setArityInfo`      arity_info
                             `setCafInfo`        cafInfo stg_idinfo
                             `setUnfoldingInfo`  unfold_info
                             `setWorkerInfo`     worker_info
@@ -370,8 +396,8 @@ mkFinalId codegen_ids is_rec id rhs
        -- passed on separately through the modules IdCoreRules
 
     ------------  Arity  --------------
-    stg_arity_info = arityInfo stg_idinfo
-    stg_arity     = arityLowerBound arity_info
+    arity_info = arityInfo stg_idinfo
+    stg_arity  = arityLowerBound arity_info
 
     ------------  Worker  --------------
        -- We only treat a function as having a worker if
@@ -396,8 +422,8 @@ mkFinalId codegen_ids is_rec id rhs
        -- compilation of this module it means "how many things can I apply
        -- this to".
     worker_info = case workerInfo core_idinfo of
-                    HasWorker work_id wrap_arity 
-                       | wrap_arity == stg_arity -> worker_info_in
+                    info@(HasWorker work_id wrap_arity)
+                       | wrap_arity == stg_arity -> info
                        | otherwise               -> pprTrace "ifaceId: arity change:" (ppr id) 
                                                     NoWorker
                     NoWorker                     -> NoWorker
@@ -508,14 +534,14 @@ addVersionInfo (Just old_iface@(ModIface { mi_version = old_version,
   = Nothing
 
   | otherwise          -- Add updated version numbers
-  = Just (final_iface, pp_tc_diffs $$ pp_sig_diffs)
+  = Just (final_iface, pp_tc_diffs)
        
   where
     final_iface = new_iface { mi_version = new_version }
     new_version = VersionInfo { vers_module  = bumpVersion no_output_change (vers_module  old_version),
                                vers_exports = bumpVersion no_export_change (vers_exports old_version),
                                vers_rules   = bumpVersion no_rule_change   (vers_rules   old_version),
-                               vers_decls   = sig_vers `plusNameEnv` tc_vers }
+                               vers_decls   = tc_vers }
 
     no_output_change = no_tc_change && no_rule_change && no_export_change
     no_usage_change  = mi_usages old_iface == mi_usages new_iface
@@ -527,17 +553,19 @@ addVersionInfo (Just old_iface@(ModIface { mi_version = old_version,
        -- Set the flag if anything changes. 
        -- Assumes that the decls are sorted by hsDeclName.
     old_vers_decls = vers_decls old_version
-    (no_tc_change,  pp_tc_diffs,  tc_vers) = diffDecls old_vers_decls (dcl_tycl old_decls) (dcl_tycl new_decls)
+    (no_tc_change,  pp_tc_diffs,  tc_vers) = diffDecls old_vers_decls old_fixities new_fixities
+                                                      (dcl_tycl old_decls) (dcl_tycl new_decls)
 
 
 
 diffDecls :: NameEnv Version                           -- Old version map
+         -> NameEnv Fixity -> NameEnv Fixity           -- Old and new fixities
          -> [RenamedTyClDecl] -> [RenamedTyClDecl]     -- Old and new decls
          -> (Bool,             -- True <=> no change
              SDoc,             -- Record of differences
              NameEnv Version)  -- New version
 
-diffDecls old_vers old new
+diffDecls old_vers old_fixities new_fixities old new
   = diff True empty emptyNameEnv old new
   where
        -- When seeing if two decls are the same, 
@@ -552,11 +580,11 @@ diffDecls old_vers old new
        = case od_name `compare` nd_name of
                LT -> diff False (pp $$ only_old od) new_vers ods      (nd:nds)
                GT -> diff False (pp $$ only_new nd) new_vers (od:ods) nds
-               EQ | od `eq` nd -> diff ok_so_far pp                     new_vers  ods nds
-                  | otherwise  -> diff False      (pp $$ changed od nd) new_vers' ods nds
+               EQ | od `eq_tc` nd -> diff ok_so_far pp                    new_vers  ods nds
+                  | otherwise     -> diff False     (pp $$ changed od nd) new_vers' ods nds
        where
-         od_name = get_name od
-         nd_name = get_name nd
+         od_name = tyClDeclName od
+         nd_name = tyClDeclName nd
          new_vers' = extendNameEnv new_vers nd_name 
                                    (bumpVersion True (lookupNameEnv_NF old_vers od_name))
 
@@ -565,3 +593,146 @@ diffDecls old_vers old new
     changed d nd = ptext SLIT("Changed in iface: ") <+> ((ptext SLIT("Old:") <+> ppr d) $$ 
                                                         (ptext SLIT("New:") <+> ppr nd))
 \end{code}
+
+
+
+%************************************************************************
+%*                                                                     *
+\subsection{Writing an interface file}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+writeIface :: Finder -> Maybe ModIface -> IO ()
+writeIface finder Nothing
+  = return ()
+
+writeIface finder (Just mod_iface)
+  = do { maybe_found <- finder mod_name ;
+       ; case maybe_found of {
+           Nothing -> printErrs (text "Can't write interface file for" <+> ppr mod_name) ;
+           Just (_, locn) ->
+
+    do { let filename = hi_file locn 
+       ; if_hdl <- openFile filename WriteMode
+       ; printForIface if_hdl (pprIface mod_iface)
+       ; hClose if_hdl
+       }}}
+  where
+    mod_name = moduleName (mi_module mod_iface)
+        
+pprIface iface
+ = vcat [ ptext SLIT("__interface")
+               <+> doubleQuotes (ptext opt_InPackage)
+               <+> ppr (mi_module iface) <+> ppr (vers_module version_info)
+               <+> pp_sub_vers
+               <+> (if mi_orphan iface then char '!' else empty)
+               <+> int opt_HiVersion
+               <+> ptext SLIT("where")
+
+       , vcat (map pprExport (mi_exports iface))
+       , vcat (map pprUsage (mi_usages iface))
+
+       , pprIfaceDecls (vers_decls version_info) 
+                       (mi_fixities iface)
+                       (mi_decls iface)
+
+       , pprDeprecs (mi_deprecs iface)
+       ]
+  where
+    version_info = mi_version iface
+    exp_vers     = vers_exports version_info
+    rule_vers   = vers_rules version_info
+
+    pp_sub_vers | exp_vers == initialVersion && rule_vers == initialVersion = empty
+               | otherwise = brackets (ppr exp_vers <+> ppr rule_vers)
+\end{code}
+
+When printing export lists, we print like this:
+       Avail   f               f
+       AvailTC C [C, x, y]     C(x,y)
+       AvailTC C [x, y]        C!(x,y)         -- Exporting x, y but not C
+
+\begin{code}
+pprExport :: (ModuleName, Avails) -> SDoc
+pprExport (mod, items)
+ = hsep [ ptext SLIT("__export "), ppr mod, hsep (map pp_avail items) ] <> semi
+  where
+    ppr_name :: Name -> SDoc   -- Print the occurrence name only
+    ppr_name n = ppr (nameOccName n)
+
+    pp_avail :: AvailInfo -> SDoc
+    pp_avail (Avail name)      = ppr_name name
+    pp_avail (AvailTC name []) = empty
+    pp_avail (AvailTC name ns) = hcat [ppr_name name, bang, pp_export ns']
+                               where
+                                 bang | name `elem` ns = empty
+                                      | otherwise      = char '|'
+                                 ns' = filter (/= name) ns
+    
+    pp_export []    = empty
+    pp_export names = braces (hsep (map ppr_name names))
+\end{code}
+
+
+\begin{code}
+pprUsage :: ImportVersion Name -> SDoc
+pprUsage (m, has_orphans, is_boot, whats_imported)
+  = hsep [ptext SLIT("import"), ppr m, 
+         pp_orphan, pp_boot,
+         pp_versions whats_imported
+    ] <> semi
+  where
+    pp_orphan | has_orphans = char '!'
+             | otherwise   = empty
+    pp_boot   | is_boot     = char '@'
+              | otherwise   = empty
+
+       -- Importing the whole module is indicated by an empty list
+    pp_versions NothingAtAll                       = empty
+    pp_versions (Everything v)                     = dcolon <+> int v
+    pp_versions (Specifically vm ve nvs vr) = dcolon <+> int vm <+> pp_export_version ve <+> int vr 
+                                             <+> hsep [ ppr n <+> int v | (n,v) <- nvs ]
+
+       -- HACK for the moment: print the export-list version even if
+       -- we don't use it, so that syntax of interface files doesn't change
+    pp_export_version Nothing  = int 1
+    pp_export_version (Just v) = int v
+\end{code}
+
+\begin{code}
+pprIfaceDecls version_map fixity_map decls
+  = vcat [ vcat [ppr i <+> semi | i <- dcl_insts decls]
+        , vcat (map ppr_decl (dcl_tycl decls))
+        , pprRules (dcl_rules decls)
+        ]
+  where
+    ppr_decl d  = (ppr_vers d <+> ppr d <> semi) $$ ppr_fixes d
+
+       -- Print the version for the decl
+    ppr_vers d = case lookupNameEnv version_map (tyClDeclName d) of
+                  Nothing -> empty
+                  Just v  -> int v
+
+       -- Print fixities relevant to the decl
+    ppr_fixes d = vcat [ ppr fix <+> ppr n <> semi
+                      | (n,_) <- tyClDeclNames d, 
+                        Just fix <- [lookupNameEnv fixity_map n]
+                      ]
+\end{code}
+
+\begin{code}
+pprRules []    = empty
+pprRules rules = hsep [ptext SLIT("{-## __R"), vcat (map ppr rules), ptext SLIT("##-}")]
+
+pprDeprecs NoDeprecs = empty
+pprDeprecs deprecs   = ptext SLIT("{-## __D") <+> guts <+> ptext SLIT("##-}")
+                    where
+                      guts = case deprecs of
+                               DeprecAll txt  -> ptext txt
+                               DeprecSome env -> pp_deprecs env
+
+pp_deprecs env = vcat (punctuate semi (map pp_deprec (nameEnvElts env)))
+              where
+                pp_deprec (name, txt) = pprOccName (nameOccName name) <+> ptext txt
+\end{code}