Zubin pushed to branch wip/9.14.2-backports at Glasgow Haskell Compiler / GHC
Commits:
-
2a285613
by Ian-Woo Kim at 2026-07-20T13:16:54+05:30
-
0b75a0c4
by Wolfgang Jeltsch at 2026-07-20T13:16:54+05:30
-
4955f0ca
by Andrea Vezzosi at 2026-07-20T13:16:54+05:30
26 changed files:
- + changelog.d/deterministic-usage-order
- changelog.d/more-efficient-home-unit-imports-finding
- compiler/GHC/ByteCode/Breakpoints.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/HsToCore/Breakpoints.hs
- + compiler/GHC/HsToCore/Breakpoints/Types.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Make.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- compiler/GHC/Unit/Module/ModIface.hs
- compiler/GHC/Unit/Module/WholeCoreBindings.hs
- compiler/GHC/Utils/Binary.hs
- compiler/ghc.cabal.in
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout
- testsuite/tests/ghci/should_run/Makefile
- + testsuite/tests/ghci/should_run/T27287.hs
- + testsuite/tests/ghci/should_run/T27287.stdout
- testsuite/tests/ghci/should_run/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Make the order of usages deterministic
|
|
| 3 | +issues: #26877
|
|
| 4 | +mrs: !15484
|
|
| 5 | +description: {
|
|
| 6 | + The order in which usages appear in interface files is now
|
|
| 7 | + deterministic.
|
|
| 8 | +} |
| ... | ... | @@ -3,13 +3,10 @@ synopsis: Introduce a cache of home module name providers |
| 3 | 3 | issues: #27055
|
| 4 | 4 | mrs: !15888
|
| 5 | 5 | description: {
|
| 6 | - This contribution optimizes the algorithm for finding out which home
|
|
| 7 | - unit provides the module that a certain import declaration refers
|
|
| 8 | - to. The previous approach has been to simply search all home units
|
|
| 9 | - in no particular order. This change introduces a cache that allows
|
|
| 10 | - for efficiently determining those complete home units that provide a
|
|
| 11 | - certain module name and changes the module-finding algorithm such
|
|
| 12 | - that it searches these units before the other home units. This leads
|
|
| 13 | - to significant performance improvements in situations where there
|
|
| 14 | - are lots of home units.
|
|
| 6 | + For finding out which home unit provides the module that a certain
|
|
| 7 | + import declaration refers to, we now use a better algorithm. The
|
|
| 8 | + previous approach was to simply search all home units in no
|
|
| 9 | + particular order. The new algorithm first searches those complete
|
|
| 10 | + home units that provide a certain module name, before searching the
|
|
| 11 | + other home units.
|
|
| 15 | 12 | } |
| ... | ... | @@ -40,7 +40,7 @@ import Control.DeepSeq |
| 40 | 40 | import Data.IntMap.Strict (IntMap)
|
| 41 | 41 | import qualified Data.IntMap.Strict as IM
|
| 42 | 42 | |
| 43 | -import GHC.HsToCore.Breakpoints
|
|
| 43 | +import GHC.HsToCore.Breakpoints.Types
|
|
| 44 | 44 | import GHC.Iface.Syntax
|
| 45 | 45 | |
| 46 | 46 | import GHC.Unit.Module (Module)
|
| ... | ... | @@ -42,7 +42,7 @@ import GHC.Types.Name.Env |
| 42 | 42 | import GHC.Utils.Outputable
|
| 43 | 43 | import GHC.Builtin.PrimOps
|
| 44 | 44 | import GHC.Types.SptEntry
|
| 45 | -import GHC.HsToCore.Breakpoints
|
|
| 45 | +import GHC.HsToCore.Breakpoints.Types
|
|
| 46 | 46 | import GHC.ByteCode.Breakpoints
|
| 47 | 47 | import GHCi.Message
|
| 48 | 48 | import GHCi.RemoteTypes
|
| ... | ... | @@ -305,4 +305,3 @@ instance Outputable UnlinkedBCO where |
| 305 | 305 | = sep [text "BCO", ppr nm, text "with",
|
| 306 | 306 | ppr (sizeFlatBag lits), text "lits",
|
| 307 | 307 | ppr (sizeFlatBag ptrs), text "ptrs" ] |
| 308 | - |
| ... | ... | @@ -693,7 +693,7 @@ hsc_typecheck keep_rn mod_summary mb_rdr_module = do |
| 693 | 693 | Nothing -> hscParse' mod_summary
|
| 694 | 694 | tc_result0 <- tcRnModule' mod_summary keep_rn' hpm
|
| 695 | 695 | if hsc_src == HsigFile
|
| 696 | - then do (iface, _) <- liftIO $ hscSimpleIface hsc_env Nothing tc_result0 mod_summary
|
|
| 696 | + then do (iface, _) <- liftIO $ hscSimpleIface hsc_env Nothing Nothing tc_result0 mod_summary
|
|
| 697 | 697 | ioMsgMaybe $ hoistTcRnMessage $
|
| 698 | 698 | tcRnMergeSignatures hsc_env hpm tc_result0 iface
|
| 699 | 699 | else return tc_result0
|
| ... | ... | @@ -871,7 +871,7 @@ hscRecompStatus |
| 871 | 871 | -- we will decide if we need them or not.
|
| 872 | 872 | bc_linkable <- checkByteCode checked_iface mod_summary (homeMod_bytecode old_linkable)
|
| 873 | 873 | obj_linkable <- liftIO $ checkObjects lcl_dflags (homeMod_object old_linkable) mod_summary
|
| 874 | - trace_if (hsc_logger hsc_env) (vcat [text "BCO linkable", nest 2 (ppr bc_linkable), text "Object Linkable", ppr obj_linkable])
|
|
| 874 | + trace_if (hsc_logger hsc_env) (vcat [text "BCO linkable", nest 2 (ppr bc_linkable), text "Object Linkable", nest 2 (ppr obj_linkable)])
|
|
| 875 | 875 | |
| 876 | 876 | let just_bc = justBytecode <$> bc_linkable
|
| 877 | 877 | just_o = justObjects <$> obj_linkable
|
| ... | ... | @@ -1018,12 +1018,13 @@ compile_for_interpreter hsc_env use = |
| 1018 | 1018 | -- | Assemble 'WholeCoreBindings' if the interface contains Core bindings.
|
| 1019 | 1019 | iface_core_bindings :: ModIface -> ModLocation -> Maybe WholeCoreBindings
|
| 1020 | 1020 | iface_core_bindings iface wcb_mod_location =
|
| 1021 | - mi_simplified_core <&> \(IfaceSimplifiedCore bindings foreign') ->
|
|
| 1021 | + mi_simplified_core <&> \(IfaceSimplifiedCore bindings wcb_modBreaks foreign') ->
|
|
| 1022 | 1022 | WholeCoreBindings {
|
| 1023 | 1023 | wcb_bindings = bindings,
|
| 1024 | 1024 | wcb_module = mi_module,
|
| 1025 | 1025 | wcb_mod_location,
|
| 1026 | - wcb_foreign = foreign'
|
|
| 1026 | + wcb_foreign = foreign',
|
|
| 1027 | + wcb_modBreaks
|
|
| 1027 | 1028 | }
|
| 1028 | 1029 | where
|
| 1029 | 1030 | ModIface {mi_module, mi_simplified_core} = iface
|
| ... | ... | @@ -1161,11 +1162,11 @@ compileWholeCoreBindings hsc_env type_env wcb = do |
| 1161 | 1162 | gen_bytecode core_binds stubs foreign_files = do
|
| 1162 | 1163 | let cgi_guts = CgInteractiveGuts wcb_module core_binds
|
| 1163 | 1164 | (typeEnvTyCons type_env) stubs foreign_files
|
| 1164 | - Nothing []
|
|
| 1165 | + wcb_modBreaks []
|
|
| 1165 | 1166 | trace_if logger (text "Generating ByteCode for" <+> ppr wcb_module)
|
| 1166 | 1167 | generateByteCode hsc_env cgi_guts wcb_mod_location
|
| 1167 | 1168 | |
| 1168 | - WholeCoreBindings {wcb_module, wcb_mod_location, wcb_foreign} = wcb
|
|
| 1169 | + WholeCoreBindings {wcb_module, wcb_mod_location, wcb_foreign, wcb_modBreaks} = wcb
|
|
| 1169 | 1170 | |
| 1170 | 1171 | logger = hsc_logger hsc_env
|
| 1171 | 1172 | |
| ... | ... | @@ -1292,7 +1293,8 @@ hscDesugarAndSimplify summary (FrontendTypecheck tc_result) tc_warnings mb_old_h |
| 1292 | 1293 | liftIO $ hscTidy hsc_env simplified_guts
|
| 1293 | 1294 | |
| 1294 | 1295 | (iface, _details) <- liftIO $
|
| 1295 | - hscSimpleIface hsc_env (Just $ cg_binds cg_guts) tc_result summary
|
|
| 1296 | + hscSimpleIface hsc_env (Just $ cg_binds cg_guts)
|
|
| 1297 | + (cg_modBreaks cg_guts) tc_result summary
|
|
| 1296 | 1298 | |
| 1297 | 1299 | liftIO $ hscMaybeWriteIface logger dflags True iface mb_old_hash (ms_location summary)
|
| 1298 | 1300 | |
| ... | ... | @@ -1307,7 +1309,7 @@ hscDesugarAndSimplify summary (FrontendTypecheck tc_result) tc_warnings mb_old_h |
| 1307 | 1309 | -- and generate a simple interface.
|
| 1308 | 1310 | _ -> do
|
| 1309 | 1311 | (iface, _details) <- liftIO $
|
| 1310 | - hscSimpleIface hsc_env Nothing tc_result summary
|
|
| 1312 | + hscSimpleIface hsc_env Nothing Nothing tc_result summary
|
|
| 1311 | 1313 | |
| 1312 | 1314 | liftIO $ hscMaybeWriteIface logger dflags True iface mb_old_hash (ms_location summary)
|
| 1313 | 1315 | |
| ... | ... | @@ -1886,17 +1888,19 @@ hscSimplify' plugins ds_result = do |
| 1886 | 1888 | -- generates interface files. See Note [simpleTidyPgm - mkBootModDetailsTc]
|
| 1887 | 1889 | hscSimpleIface :: HscEnv
|
| 1888 | 1890 | -> Maybe CoreProgram
|
| 1891 | + -> Maybe ModBreaks
|
|
| 1889 | 1892 | -> TcGblEnv
|
| 1890 | 1893 | -> ModSummary
|
| 1891 | 1894 | -> IO (ModIface, ModDetails)
|
| 1892 | -hscSimpleIface hsc_env mb_core_program tc_result summary
|
|
| 1893 | - = runHsc hsc_env $ hscSimpleIface' mb_core_program tc_result summary
|
|
| 1895 | +hscSimpleIface hsc_env mb_core_program mb_modBreaks tc_result summary
|
|
| 1896 | + = runHsc hsc_env $ hscSimpleIface' mb_core_program mb_modBreaks tc_result summary
|
|
| 1894 | 1897 | |
| 1895 | 1898 | hscSimpleIface' :: Maybe CoreProgram
|
| 1899 | + -> Maybe ModBreaks
|
|
| 1896 | 1900 | -> TcGblEnv
|
| 1897 | 1901 | -> ModSummary
|
| 1898 | 1902 | -> Hsc (ModIface, ModDetails)
|
| 1899 | -hscSimpleIface' mb_core_program tc_result summary = do
|
|
| 1903 | +hscSimpleIface' mb_core_program mb_modBreaks tc_result summary = do
|
|
| 1900 | 1904 | hsc_env <- getHscEnv
|
| 1901 | 1905 | logger <- getLogger
|
| 1902 | 1906 | details <- liftIO $ mkBootModDetailsTc logger tc_result
|
| ... | ... | @@ -1904,7 +1908,7 @@ hscSimpleIface' mb_core_program tc_result summary = do |
| 1904 | 1908 | new_iface
|
| 1905 | 1909 | <- {-# SCC "MkFinalIface" #-}
|
| 1906 | 1910 | liftIO $
|
| 1907 | - mkIfaceTc hsc_env safe_mode details summary mb_core_program tc_result
|
|
| 1911 | + mkIfaceTc hsc_env safe_mode details summary mb_core_program mb_modBreaks tc_result
|
|
| 1908 | 1912 | -- And the answer is ...
|
| 1909 | 1913 | liftIO $ dumpIfaceStats hsc_env
|
| 1910 | 1914 | return (new_iface, details)
|
| ... | ... | @@ -15,7 +15,7 @@ |
| 15 | 15 | -- See Note [ModBreaks vs InternalModBreaks] and Note [Breakpoint identifiers]
|
| 16 | 16 | module GHC.HsToCore.Breakpoints
|
| 17 | 17 | ( -- * ModBreaks
|
| 18 | - mkModBreaks, ModBreaks(..)
|
|
| 18 | + mkModBreaks, ModBreaks(..), modBreaks_locs
|
|
| 19 | 19 | |
| 20 | 20 | -- ** Re-exports BreakpointId
|
| 21 | 21 | , BreakpointId(..), BreakTickIndex
|
| ... | ... | @@ -25,46 +25,12 @@ import GHC.Prelude |
| 25 | 25 | import Data.Array
|
| 26 | 26 | |
| 27 | 27 | import GHC.HsToCore.Ticks (Tick (..))
|
| 28 | +import GHC.HsToCore.Breakpoints.Types
|
|
| 28 | 29 | import GHC.Data.SizedSeq
|
| 29 | -import GHC.Types.SrcLoc (SrcSpan)
|
|
| 30 | -import GHC.Types.Name (OccName)
|
|
| 31 | -import GHC.Types.Tickish (BreakTickIndex, BreakpointId(..))
|
|
| 32 | 30 | import GHC.Unit.Module (Module)
|
| 33 | 31 | import GHC.Utils.Outputable
|
| 34 | 32 | import Data.List (intersperse)
|
| 35 | - |
|
| 36 | ---------------------------------------------------------------------------------
|
|
| 37 | --- ModBreaks
|
|
| 38 | ---------------------------------------------------------------------------------
|
|
| 39 | - |
|
| 40 | --- | All the information about the source-relevant breakpoints for a module
|
|
| 41 | ---
|
|
| 42 | --- This information is constructed once during desugaring (with `mkModBreaks`)
|
|
| 43 | --- from breakpoint ticks and fixed/unchanged from there on forward. It could be
|
|
| 44 | --- exported as an abstract datatype because it should never be updated after
|
|
| 45 | --- construction, only queried.
|
|
| 46 | ---
|
|
| 47 | --- The arrays can be indexed using the int in the corresponding 'BreakpointId'
|
|
| 48 | --- (i.e. the 'BreakpointId' whose 'Module' matches the 'Module' corresponding
|
|
| 49 | --- to these 'ModBreaks') with the accessors 'modBreaks_locs', 'modBreaks_vars',
|
|
| 50 | --- and 'modBreaks_decls'.
|
|
| 51 | -data ModBreaks
|
|
| 52 | - = ModBreaks
|
|
| 53 | - { modBreaks_locs :: !(Array BreakTickIndex SrcSpan)
|
|
| 54 | - -- ^ An array giving the source span of each breakpoint.
|
|
| 55 | - , modBreaks_vars :: !(Array BreakTickIndex [OccName])
|
|
| 56 | - -- ^ An array giving the names of the free variables at each breakpoint.
|
|
| 57 | - , modBreaks_decls :: !(Array BreakTickIndex [String])
|
|
| 58 | - -- ^ An array giving the names of the declarations enclosing each breakpoint.
|
|
| 59 | - -- See Note [Field modBreaks_decls]
|
|
| 60 | - , modBreaks_ccs :: !(Array BreakTickIndex (String, String))
|
|
| 61 | - -- ^ Array pointing to cost centre info for each breakpoint;
|
|
| 62 | - -- actual 'CostCentre' allocation is done at link-time.
|
|
| 63 | - , modBreaks_module :: !Module
|
|
| 64 | - -- ^ The module to which this ModBreaks is associated.
|
|
| 65 | - -- We also cache this here for internal sanity checks.
|
|
| 66 | - }
|
|
| 67 | - |
|
| 33 | +import GHC.Utils.Binary (BinSrcSpan(BinSrcSpan))
|
|
| 68 | 34 | -- | Initialize memory for breakpoint data that is shared between the bytecode
|
| 69 | 35 | -- generator and the interpreter.
|
| 70 | 36 | --
|
| ... | ... | @@ -91,7 +57,7 @@ mkModBreaks interpreterProfiled modl extendedMixEntries |
| 91 | 57 | ]
|
| 92 | 58 | | otherwise = listArray (0, -1) []
|
| 93 | 59 | in ModBreaks
|
| 94 | - { modBreaks_locs = locsTicks
|
|
| 60 | + { modBreaks_locs_ = fmap BinSrcSpan locsTicks
|
|
| 95 | 61 | , modBreaks_vars = varsTicks
|
| 96 | 62 | , modBreaks_decls = declsTicks
|
| 97 | 63 | , modBreaks_ccs = ccs
|
| 1 | +{-# LANGUAGE RecordWildCards #-}
|
|
| 2 | + |
|
| 3 | +-- | Information attached to Breakpoints generated from Ticks
|
|
| 4 | +--
|
|
| 5 | +-- The breakpoint information stored in 'ModBreaks' is generated during
|
|
| 6 | +-- desugaring from the ticks annotating the source expressions.
|
|
| 7 | +--
|
|
| 8 | +-- This information can be queried per-breakpoint using the 'BreakpointId'
|
|
| 9 | +-- datatype, which indexes tick-level breakpoint information.
|
|
| 10 | +--
|
|
| 11 | +-- 'ModBreaks' and 'BreakpointId's are not to be confused with
|
|
| 12 | +-- 'InternalModBreaks' and 'InternalBreakId's. The latter are constructed
|
|
| 13 | +-- during bytecode generation and can be found in 'GHC.ByteCode.Breakpoints'.
|
|
| 14 | +--
|
|
| 15 | +-- See Note [ModBreaks vs InternalModBreaks] and Note [Breakpoint identifiers]
|
|
| 16 | +module GHC.HsToCore.Breakpoints.Types
|
|
| 17 | + ( -- * ModBreaks
|
|
| 18 | + ModBreaks(..), modBreaks_locs
|
|
| 19 | + |
|
| 20 | + -- ** Re-exports BreakpointId
|
|
| 21 | + , BreakpointId(..), BreakTickIndex
|
|
| 22 | + ) where
|
|
| 23 | + |
|
| 24 | +import GHC.Prelude
|
|
| 25 | +import Data.Array
|
|
| 26 | + |
|
| 27 | +import GHC.Types.SrcLoc (SrcSpan)
|
|
| 28 | +import GHC.Types.Name (OccName)
|
|
| 29 | +import GHC.Types.Tickish (BreakTickIndex, BreakpointId(..))
|
|
| 30 | +import GHC.Unit.Module (Module)
|
|
| 31 | +import Data.Coerce
|
|
| 32 | +import GHC.Utils.Binary (BinSrcSpan(..), Binary(..))
|
|
| 33 | +import Control.DeepSeq
|
|
| 34 | + |
|
| 35 | +--------------------------------------------------------------------------------
|
|
| 36 | +-- ModBreaks
|
|
| 37 | +--------------------------------------------------------------------------------
|
|
| 38 | + |
|
| 39 | +-- | All the information about the source-relevant breakpoints for a module
|
|
| 40 | +--
|
|
| 41 | +-- This information is constructed once during desugaring (with `mkModBreaks`)
|
|
| 42 | +-- from breakpoint ticks and fixed/unchanged from there on forward. It could be
|
|
| 43 | +-- exported as an abstract datatype because it should never be updated after
|
|
| 44 | +-- construction, only queried.
|
|
| 45 | +--
|
|
| 46 | +-- The arrays can be indexed using the int in the corresponding 'BreakpointId'
|
|
| 47 | +-- (i.e. the 'BreakpointId' whose 'Module' matches the 'Module' corresponding
|
|
| 48 | +-- to these 'ModBreaks') with the accessors 'modBreaks_locs', 'modBreaks_vars',
|
|
| 49 | +-- and 'modBreaks_decls'.
|
|
| 50 | +data ModBreaks
|
|
| 51 | + = ModBreaks
|
|
| 52 | + { modBreaks_locs_ :: !(Array BreakTickIndex BinSrcSpan)
|
|
| 53 | + -- ^ An array giving the source span of each breakpoint.
|
|
| 54 | + , modBreaks_vars :: !(Array BreakTickIndex [OccName])
|
|
| 55 | + -- ^ An array giving the names of the free variables at each breakpoint.
|
|
| 56 | + , modBreaks_decls :: !(Array BreakTickIndex [String])
|
|
| 57 | + -- ^ An array giving the names of the declarations enclosing each breakpoint.
|
|
| 58 | + -- See Note [Field modBreaks_decls]
|
|
| 59 | + , modBreaks_ccs :: !(Array BreakTickIndex (String, String))
|
|
| 60 | + -- ^ Array pointing to cost centre info for each breakpoint;
|
|
| 61 | + -- actual 'CostCentre' allocation is done at link-time.
|
|
| 62 | + , modBreaks_module :: !Module
|
|
| 63 | + -- ^ The module to which this ModBreaks is associated.
|
|
| 64 | + -- We also cache this here for internal sanity checks.
|
|
| 65 | + }
|
|
| 66 | + |
|
| 67 | +modBreaks_locs :: ModBreaks -> Array BreakTickIndex SrcSpan
|
|
| 68 | +modBreaks_locs = coerce . modBreaks_locs_
|
|
| 69 | + |
|
| 70 | +instance Binary ModBreaks where
|
|
| 71 | + get bh = ModBreaks <$> get bh <*> get bh <*> get bh <*> get bh <*> get bh
|
|
| 72 | + |
|
| 73 | + put_ bh ModBreaks {..} =
|
|
| 74 | + put_ bh modBreaks_locs_
|
|
| 75 | + *> put_ bh modBreaks_vars
|
|
| 76 | + *> put_ bh modBreaks_decls
|
|
| 77 | + *> put_ bh modBreaks_ccs
|
|
| 78 | + *> put_ bh modBreaks_module
|
|
| 79 | + |
|
| 80 | +instance NFData ModBreaks where
|
|
| 81 | + rnf (ModBreaks a b c d e) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d `seq` rnf e |
|
| \ No newline at end of file |
| ... | ... | @@ -73,6 +73,8 @@ data UsageConfig = UsageConfig |
| 73 | 73 | { uc_safe_implicit_imps_req :: !Bool -- ^ Are all implicit imports required to be safe for this Safe Haskell mode?
|
| 74 | 74 | }
|
| 75 | 75 | |
| 76 | +-- | Build the list of 'Usage's that drive recompilation checking.
|
|
| 77 | +-- The resulting list is deterministically sorted.
|
|
| 76 | 78 | mkUsageInfo :: UsageConfig -> Plugins -> FinderCache -> UnitEnv
|
| 77 | 79 | -> Module -> ImportedMods -> [ImportUserSpec] -> NameSet
|
| 78 | 80 | -> [FilePath] -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded
|
| ... | ... | @@ -100,10 +102,10 @@ mkUsageInfo uc plugins fc unit_env |
| 100 | 102 | }
|
| 101 | 103 | | (mod, hash) <- merged ]
|
| 102 | 104 | ++ object_usages
|
| 103 | - usages `seqList` return usages
|
|
| 104 | - -- seq the list of Usages returned: occasionally these
|
|
| 105 | - -- don't get evaluated for a while and we can end up hanging on to
|
|
| 106 | - -- the entire collection of Ifaces.
|
|
| 105 | + usages `seqList` return (sortBy stableUsageCmp usages)
|
|
| 106 | + -- The use of 'seqList' is important because occasionally the returned list
|
|
| 107 | + -- is not evaluated for a while, so that with too much laziness here we
|
|
| 108 | + -- could end up hanging on to the entire collection of 'Iface's.
|
|
| 107 | 109 | |
| 108 | 110 | {- Note [Plugin dependencies]
|
| 109 | 111 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -62,6 +62,8 @@ import GHC.Iface.Rename |
| 62 | 62 | import GHC.Iface.Env
|
| 63 | 63 | import GHC.Iface.Errors as Iface_Errors
|
| 64 | 64 | |
| 65 | +import GHC.HsToCore.Breakpoints.Types (modBreaks_locs)
|
|
| 66 | + |
|
| 65 | 67 | import GHC.Tc.Errors.Types
|
| 66 | 68 | import GHC.Tc.Utils.Monad
|
| 67 | 69 | |
| ... | ... | @@ -112,6 +114,7 @@ import GHC.Unit.Env |
| 112 | 114 | import GHC.Data.Maybe
|
| 113 | 115 | |
| 114 | 116 | import Control.Monad
|
| 117 | +import qualified Data.Foldable as Foldable
|
|
| 115 | 118 | import Data.Map ( toList )
|
| 116 | 119 | import System.FilePath
|
| 117 | 120 | import System.Directory
|
| ... | ... | @@ -1159,9 +1162,11 @@ pprModIface unit_state iface |
| 1159 | 1162 | , vcat [ppr ver $$ nest 2 (ppr decl) | (ver,decl) <- mi_decls iface]
|
| 1160 | 1163 | , case mi_simplified_core iface of
|
| 1161 | 1164 | Nothing -> empty
|
| 1162 | - Just (IfaceSimplifiedCore eds fs) ->
|
|
| 1165 | + Just (IfaceSimplifiedCore eds mbs fs) ->
|
|
| 1163 | 1166 | vcat [ text "extra decls:"
|
| 1164 | 1167 | $$ nest 2 (vcat ([ppr bs | bs <- eds]))
|
| 1168 | + , text "mod breaks:"
|
|
| 1169 | + $$ nest 2 (ppr $ Foldable.toList . modBreaks_locs <$> mbs)
|
|
| 1165 | 1170 | , text "foreign stubs:"
|
| 1166 | 1171 | $$ nest 2 (ppr fs)
|
| 1167 | 1172 | ]
|
| ... | ... | @@ -34,6 +34,7 @@ import GHC.Iface.Syntax |
| 34 | 34 | import GHC.Iface.Recomp
|
| 35 | 35 | import GHC.Iface.Load
|
| 36 | 36 | import GHC.Iface.Ext.Fields
|
| 37 | +import GHC.HsToCore.Breakpoints.Types (ModBreaks)
|
|
| 37 | 38 | |
| 38 | 39 | import GHC.CoreToIface
|
| 39 | 40 | |
| ... | ... | @@ -122,11 +123,12 @@ mkPartialIface hsc_env core_prog mod_details mod_summary import_decls |
| 122 | 123 | , mg_safe_haskell = safe_mode
|
| 123 | 124 | , mg_trust_pkg = self_trust
|
| 124 | 125 | , mg_docs = docs
|
| 126 | + , mg_modBreaks = modBreaks
|
|
| 125 | 127 | }
|
| 126 | 128 | = do
|
| 127 | 129 | self_recomp <- traverse (mkSelfRecomp hsc_env this_mod (ms_hs_hash mod_summary)) usages
|
| 128 | 130 | return $ mkIface_ hsc_env this_mod core_prog hsc_src deps rdr_env import_decls fix_env warns self_trust
|
| 129 | - safe_mode self_recomp docs mod_details
|
|
| 131 | + safe_mode self_recomp docs mod_details modBreaks
|
|
| 130 | 132 | |
| 131 | 133 | -- | Fully instantiate an interface. Adds fingerprints and potentially code
|
| 132 | 134 | -- generator produced information.
|
| ... | ... | @@ -228,9 +230,10 @@ mkIfaceTc :: HscEnv |
| 228 | 230 | -> ModDetails -- gotten from mkBootModDetails, probably
|
| 229 | 231 | -> ModSummary
|
| 230 | 232 | -> Maybe CoreProgram
|
| 233 | + -> Maybe ModBreaks
|
|
| 231 | 234 | -> TcGblEnv -- Usages, deprecations, etc
|
| 232 | 235 | -> IO ModIface
|
| 233 | -mkIfaceTc hsc_env safe_mode mod_details mod_summary mb_program
|
|
| 236 | +mkIfaceTc hsc_env safe_mode mod_details mod_summary mb_program mb_modBreaks
|
|
| 234 | 237 | tc_result@TcGblEnv{ tcg_mod = this_mod,
|
| 235 | 238 | tcg_src = hsc_src,
|
| 236 | 239 | tcg_imports = imports,
|
| ... | ... | @@ -258,6 +261,7 @@ mkIfaceTc hsc_env safe_mode mod_details mod_summary mb_program |
| 258 | 261 | (imp_trust_own_pkg imports) safe_mode self_recomp
|
| 259 | 262 | docs
|
| 260 | 263 | mod_details
|
| 264 | + mb_modBreaks
|
|
| 261 | 265 | |
| 262 | 266 | mkFullIface hsc_env partial_iface Nothing Nothing NoStubs []
|
| 263 | 267 | |
| ... | ... | @@ -302,6 +306,7 @@ mkIface_ :: HscEnv -> Module -> CoreProgram -> HscSource |
| 302 | 306 | -> Maybe IfaceSelfRecomp
|
| 303 | 307 | -> Maybe Docs
|
| 304 | 308 | -> ModDetails
|
| 309 | + -> Maybe ModBreaks
|
|
| 305 | 310 | -> PartialModIface
|
| 306 | 311 | mkIface_ hsc_env
|
| 307 | 312 | this_mod core_prog hsc_src deps rdr_env import_decls fix_env src_warns
|
| ... | ... | @@ -319,15 +324,17 @@ mkIface_ hsc_env |
| 319 | 324 | -- only at the TypeEnv. The previous Tidy phase has
|
| 320 | 325 | -- put exactly the info into the TypeEnv that we want
|
| 321 | 326 | -- to expose in the interface
|
| 322 | - |
|
| 327 | + modBreaks
|
|
| 323 | 328 | = do
|
| 324 | 329 | let home_unit = hsc_home_unit hsc_env
|
| 325 | 330 | semantic_mod = homeModuleNameInstantiation home_unit (moduleName this_mod)
|
| 326 | 331 | entities = typeEnvElts type_env
|
| 327 | 332 | show_linear_types = xopt LangExt.LinearTypes (hsc_dflags hsc_env)
|
| 328 | 333 | |
| 329 | - simplified_core = if gopt Opt_WriteIfSimplifiedCore dflags then Just (IfaceSimplifiedCore [ toIfaceTopBind b | b <- core_prog ] emptyIfaceForeign)
|
|
| 330 | - else Nothing
|
|
| 334 | + simplified_core =
|
|
| 335 | + if gopt Opt_WriteIfSimplifiedCore dflags
|
|
| 336 | + then Just (IfaceSimplifiedCore [ toIfaceTopBind b | b <- core_prog ] modBreaks emptyIfaceForeign)
|
|
| 337 | + else Nothing
|
|
| 331 | 338 | decls = [ tyThingToIfaceDecl show_linear_types entity
|
| 332 | 339 | | entity <- entities,
|
| 333 | 340 | let name = getName entity,
|
| ... | ... | @@ -1208,7 +1208,7 @@ addFingerprints hsc_env iface0 = do |
| 1208 | 1208 | |
| 1209 | 1209 | sorted_extra_decls :: Maybe IfaceSimplifiedCore
|
| 1210 | 1210 | sorted_extra_decls = mi_simplified_core iface0 <&> \simpl_core ->
|
| 1211 | - IfaceSimplifiedCore (sortOn binding_key (mi_sc_extra_decls simpl_core)) (mi_sc_foreign simpl_core)
|
|
| 1211 | + IfaceSimplifiedCore (sortOn binding_key (mi_sc_extra_decls simpl_core)) (mi_sc_modBreaks simpl_core) (mi_sc_foreign simpl_core)
|
|
| 1212 | 1212 | |
| 1213 | 1213 | -- The interface hash depends on:
|
| 1214 | 1214 | -- - the ABI hash, plus
|
| ... | ... | @@ -47,6 +47,7 @@ module GHC.Linker.Types |
| 47 | 47 | , linkableFilterByteCode
|
| 48 | 48 | , linkableFilterNative
|
| 49 | 49 | , partitionLinkables
|
| 50 | + , linkableAllBCOs
|
|
| 50 | 51 | )
|
| 51 | 52 | where
|
| 52 | 53 | |
| ... | ... | @@ -352,6 +353,17 @@ linkableIsNativeCodeOnly l = all isNativeCode (NE.toList (linkableParts l)) |
| 352 | 353 | linkableBCOs :: Linkable -> [CompiledByteCode]
|
| 353 | 354 | linkableBCOs l = [ cbc | BCOs cbc <- NE.toList (linkableParts l) ]
|
| 354 | 355 | |
| 356 | +linkableAllBCOs :: Linkable -> [CompiledByteCode]
|
|
| 357 | +linkableAllBCOs l = mapMaybe bcos $ NE.toList (linkableParts l)
|
|
| 358 | + where
|
|
| 359 | + -- Note: explicit match on all constructors to trigger warning when new ones are introduced.
|
|
| 360 | + bcos (BCOs bco) = Just bco
|
|
| 361 | + bcos (LazyBCOs bco _fs) = Just bco -- TODO: _fs ?
|
|
| 362 | + bcos DotA{} = Nothing
|
|
| 363 | + bcos DotDLL{} = Nothing
|
|
| 364 | + bcos CoreBindings{} = Nothing
|
|
| 365 | + bcos DotO{} = Nothing
|
|
| 366 | + |
|
| 355 | 367 | -- | List the native linkable parts (.o/.so/.dll) of a linkable
|
| 356 | 368 | linkableNativeParts :: Linkable -> [LinkablePart]
|
| 357 | 369 | linkableNativeParts l = NE.filter isNativeCode (linkableParts l)
|
| ... | ... | @@ -737,7 +737,7 @@ getModBreaks :: HomeModInfo -> Maybe InternalModBreaks |
| 737 | 737 | getModBreaks hmi
|
| 738 | 738 | | Just linkable <- homeModInfoByteCode hmi,
|
| 739 | 739 | -- The linkable may have 'DotO's as well; only consider BCOs. See #20570.
|
| 740 | - [cbc] <- linkableBCOs linkable
|
|
| 740 | + [cbc] <- linkableAllBCOs linkable
|
|
| 741 | 741 | = bc_breaks cbc
|
| 742 | 742 | | otherwise
|
| 743 | 743 | = Nothing -- probably object code
|
| ... | ... | @@ -2,6 +2,9 @@ |
| 2 | 2 | {-# LANGUAGE PatternSynonyms #-}
|
| 3 | 3 | {-# LANGUAGE ExplicitNamespaces #-}
|
| 4 | 4 | {-# LANGUAGE DerivingVia #-}
|
| 5 | + |
|
| 6 | +{-# OPTIONS_GHC -Wwarn=incomplete-record-selectors #-}
|
|
| 7 | + |
|
| 5 | 8 | -- | Dependencies and Usage of a module
|
| 6 | 9 | module GHC.Unit.Module.Deps
|
| 7 | 10 | ( Dependencies(dep_direct_mods
|
| ... | ... | @@ -19,6 +22,7 @@ module GHC.Unit.Module.Deps |
| 19 | 22 | , noDependencies
|
| 20 | 23 | , pprDeps
|
| 21 | 24 | , Usage (..)
|
| 25 | + , stableUsageCmp
|
|
| 22 | 26 | , HomeModImport (..)
|
| 23 | 27 | , HomeModImportedAvails (..)
|
| 24 | 28 | , ImportAvails (..)
|
| ... | ... | @@ -45,6 +49,7 @@ import GHC.Utils.Fingerprint |
| 45 | 49 | import GHC.Utils.Binary
|
| 46 | 50 | import GHC.Utils.Outputable
|
| 47 | 51 | |
| 52 | +import Data.Function (on)
|
|
| 48 | 53 | import Data.List (sortBy, sort, partition)
|
| 49 | 54 | import Data.Set (Set)
|
| 50 | 55 | import qualified Data.Set as Set
|
| ... | ... | @@ -464,6 +469,42 @@ instance Binary Usage where |
| 464 | 469 | return UsageHomeModuleInterface { usg_mod_name = mod, usg_unit_id = uid, usg_iface_hash = hash }
|
| 465 | 470 | i -> error ("Binary.get(Usage): " ++ show i)
|
| 466 | 471 | |
| 472 | +-- | Compares 'Usage's by constructor and, if the constructors are the same, by
|
|
| 473 | +-- identifying strings, to achieve a predictable ordering.
|
|
| 474 | +stableUsageCmp :: Usage -> Usage -> Ordering
|
|
| 475 | +stableUsageCmp
|
|
| 476 | + usage1@UsagePackageModule {}
|
|
| 477 | + usage2@UsagePackageModule {}
|
|
| 478 | + = (compare `on` usg_mod) usage1 usage2
|
|
| 479 | +stableUsageCmp
|
|
| 480 | + usage1@UsageHomeModule {}
|
|
| 481 | + usage2@UsageHomeModule {}
|
|
| 482 | + = (compare `on` Module <$> usg_unit_id <*> usg_mod_name) usage1 usage2
|
|
| 483 | +stableUsageCmp
|
|
| 484 | + usage1@UsageFile {}
|
|
| 485 | + usage2@UsageFile {}
|
|
| 486 | + = (lexicalCompareFS `on` usg_file_path) usage1 usage2
|
|
| 487 | +stableUsageCmp
|
|
| 488 | + usage1@UsageHomeModuleInterface {}
|
|
| 489 | + usage2@UsageHomeModuleInterface {}
|
|
| 490 | + = (compare `on` Module <$> usg_unit_id <*> usg_mod_name) usage1 usage2
|
|
| 491 | +stableUsageCmp
|
|
| 492 | + usage1@UsageMergedRequirement {}
|
|
| 493 | + usage2@UsageMergedRequirement {}
|
|
| 494 | + = (compare `on` usg_mod) usage1 usage2
|
|
| 495 | +stableUsageCmp
|
|
| 496 | + usage1
|
|
| 497 | + usage2
|
|
| 498 | + = (compare `on` constructorIndex) usage1 usage2
|
|
| 499 | + where
|
|
| 500 | + |
|
| 501 | + constructorIndex :: Usage -> Int
|
|
| 502 | + constructorIndex UsagePackageModule {} = 0
|
|
| 503 | + constructorIndex UsageHomeModule {} = 1
|
|
| 504 | + constructorIndex UsageFile {} = 2
|
|
| 505 | + constructorIndex UsageHomeModuleInterface {} = 3
|
|
| 506 | + constructorIndex UsageMergedRequirement {} = 4
|
|
| 507 | + |
|
| 467 | 508 | -- | Records the imports that we depend on from a home module,
|
| 468 | 509 | -- for recompilation checking.
|
| 469 | 510 | --
|
| ... | ... | @@ -7,7 +7,7 @@ where |
| 7 | 7 | |
| 8 | 8 | import GHC.Prelude
|
| 9 | 9 | |
| 10 | -import GHC.HsToCore.Breakpoints
|
|
| 10 | +import GHC.HsToCore.Breakpoints.Types
|
|
| 11 | 11 | import GHC.ForeignSrcLang
|
| 12 | 12 | |
| 13 | 13 | import GHC.Hs
|
| ... | ... | @@ -127,6 +127,8 @@ import GHC.Iface.Flags |
| 127 | 127 | import GHC.Iface.Ext.Fields
|
| 128 | 128 | import GHC.Iface.Recomp.Types
|
| 129 | 129 | |
| 130 | +import GHC.HsToCore.Breakpoints.Types
|
|
| 131 | + |
|
| 130 | 132 | import GHC.Unit
|
| 131 | 133 | import GHC.Unit.Module.Deps
|
| 132 | 134 | import GHC.Unit.Module.Warnings
|
| ... | ... | @@ -425,6 +427,8 @@ data IfaceSimplifiedCore = IfaceSimplifiedCore { |
| 425 | 427 | -- ^ Extra variable definitions which are **NOT** exposed but when
|
| 426 | 428 | -- combined with mi_decls allows us to restart code generation.
|
| 427 | 429 | -- See Note [Interface Files with Core Definitions] and Note [Interface File with Core: Sharing RHSs]
|
| 430 | + , mi_sc_modBreaks :: Maybe ModBreaks
|
|
| 431 | + -- ^ If breakpoints are present in @mi_sc_extra_decls@ this field provides this field provides the metadata required by the bytecode debugger.
|
|
| 428 | 432 | , mi_sc_foreign :: IfaceForeign
|
| 429 | 433 | -- ^ Foreign stubs and files to supplement 'mi_extra_decls_'.
|
| 430 | 434 | -- See Note [Foreign stubs and TH bytecode linking]
|
| ... | ... | @@ -754,14 +758,16 @@ instance Binary IfaceAbiHashes where |
| 754 | 758 | }
|
| 755 | 759 | |
| 756 | 760 | instance Binary IfaceSimplifiedCore where
|
| 757 | - put_ bh (IfaceSimplifiedCore eds fs) = do
|
|
| 761 | + put_ bh (IfaceSimplifiedCore eds mbs fs) = do
|
|
| 758 | 762 | put_ bh eds
|
| 763 | + put_ bh mbs
|
|
| 759 | 764 | put_ bh fs
|
| 760 | 765 | |
| 761 | 766 | get bh = do
|
| 762 | 767 | eds <- get bh
|
| 768 | + mbs <- get bh
|
|
| 763 | 769 | fs <- get bh
|
| 764 | - return (IfaceSimplifiedCore eds fs)
|
|
| 770 | + return (IfaceSimplifiedCore eds mbs fs)
|
|
| 765 | 771 | |
| 766 | 772 | emptyPartialModIface :: Module -> PartialModIface
|
| 767 | 773 | emptyPartialModIface mod
|
| ... | ... | @@ -870,7 +876,7 @@ instance NFData IfaceModInfo where |
| 870 | 876 | |
| 871 | 877 | |
| 872 | 878 | instance NFData IfaceSimplifiedCore where
|
| 873 | - rnf (IfaceSimplifiedCore eds fs) = rnf eds `seq` rnf fs
|
|
| 879 | + rnf (IfaceSimplifiedCore eds mbs fs) = rnf eds `seq` rnf mbs `seq` rnf fs
|
|
| 874 | 880 | |
| 875 | 881 | instance NFData IfaceAbiHashes where
|
| 876 | 882 | rnf (IfaceAbiHashes a1 a2 a3 a4 a5 a6)
|
| ... | ... | @@ -9,6 +9,7 @@ import GHC.Cmm.CLabel |
| 9 | 9 | import GHC.Driver.DynFlags (DynFlags (targetPlatform), initSDocContext)
|
| 10 | 10 | import GHC.ForeignSrcLang (ForeignSrcLang (..))
|
| 11 | 11 | import GHC.Iface.Syntax
|
| 12 | +import GHC.HsToCore.Breakpoints.Types (ModBreaks)
|
|
| 12 | 13 | import GHC.Prelude
|
| 13 | 14 | import GHC.Types.ForeignStubs
|
| 14 | 15 | import GHC.Unit.Module.Location
|
| ... | ... | @@ -127,6 +128,7 @@ the object files. |
| 127 | 128 | |
| 128 | 129 | data WholeCoreBindings = WholeCoreBindings
|
| 129 | 130 | { wcb_bindings :: [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo] -- ^ serialised tidied core bindings.
|
| 131 | + , wcb_modBreaks :: Maybe ModBreaks -- ^ if @wcb_bindings@ contains breakpoints, this field provides the metadata required by the bytecode debugger.
|
|
| 130 | 132 | , wcb_module :: Module -- ^ The module which the bindings are for
|
| 131 | 133 | , wcb_mod_location :: ModLocation -- ^ The location where the sources reside.
|
| 132 | 134 | -- | Stubs for foreign declarations and files added via
|
| ... | ... | @@ -1919,6 +1919,7 @@ instance Binary UnhelpfulSpanReason where |
| 1919 | 1919 | _ -> UnhelpfulOther <$> get bh
|
| 1920 | 1920 | |
| 1921 | 1921 | newtype BinSrcSpan = BinSrcSpan { unBinSrcSpan :: SrcSpan }
|
| 1922 | + deriving newtype NFData
|
|
| 1922 | 1923 | |
| 1923 | 1924 | -- See Note [Source Location Wrappers]
|
| 1924 | 1925 | instance Binary BinSrcSpan where
|
| ... | ... | @@ -574,6 +574,7 @@ Library |
| 574 | 574 | GHC.HsToCore.Arrows
|
| 575 | 575 | GHC.HsToCore.Binds
|
| 576 | 576 | GHC.HsToCore.Breakpoints
|
| 577 | + GHC.HsToCore.Breakpoints.Types
|
|
| 577 | 578 | GHC.HsToCore.Coverage
|
| 578 | 579 | GHC.HsToCore.Docs
|
| 579 | 580 | GHC.HsToCore.Errors.Ppr
|
| ... | ... | @@ -105,6 +105,7 @@ GHC.Hs.Pat |
| 105 | 105 | GHC.Hs.Specificity
|
| 106 | 106 | GHC.Hs.Type
|
| 107 | 107 | GHC.Hs.Utils
|
| 108 | +GHC.HsToCore.Breakpoints.Types
|
|
| 108 | 109 | GHC.Iface.Errors.Types
|
| 109 | 110 | GHC.Iface.Ext.Fields
|
| 110 | 111 | GHC.Iface.Flags
|
| ... | ... | @@ -109,6 +109,7 @@ GHC.Hs.Pat |
| 109 | 109 | GHC.Hs.Specificity
|
| 110 | 110 | GHC.Hs.Type
|
| 111 | 111 | GHC.Hs.Utils
|
| 112 | +GHC.HsToCore.Breakpoints.Types
|
|
| 112 | 113 | GHC.HsToCore.Errors.Types
|
| 113 | 114 | GHC.HsToCore.Pmc.Solver.Types
|
| 114 | 115 | GHC.Iface.Errors.Types
|
| ... | ... | @@ -24,9 +24,9 @@ instance Monad Solo -- Defined in ‘GHC.Internal.Base’ |
| 24 | 24 | instance Bounded a => Bounded (Solo a)
|
| 25 | 25 | -- Defined in ‘GHC.Internal.Enum’
|
| 26 | 26 | instance Enum a => Enum (Solo a) -- Defined in ‘GHC.Internal.Enum’
|
| 27 | -instance Read a => Read (Solo a) -- Defined in ‘GHC.Internal.Read’
|
|
| 28 | 27 | instance Eq a => Eq (Solo a) -- Defined in ‘GHC.Internal.Classes’
|
| 29 | 28 | instance Ord a => Ord (Solo a) -- Defined in ‘GHC.Internal.Classes’
|
| 29 | +instance Read a => Read (Solo a) -- Defined in ‘GHC.Internal.Read’
|
|
| 30 | 30 | instance Show a => Show (Solo a) -- Defined in ‘GHC.Internal.Show’
|
| 31 | 31 | instance Monoid a => Monoid (Solo a)
|
| 32 | 32 | -- Defined in ‘GHC.Internal.Base’
|
| ... | ... | @@ -13,3 +13,7 @@ TopEnvIface: |
| 13 | 13 | # Second compilation starts from interface files, but still can print "a"
|
| 14 | 14 | "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) TopEnvIface -v1 -e "a" -e ":q" -fwrite-if-simplified-core -fwrite-interface
|
| 15 | 15 | |
| 16 | +T27287:
|
|
| 17 | + "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) T27287.hs -v1 -fno-hide-source-paths -e ":q" -fwrite-if-simplified-core -fwrite-interface
|
|
| 18 | + # Second compilation starts from interface files, but still can print "a"
|
|
| 19 | + "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) T27287.hs -v1 -fno-hide-source-paths -e ":break T27287 5" -e ":q" -fwrite-if-simplified-core -fwrite-interface |
| 1 | +module T27287 where
|
|
| 2 | + |
|
| 3 | +myfun :: String -> String
|
|
| 4 | +myfun xs = case reverse xs of
|
|
| 5 | + [] -> "empty"
|
|
| 6 | + xy -> xy |
| 1 | +[1 of 1] Compiling T27287 ( T27287.hs, interpreted )[main]
|
|
| 2 | +Ok, one module loaded.
|
|
| 3 | +Leaving GHCi.
|
|
| 4 | +Ok, one module loaded.
|
|
| 5 | +Breakpoint 0 activated at T27287.hs:5:9-15
|
|
| 6 | +Leaving GHCi. |
| ... | ... | @@ -8,6 +8,7 @@ test('ghcirun002', just_ghci, compile_and_run, ['']) |
| 8 | 8 | test('ghcirun003', just_ghci, compile_and_run, [''])
|
| 9 | 9 | test('T2589', just_ghci, compile_and_run, [''])
|
| 10 | 10 | test('T2881', just_ghci, compile_and_run, [''])
|
| 11 | +test('T27287', [just_ghci, combined_output], makefile_test, [])
|
|
| 11 | 12 | |
| 12 | 13 | test('T3171',
|
| 13 | 14 | [when(opsys('mingw32'),skip),
|