Hannes Siebenhandl pushed to branch wip/fendor/ghci-multiple-home-units-with-debugger at Glasgow Haskell Compiler / GHC Commits: f071532b by fendor at 2025-04-17T13:33:14+02:00 Make GHCi debugger compatible with multiple home units FIXME: proper commit message - - - - - 11 changed files: - compiler/GHC/ByteCode/Asm.hs - compiler/GHC/ByteCode/Instr.hs - compiler/GHC/ByteCode/Types.hs - compiler/GHC/HsToCore/Breakpoints.hs - compiler/GHC/Runtime/Eval.hs - compiler/GHC/Runtime/Interpreter.hs - compiler/GHC/StgToByteCode.hs - libraries/ghci/GHCi/Message.hs - libraries/ghci/GHCi/Run.hs - rts/Exception.cmm - rts/Interpreter.c Changes: ===================================== compiler/GHC/ByteCode/Asm.hs ===================================== @@ -732,13 +732,16 @@ assembleI platform i = case i of CCALL off m_addr i -> do np <- addr m_addr emit_ bci_CCALL [wOp off, Op np, SmallOp i] PRIMCALL -> emit_ bci_PRIMCALL [] - BRK_FUN arr tick_mod tickx info_mod infox cc -> + BRK_FUN arr tick_mod tick_mod_id tickx info_mod info_mod_id infox cc -> do p1 <- ptr (BCOPtrBreakArray arr) tick_addr <- addr tick_mod + tick_unitid_addr <- addr tick_mod_id info_addr <- addr info_mod + info_unitid_addr <- addr info_mod_id np <- addr cc emit_ bci_BRK_FUN [ Op p1 , Op tick_addr, Op info_addr + , Op tick_unitid_addr, Op info_unitid_addr , SmallOp tickx, SmallOp infox , Op np ] ===================================== compiler/GHC/ByteCode/Instr.hs ===================================== @@ -37,6 +37,7 @@ import GHC.Stg.Syntax import GHCi.BreakArray (BreakArray) import Language.Haskell.Syntax.Module.Name (ModuleName) import GHC.Types.Unique +import GHC.Unit.Types (UnitId) -- ---------------------------------------------------------------------------- -- Bytecode instructions @@ -233,8 +234,10 @@ data BCInstr -- Breakpoints | BRK_FUN (ForeignRef BreakArray) (RemotePtr ModuleName) -- breakpoint tick module + (RemotePtr UnitId) -- breakpoint tick module unit id !Word16 -- breakpoint tick index (RemotePtr ModuleName) -- breakpoint info module + (RemotePtr UnitId) -- breakpoint info module unit id !Word16 -- breakpoint info index (RemotePtr CostCentre) @@ -403,10 +406,10 @@ instance Outputable BCInstr where ppr ENTER = text "ENTER" ppr (RETURN pk) = text "RETURN " <+> ppr pk ppr (RETURN_TUPLE) = text "RETURN_TUPLE" - ppr (BRK_FUN _ _tick_mod tickx _info_mod infox _) + ppr (BRK_FUN _ _tick_mod _tick_mod_id tickx _info_mod _info_mod_id infox _) = text "BRK_FUN" <+> text "<breakarray>" - <+> text "<tick_module>" <+> ppr tickx - <+> text "<info_module>" <+> ppr infox + <+> text "<tick_module>" <+> text "<tick_module_unitid>" <+> ppr tickx + <+> text "<info_module>" <+> text "<info_module_unitid>" <+> ppr infox <+> text "<cc>" #if MIN_VERSION_rts(1,0,3) ppr (BCO_NAME nm) = text "BCO_NAME" <+> text (show nm) ===================================== compiler/GHC/ByteCode/Types.hs ===================================== @@ -50,6 +50,7 @@ import GHC.Stack.CCS import GHC.Cmm.Expr ( GlobalRegSet, emptyRegSet, regSetToList ) import GHC.Iface.Syntax import Language.Haskell.Syntax.Module.Name (ModuleName) +import GHC.Unit.Types (UnitId) -- ----------------------------------------------------------------------------- -- Compiled Byte Code @@ -263,6 +264,9 @@ data ModBreaks , modBreaks_breakInfo :: IntMap CgBreakInfo -- ^ info about each breakpoint from the bytecode generator , modBreaks_module :: RemotePtr ModuleName + -- ^ info about the module in which we are setting the breakpoint + , modBreaks_module_unitid :: RemotePtr UnitId + -- ^ The 'UnitId' of the 'ModuleName' } seqModBreaks :: ModBreaks -> () @@ -273,7 +277,8 @@ seqModBreaks ModBreaks{..} = rnf modBreaks_decls `seq` rnf modBreaks_ccs `seq` rnf (fmap seqCgBreakInfo modBreaks_breakInfo) `seq` - rnf modBreaks_module + rnf modBreaks_module `seq` + rnf modBreaks_module_unitid -- | Construct an empty ModBreaks emptyModBreaks :: ModBreaks @@ -286,6 +291,7 @@ emptyModBreaks = ModBreaks , modBreaks_ccs = array (0,-1) [] , modBreaks_breakInfo = IntMap.empty , modBreaks_module = toRemotePtr nullPtr + , modBreaks_module_unitid = toRemotePtr nullPtr } {- ===================================== compiler/GHC/HsToCore/Breakpoints.hs ===================================== @@ -34,7 +34,7 @@ mkModBreaks interp mod extendedMixEntries breakArray <- GHCi.newBreakArray interp count ccs <- mkCCSArray interp mod count entries - mod_ptr <- GHCi.newModuleName interp (moduleName mod) + (mod_ptr, mod_id_ptr) <- GHCi.newModule interp mod let locsTicks = listArray (0,count-1) [ tick_loc t | t <- entries ] varsTicks = listArray (0,count-1) [ tick_ids t | t <- entries ] @@ -46,6 +46,7 @@ mkModBreaks interp mod extendedMixEntries , modBreaks_decls = declsTicks , modBreaks_ccs = ccs , modBreaks_module = mod_ptr + , modBreaks_module_unitid = mod_id_ptr } mkCCSArray ===================================== compiler/GHC/Runtime/Eval.hs ===================================== @@ -345,7 +345,7 @@ handleRunStatus step expr bindings final_ids status history0 = do -- Just case: we stopped at a breakpoint EvalBreak apStack_ref (Just eval_break) resume_ctxt ccs -> do - ibi <- liftIO $ evalBreakpointToId (hsc_HPT hsc_env) eval_break + let ibi = evalBreakpointToId eval_break tick_brks <- liftIO $ readModBreaks hsc_env (ibi_tick_mod ibi) let span = modBreaks_locs tick_brks ! ibi_tick_index ibi ===================================== compiler/GHC/Runtime/Interpreter.hs ===================================== @@ -21,7 +21,7 @@ module GHC.Runtime.Interpreter , mkCostCentres , costCentreStackInfo , newBreakArray - , newModuleName + , newModule , storeBreakpoint , breakpointStatus , getBreakpointVar @@ -93,9 +93,7 @@ import GHC.Utils.Outputable(brackets, ppr, showSDocUnsafe) import GHC.Utils.Fingerprint import GHC.Unit.Module -import GHC.Unit.Module.ModIface import GHC.Unit.Home.ModInfo -import GHC.Unit.Home.PackageTable import GHC.Unit.Env #if defined(HAVE_INTERNAL_INTERPRETER) @@ -119,6 +117,7 @@ import qualified GHC.InfoProv as InfoProv import GHC.Builtin.Names import GHC.Types.Name +import qualified GHC.Unit.Home.Graph as HUG -- Standard libraries import GHC.Exts @@ -377,9 +376,13 @@ newBreakArray interp size = do breakArray <- interpCmd interp (NewBreakArray size) mkFinalizedHValue interp breakArray -newModuleName :: Interp -> ModuleName -> IO (RemotePtr ModuleName) -newModuleName interp mod_name = - castRemotePtr <$> interpCmd interp (NewBreakModule (moduleNameString mod_name)) +newModule :: Interp -> Module -> IO (RemotePtr ModuleName, RemotePtr UnitId) +newModule interp mod = do + let + mod_name = moduleNameString $ moduleName mod + mod_id = unitIdString $ toUnitId $ moduleUnit mod + (mod_ptr, mod_id_ptr) <- interpCmd interp (NewBreakModule mod_name mod_id) + pure (castRemotePtr mod_ptr, castRemotePtr mod_id_ptr) storeBreakpoint :: Interp -> ForeignRef BreakArray -> Int -> Int -> IO () storeBreakpoint interp ref ix cnt = do -- #19157 @@ -415,19 +418,21 @@ seqHValue interp unit_env ref = status <- interpCmd interp (Seq hval) handleSeqHValueStatus interp unit_env status -evalBreakpointToId :: HomePackageTable -> EvalBreakpoint -> IO InternalBreakpointId -evalBreakpointToId hpt eval_break = - let load_mod x = mi_module . hm_iface . expectJust <$> lookupHpt hpt (mkModuleName x) - in do - tickl <- load_mod (eb_tick_mod eval_break) - infol <- load_mod (eb_info_mod eval_break) - return - InternalBreakpointId - { ibi_tick_mod = tickl - , ibi_tick_index = eb_tick_index eval_break - , ibi_info_mod = infol - , ibi_info_index = eb_info_index eval_break - } +evalBreakpointToId :: EvalBreakpoint -> InternalBreakpointId +evalBreakpointToId eval_break = + let + mkUnitId u = RealUnit (Definite $ stringToUnitId u) + + toModule u n = mkModule (mkUnitId u) (mkModuleName n) + tickl = toModule (eb_tick_mod_unit eval_break) (eb_tick_mod eval_break) + infol = toModule (eb_info_mod_unit eval_break) (eb_info_mod eval_break) + in + InternalBreakpointId + { ibi_tick_mod = tickl + , ibi_tick_index = eb_tick_index eval_break + , ibi_info_mod = infol + , ibi_info_index = eb_info_index eval_break + } -- | Process the result of a Seq or ResumeSeq message. #2950 handleSeqHValueStatus :: Interp -> UnitEnv -> EvalStatus () -> IO (EvalResult ()) @@ -447,12 +452,12 @@ handleSeqHValueStatus interp unit_env eval_status = mkGeneralSrcSpan (fsLit "<unknown>") Just break -> do - bi <- evalBreakpointToId (ue_hpt unit_env) break + let bi = evalBreakpointToId break -- Just case: Stopped at a breakpoint, extract SrcSpan information -- from the breakpoint. breaks_tick <- getModBreaks . expectJust <$> - lookupHpt (ue_hpt unit_env) (moduleName (ibi_tick_mod bi)) + HUG.lookupHugByModule (ibi_tick_mod bi) (ue_home_unit_graph unit_env) put $ brackets . ppr $ (modBreaks_locs breaks_tick) ! ibi_tick_index bi ===================================== compiler/GHC/StgToByteCode.hs ===================================== @@ -416,7 +416,7 @@ schemeER_wrk d p (StgTick (Breakpoint tick_ty tick_no fvs tick_mod) rhs) = do Nothing -> pure code Just current_mod_breaks -> break_info hsc_env tick_mod current_mod mb_current_mod_breaks >>= \case Nothing -> pure code - Just ModBreaks {modBreaks_flags = breaks, modBreaks_module = tick_mod_ptr, modBreaks_ccs = cc_arr} -> do + Just ModBreaks {modBreaks_flags = breaks, modBreaks_module = tick_mod_ptr, modBreaks_module_unitid = tick_mod_id_ptr, modBreaks_ccs = cc_arr} -> do platform <- profilePlatform <$> getProfile let idOffSets = getVarOffSets platform d p fvs ty_vars = tyCoVarsOfTypesWellScoped (tick_ty:map idType fvs) @@ -425,6 +425,7 @@ schemeER_wrk d p (StgTick (Breakpoint tick_ty tick_no fvs tick_mod) rhs) = do breakInfo = dehydrateCgBreakInfo ty_vars (map toWord idOffSets) tick_ty let info_mod_ptr = modBreaks_module current_mod_breaks + info_mod_id_ptr = modBreaks_module_unitid current_mod_breaks infox <- newBreakInfo breakInfo let cc | Just interp <- hsc_interp hsc_env @@ -437,7 +438,7 @@ schemeER_wrk d p (StgTick (Breakpoint tick_ty tick_no fvs tick_mod) rhs) = do in if fromIntegral r == x then r else pprPanic "schemeER_wrk: breakpoint tick/info index too large!" (ppr x) - breakInstr = BRK_FUN breaks tick_mod_ptr (toW16 tick_no) info_mod_ptr (toW16 infox) cc + breakInstr = BRK_FUN breaks tick_mod_ptr tick_mod_id_ptr (toW16 tick_no) info_mod_ptr info_mod_id_ptr (toW16 infox) cc return $ breakInstr `consOL` code schemeER_wrk d p rhs = schemeE d 0 p rhs ===================================== libraries/ghci/GHCi/Message.hs ===================================== @@ -23,6 +23,7 @@ module GHCi.Message , getMessage, putMessage, getTHMessage, putTHMessage , Pipe, mkPipeFromHandles, mkPipeFromContinuations, remoteCall, remoteTHCall, readPipe, writePipe , BreakModule + , BreakUnitId , LoadedDLL ) where @@ -245,8 +246,9 @@ data Message a where -- | Allocate a string for a breakpoint module name. -- This uses an empty dummy type because @ModuleName@ isn't available here. NewBreakModule - :: String - -> Message (RemotePtr BreakModule) + :: String -- ^ @ModuleName@ + -> String -- ^ @UnitId@ for the given @ModuleName@ + -> Message (RemotePtr BreakModule, RemotePtr BreakUnitId) deriving instance Show (Message a) @@ -410,10 +412,12 @@ data EvalStatus_ a b instance Binary a => Binary (EvalStatus_ a b) data EvalBreakpoint = EvalBreakpoint - { eb_tick_mod :: String -- ^ Breakpoint tick module - , eb_tick_index :: Int -- ^ Breakpoint tick index - , eb_info_mod :: String -- ^ Breakpoint info module - , eb_info_index :: Int -- ^ Breakpoint info index + { eb_tick_mod :: String -- ^ Breakpoint tick module + , eb_tick_mod_unit :: String -- ^ Breakpoint tick module unit id + , eb_tick_index :: Int -- ^ Breakpoint tick index + , eb_info_mod :: String -- ^ Breakpoint info module + , eb_info_mod_unit :: String -- ^ Breakpoint tick module unit id + , eb_info_index :: Int -- ^ Breakpoint info index } deriving (Generic, Show) @@ -430,6 +434,10 @@ instance Binary a => Binary (EvalResult a) -- that type isn't available here. data BreakModule +-- | A dummy type that tags the pointer to a breakpoint's @UnitId@, because +-- that type isn't available here. +data BreakUnitId + -- | A dummy type that tags pointers returned by 'LoadDLL'. data LoadedDLL @@ -580,7 +588,7 @@ getMessage = do 36 -> Msg <$> (Seq <$> get) 37 -> Msg <$> return RtsRevertCAFs 38 -> Msg <$> (ResumeSeq <$> get) - 39 -> Msg <$> (NewBreakModule <$> get) + 39 -> Msg <$> (NewBreakModule <$> get <*> get) 40 -> Msg <$> (LookupSymbolInDLL <$> get <*> get) 41 -> Msg <$> (WhereFrom <$> get) _ -> error $ "Unknown Message code " ++ (show b) @@ -627,7 +635,7 @@ putMessage m = case m of Seq a -> putWord8 36 >> put a RtsRevertCAFs -> putWord8 37 ResumeSeq a -> putWord8 38 >> put a - NewBreakModule name -> putWord8 39 >> put name + NewBreakModule name unitid -> putWord8 39 >> put name >> put unitid LookupSymbolInDLL dll str -> putWord8 40 >> put dll >> put str WhereFrom a -> putWord8 41 >> put a ===================================== libraries/ghci/GHCi/Run.hs ===================================== @@ -95,7 +95,10 @@ run m = case m of MkCostCentres mod ccs -> mkCostCentres mod ccs CostCentreStackInfo ptr -> ccsToStrings (fromRemotePtr ptr) NewBreakArray sz -> mkRemoteRef =<< newBreakArray sz - NewBreakModule name -> newModuleName name + NewBreakModule name unitid -> do + namePtr <- newModuleName name + uidPtr <- newUnitId unitid + pure (namePtr, uidPtr) SetupBreakpoint ref ix cnt -> do arr <- localRef ref; _ <- setupBreakpoint arr ix cnt @@ -335,7 +338,7 @@ withBreakAction opts breakMVar statusMVar act -- as soon as it is hit, or in resetBreakAction below. onBreak :: BreakpointCallback - onBreak tick_mod# tickx# info_mod# infox# is_exception apStack = do + onBreak tick_mod# tick_mod_uid# tickx# info_mod# info_mod_uid# infox# is_exception apStack = do tid <- myThreadId let resume = ResumeContext { resumeBreakMVar = breakMVar @@ -349,8 +352,10 @@ withBreakAction opts breakMVar statusMVar act then pure Nothing else do tick_mod <- peekCString (Ptr tick_mod#) + tick_mod_uid <- peekCString (Ptr tick_mod_uid#) info_mod <- peekCString (Ptr info_mod#) - pure (Just (EvalBreakpoint tick_mod (I# tickx#) info_mod (I# infox#))) + info_mod_uid <- peekCString (Ptr info_mod_uid#) + pure (Just (EvalBreakpoint tick_mod tick_mod_uid (I# tickx#) info_mod info_mod_uid (I# infox#))) putMVar statusMVar $ EvalBreak apStack_r breakpoint resume_r ccs takeMVar breakMVar @@ -400,8 +405,10 @@ resetStepFlag = poke stepFlag 0 type BreakpointCallback = Addr# -- pointer to the breakpoint tick module name + -> Addr# -- pointer to the breakpoint tick module unit id -> Int# -- breakpoint tick index -> Addr# -- pointer to the breakpoint info module name + -> Addr# -- pointer to the breakpoint info module unit id -> Int# -- breakpoint info index -> Bool -- exception? -> HValue -- the AP_STACK, or exception @@ -414,8 +421,8 @@ noBreakStablePtr :: StablePtr BreakpointCallback noBreakStablePtr = unsafePerformIO $ newStablePtr noBreakAction noBreakAction :: BreakpointCallback -noBreakAction _ _ _ _ False _ = putStrLn "*** Ignoring breakpoint" -noBreakAction _ _ _ _ True _ = return () -- exception: just continue +noBreakAction _ _ _ _ _ _ False _ = putStrLn "*** Ignoring breakpoint" +noBreakAction _ _ _ _ _ _ True _ = return () -- exception: just continue -- Malloc and copy the bytes. We don't have any way to monitor the -- lifetime of this memory, so it just leaks. @@ -453,6 +460,10 @@ newModuleName :: String -> IO (RemotePtr BreakModule) newModuleName name = castRemotePtr . toRemotePtr <$> newCString name +newUnitId :: String -> IO (RemotePtr BreakUnitId) +newUnitId name = + castRemotePtr . toRemotePtr <$> newCString name + getIdValFromApStack :: HValue -> Int -> IO (Maybe HValue) getIdValFromApStack apStack (I# stackDepth) = do case getApStackVal# apStack stackDepth of ===================================== rts/Exception.cmm ===================================== @@ -535,12 +535,16 @@ retry_pop_stack: // be per-thread. CInt[rts_stop_on_exception] = 0; ("ptr" ioAction) = ccall deRefStablePtr (W_[rts_breakpoint_io_action] "ptr"); - Sp = Sp - WDS(13); - Sp(12) = exception; - Sp(11) = stg_raise_ret_info; - Sp(10) = exception; - Sp(9) = ghczminternal_GHCziInternalziTypes_True_closure; // True <=> an exception - Sp(8) = stg_ap_ppv_info; + Sp = Sp - WDS(17); + Sp(16) = exception; + Sp(15) = stg_raise_ret_info; + Sp(14) = exception; + Sp(13) = ghczminternal_GHCziInternalziTypes_True_closure; // True <=> an exception + Sp(12) = stg_ap_ppv_info; + Sp(11) = 0; + Sp(10) = stg_ap_n_info; + Sp(9) = 0; + Sp(8) = stg_ap_n_info; Sp(7) = 0; Sp(6) = stg_ap_n_info; Sp(5) = 0; ===================================== rts/Interpreter.c ===================================== @@ -1245,9 +1245,9 @@ run_BCO: /* check for a breakpoint on the beginning of a let binding */ case bci_BRK_FUN: { - int arg1_brk_array, arg2_tick_mod, arg3_info_mod, arg4_tick_index, arg5_info_index; + int arg1_brk_array, arg2_tick_mod, arg3_info_mod, arg4_tick_mod_id, arg5_info_mod_id, arg6_tick_index, arg7_info_index; #if defined(PROFILING) - int arg6_cc; + int arg8_cc; #endif StgArrBytes *breakPoints; int returning_from_break; @@ -1264,10 +1264,12 @@ run_BCO: arg1_brk_array = BCO_GET_LARGE_ARG; arg2_tick_mod = BCO_GET_LARGE_ARG; arg3_info_mod = BCO_GET_LARGE_ARG; - arg4_tick_index = BCO_NEXT; - arg5_info_index = BCO_NEXT; + arg4_tick_mod_id = BCO_GET_LARGE_ARG; + arg5_info_mod_id = BCO_GET_LARGE_ARG; + arg6_tick_index = BCO_NEXT; + arg7_info_index = BCO_NEXT; #if defined(PROFILING) - arg6_cc = BCO_GET_LARGE_ARG; + arg8_cc = BCO_GET_LARGE_ARG; #else BCO_GET_LARGE_ARG; #endif @@ -1280,7 +1282,7 @@ run_BCO: #if defined(PROFILING) cap->r.rCCCS = pushCostCentre(cap->r.rCCCS, - (CostCentre*)BCO_LIT(arg6_cc)); + (CostCentre*)BCO_LIT(arg8_cc)); #endif // if we are returning from a break then skip this section @@ -1292,11 +1294,11 @@ run_BCO: // stop the current thread if either the // "rts_stop_next_breakpoint" flag is true OR if the // ignore count for this particular breakpoint is zero - StgInt ignore_count = ((StgInt*)breakPoints->payload)[arg4_tick_index]; + StgInt ignore_count = ((StgInt*)breakPoints->payload)[arg6_tick_index]; if (rts_stop_next_breakpoint == false && ignore_count > 0) { // decrement and write back ignore count - ((StgInt*)breakPoints->payload)[arg4_tick_index] = --ignore_count; + ((StgInt*)breakPoints->payload)[arg6_tick_index] = --ignore_count; } else if (rts_stop_next_breakpoint == true || ignore_count == 0) { @@ -1330,8 +1332,10 @@ run_BCO: // continue execution of this BCO when the IO action returns. // // ioAction :: Addr# -- the breakpoint tick module + // -> Addr# -- the breakpoint tick module unit id // -> Int# -- the breakpoint tick index // -> Addr# -- the breakpoint info module + // -> Addr# -- the breakpoint info module unit id // -> Int# -- the breakpoint info index // -> Bool -- exception? // -> HValue -- the AP_STACK, or exception @@ -1340,17 +1344,21 @@ run_BCO: ioAction = (StgClosure *) deRefStablePtr ( rts_breakpoint_io_action); - Sp_subW(15); - SpW(14) = (W_)obj; - SpW(13) = (W_)&stg_apply_interp_info; - SpW(12) = (W_)new_aps; - SpW(11) = (W_)False_closure; // True <=> an exception - SpW(10) = (W_)&stg_ap_ppv_info; - SpW(9) = (W_)arg5_info_index; + Sp_subW(19); + SpW(18) = (W_)obj; + SpW(17) = (W_)&stg_apply_interp_info; + SpW(16) = (W_)new_aps; + SpW(15) = (W_)False_closure; // True <=> an exception + SpW(14) = (W_)&stg_ap_ppv_info; + SpW(13) = (W_)arg7_info_index; + SpW(12) = (W_)&stg_ap_n_info; + SpW(11) = (W_)BCO_LIT(arg5_info_mod_id); + SpW(10) = (W_)&stg_ap_n_info; + SpW(9) = (W_)BCO_LIT(arg3_info_mod); SpW(8) = (W_)&stg_ap_n_info; - SpW(7) = (W_)BCO_LIT(arg3_info_mod); + SpW(7) = (W_)arg6_tick_index; SpW(6) = (W_)&stg_ap_n_info; - SpW(5) = (W_)arg4_tick_index; + SpW(5) = (W_)BCO_LIT(arg4_tick_mod_id); SpW(4) = (W_)&stg_ap_n_info; SpW(3) = (W_)BCO_LIT(arg2_tick_mod); SpW(2) = (W_)&stg_ap_n_info; View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f071532b8f1b11ef4a09989f879d8fb3... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f071532b8f1b11ef4a09989f879d8fb3... You're receiving this email because of your account on gitlab.haskell.org.