[Git][ghc/ghc][wip/romes/async-rpc] fixup: shorten GHCi.Message exported to not include asyncmanager stuff
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
7d5fc67e by Rodrigo Mesquita at 2026-10-14T18:37:17+01:00
fixup: shorten GHCi.Message exported to not include asyncmanager stuff
- - - - -
1 changed file:
- libraries/ghci/GHCi/Message.hs
Changes:
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -36,16 +36,6 @@ module GHCi.Message
, BreakModule
, BreakUnitId
, LoadedDLL
-
- -- * Low-level interface for async bidirectional RPC
- , remoteAsyncReq
- , handleAsyncReq
- -- ** Consume `reply` exactly once
- , WriteReply -- abstract
- , writeReply
- -- ** Async matching internals
- , AsyncManager(..), CorrelationId
- , IddMsg(..), newAsyncManager
) where
import Prelude -- See note [Why do we import Prelude here?]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7d5fc67e1bbc53107ae639eaf613c2e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7d5fc67e1bbc53107ae639eaf613c2e…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/async-rpc] refactor: Move Async RPC abstraction into GHCi.Message.Async
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
f8c724a7 by Rodrigo Mesquita at 2026-10-14T18:34:12+01:00
refactor: Move Async RPC abstraction into GHCi.Message.Async
Carves out the Async RPC over the Pipe abstraction to a new module,
making the GHCi.Message more about the specific interpreter messages
rather than about the IPC protocol.
- - - - -
3 changed files:
- libraries/ghci/GHCi/Message.hs
- + libraries/ghci/GHCi/Message/Async.hs
- libraries/ghci/ghci.cabal.in
Changes:
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -69,35 +69,22 @@ import GHC.IO (IO(..))
import Control.Concurrent
import Control.DeepSeq
import Control.Exception
-#if MIN_VERSION_base(4,20,0)
-import Control.Exception.Context
-#endif
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
import Data.ByteString (ByteString)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Builder as B
import qualified Data.ByteString.Lazy as LB
import qualified Data.ByteString.Short as BS
import Data.Dynamic
import Data.Typeable (TypeRep)
import Data.IORef
import Data.Map (Map)
-import Data.IntMap (IntMap)
-import qualified Data.IntMap as IntMap
import Foreign
import GHC.Generics
import GHC.Stack.CCS
import qualified GHC.Boot.TH.Syntax as TH
import qualified GHC.Boot.TH.Monad as TH
-import System.Exit
-import System.IO
-import Control.Monad
-import Control.Concurrent.STM
-import Data.Bifunctor
-import GHC.Base (State#)
-import GHC.Exts (RealWorld)
+import GHCi.Message.Async
-- -----------------------------------------------------------------------------
-- The RPC protocol between GHC and the interactive server
@@ -487,45 +474,6 @@ data BreakUnitId
-- | A dummy type that tags pointers returned by 'LoadDLLs'.
data LoadedDLL
--- SomeException can't be serialized because it contains dynamic
--- types. However, we do very limited things with the exceptions that
--- are thrown by interpreted computations:
---
--- * We print them, e.g. "*** Exception: <something>"
--- * UserInterrupt has a special meaning
--- * In ghc -e, exitWith should exit with the appropriate exit code
---
--- So all we need to do is distinguish UserInterrupt and ExitCode, and
--- all other exceptions can be represented by their 'show' string.
---
-data SerializableException
- = EUserInterrupt
- | EExitCode ExitCode
- | EOtherException String
- deriving (Generic, Show)
-
-toSerializableException :: SomeException -> SerializableException
-toSerializableException ex
- | Just UserInterrupt <- fromException ex = EUserInterrupt
- | Just (ec::ExitCode) <- fromException ex = (EExitCode ec)
- | otherwise = EOtherException $
-#if MIN_VERSION_base(4,20,0)
- -- Exception plus backtrace as seen in `displayExceptionWithInfo`
- case displayExceptionContext (someExceptionContext ex) of
- "" -> displayException (ex :: SomeException)
- cx -> displayException (ex :: SomeException) ++ "\n\n" ++ cx
-#else
- show (ex :: SomeException)
-#endif
-
-fromSerializableException :: SerializableException -> SomeException
-fromSerializableException EUserInterrupt = toException UserInterrupt
-fromSerializableException (EExitCode c) = toException c
-fromSerializableException (EOtherException str) = toException (ErrorCall str)
-
-instance Binary ExitCode
-instance Binary SerializableException
-
data THResult a
= THException String
| THComplete a
@@ -719,65 +667,6 @@ serializeBCOs rbcos = parMap doChunk (chunkList 100 rbcos)
chunkList _ [] = []
chunkList n xs = as : chunkList n bs where (as,bs) = splitAt n xs
--- -----------------------------------------------------------------------------
--- Reading/writing messages
-
--- | An opaque pipe for bidirectional binary data transmission.
-data Pipe = Pipe
- { getSome :: !(IO ByteString)
- , putAll :: !(B.Builder -> IO ())
- , pipeLeftovers :: !(IORef (Maybe ByteString))
- , pipeLock :: !(MVar ()) -- ^ Lock to prevent concurrent writes to the stream
- , pipeAsyncMngr :: !AsyncManager
- , pipeIsClosed :: !(TVar Bool)
- -- ^ After the pipe is closed, requests can no longer be received nor sent
- }
-
--- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
--- Must call 'closePipe' at the end to clean up.
-mkPipeFromHandles :: Handle -> Handle -> IO Pipe
-mkPipeFromHandles pipeRead pipeWrite = do
- let getSome = B.hGetSome pipeRead (32*1024)
- putAll b = do
- B.hPutBuilder pipeWrite b
- hFlush pipeWrite
- mkPipeFromContinuations getSome putAll
-
--- | Make a 'Pipe' from a reader function and a writer function.
--- The pipe should be closed with 'closePipe'.
-mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO Pipe
-mkPipeFromContinuations getSome putAll = mdo
- pipeLeftovers <- newIORef Nothing
- pipeLock <- newMVar ()
- pipeIsClosed <- newTVarIO False
- mdo
- pipeAsyncMngr <- newAsyncManager p
- let p = Pipe { .. }
- pure p
-
--- | Cleanly close our end of the Pipe, terminating the async manager's
--- drain-read-end thread. Calling 'closePipe' on an already closed pipe is a no-op.
---
--- If the pipe was created using 'mkPipeFromHandles', the handles must only by
--- closed after calling 'closePipe'.
---
--- The requests we've sent that are pending are all canceled with
--- 'PipeClosedException' by ourselves.
---
--- The remote process does not necessarily detect that this end of the pipe was
--- closed, but it must handle cleanly and clean up after exceptions caused from
--- it such as a handle being closed.
-closePipe :: Pipe -> IO ()
-closePipe Pipe{..} = do
-
- isAlreadyClosed <- atomically $ swapTVar pipeIsClosed True
-
- unless isAlreadyClosed $ do
-
- -- releases handle lock taken by reading, and cleans up by replying to every
- -- pending request with 'PipeClosedException'.
- throwTo (asyncDrainTid pipeAsyncMngr) PipeClosedException
-
-- | Send a 'Message' request to and wait for a reply from the interpreter
-- (blocks on the calling side, doesn't necessarily block the interpreter)
remoteCall :: Binary a => Pipe -> Message a -> IO a
@@ -815,314 +704,3 @@ handleRemoteTHCall pipe ii k =
case k msg |#> s0 of -- unIO for linearity checking
(# s1, a #) -> writeReply writer (put a) |#> s1
---------------------------------------------------------------------------------
--- * Matching requests with async replies
---------------------------------------------------------------------------------
-
--- | Send an asynchronous request and block waiting for a matching reply
-remoteAsyncReq :: Pipe -> Put -> (Maybe InboxId) -> IO (Either SomeException LB.ByteString)
-remoteAsyncReq pipe msg ii = do
-
- let pendingReplies = pendingReply (pipeAsyncMngr pipe)
-
- -- Mark pending request
- uq <- freshId (pipeAsyncMngr pipe)
- wv <- newEmptyTMVarIO
-
- isClosed <- atomically $ do
- isClosed <- readTVar (pipeIsClosed pipe)
- if isClosed
- then pure True
- else do
- modifyTVar pendingReplies (IntMap.insert uq wv)
- pure False
-
- when isClosed $ do
- throwIO PipeClosedException
-
- -- Write request
- writePipe pipe (Request uq ii (runPut msg))
- `onException`
- -- the req wasn't done, or no one will be reading the reply:
- -- in either case, drop it from the pending map.
- atomically (modifyTVar pendingReplies (IntMap.delete uq))
-
- -- Block waiting for reply
- atomically (takeTMVar wv)
- `onException`
- atomically (modifyTVar pendingReplies (IntMap.delete uq))
-
--- | Receive a single asynchronous request and send a matching reply
--- asynchronously (the handler may fork a computation that eventually replies
--- and can return before actually replying, as long as it eventually does).
---
--- The handler receives as an argument the function to use to reply to the request.
--- It must be used exactly once (all requests expect a reply, and you can't
--- reply more than once). Linear types enforce the handler replies in every
--- branch, and if the handler otherwise throws, it is handled by 'handleAsyncReq'.
-handleAsyncReq :: Pipe -> Get a -> (Maybe InboxId) -> (WriteReply Put %1 -> a -> IO r) -> IO r
-handleAsyncReq pipe getit mii k = mask $ \restore -> do
- let am = pipeAsyncMngr pipe
- ack <- newEmptyMVar
-
- -- Block waiting for request
- mbreq <- atomically $ do
- isClosed <- readTVar (pipeIsClosed pipe)
- if isClosed
- then return Nothing
- else Just <$> case mii of
- Nothing -> readTQueue $ requestsQueue am
- Just ii -> readTQueue =<< getMailbox am ii
-
- case mbreq of
- Nothing -> throwIO PipeClosedException
- Just (uq, bs) -> do
- let reply p = do
- writePipe pipe (Reply uq (bimap toSerializableException runPut p))
- putMVar ack ()
-
- -- Run the continuation which must reply exactly once with 'writeReply'
- case runGetOrFail getit bs of
- Left (_, _, err) -> do
- let exc = DecodeAsyncReqException err
- reply (Left (toException exc))
- throwIO exc
- Right (_, _, it) ->
- restore (k (WriteReply reply) it)
-
- -- If there's an exception before the reply is sent, reply with an error instead.
- `catch` \(e :: SomeException) -> do
-
- tryTakeMVar ack >>= \case
- Just () ->
- -- Already replied
- throwIO e
- Nothing -> do
- -- Reply with an error
- reply (Left e)
- throwIO e
-
--- | An exception that can be thrown on the host process representing an
--- expection that happened on the remote one.
-newtype RemoteProcessException = RemoteProcessException SerializableException deriving Show
-instance Exception RemoteProcessException
-
--- | 'handleSyncReq' failed to decode an incoming request payload using the given @'Get' a@
-newtype DecodeAsyncReqException = DecodeAsyncReqException String deriving Show
-instance Exception DecodeAsyncReqException
-
--- | We've closed the pipe on our end and we terminate all pending requests with this.
-data PipeClosedException = PipeClosedException deriving Show
-instance Exception PipeClosedException
-
--- ** Async matching "internals" -----------------------------------------------
-
-data AsyncManager = AsyncManager
- { pendingReply :: !(TVar (IntMap (TMVar (Either SomeException LB.ByteString))))
- -- ^ When a request is sent, keep track that we're expecting a reply. The
- -- TMVar is signaled when the reply arrives. The exception can originate
- -- from the host process, or from the remote one (with 'RemoteProcessException').
- , requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
- -- ^ A queue of pending received requests. When a request is received
- -- /without/ a specific 'InboxId', queue it here. A request handler will
- -- pop the next request from it. (Nothing except the 'drainPipe' thread
- -- should read from the pipe). Read with @'handleAsyncReq' p get Nothing@.
- , mailboxQueues :: !(TVar (IntMap (TQueue (CorrelationId, LB.ByteString))))
- -- ^ Requests for a specific 'InboxId' that can be read using
- -- @'handleAsyncReq' p get (Just <mailboxid>)@.
- , asyncDrainTid :: !ThreadId
- , asyncFreshId :: !(IORef Int)
- }
-
--- | Each 'Request' expects a 'Reply' with a matching 'CorrelationId'.
-type CorrelationId = Int
-
--- | A 'Request' may optionally declare an 'InboxId'. If it does, it can only
--- be read by a 'handleAsyncReq' for that specific 'InboxId'. That is,
--- @'handleAsyncReq' p get Nothing@ will not receive messages meant for a
--- specific inbox.
---
--- This is useful to prevent mixing up reverse requests, where we want to send
--- many reverse requests but have the caller handle them without interleaving
--- them.
-newtype InboxId = InboxId Int deriving (Show, Binary)
-
--- | Create a new mailbox from where we can read all requests addressed to this 'InboxId'.
-newMailbox :: AsyncManager -> IO InboxId
-newMailbox am = InboxId <$> freshId am
- -- `mailboxQueues` is written on demand, no need to do it here.
-
--- | Clean up a created mailbox by 'InboxId'.
-closeMailbox :: AsyncManager -> InboxId -> IO ()
-closeMailbox AsyncManager{..} (InboxId ii) =
- atomically (modifyTVar mailboxQueues (IntMap.delete ii))
-
--- | We have a single type for requests and replies because both the host and
--- interpreter may send requests *and* replies on the write end *and* on recv
--- both on the read end.
---
--- For instance:
--- 1. The host sends a RunTH request on its Host Write End (HWE)
--- 2. The interpreter sends a NewName TH request on its Interpreter Write End (IWE)
--- 3. The host sends a reply to the TH request on its HWE
--- 4. The interpreter sends the reply to the RunTH request on its IWE
---
--- Additionally, we want to keep concurrent TH requests for independent RunTH
--- calls separated. Thus the interpreter requests additionally specify a
--- mailbox announced by RunTH:
--- 1. To interpreter: @Request _ Nothing (RunTH <mailid>)@
--- 2. From interpreter: @Request _ (Just <mailid>) NewName@
--- And the host will relate the <mailid> reverse requests to that RunTH run
--- exclusively.
-data IddMsg = Request !CorrelationId !(Maybe InboxId) LB.ByteString
- | Reply !CorrelationId (Either SerializableException LB.ByteString)
-
-instance Binary IddMsg where
- put (Request i ii bs) = putWord8 0 >> put i >> put ii >> put bs
- put (Reply i bs) = putWord8 1 >> put i >> put bs
- get = do
- b <- getWord8
- case b of
- 0 -> Request <$> get <*> get <*> get
- 1 -> Reply <$> get <*> get
- _ -> fail $ "impossible: Binary IddMsg - " ++ show b
-
-newAsyncManager :: Pipe -> IO AsyncManager
-newAsyncManager pipe = do
- asyncFreshId <- newIORef 0
- pendingReply <- newTVarIO IntMap.empty
- requestsQueue <- newTQueueIO
- mailboxQueues <- newTVarIO IntMap.empty
-
- -- Fork thread to drain read end.
- -- (cleaned up on 'closePipe')
- asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
-
- let am = AsyncManager{..}
- pure am
-
--- | Drains the read end of the Pipe and match 'Reply'es against pending 'Request's.
---
--- In pratice, we'll have both a thread draining the read end of the host's
--- 'Pipe' and a thread draining the read end of the interpreter's 'Pipe', since
--- both processes construct a 'Pipe' and send and receive messages
-drainPipe :: Pipe -> (forall x. IO x -> IO x) -> IO ()
-drainPipe p unmask = catch (unmask loop) replyToPendings where
- loop = do
- readPipe p >>= \case
- -- Receive a reply to a pending notification
- Just (Reply uq bs) -> do
- atomically $ do
- m <- readTVar (pendingReply am)
- case IntMap.lookup uq m of
- Nothing ->
- -- the req was dropped from pending bc there was an exception, and no
- -- one's listening any more. the reply is ignored
- pure ()
- Just wv -> do
- writeTVar (pendingReply am) (IntMap.delete uq m)
- -- signal matching request
- putTMVar wv (bimap (toException . RemoteProcessException) id bs)
- loop
- -- Receive a request and queue it
- Just (Request uq mbi bs) -> do
- atomically $ case mbi of
- Nothing ->
- -- "Global" request
- writeTQueue (requestsQueue am) (uq,bs)
- Just ii -> do
- -- "Specific mailbox" request
- q <- getMailbox am ii
- writeTQueue q (uq,bs)
- loop
- -- EOF on read handle
- Nothing -> do
- -- clean up pipe then exit loop
- replyToPendings (toException PipeClosedException)
- pure ()
-
- replyToPendings e = atomically $ do
- writeTVar (pipeIsClosed p) True
- pendings <- swapTVar (pendingReply am) IntMap.empty -- take pending
- mapM_ (\p -> putTMVar p (Left e {-host exception-})) pendings
-
- am = pipeAsyncMngr p
-
-freshId :: AsyncManager -> IO Int
-freshId AsyncManager{asyncFreshId} =
- atomicModifyIORef' asyncFreshId $ \i -> (i+1, i)
-
-getMailbox :: AsyncManager -> InboxId -> STM (TQueue (CorrelationId, LB.ByteString))
-getMailbox am (InboxId ii) = do
- mboxes <- readTVar (mailboxQueues am)
- case IntMap.lookup ii mboxes of
- Nothing -> do
- q <- newTQueue
- writeTVar (mailboxQueues am) (IntMap.insert ii q mboxes)
- pure q
- Just q ->
- pure q
-
---------------------------------------------------------------------------------
--- * Pipe thread-safe read/write
---------------------------------------------------------------------------------
--- All messages on the wire are 'IddMsg', which contain a correlation header
--- and identify whether they are requests or replies.
-
-writePipe :: Pipe -> IddMsg -> IO ()
-writePipe p@Pipe{..} = withLock p . putAll . execPut . put
-
--- | Only the 'drainPipe' thread should read from the pipe directly. Other
--- functions should wait for the matching reply to be delivered asynchronously.
--- (see 'withAsyncReq'). It doesn't lock to read bc it assumes single reader is 'drainPipe'.
---
--- Returns @Nothing@ on EOF.
-readPipe :: Pipe -> IO (Maybe IddMsg)
-readPipe Pipe{..} = do
- leftovers <- readIORef pipeLeftovers
- m <- getBin getSome get leftovers
- case m of
- Nothing -> return Nothing
- Just (result, new_leftovers) -> do
- writeIORef pipeLeftovers new_leftovers
- return (Just result)
-
-getBin
- :: (IO ByteString) -> Get a -> Maybe ByteString
- -> IO (Maybe (a, Maybe ByteString))
-
-getBin getsome get leftover = go leftover (runGetIncremental get)
- where
- go Nothing (Done leftover _ msg) =
- return (Just (msg, if B.null leftover then Nothing else Just leftover))
- go _ Done{} = throwIO (ErrorCall "getBin: Done with leftovers")
- go (Just leftover) (Partial fun) = do
- go Nothing (fun (Just leftover))
- go Nothing (Partial fun) = do
- -- putStrLn "before hGetSome"
- b <- getsome
- -- putStrLn $ "hGetSome: " ++ show (B.length b)
- if B.null b
- then return Nothing
- else go Nothing (fun (Just b))
- go _lft (Fail _rest _off str) =
- throwIO (ErrorCall ("getBin: " ++ str))
-
-withLock :: Pipe -> IO c -> IO c
-withLock Pipe{..} = bracket (takeMVar pipeLock) (putMVar pipeLock) . const
-
---------------------------------------------------------------------------------
--- * Consume `reply` exactly once
---------------------------------------------------------------------------------
-
--- | Every request should get exactly one reply.
-data WriteReply a where
- WriteReply :: (Either SomeException a -> IO ()) %1 -> WriteReply a
-
--- | Write a reply exactly once (consuming the ability to write more replies)
-writeReply :: WriteReply a %1 -> a -> IO ()
-writeReply (WriteReply r) = \x -> r (Right x)
-
--- | Infix, linear, 'unIO'
-(|#>) :: IO a %1 -> State# RealWorld -> (# State# RealWorld, a #)
-(|#>) (IO fs) s = fs s
=====================================
libraries/ghci/GHCi/Message/Async.hs
=====================================
@@ -0,0 +1,480 @@
+{-# LANGUAGE RecursiveDo, GADTs, DeriveGeneric, StandaloneDeriving, ScopedTypeVariables,
+ GeneralizedNewtypeDeriving, ExistentialQuantification, RecordWildCards, CPP,
+ NamedFieldPuns, LambdaCase, TypeAbstractions, TypeApplications,
+ LinearTypes, RankNTypes, UnboxedTuples, BlockArguments, TupleSections, MagicHash #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-orphans #-}
+module GHCi.Message.Async
+ (
+ -- * Pipes
+ Pipe(..), mkPipeFromHandles, mkPipeFromContinuations
+ , closePipe, PipeClosedException(..)
+
+ -- * Interface for async bidirectional RPC
+ , remoteAsyncReq, handleAsyncReq
+ , RemoteProcessException(..), DecodeAsyncReqException(..)
+
+ -- ** Mailboxes
+ , InboxId(..)
+ , newMailbox
+ , closeMailbox
+
+ -- ** Async matching internals
+ , AsyncManager(..), CorrelationId
+ , IddMsg(..), newAsyncManager
+
+ -- ** Consume `reply` exactly once
+ , WriteReply(..)
+ , writeReply, (|#>)
+
+ -- * Serializable exceptions
+ , SerializableException(..)
+ , toSerializableException, fromSerializableException
+ ) where
+
+import Prelude -- See note [Why do we import Prelude here?]
+import GHCi.TH.Binary () -- For Binary instances
+
+import GHC.IO (IO(..))
+import Control.Concurrent
+import Control.Exception
+#if MIN_VERSION_base(4,20,0)
+import Control.Exception.Context
+#endif
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Builder as B
+import qualified Data.ByteString.Lazy as LB
+import Data.IORef
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as IntMap
+import GHC.Generics
+import System.Exit
+import System.IO
+import Control.Monad
+import Control.Concurrent.STM
+import Data.Bifunctor
+import GHC.Base (State#)
+import GHC.Exts (RealWorld)
+
+--------------------------------------------------------------------------------
+-- * Reading/writing and Pipes
+--------------------------------------------------------------------------------
+
+-- | An opaque pipe for bidirectional binary data transmission.
+data Pipe = Pipe
+ { getSome :: !(IO ByteString)
+ , putAll :: !(B.Builder -> IO ())
+ , pipeLeftovers :: !(IORef (Maybe ByteString))
+ , pipeLock :: !(MVar ()) -- ^ Lock to prevent concurrent writes to the stream
+ , pipeAsyncMngr :: !AsyncManager
+ , pipeIsClosed :: !(TVar Bool)
+ -- ^ After the pipe is closed, requests can no longer be received nor sent
+ }
+
+-- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
+-- Must call 'closePipe' at the end to clean up.
+mkPipeFromHandles :: Handle -> Handle -> IO Pipe
+mkPipeFromHandles pipeRead pipeWrite = do
+ let getSome = B.hGetSome pipeRead (32*1024)
+ putAll b = do
+ B.hPutBuilder pipeWrite b
+ hFlush pipeWrite
+ mkPipeFromContinuations getSome putAll
+
+-- | Make a 'Pipe' from a reader function and a writer function.
+-- The pipe should be closed with 'closePipe'.
+mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO Pipe
+mkPipeFromContinuations getSome putAll = mdo
+ pipeLeftovers <- newIORef Nothing
+ pipeLock <- newMVar ()
+ pipeIsClosed <- newTVarIO False
+ mdo
+ pipeAsyncMngr <- newAsyncManager p
+ let p = Pipe { .. }
+ pure p
+
+-- | Cleanly close our end of the Pipe, terminating the async manager's
+-- drain-read-end thread. Calling 'closePipe' on an already closed pipe is a no-op.
+--
+-- If the pipe was created using 'mkPipeFromHandles', the handles must only by
+-- closed after calling 'closePipe'.
+--
+-- The requests we've sent that are pending are all canceled with
+-- 'PipeClosedException' by ourselves.
+--
+-- The remote process does not necessarily detect that this end of the pipe was
+-- closed, but it must handle cleanly and clean up after exceptions caused from
+-- it such as a handle being closed.
+closePipe :: Pipe -> IO ()
+closePipe Pipe{..} = do
+
+ isAlreadyClosed <- atomically $ swapTVar pipeIsClosed True
+
+ unless isAlreadyClosed $ do
+
+ -- releases handle lock taken by reading, and cleans up by replying to every
+ -- pending request with 'PipeClosedException'.
+ throwTo (asyncDrainTid pipeAsyncMngr) PipeClosedException
+
+-- | We've closed the pipe on our end and we terminate all pending requests with this.
+data PipeClosedException = PipeClosedException deriving Show
+instance Exception PipeClosedException
+
+--------------------------------------------------------------------------------
+-- ** Pipe thread-safe read/write
+--------------------------------------------------------------------------------
+-- All messages on the wire are 'IddMsg', which contain a correlation header
+-- and identify whether they are requests or replies.
+
+writePipe :: Pipe -> IddMsg -> IO ()
+writePipe p@Pipe{..} = withLock p . putAll . execPut . put
+
+-- | Only the 'drainPipe' thread should read from the pipe directly. Other
+-- functions should wait for the matching reply to be delivered asynchronously.
+-- (see 'withAsyncReq'). It doesn't lock to read bc it assumes single reader is 'drainPipe'.
+--
+-- Returns @Nothing@ on EOF.
+readPipe :: Pipe -> IO (Maybe IddMsg)
+readPipe Pipe{..} = do
+ leftovers <- readIORef pipeLeftovers
+ m <- getBin getSome get leftovers
+ case m of
+ Nothing -> return Nothing
+ Just (result, new_leftovers) -> do
+ writeIORef pipeLeftovers new_leftovers
+ return (Just result)
+
+getBin
+ :: (IO ByteString) -> Get a -> Maybe ByteString
+ -> IO (Maybe (a, Maybe ByteString))
+
+getBin getsome get leftover = go leftover (runGetIncremental get)
+ where
+ go Nothing (Done leftover _ msg) =
+ return (Just (msg, if B.null leftover then Nothing else Just leftover))
+ go _ Done{} = throwIO (ErrorCall "getBin: Done with leftovers")
+ go (Just leftover) (Partial fun) = do
+ go Nothing (fun (Just leftover))
+ go Nothing (Partial fun) = do
+ -- putStrLn "before hGetSome"
+ b <- getsome
+ -- putStrLn $ "hGetSome: " ++ show (B.length b)
+ if B.null b
+ then return Nothing
+ else go Nothing (fun (Just b))
+ go _lft (Fail _rest _off str) =
+ throwIO (ErrorCall ("getBin: " ++ str))
+
+withLock :: Pipe -> IO c -> IO c
+withLock Pipe{..} = bracket (takeMVar pipeLock) (putMVar pipeLock) . const
+
+--------------------------------------------------------------------------------
+-- * Matching requests with async replies
+--------------------------------------------------------------------------------
+
+-- | Send an asynchronous request and block waiting for a matching reply
+remoteAsyncReq :: Pipe -> Put -> (Maybe InboxId) -> IO (Either SomeException LB.ByteString)
+remoteAsyncReq pipe msg ii = do
+
+ let pendingReplies = pendingReply (pipeAsyncMngr pipe)
+
+ -- Mark pending request
+ uq <- freshId (pipeAsyncMngr pipe)
+ wv <- newEmptyTMVarIO
+
+ isClosed <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ if isClosed
+ then pure True
+ else do
+ modifyTVar pendingReplies (IntMap.insert uq wv)
+ pure False
+
+ when isClosed $ do
+ throwIO PipeClosedException
+
+ -- Write request
+ writePipe pipe (Request uq ii (runPut msg))
+ `onException`
+ -- the req wasn't done, or no one will be reading the reply:
+ -- in either case, drop it from the pending map.
+ atomically (modifyTVar pendingReplies (IntMap.delete uq))
+
+ -- Block waiting for reply
+ atomically (takeTMVar wv)
+ `onException`
+ atomically (modifyTVar pendingReplies (IntMap.delete uq))
+
+-- | Receive a single asynchronous request and send a matching reply
+-- asynchronously (the handler may fork a computation that eventually replies
+-- and can return before actually replying, as long as it eventually does).
+--
+-- The handler receives as an argument the function to use to reply to the request.
+-- It must be used exactly once (all requests expect a reply, and you can't
+-- reply more than once). Linear types enforce the handler replies in every
+-- branch, and if the handler otherwise throws, it is handled by 'handleAsyncReq'.
+handleAsyncReq :: Pipe -> Get a -> (Maybe InboxId) -> (WriteReply Put %1 -> a -> IO r) -> IO r
+handleAsyncReq pipe getit mii k = mask $ \restore -> do
+ let am = pipeAsyncMngr pipe
+ ack <- newEmptyMVar
+
+ -- Block waiting for request
+ mbreq <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ if isClosed
+ then return Nothing
+ else Just <$> case mii of
+ Nothing -> readTQueue $ requestsQueue am
+ Just ii -> readTQueue =<< getMailbox am ii
+
+ case mbreq of
+ Nothing -> throwIO PipeClosedException
+ Just (uq, bs) -> do
+ let reply p = do
+ writePipe pipe (Reply uq (bimap toSerializableException runPut p))
+ putMVar ack ()
+
+ -- Run the continuation which must reply exactly once with 'writeReply'
+ case runGetOrFail getit bs of
+ Left (_, _, err) -> do
+ let exc = DecodeAsyncReqException err
+ reply (Left (toException exc))
+ throwIO exc
+ Right (_, _, it) ->
+ restore (k (WriteReply reply) it)
+
+ -- If there's an exception before the reply is sent, reply with an error instead.
+ `catch` \(e :: SomeException) -> do
+
+ tryTakeMVar ack >>= \case
+ Just () ->
+ -- Already replied
+ throwIO e
+ Nothing -> do
+ -- Reply with an error
+ reply (Left e)
+ throwIO e
+
+-- | 'handleSyncReq' failed to decode an incoming request payload using the given @'Get' a@
+newtype DecodeAsyncReqException = DecodeAsyncReqException String deriving Show
+instance Exception DecodeAsyncReqException
+
+
+-- ** Mailboxes ----------------------------------------------------------------
+
+-- | A 'Request' may optionally declare an 'InboxId'. If it does, it can only
+-- be read by a 'handleAsyncReq' for that specific 'InboxId'. That is,
+-- @'handleAsyncReq' p get Nothing@ will not receive messages meant for a
+-- specific inbox.
+--
+-- This is useful to prevent mixing up reverse requests, where we want to send
+-- many reverse requests but have the caller handle them without interleaving
+-- them.
+newtype InboxId = InboxId Int deriving (Show, Binary)
+
+-- | Create a new mailbox from where we can read all requests addressed to this 'InboxId'.
+newMailbox :: AsyncManager -> IO InboxId
+newMailbox am = InboxId <$> freshId am
+ -- `mailboxQueues` is written on demand, no need to do it here.
+
+-- | Clean up a created mailbox by 'InboxId'.
+closeMailbox :: AsyncManager -> InboxId -> IO ()
+closeMailbox AsyncManager{..} (InboxId ii) =
+ atomically (modifyTVar mailboxQueues (IntMap.delete ii))
+
+-- | Get or create a new mailbox for this 'InboxId'
+getMailbox :: AsyncManager -> InboxId -> STM (TQueue (CorrelationId, LB.ByteString))
+getMailbox am (InboxId ii) = do
+ mboxes <- readTVar (mailboxQueues am)
+ case IntMap.lookup ii mboxes of
+ Nothing -> do
+ q <- newTQueue
+ writeTVar (mailboxQueues am) (IntMap.insert ii q mboxes)
+ pure q
+ Just q ->
+ pure q
+
+-- ** Async matching "internals" -----------------------------------------------
+
+data AsyncManager = AsyncManager
+ { pendingReply :: !(TVar (IntMap (TMVar (Either SomeException LB.ByteString))))
+ -- ^ When a request is sent, keep track that we're expecting a reply. The
+ -- TMVar is signaled when the reply arrives. The exception can originate
+ -- from the host process, or from the remote one (with 'RemoteProcessException').
+ , requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
+ -- ^ A queue of pending received requests. When a request is received
+ -- /without/ a specific 'InboxId', queue it here. A request handler will
+ -- pop the next request from it. (Nothing except the 'drainPipe' thread
+ -- should read from the pipe). Read with @'handleAsyncReq' p get Nothing@.
+ , mailboxQueues :: !(TVar (IntMap (TQueue (CorrelationId, LB.ByteString))))
+ -- ^ Requests for a specific 'InboxId' that can be read using
+ -- @'handleAsyncReq' p get (Just <mailboxid>)@.
+ , asyncDrainTid :: !ThreadId
+ , asyncFreshId :: !(IORef Int)
+ }
+
+-- | Each 'Request' expects a 'Reply' with a matching 'CorrelationId'.
+type CorrelationId = Int
+
+-- | We have a single type for requests and replies because both the host and
+-- interpreter may send requests *and* replies on the write end *and* on recv
+-- both on the read end.
+--
+-- For instance:
+-- 1. The host sends a RunTH request on its Host Write End (HWE)
+-- 2. The interpreter sends a NewName TH request on its Interpreter Write End (IWE)
+-- 3. The host sends a reply to the TH request on its HWE
+-- 4. The interpreter sends the reply to the RunTH request on its IWE
+--
+-- Additionally, we want to keep concurrent TH requests for independent RunTH
+-- calls separated. Thus the interpreter requests additionally specify a
+-- mailbox announced by RunTH:
+-- 1. To interpreter: @Request _ Nothing (RunTH <mailid>)@
+-- 2. From interpreter: @Request _ (Just <mailid>) NewName@
+-- And the host will relate the <mailid> reverse requests to that RunTH run
+-- exclusively.
+data IddMsg = Request !CorrelationId !(Maybe InboxId) LB.ByteString
+ | Reply !CorrelationId (Either SerializableException LB.ByteString)
+
+instance Binary IddMsg where
+ put (Request i ii bs) = putWord8 0 >> put i >> put ii >> put bs
+ put (Reply i bs) = putWord8 1 >> put i >> put bs
+ get = do
+ b <- getWord8
+ case b of
+ 0 -> Request <$> get <*> get <*> get
+ 1 -> Reply <$> get <*> get
+ _ -> fail $ "impossible: Binary IddMsg - " ++ show b
+
+newAsyncManager :: Pipe -> IO AsyncManager
+newAsyncManager pipe = do
+ asyncFreshId <- newIORef 0
+ pendingReply <- newTVarIO IntMap.empty
+ requestsQueue <- newTQueueIO
+ mailboxQueues <- newTVarIO IntMap.empty
+
+ -- Fork thread to drain read end.
+ -- (cleaned up on 'closePipe')
+ asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
+
+ let am = AsyncManager{..}
+ pure am
+
+-- | Drains the read end of the Pipe and match 'Reply'es against pending 'Request's.
+--
+-- In pratice, we'll have both a thread draining the read end of the host's
+-- 'Pipe' and a thread draining the read end of the interpreter's 'Pipe', since
+-- both processes construct a 'Pipe' and send and receive messages
+drainPipe :: Pipe -> (forall x. IO x -> IO x) -> IO ()
+drainPipe p unmask = catch (unmask loop) replyToPendings where
+ loop = do
+ readPipe p >>= \case
+ -- Receive a reply to a pending notification
+ Just (Reply uq bs) -> do
+ atomically $ do
+ m <- readTVar (pendingReply am)
+ case IntMap.lookup uq m of
+ Nothing ->
+ -- the req was dropped from pending bc there was an exception, and no
+ -- one's listening any more. the reply is ignored
+ pure ()
+ Just wv -> do
+ writeTVar (pendingReply am) (IntMap.delete uq m)
+ -- signal matching request
+ putTMVar wv (bimap (toException . RemoteProcessException) id bs)
+ loop
+ -- Receive a request and queue it
+ Just (Request uq mbi bs) -> do
+ atomically $ case mbi of
+ Nothing ->
+ -- "Global" request
+ writeTQueue (requestsQueue am) (uq,bs)
+ Just ii -> do
+ -- "Specific mailbox" request
+ q <- getMailbox am ii
+ writeTQueue q (uq,bs)
+ loop
+ -- EOF on read handle
+ Nothing -> do
+ -- clean up pipe then exit loop
+ replyToPendings (toException PipeClosedException)
+ pure ()
+
+ replyToPendings e = atomically $ do
+ writeTVar (pipeIsClosed p) True
+ pendings <- swapTVar (pendingReply am) IntMap.empty -- take pending
+ mapM_ (\p -> putTMVar p (Left e {-host exception-})) pendings
+
+ am = pipeAsyncMngr p
+
+-- | An exception that can be thrown on the host process representing an
+-- expection that happened on the remote one.
+newtype RemoteProcessException = RemoteProcessException SerializableException deriving Show
+instance Exception RemoteProcessException
+
+-- | Come up with a fresh id for 'CorrelationId' or 'InboxId'
+freshId :: AsyncManager -> IO Int
+freshId AsyncManager{asyncFreshId} =
+ atomicModifyIORef' asyncFreshId $ \i -> (i+1, i)
+
+--------------------------------------------------------------------------------
+-- * Consume `reply` exactly once
+--------------------------------------------------------------------------------
+
+-- | Every request should get exactly one reply.
+data WriteReply a where
+ WriteReply :: (Either SomeException a -> IO ()) %1 -> WriteReply a
+
+-- | Write a reply exactly once (consuming the ability to write more replies)
+writeReply :: WriteReply a %1 -> a -> IO ()
+writeReply (WriteReply r) = \x -> r (Right x)
+
+-- | Infix, linear, 'unIO'
+(|#>) :: IO a %1 -> State# RealWorld -> (# State# RealWorld, a #)
+(|#>) (IO fs) s = fs s
+
+--------------------------------------------------------------------------------
+-- * Serializable Exceptions
+--------------------------------------------------------------------------------
+
+-- SomeException can't be serialized because it contains dynamic
+-- types. However, we do very limited things with the exceptions that
+-- are thrown by interpreted computations:
+--
+-- * We print them, e.g. "*** Exception: <something>"
+-- * UserInterrupt has a special meaning
+-- * In ghc -e, exitWith should exit with the appropriate exit code
+--
+-- So all we need to do is distinguish UserInterrupt and ExitCode, and
+-- all other exceptions can be represented by their 'show' string.
+--
+data SerializableException
+ = EUserInterrupt
+ | EExitCode ExitCode
+ | EOtherException String
+ deriving (Generic, Show)
+
+toSerializableException :: SomeException -> SerializableException
+toSerializableException ex
+ | Just UserInterrupt <- fromException ex = EUserInterrupt
+ | Just (ec::ExitCode) <- fromException ex = (EExitCode ec)
+ | otherwise = EOtherException $
+#if MIN_VERSION_base(4,20,0)
+ -- Exception plus backtrace as seen in `displayExceptionWithInfo`
+ case displayExceptionContext (someExceptionContext ex) of
+ "" -> displayException (ex :: SomeException)
+ cx -> displayException (ex :: SomeException) ++ "\n\n" ++ cx
+#else
+ show (ex :: SomeException)
+#endif
+
+fromSerializableException :: SerializableException -> SomeException
+fromSerializableException EUserInterrupt = toException UserInterrupt
+fromSerializableException (EExitCode c) = toException c
+fromSerializableException (EOtherException str) = toException (ErrorCall str)
+
+instance Binary ExitCode
+instance Binary SerializableException
=====================================
libraries/ghci/ghci.cabal.in
=====================================
@@ -76,6 +76,7 @@ library
exposed-modules:
GHCi.BreakArray
GHCi.Message
+ GHCi.Message.Async
GHCi.ResolvedBCO
GHCi.RemoteTypes
GHCi.FFI
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8c724a7e68b95a7883e466621e4a7c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8c724a7e68b95a7883e466621e4a7c…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/async-rpc] 3 commits: fixup: doc fix
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
f241c56e by Rodrigo Mesquita at 2026-10-14T16:29:08+01:00
fixup: doc fix
- - - - -
8b328bd5 by Rodrigo Mesquita at 2026-10-14T17:30:30+01:00
fixup: if is closed discard pending too; write reply would fail
- - - - -
0b6cc8c7 by Rodrigo Mesquita at 2026-10-14T18:06:19+01:00
interp: Add separate mailboxes for TH async RPC
When the host sends a `RunTH` command to the interpreter, it expects a
few "reverse requests" from the interpreter (e.g. `NewName`) which must
be run in the same context that initiated the `RunTH`.
When the host is evaluating many splices at once, it will send many
`RunTH` commands to the interpreter and start listening for reverse
requests in the meantime. However, a received request previously had no
way to be associated to the scope of a particular `RunTH` invocation, so
the reverse commands could get replied to from incorrect contexts, or
e.g. a `RunTHDone` from the interp. could terminate early the wrong
`RunTH` computation.
In this commit we add independent mailboxes to the interpreter
asynchronous RPC infrastructure, allowing a RunTH to announce a
particular `InboxId` that all reverse requests for that RunTH should
use. On the host, we create that mailbox id and have
`handleRemoteTHCall` listen on that specific mailbox rather than for
requests without a specific mailbox address.
- - - - -
5 changed files:
- compiler/GHC/Runtime/Interpreter/Process.hs
- compiler/GHC/Tc/Gen/Splice.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Server.hs
- libraries/ghci/GHCi/TH.hs
Changes:
=====================================
compiler/GHC/Runtime/Interpreter/Process.hs
=====================================
@@ -48,7 +48,7 @@ sendMessage i msg =
sendTHComputation
:: Binary a
=> ExtInterpInstance d
- -> Message a
+ -> (InboxId -> Message a)
-> (forall b. Binary b => THMessage b -> IO (Bool, b))
-- ^ Handle one message. Return True to end the loop.
-- This handler must never uninterrutibly mask an expression which may get
@@ -56,21 +56,22 @@ sendTHComputation
-> IO a
sendTHComputation i msg reqHandler = mask $ \restore -> do
-- inline "withAsync/wait"
- var <- newEmptyTMVarIO
- ht <- forkFinally (restore handlerLoop) (atomically . putTMVar var)
- restore (sendMessage i msg <* atomically (readTMVar var))
+ var <- newEmptyTMVarIO
+ mbox <- newMailbox (pipeAsyncMngr (interpPipe proc))
+ ht <- forkFinally (restore (handlerLoop mbox)) (atomically . putTMVar var)
+ restore (sendMessage i (msg mbox) <* atomically (readTMVar var))
`onException` uninterruptibleMask_ (throwTo ht CancelTHHandler)
where
proc = instProcess i
- handleOne doneRef = do
- handleRemoteTHCall (interpPipe proc) $ \req -> do
+ handleOne mbox doneRef = do
+ handleRemoteTHCall (interpPipe proc) mbox $ \req -> do
(stop, res) <- reqHandler req
writeIORef doneRef stop
return res
- handlerLoop = do
+ handlerLoop mbox = do
doneRef <- newIORef False
let loop = do
- handleOne doneRef
+ handleOne mbox doneRef
isDone <- readIORef doneRef
if isDone then return ()
else loop
=====================================
compiler/GHC/Tc/Gen/Splice.hs
=====================================
@@ -1170,7 +1170,7 @@ runRemoteModFinalizers (ThModFinalizers finRefs) = do
recovers <- newIORef []
withForeignRef fhv $ \st ->
withForeignRefs finRefs $ \qrefs ->
- sendTHComputation inst (RunModFinalizers st qrefs) $ \msg ->
+ sendTHComputation inst (\ii -> RunModFinalizers ii st qrefs) $ \msg ->
runInIO (runRemoteTH recovers msg)
checkQResult qr
@@ -1830,7 +1830,7 @@ runTH ty fhv = do
recovers <- newIORef []
withForeignRef rstate $ \state_hv ->
withForeignRef fhv $ \q_hv ->
- sendTHComputation inst (RunTH state_hv q_hv ty (Just loc)) $ \msg ->
+ sendTHComputation inst (\ii -> RunTH ii state_hv q_hv ty (Just loc)) $ \msg ->
runInIO (runRemoteTH recovers msg)
bs <- checkQResult qr
return $! runGet get (LB.fromStrict bs)
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -29,6 +29,10 @@ module GHCi.Message
, handleRemoteCall, handleRemoteTHCall
, RemoteProcessException(..), PipeClosedException(..)
+ , InboxId(..)
+ , newMailbox
+ , closeMailbox
+
, BreakModule
, BreakUnitId
, LoadedDLL
@@ -237,14 +241,16 @@ data Message a where
-- they did, we have to serialize the value anyway, so we might
-- as well serialize it to force it.
RunTH
- :: RemoteRef (IORef QState)
+ :: InboxId -- ^ announce mailbox for related 'remoteTHCall's
+ -> RemoteRef (IORef QState)
-> HValueRef {- e.g. TH.Q TH.Exp -}
-> THResultType
-> Maybe TH.Loc
-> Message (QResult ByteString)
-- | Run the given mod finalizers.
- RunModFinalizers :: RemoteRef (IORef QState)
+ RunModFinalizers :: InboxId -- ^ announce mailbox for related 'remoteTHCall's
+ -> RemoteRef (IORef QState)
-> [RemoteRef (TH.Q ())]
-> Message (QResult ())
@@ -536,11 +542,13 @@ instance Binary THResultType
-- StartTH message. A new one is created per module that GHC
-- typechecks.
data QState = QState
- { qsMap :: Map TypeRep Dynamic
+ { qsMap :: Map TypeRep Dynamic
-- ^ persistent data between splices in a module
- , qsLocation :: Maybe TH.Loc
+ , qsLocation :: Maybe TH.Loc
-- ^ location for current splice, if any
- , qsPipe :: Pipe
+ , qsInboxId :: InboxId
+ -- ^ the mailbox expecting requests for each RunTH-like run
+ , qsPipe :: Pipe
-- ^ pipe to communicate with GHC
}
instance Show QState where show _ = "<QState>"
@@ -623,9 +631,9 @@ getMessage = do
29 -> Msg <$> (BreakpointStatus <$> get <*> get)
30 -> Msg <$> (GetBreakpointVar <$> get <*> get)
31 -> Msg <$> return StartTH
- 32 -> Msg <$> (RunModFinalizers <$> get <*> get)
+ 32 -> Msg <$> (RunModFinalizers <$> get <*> get <*> get)
33 -> Msg <$> (AddSptEntry <$> get <*> get)
- 34 -> Msg <$> (RunTH <$> get <*> get <*> get <*> get)
+ 34 -> Msg <$> (RunTH <$> get <*> get <*> get <*> get <*> get)
35 -> Msg <$> (GetClosure <$> get)
36 -> Msg <$> (Seq <$> get)
37 -> Msg <$> return RtsRevertCAFs
@@ -671,9 +679,9 @@ putMessage m = case m of
BreakpointStatus arr ix -> putWord8 29 >> put arr >> put ix
GetBreakpointVar a b -> putWord8 30 >> put a >> put b
StartTH -> putWord8 31
- RunModFinalizers a b -> putWord8 32 >> put a >> put b
+ RunModFinalizers a b c -> putWord8 32 >> put a >> put b >> put c
AddSptEntry a b -> putWord8 33 >> put a >> put b
- RunTH st q loc ty -> putWord8 34 >> put st >> put q >> put loc >> put ty
+ RunTH ii st q loc ty -> putWord8 34 >> put ii >> put st >> put q >> put loc >> put ty
GetClosure a -> putWord8 35 >> put a
Seq a -> putWord8 36 >> put a
RtsRevertCAFs -> putWord8 37
@@ -774,7 +782,7 @@ closePipe Pipe{..} = do
-- (blocks on the calling side, doesn't necessarily block the interpreter)
remoteCall :: Binary a => Pipe -> Message a -> IO a
remoteCall pipe msg = either throwIO (pure . runGet get)
- =<< remoteAsyncReq pipe (putMessage msg)
+ =<< remoteAsyncReq pipe (putMessage msg) Nothing
-- | Handle a single 'Message' request on the interpreter and reply to the host
handleRemoteCall :: Pipe
@@ -782,22 +790,28 @@ handleRemoteCall :: Pipe
-> (forall a. (Show a, Binary a) => Message a -> IO a)
-> IO ()
handleRemoteCall pipe hook k =
- handleAsyncReq pipe getMessage \writer msg0 -> IO \s0 ->
+ handleAsyncReq pipe getMessage Nothing \writer msg0 -> IO \s0 ->
case hook msg0 |#> s0 of -- unIO for linearity checking
(# s1, Msg msg #) -> case k msg |#> s1 of
(# s2, a #) -> writeReply writer (put a) |#> s2
-- | Send a 'THMessage' request to the host and wait for a reply from the host
--- to the interpreter
-remoteTHCall :: Binary a => Pipe -> THMessage a -> IO a
-remoteTHCall pipe msg = either throwIO (pure . runGet get)
- =<< remoteAsyncReq pipe (putTHMessage msg)
+-- to the interpreter.
+--
+-- A 'THMessage' request from the interpreter is always associated with a
+-- 'RunTH'-like run. Since the host may have many in-flight TH runs, we always
+-- want a specific host 'InboxId' (which is announced in the 'RunTH' request)
+remoteTHCall :: Binary a => Pipe -> InboxId -> THMessage a -> IO a
+remoteTHCall pipe ii msg = either throwIO (pure . runGet get)
+ =<< remoteAsyncReq pipe (putTHMessage msg) (Just ii)
-- | Handle a 'THMessage' request on the host and reply to the interpreter.
+-- Symmterically to 'remoteTHCall', we always want to specify the 'InboxId' for
+-- RunTH "reverse requests"
handleRemoteTHCall
- :: Pipe -> (forall a. (Show a, Binary a) => THMessage a -> IO a) -> IO ()
-handleRemoteTHCall pipe k =
- handleAsyncReq pipe getTHMessage \writer (THMsg msg) -> IO \s0 ->
+ :: Pipe -> InboxId -> (forall a. (Show a, Binary a) => THMessage a -> IO a) -> IO ()
+handleRemoteTHCall pipe ii k =
+ handleAsyncReq pipe getTHMessage (Just ii) \writer (THMsg msg) -> IO \s0 ->
case k msg |#> s0 of -- unIO for linearity checking
(# s1, a #) -> writeReply writer (put a) |#> s1
@@ -806,13 +820,13 @@ handleRemoteTHCall pipe k =
--------------------------------------------------------------------------------
-- | Send an asynchronous request and block waiting for a matching reply
-remoteAsyncReq :: Pipe -> Put -> IO (Either SomeException LB.ByteString)
-remoteAsyncReq pipe msg = do
+remoteAsyncReq :: Pipe -> Put -> (Maybe InboxId) -> IO (Either SomeException LB.ByteString)
+remoteAsyncReq pipe msg ii = do
let pendingReplies = pendingReply (pipeAsyncMngr pipe)
-- Mark pending request
- uq <- freshReqId (pipeAsyncMngr pipe)
+ uq <- freshId (pipeAsyncMngr pipe)
wv <- newEmptyTMVarIO
isClosed <- atomically $ do
@@ -827,7 +841,7 @@ remoteAsyncReq pipe msg = do
throwIO PipeClosedException
-- Write request
- writePipe pipe (Request uq (runPut msg))
+ writePipe pipe (Request uq ii (runPut msg))
`onException`
-- the req wasn't done, or no one will be reading the reply:
-- in either case, drop it from the pending map.
@@ -845,18 +859,20 @@ remoteAsyncReq pipe msg = do
-- The handler receives as an argument the function to use to reply to the request.
-- It must be used exactly once (all requests expect a reply, and you can't
-- reply more than once). Linear types enforce the handler replies in every
--- branch, and if the handler otherwise throws, it is handled by 'withAsyncHandler'.
-handleAsyncReq :: Pipe -> Get a -> (WriteReply Put %1 -> a -> IO r) -> IO r
-handleAsyncReq pipe getit k = mask $ \restore -> do
+-- branch, and if the handler otherwise throws, it is handled by 'handleAsyncReq'.
+handleAsyncReq :: Pipe -> Get a -> (Maybe InboxId) -> (WriteReply Put %1 -> a -> IO r) -> IO r
+handleAsyncReq pipe getit mii k = mask $ \restore -> do
+ let am = pipeAsyncMngr pipe
ack <- newEmptyMVar
-- Block waiting for request
mbreq <- atomically $ do
isClosed <- readTVar (pipeIsClosed pipe)
- isEmpty <- isEmptyTQueue (requestsQueue (pipeAsyncMngr pipe))
- if isClosed && isEmpty
+ if isClosed
then return Nothing
- else Just <$> readTQueue (requestsQueue (pipeAsyncMngr pipe))
+ else Just <$> case mii of
+ Nothing -> readTQueue $ requestsQueue am
+ Just ii -> readTQueue =<< getMailbox am ii
case mbreq of
Nothing -> throwIO PipeClosedException
@@ -907,15 +923,40 @@ data AsyncManager = AsyncManager
-- TMVar is signaled when the reply arrives. The exception can originate
-- from the host process, or from the remote one (with 'RemoteProcessException').
, requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
- -- ^ A queue of pending received requests. When a request is received,
- -- queue it here. A request handler will pop the next request from it.
- -- (Nothing except the 'drainPipe' thread should read from the pipe).
+ -- ^ A queue of pending received requests. When a request is received
+ -- /without/ a specific 'InboxId', queue it here. A request handler will
+ -- pop the next request from it. (Nothing except the 'drainPipe' thread
+ -- should read from the pipe). Read with @'handleAsyncReq' p get Nothing@.
+ , mailboxQueues :: !(TVar (IntMap (TQueue (CorrelationId, LB.ByteString))))
+ -- ^ Requests for a specific 'InboxId' that can be read using
+ -- @'handleAsyncReq' p get (Just <mailboxid>)@.
, asyncDrainTid :: !ThreadId
- , asyncNextReq :: !(IORef Int)
+ , asyncFreshId :: !(IORef Int)
}
+-- | Each 'Request' expects a 'Reply' with a matching 'CorrelationId'.
type CorrelationId = Int
+-- | A 'Request' may optionally declare an 'InboxId'. If it does, it can only
+-- be read by a 'handleAsyncReq' for that specific 'InboxId'. That is,
+-- @'handleAsyncReq' p get Nothing@ will not receive messages meant for a
+-- specific inbox.
+--
+-- This is useful to prevent mixing up reverse requests, where we want to send
+-- many reverse requests but have the caller handle them without interleaving
+-- them.
+newtype InboxId = InboxId Int deriving (Show, Binary)
+
+-- | Create a new mailbox from where we can read all requests addressed to this 'InboxId'.
+newMailbox :: AsyncManager -> IO InboxId
+newMailbox am = InboxId <$> freshId am
+ -- `mailboxQueues` is written on demand, no need to do it here.
+
+-- | Clean up a created mailbox by 'InboxId'.
+closeMailbox :: AsyncManager -> InboxId -> IO ()
+closeMailbox AsyncManager{..} (InboxId ii) =
+ atomically (modifyTVar mailboxQueues (IntMap.delete ii))
+
-- | We have a single type for requests and replies because both the host and
-- interpreter may send requests *and* replies on the write end *and* on recv
-- both on the read end.
@@ -925,28 +966,37 @@ type CorrelationId = Int
-- 2. The interpreter sends a NewName TH request on its Interpreter Write End (IWE)
-- 3. The host sends a reply to the TH request on its HWE
-- 4. The interpreter sends the reply to the RunTH request on its IWE
-data IddMsg = Request !CorrelationId LB.ByteString
+--
+-- Additionally, we want to keep concurrent TH requests for independent RunTH
+-- calls separated. Thus the interpreter requests additionally specify a
+-- mailbox announced by RunTH:
+-- 1. To interpreter: @Request _ Nothing (RunTH <mailid>)@
+-- 2. From interpreter: @Request _ (Just <mailid>) NewName@
+-- And the host will relate the <mailid> reverse requests to that RunTH run
+-- exclusively.
+data IddMsg = Request !CorrelationId !(Maybe InboxId) LB.ByteString
| Reply !CorrelationId (Either SerializableException LB.ByteString)
instance Binary IddMsg where
- put (Request i bs) = putWord8 0 >> put i >> put bs
- put (Reply i bs) = putWord8 1 >> put i >> put bs
+ put (Request i ii bs) = putWord8 0 >> put i >> put ii >> put bs
+ put (Reply i bs) = putWord8 1 >> put i >> put bs
get = do
b <- getWord8
case b of
- 0 -> Request <$> get <*> get
+ 0 -> Request <$> get <*> get <*> get
1 -> Reply <$> get <*> get
_ -> fail $ "impossible: Binary IddMsg - " ++ show b
newAsyncManager :: Pipe -> IO AsyncManager
newAsyncManager pipe = do
- asyncNextReq <- newIORef 0
+ asyncFreshId <- newIORef 0
pendingReply <- newTVarIO IntMap.empty
- requestsQueue <- newTQueueIO
+ requestsQueue <- newTQueueIO
+ mailboxQueues <- newTVarIO IntMap.empty
-- Fork thread to drain read end.
-- (cleaned up on 'closePipe')
- asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
+ asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
let am = AsyncManager{..}
pure am
@@ -975,8 +1025,15 @@ drainPipe p unmask = catch (unmask loop) replyToPendings where
putTMVar wv (bimap (toException . RemoteProcessException) id bs)
loop
-- Receive a request and queue it
- Just (Request uq bs) -> do
- atomically $ writeTQueue (requestsQueue am) (uq,bs)
+ Just (Request uq mbi bs) -> do
+ atomically $ case mbi of
+ Nothing ->
+ -- "Global" request
+ writeTQueue (requestsQueue am) (uq,bs)
+ Just ii -> do
+ -- "Specific mailbox" request
+ q <- getMailbox am ii
+ writeTQueue q (uq,bs)
loop
-- EOF on read handle
Nothing -> do
@@ -991,9 +1048,20 @@ drainPipe p unmask = catch (unmask loop) replyToPendings where
am = pipeAsyncMngr p
-freshReqId :: AsyncManager -> IO CorrelationId
-freshReqId AsyncManager{asyncNextReq} =
- atomicModifyIORef' asyncNextReq $ \i -> (i+1, i)
+freshId :: AsyncManager -> IO Int
+freshId AsyncManager{asyncFreshId} =
+ atomicModifyIORef' asyncFreshId $ \i -> (i+1, i)
+
+getMailbox :: AsyncManager -> InboxId -> STM (TQueue (CorrelationId, LB.ByteString))
+getMailbox am (InboxId ii) = do
+ mboxes <- readTVar (mailboxQueues am)
+ case IntMap.lookup ii mboxes of
+ Nothing -> do
+ q <- newTQueue
+ writeTVar (mailboxQueues am) (IntMap.insert ii q mboxes)
+ pure q
+ Just q ->
+ pure q
--------------------------------------------------------------------------------
-- * Pipe thread-safe read/write
=====================================
libraries/ghci/GHCi/Server.hs
=====================================
@@ -74,8 +74,8 @@ servWithCustom verbose hook pipe restore customHandler = newIORef False >>= loop
CustomMessage tag payload -> handleCustom tag payload
Shutdown -> writeIORef doneRef True >> reply ()
-- TODO: These two should probably be done off the main thread
- RunTH st q ty loc -> wrapRunTH $ runTH pipe st q ty loc
- RunModFinalizers st qrefs -> wrapRunTH $ runModFinalizerRefs pipe st qrefs
+ RunTH ii st q ty loc -> wrapRunTH ii $ runTH pipe ii st q ty loc
+ RunModFinalizers ii st qrefs -> wrapRunTH ii $ runModFinalizerRefs pipe ii st qrefs
_other -> run msg >>= reply
isDone <- readIORef doneRef
@@ -98,13 +98,13 @@ servWithCustom verbose hook pipe restore customHandler = newIORef False >>= loop
-- THMessage requests, and then finally send RunTHDone followed by a
-- QResult. For an overview of how TH works with Remote GHCi, see
-- Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.
- wrapRunTH :: forall a. (Binary a, Show a) => IO a -> IO (QResult a)
- wrapRunTH io = do
+ wrapRunTH :: forall a. (Binary a, Show a) => InboxId -> IO a -> IO (QResult a)
+ wrapRunTH ii io = do
when verbose $ trace "wrapRunTH..."
r <- try io
when verbose $ trace "wrapRunTH done."
when verbose $ trace "writing RunTHDone."
- remoteTHCall pipe RunTHDone
+ remoteTHCall pipe ii RunTHDone
case r of
Left e
| Just (GHCiQException _ err) <- fromException e -> do
=====================================
libraries/ghci/GHCi/TH.hs
=====================================
@@ -115,8 +115,8 @@ import qualified GHC.Boot.TH.Monad as TH
import Unsafe.Coerce
-- | Create a new instance of 'QState'
-initQState :: Pipe -> QState
-initQState p = QState M.empty Nothing p
+initQState :: Pipe -> InboxId -> QState
+initQState p ii = QState M.empty Nothing ii p
-- | The monad in which we run TH computations on the server
newtype GHCiQ a = GHCiQ { runGHCiQ :: IORef QState -> IO a }
@@ -156,7 +156,7 @@ noLoc = TH.Loc "<no file>" "<no package>" "<no module>" (0,0) (0,0)
ghcCmd :: Binary a => THMessage (THResult a) -> GHCiQ a
ghcCmd m = GHCiQ $ \sRef -> do
s <- readIORef sRef
- r <- remoteTHCall (qsPipe s) m
+ r <- remoteTHCall (qsPipe s) (qsInboxId s) m
case r of
THException str -> throwIO (GHCiQException s str)
THComplete res -> return res
@@ -189,9 +189,9 @@ metaHandlersGHCiQ runInIO = TH.MetaHandlers {
-- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice
, mRecover = \h a -> runInIO $ GHCiQ $ \sRef -> mask $ \unmask -> do
s <- readIORef sRef
- remoteTHCall (qsPipe s) StartRecover
+ remoteTHCall (qsPipe s) (qsInboxId s) StartRecover
e <- try $ unmask $ runGHCiQ (runQinGHCiQ a <* ghcCmd FailIfErrs) sRef
- remoteTHCall (qsPipe s) (EndRecover (isLeft e))
+ remoteTHCall (qsPipe s) (qsInboxId s) (EndRecover (isLeft e))
case e of
Left GHCiQException{} ->
runGHCiQ (runQinGHCiQ h) sRef
@@ -233,26 +233,28 @@ metaHandlersGHCiQ runInIO = TH.MetaHandlers {
-- a new IORef QState, and return a RemoteRef to it.
startTH :: IO (RemoteRef (IORef QState))
startTH = do
- r <- newIORef (initQState (error "startTH: no pipe"))
+ r <- newIORef (initQState (error "startTH: no pipe") (error "startTH: no inbox id"))
mkRemoteRef r
-- | Runs the mod finalizers.
--
-- The references must be created on the caller process.
-runModFinalizerRefs :: Pipe -> RemoteRef (IORef QState)
+runModFinalizerRefs :: Pipe -> InboxId
+ -> RemoteRef (IORef QState)
-> [RemoteRef (TH.Q ())]
-> IO ()
-runModFinalizerRefs pipe rstate qrefs = do
+runModFinalizerRefs pipe ii rstate qrefs = do
qs <- mapM localRef qrefs
qstateref <- localRef rstate
qstate <- readIORef qstateref
- qstate' <- newIORef $ qstate { qsPipe = pipe }
+ qstate' <- newIORef $ qstate { qsInboxId = ii, qsPipe = pipe }
_ <- runGHCiQ (runQinGHCiQ $ sequence_ qs) qstate'
return ()
-- | The implementation of the 'RunTH' message
runTH
:: Pipe
+ -> InboxId
-> RemoteRef (IORef QState)
-- ^ The TH state, created by 'startTH'
-> HValueRef
@@ -264,13 +266,13 @@ runTH
-> IO ByteString
-- ^ Returns an (encoded) result that depends on the THResultType
-runTH pipe rstate rhv ty mb_loc = do
+runTH pipe ii rstate rhv ty mb_loc = do
hv <- localRef rhv
case ty of
- THExp -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Exp)
- THPat -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Pat)
- THType -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Type)
- THDec -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q [TH.Dec])
+ THExp -> runTHQ pipe ii rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Exp)
+ THPat -> runTHQ pipe ii rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Pat)
+ THType -> runTHQ pipe ii rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Type)
+ THDec -> runTHQ pipe ii rstate mb_loc (unsafeCoerce hv :: TH.Q [TH.Dec])
THAnnWrapper -> do
hv <- unsafeCoerce <$> localRef rhv
case hv :: AnnotationWrapper of
@@ -279,10 +281,10 @@ runTH pipe rstate rhv ty mb_loc = do
-- | Run a Q computation.
runTHQ
- :: Binary a => Pipe -> RemoteRef (IORef QState) -> Maybe TH.Loc -> TH.Q a
+ :: Binary a => Pipe -> InboxId -> RemoteRef (IORef QState) -> Maybe TH.Loc -> TH.Q a
-> IO ByteString
-runTHQ pipe rstate mb_loc ghciq = do
+runTHQ pipe ii rstate mb_loc ghciq = do
qstateref <- localRef rstate
- modifyIORef' qstateref (\qstate -> qstate { qsLocation = mb_loc, qsPipe = pipe })
+ modifyIORef' qstateref (\qstate -> qstate { qsLocation = mb_loc, qsInboxId = ii, qsPipe = pipe })
r <- runGHCiQ (runQinGHCiQ ghciq) qstateref
return $! LB.toStrict (runPut (put r))
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e1e7a5284ab322a347ce7b58c52624…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e1e7a5284ab322a347ce7b58c52624…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc] Pushed new branch wip/int-index/curried-ctxts
by Vladislav Zavialov (@int-index) 14 Oct '26
by Vladislav Zavialov (@int-index) 14 Oct '26
14 Oct '26
Vladislav Zavialov pushed new branch wip/int-index/curried-ctxts at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/int-index/curried-ctxts
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/andreask/cfg-fix] 44 commits: Add regression test for #7803
by Sylvain Henry (@hsyl20) 14 Oct '26
by Sylvain Henry (@hsyl20) 14 Oct '26
14 Oct '26
Sylvain Henry pushed to branch wip/andreask/cfg-fix at Glasgow Haskell Compiler / GHC
Commits:
234bab08 by Vladislav Zavialov at 2026-09-29T17:22:54-04:00
Add regression test for #7803
Commit 2effe18ab5, first shipped with GHC 8.2.1, fixed #7803 "Superclass
methods are left unspecialized".
This change turns the example from the ticket description into a regression
test, checking that foo1 is fully specialised at Double, with no calls to the
overloaded exp5Tail.
Closes #7803
Assisted-by: Claude Opus 5
- - - - -
c406135c by Andreas Klebinger at 2026-10-01T06:57:28-04:00
RTS: Update flag parsing/handling:
Using 0 as argument to any of these flags now resets the flags setting to default:
- ``-n0`` disables nursery chunking.
- ``-K0`` (or a bare ``-K``) removes the stack size limit.
- ``-M0`` removes the heap size limit.
- ``-H0`` resets the suggested heap size, including the nursery scaling set by ``-H``.
- ``-AL0`` makes the large object allocation limit follow ``-A`` again.
GC Generations are now limited to 64.
-n larger than -A now emits a warning and disables chunking.
Fixes #27830 #27829
- - - - -
984ee82b by Andreas Klebinger at 2026-10-01T06:58:10-04:00
Fix MultiLayerModules timeout multiplier.
We accidentally used the compile multiplier for ghci commands which
use the run timeout setting to set the timeout.
Fixes #26263 and #27796.
- - - - -
50a9cf26 by Andreas Klebinger at 2026-10-01T06:58:10-04:00
testsuite: Include runtime in JUnit report.
- - - - -
e358619f by Alan Zimmerman at 2026-10-01T06:59:01-04:00
EPA: Use EpaLocation in EpVirtualBraces
Haddock processing uses EpLayout.EpVirtualBraces to record the current
layout column, for allocating haddock comments correctly.
For exact printing, we need the line as well as the col, so we know
how many lines to skip when the indented code block starts.
This commit updates EpLayout.EpVirtualBraces so that it carries an
EpaLocation instead of just the Int column, to allow it to be used for
both purposes.
It is a simple mechanical change, with no other impact, but it allows
us to use EpLayout instead of AnnListLayout in AnnList, in the next
commit in this series.
- - - - -
1d2a26ee by Andrew Lelechenko at 2026-10-01T20:22:04-04:00
Remove bitSize from MINIMAL pragma of class Bits
Cf. https://github.com/haskell/core-libraries-committee/issues/441
The function is deprecated for 12 years and has a default implementation,
yet it was never removed from the MINIMAL pragma, putting library authors
in a very awkward position. Let's finally remove bitSize from MINIMAL.
- - - - -
3cc21e4c by Hannes Siebenhandl at 2026-10-01T20:22:59-04:00
Add regression test for #27641
- - - - -
c35096f5 by Hannes Siebenhandl at 2026-10-01T20:23:00-04:00
Don't `:add` modules the loader will reject
Only home modules can be actually added to the loader context, thus we
only accept the module if it belongs to a home unit.
- - - - -
a563cb27 by Simon Jakobi at 2026-10-02T17:37:36-04:00
ci: accept modified changelog.d entries in lint-changelog
The changelog guide says to extend an existing entry rather than add a
new one when the feature is already documented. lint_changelog only
counted added files, so such MRs failed before the validator ran.
Fixes #27779.
Assisted-by: Claude Fable 5.1
- - - - -
03082cd4 by Simon Jakobi at 2026-10-02T17:38:17-04:00
changelog.d: Fix warning flag name in the semaphore-v2 entry
The entry refers to -Wsemaphore-version-mismatch, which does not exist.
The warning GHC emits when it cannot use a -jsem semaphore, including
on a protocol version mismatch, is -Wsemaphore-open-failure.
Context: !15729, #25087
Assisted-by: Claude Fable 5.1
- - - - -
a93705ee by Simon Jakobi at 2026-10-02T17:38:17-04:00
changelog.d: Fix the CLC proposal link in the T27455 entry
The entry used the Sphinx :ref: role with a URL as its target. :ref:
resolves only internal labels, so the generated release notes would
show the proposal name without a link. Use the plain hyperlink form
like the other entries.
Context: !16274, #27455
Assisted-by: Claude Fable 5.1
- - - - -
54381079 by Hannes Siebenhandl at 2026-10-02T17:39:01-04:00
Extend the `TrustOverlay` for wired-in units
The TrustOverlay only contained the `UnitId` of all on-disk units.
However, wired-in units are referred to by their wired-in unit id, which
is not the same as the on-disk unit id.
Thus, trust checks for wired-in units failed because the lookup was
using the wired-in unit, which wasn't present in the `TrustOverlay`.
We fix #27834, by copying the trust info for wired-in unit ids.
- - - - -
f8cfb1a5 by Hécate Kleidukos at 2026-10-04T19:36:39+02:00
[CLC 341] Strict variants of mutable types
This commit is the implementation of CLC Proposal 341
https://github.com/haskell/core-libraries-committee/issues/341
- - - - -
575c4bc6 by Rodrigo Mesquita at 2026-10-05T12:32:02-04:00
ttg: HsModuleName in the AST, ModuleName in GHC
This commit makes the AST fully independent from GHC by parsing a
"module name" as an `HsModuleName (= HText)` rather than constructing an
`ModuleName (= FastString)` straight away.
In the GHC side, everything remains as a `ModuleName`. We turn AST
`HsModuleName`s into fast strings with the cheap `mkFastStringShortText`
when we process the AST for manipulation within the compiler.
There are no performance regressions introduced by this approach.
This is the final piece of #21592
Alternative plans were discussed in
gitlab.haskell.org/ghc/ghc/-/merge_requests/16546#note_694647,
but the Simon suggested one won in simplicity and performance when
compared against my attempt at implementing my original plan of having
a family `ModuleNameP`.
- - - - -
b3b89d32 by mangoiv at 2026-10-05T22:03:32-04:00
hadrian: Persist the used flavour name in the build root (#20650)
This executes the idea proposed in #20650, teaching Hadrian to persist
the flavour used to build a build root. This both makes use of multiple
build roots more ergonomic (as the user no longer needs to repeat
themselves) and avoids a tiresome foot-gun (forgetting to specify a
flavour, often wholly invalidating a build root).
Closes #20650.
Co-authored-by: Sylvain Henry <sylvain(a)haskus.fr>
- - - - -
f977837b by mangoiv at 2026-10-05T22:04:21-04:00
configure: fix sed script and subst tooldir script
Fixes #27672
Adds sanity checking for SUBST_TOOLDIR
Metric Decrease:
LinkableUsage01
MultiLayerModulesTH_OneShot
T10547
T12234
T12425
T13035
T13820
T18140
T18730
T18923
T9198
TcPlugin_RewritePerf
mhu-perf
- - - - -
f1cb08be by Hannes Siebenhandl at 2026-10-05T22:05:03-04:00
Prepend in-memory unit database for backpack
The packageDbFlags are stored in reverse order. Thus, prepending will
ensure that the in-memory unit database is the last item on the unit
database stack.
Further, the in-memory unit database needs to be able to shadow units
from earlier unit databases in the unit database stack.
Moreover, a `-clear-package-db` would remove the in-memory database if
it weren't the last item on the stack.
Closes #27835
- - - - -
8d49ed42 by Kazu Yamamoto at 2026-10-06T19:37:25-04:00
Do not leave standard file descriptors free at startup (#24450)
A process may be started with stdout closed -- `prog >&-` from a shell does
exactly that. Nothing then reserves descriptor 1, and open(), pipe() and
socket() hand out the lowest free descriptor, so the next descriptor the
runtime creates for itself takes it: the ticker's pipe, or a capability's IO
manager control pipe.
'GHC.Internal.IO.StdHandles.stdout' is a CAF, built the first time the program
writes to stdout, which is long after that has happened. It builds a 'Handle'
over descriptor 1, which now belongs to the runtime, and nothing reports an
error because the descriptor is perfectly good. The program then writes its
output into the runtime's own plumbing.
What that looks like from the outside is a hang. The descriptor the Handle has
taken over is the read end of a pipe, so poll() for POLLOUT on it never says
yes and epoll_ctl() accepts a registration for it; the thread waiting to write
blocks for ever. Closing the Handle at exit closes the runtime's descriptor,
which is worse: the ticker then fails on every tick.
'hs_init_ghc' now opens /dev/null onto whichever of descriptors 0, 1 and 2 are
closed, before the runtime creates any descriptor of its own. Each is opened
for the access that descriptor would normally have, read-only for 0 and
write-only for 1 and 2, so that writing to 'stdin' or reading from 'stdout'
still fails rather than quietly succeeding against /dev/null. See
Note [Standard file descriptors must be open].
Moving only the descriptors the runtime opens would not do: the first file the
program itself opened would land on the free number instead. The fix has to
leave no free number. A program run this way therefore finds stdout connected
to /dev/null rather than failing with EBADF on the first write, which is what
such a program would get from most other language runtimes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply(a)anthropic.com>
- - - - -
65bcf9e9 by Simon Jakobi at 2026-10-06T19:38:09-04:00
changelog: Repair the sentence describing #27477
The description read "programs using could crash", the object of
"using" having been lost. The synopsis already says which programs are
affected, so drop the dangling word.
Assisted-by: Claude Fable 5.1
- - - - -
15ece296 by mangoiv at 2026-10-08T15:14:56-04:00
ci: move hadrian/ghci into the lint category so it can be picked up by more runners
Fixes #27817
- - - - -
c0823135 by mangoiv at 2026-10-08T15:14:56-04:00
ci: make lint-adjacent jobs into platform independent
- move lint jobs into lint-anyarch tag
- unify lint jobs under the same platform independent container
- - - - -
f76464cf by Zubin Duggal at 2026-10-08T15:15:39-04:00
DmdAnal: Make AbsDmd neutral in maxDmd
fixes #27845
- - - - -
ffa56ae4 by sheaf at 2026-10-09T17:15:59+02:00
Preserve sign of zero when parsing -0.0#
This commit leverages the recently added GHC.Types.Literal.Floating
module in order to preserve the sign of negative zero from
source-written floating-point literals.
Fixes #27545
- - - - -
d538cac9 by Facundo Domínguez at 2026-10-09T20:26:38-04:00
Propagate annotations to simple interfaces
This allows annotations to be visible to plugins when run by
haddock. The LiquidHaskell plugin needs the annotations, in particular.
Fixes #26761
- - - - -
32c4f4ab by meooow25 at 2026-10-09T20:27:28-04:00
Bump containers submodule to 0.8.1
And fix affected tests.
* T4086a: containers depends on template-haskell-lift now instead of
template-haskell.
* T22611: Updated to be less dependent on containers internals.
- - - - -
c786065e by sheaf at 2026-10-09T20:28:20-04:00
Use an explicit pattern match in toIfaceIdDetails
The old wildcard pattern let this code bit rot: it did not properly account
for recently added 'IdDetails' such as 'RepPolyId', which resulted in
spurious 'pprTrace' messages that could even pollute HLS sessions.
Fixes #23785
- - - - -
e7cacf59 by sheaf at 2026-10-09T20:29:09-04:00
Take into account ifKind in freeNamesIfDecl
Commit 3c2f4bb4 added the 'ifKind' field to various constructors
of 'IfaceDecl', which preserves the original kind (before type synonym
expansion) for better error messages.
However, that commit forgot to update 'freeNamesIfDecl' correspondingly.
This could result in a "urk! lookup local fingerpring" crash when e.g.
a type's kind uses a type synonym defined later, as we failed to add a
dependency on the type synonym during dependency analysis.
Fixes #27916
- - - - -
41e80f41 by Simon Peyton Jones at 2026-10-11T14:38:51-04:00
Simplify the error-suppression mechanism in GHC.Tc.Errors
In `GHC.Tc.Errors.reportWanteds` we suppress some errors in favour of
others. But the mechanism had grown crufty, and #27331 exposed a bug.
This MR makes it simpler and more uniform, by doing everything via
the `ei_suppress` field of the ErrorItem.
Claude then found another closely-related bug (see the review linked
on !16564). That bug is immortalised in new test T18851d.
I'm still not happy with `ignoreConstraint` but we can worry about
that another day; I have not touched it.
Main changes
* Instead of /predicting/ when an error will be reported, we set
`cec_error_reported` when an error is /actually/ reported. (I renamed
it from `cec_suppress` to reflect this change.)
Now errors are suppressed only when a preceding error has actually been
reported. Simpler and more robust.
See `Note [cec_error_reported: suppressing less-serious errors]`
The big changes are in `reportWanteds` which looks very different.
* New `Note [ei_suppress: suppressing confusing errors]` describes the
(quite separate) `ei_suppress` mechanism for suppressing confusing
errors, mainly arising from non-empty rewriter sets
* In `tryTypeClassDefaulting`, instead of switching off type-class defaulting
if there is an insoluble /anywhere/, we just switch it off locally when
we find an insoluble sub-tree. See Note [When to do type-class defaulting]
and (W5) in Note [ApproximateWC]
* Greatly simplify `insolubleWantedCt`.
* I made type-class-defaulting be a bit more eager, only switched off when
a peer or ancestor (but not child) constraint is insoluble. See
Note [When to do type-class defaulting].
Small refactorings
* Rename `askNoErrs` to `askErrsFound` and flip its polarity
* The code for "check if ei_suppress is true of all items" code is
moved to `mkErrorItems` rather than in the large `reportWanteds`.
* Code for `has_gadt_match` moves to `mkGivenErrorReporter`, rather than
polluting the large `reportWanteds`.
* Many tests with `-fdefer-type-errors` get more warnings, especially
about ambiguous (Num a) constraints. This is by-design: see
Note [Always warn with -fdefer-type-errors]. Previously GHC was not
honouring this Note but now it is, hence the extra errors.
- - - - -
f217fc31 by Cheng Shao at 2026-10-11T14:39:33-04:00
compiler: ensure cross ghc --info output target unit ids
Previously, for cross ghcs, the base/ghc/ghc-internal unit ids in
--info output do not match the target unit ids reported by ghc-pkg,
breaking cross ghc release builds with hash-unit-ids when being used
with cabal 3.18. This patch fixes it by making --info output target
unit ids. Fixes #27919.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
d07a5176 by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Remove the Eq and Ord instances for ForAllTyFlag
In Note [Comparing visibility] in GHC.Core.TyCo.Compare we read that
"we always compare ForAllTyFlags with `eqForAllVis` or `cmpForAllVis`"
But alas there was an Eq and Ord instance for ForAllTyFlag, which made it
impossible to check this claim. So I made these changes:
* Move the definition of ForAllTyFlag and its related functions from
L.H.S.Specificity (where it did not belong, since L.H.S didn't use it)
into GHC.Types.Var.
* Remove the Eq and Ord instances for ForAllTyFlag
* Deal with the fallout, by using `eqForAllVis` or related functions.
* In interface files we use IfaceType as a key for a de-deduplication map
See Note [Deduplication during iface binary serialisation]
in GHC.Iface.Binary. For this we need /exact/ comparison, so we use a
newtype wrapper for IfaceForAllTyFlag, and give use `cmpForAllTyFlagExactly`
to implement its Ord instance. Tiresome but easy.
* Rename `toIfaceForAllBndrs` to `toIfaceForAllTyBndrs`
- - - - -
ea5c054d by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Refactor type and coercion substitution
This MR refactors type and coercion substitution so that they use
TyCoMapper, rather than hand-rolled code. Fundamentally it's a simple
idea, but I had to do quite a bit of refactoring to make it fly.
I got Claude to help me this this refactoring, but I paid careful
attention to every line of code.
Main changes:
* New module GHC.Core.TyCo.Make which defines TyCoMapper, and a bunch of other
simple "smart constructors" for types and coercions. See
GHC.Core.TyCo.Rep Note [The Type-related module hierarchy], esp (TRM1)
* The new TyCoMapper has new fields `tcm_tcapp_ty` and `tcm_tcapp_co` to handle
type-constructor applications. These replace `tcm_tycon`.
* Rewrite GHC.Core.TyCo.Subst to use TyCoMapper
* Rewrite GHC.Core.Type.expandTypeSynonyms to use TyCoMapper.
* Implement `seqType` and `seqCo` with TyCoFolder. To to this I had to add
a `tcf_lit` field to TyCoFolder.
* A new module GHC.Utils.StrictIdentity to define a strict identity monad,
which itself is used for
substTyCoMapper :: TyCoMapper Subst StrictIdentity
* I removed an apparently-unused instance for Binary Levity
Refactoring `seq` functions
* In GHC.Core.Seq, redefine `seqOneShot`, `seqRuleInfo`, and friends to return
`SeqResult`, a new type defined in GHC.Types.Baseic, rather than `()`. We
can define a Monoid instance for `SeqResult` that does the right thing.
* This has knock-on effects across the compiler.
- - - - -
34165e23 by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Refactor TyCoMapper to take a single monad; add StrictReaderT
TyCoMapper used to be parameterised over both an environment and a
monad, with fields like
tcm_tyvar :: env -> TyVar -> m Type
Now it is parameterised over just the monad:
tcm_tyvar :: TyVar -> m Type
If the mapping needs an environment, `m` is a reader monad over that
environment. Many uses (e.g. zonkTcTypeMapper, swizzleMapper) need no
environment at all, and used to pass a dummy (). The binder field
becomes
tcm_tycobinder :: forall r. TyCoVar -> ForAllTyFlag
-> (TyCoVar -> m r) -> m r
and runs the continuation in the extended environment. mapTyCoX is gone;
only mapTyCo remains.
To make this efficient, the reader monad must use the one-shot trick and
be strict in the environment. So:
* New module GHC.Utils.Monad.StrictReader defines
newtype StrictReaderT r m a
with hand-written instances. The builder of the pattern synonym
`StrictReaderT` is a smart constructor that adds both `oneShot` and a
bang on the environment, so every StrictReaderT computation is strict
in the environment. See Note [Instances for StrictReaderT].
* ZonkT becomes type ZonkT = StrictReaderT ZonkEnv
ZonkM becomes type ZonkM = StrictReaderT ZonkGblEnv IO
replacing their own hand-written instances.
* Substitution uses
type SubstM = StrictReaderT Subst StrictIdentity
substTyCoMapper :: TyCoMapper SubstM
and similarly expandTypeSynonymMapper.
* zonk_tycomapper :: TyCoMapper (ZonkT TcM) is now much simpler.
* swizzleMapper uses StrictIdentity rather than Identity.
* Module renaming, so that the strict monads are all peers in GHC.Utils.Monad:
GHC.Utils.StrictIdentity ==> GHC.Utils.Monad.StrictIdentity
GHC.Utils.Monad.State.Strict ==> GHC.Utils.Monad.StrictState
alongside the new GHC.Utils.Monad.StrictReader.
Co-Authored-By: Claude Opus 5.5 <noreply(a)anthropic.com>
- - - - -
c0299e15 by Simon Jakobi at 2026-10-13T18:11:23-04:00
testsuite: Line-buffer the driver's stdout
Python block-buffers stdout when it is a pipe. This is the case for
example in CI. As a result there is often no indication of progress at
all until the run ends.
To fix this, we configure it explicitly to use line buffering instead.
stderr is reconfigured too, for consistency: Python line-buffers it by
default since 3.9.
The type-ignores are for mypy, which types these handles as TextIO — an
interface without reconfigure.
Fixes #12934.
Assisted-by: Claude Fable 5
- - - - -
9b5beaa2 by Florian Ragwitz at 2026-10-13T18:12:05-04:00
hpc: Improve coverage semantics for instance methods and instances
Users care about which of their explicitly written methods are or aren't
covered, not whether all of the inherited methods are covered as well. Those
should be covered where they are implemented.
We therefore skip generating top-level boxes for inherited methods which will
appear as uncovered with no clear indication as to why, and no way to fix other
than exercising code the user has not written.
Previously, top-level boxes for those inherited methods would use the instance
head as their source span. Now that those boxes are gone, we can use that
source span for another purpose:
We introduce a new top-level box for each class instance with its instance head
as the source span, and use that to indicate whether or not an instance has
been covered at all.
We do this by making any method of the instance, inherited/generated or
otherwise, tick the box for the instance head. Excluded from this are
methodless classes, which could never be covered using the current mechanism.
This could be improved upon further.
These changes apply to ticking for program coverage only, and don't affect
source notes. Additionally, we update the scope of record selector ticking to
be limited to program coverage as well, as the oversight to also extend it to
source notes has previously caused bugs and because record selector ticking is
coverage-specific.
Fixes #17155, #15932.
- - - - -
836ec50a by Simon Peyton Jones at 2026-10-13T18:12:54-04:00
Refactor fundeps again
This MR fixes #27828; there was a bad scoping error in `trim_qtvs`.
The main changes are
* Refactor `trim_qtvs` completely to eliminate the bug.
* Remove a redundant `apartnessCheck` from `mkTopOpenFamEqFDs`.
* Lots of improvements to the Notes in FunDeps.hs
I also did a couple of pure refactors:
* I realised that we were carefully caching `cab_incomps` in each
`CoAxBranch`, to make it more efficient to reduce a type family application,
but we were not doing the same thing when computing relevance for functional
dependencies; see Note [Relevance] in GHC.Tc.Solver.FunDeps. So I added
`cab_overlaps`, which is now used in `noEarlierMatch`.
* Use a proper data types, `AmIUnifying` and `OuterKindsFlag`, for two
previously-Boolean arguments to `tc_unify_tys`. This is a pure refactor.
- - - - -
889ef371 by Simon Peyton Jones at 2026-10-13T18:13:36-04:00
Refactor and simplify solver's unification code
Ticket #27844 showed that GHC's constraint solver could miss an
occurs check, and thereby build an infinite type.
This patch significantly simplifes the code in
GHC.Tc.Solver.Equality.canEqCanLHSFinish_try_unification
GHC.Tc.Solver.Equality.canEqCanLHSFinish_no_unification
It is now significantly simpler, and requires much less subtle
reasoning to understand.
Main changes
* If `canEqCanLHSFinish_try_unification` fails to unify, it now /always/ calls
`canEqCanLHSFinish_no_unification` rather than trying to finish the job itself.
* `GHC.Tc.Solver.Monad.checkTypeEq` is moved from Monad into Equality, and renamed
to `GHC.Tc.Solver.Equality.checkTypeEqNoUnification`.
* `canEqCanLHSFinish_no_unification` calls `checkTypeEqNoUnification` to test
the invariants for an inert equality. Before this MR there was a special case for
`do_not_prevent_rewriting`, but that has gone; instead it's just a consequence of
the flags passed to `checkTyEqRhs`, documented in the `PuOK` case of
`canEqCanLHSFinish_no_unification`.
* There is only one bit of "swap back" code, in `finish_no_unify` in
`canEqCanLHSFinish_try_unification. Previously this code was duplicated.
* `kickOutAfterFillingCoercionHole` should account for equalities with a
TyFamLHS. See (KOC5) in Note [Kick out after filling a coercion hole]
* No need to gather coercion holes in the `PuOK` case of `canEqCanLHSFinish_no_unification`.
See documentation with that code.
- - - - -
f9193e53 by Simon Peyton Jones at 2026-10-13T18:13:37-04:00
One line fix to fallout from
...#27731, !16564, simplifying Tc.Errors machinery
There was an over-eager assert!
- - - - -
cdd56b7a by Florian Ragwitz at 2026-10-13T18:14:19-04:00
Fix several sphinx errors and warnings from rendering the release notes
The extension references which are removed here are for extension which were
already previously undocumented, so just removing them seems fine for now.
- - - - -
4f00da25 by Florian Ragwitz at 2026-10-13T18:14:19-04:00
Link to QualifiedStrings documentation
- - - - -
97adaf89 by Simon Peyton Jones at 2026-10-14T00:07:27+01:00
Line up simpleUnifyCheck and check_ty_eq_rhs
Even after #26543 was allegedly fixed, the original repo case in the
Description continued to fail. The reason was that the QuickLook
unifier, `qlUnify`, used `simpleUnifyCheck` for checking unification
invariants; and `simpleUnifyCheck` conservatively rejected any RHS
with a coercion hole in it. The upshot was that QuickLook was not
as clever as it should be, wrongly failing to do an impredicative
instantiation.
Interestingly `check_ty_eq_rhs`, which does the same job during
constraint solving, was more liberal: it just looked at the free vars
of the coercion, and allowed coercion holes.
This MR lines them up, adding some careful notes. See
Note [simpleUnifyCheck]
Note [Occurs check and coercions]
Note [Unification preconditions] esp (COERCIONS)
Things are better than before, but I am still uncomfortable about the
possibilty that a hole might be filled with a coercion that mentions
the LHS type variable; for now I have left this discomfort documented
in Note [Occurs check and coercions].
Main changes
* `qlUnify` now only unifies /instantiation variables/ not regular unification
variables. This makes QuickLook more predictable. See (UQL6) in
Note [QuickLook unification].
* `qlUnify` no longer needs to call `simpleUnifyCheck`, so we can get rid of
`UC_QuickLook` and its code in `simpleUnifyCheck`.
* I found I could get rid of `mkOccFolders` in favour of `anyFreeVarsOfType`.
Hooray.
* Improve documentation of CycleBreakerTv, and rename it to GivenCycleBreakerTv
because they are only uesd for Givens!
* I accepted a 9% worsening in compile time allocation on T5030
because the newly-consistent story triggered #27934.
Other changes
* I refactored `atvFolder` so that it pairs the "interesting-var" function
with the in-scope set, to avoid building a closure with a free variable.
Now we get nice tight loops for `anyFreeVarsOfType` and friends.
See Note [Any-free-var folder].
Metric Increase:
T5030
- - - - -
5f7b3047 by Simon Peyton Jones at 2026-10-14T00:07:27+01:00
Fix order-dependence in QuickLook
Ticket #27917 pointed out that QuickLook was order-dependent
-- very bad!
The solution is in `qlUnify`: spot when we are unifying a type with a
definite monotype, and then monomorphise any free instantiation
variables of that type.
See (UQL8) and (UQL9) in Note [QuickLook unification].
- - - - -
c391c70c by Simon Peyton Jones at 2026-10-14T00:07:28+01:00
Add some comments about RuntimeUnk
This commit just adds comments explaining why we don't need to worry about
RuntimeUnkTv in the constraint solver.
It also tightens up docmentation of earlier (overlapping) commits in
this MR.
- - - - -
7df3ba2f by Andrea Vezzosi at 2026-10-14T06:15:48-04:00
add runGhcWithSignalHandlers and remove handling from runGhc[T]
it only ever makes sense to call `withSignalHandlers` from `main`, otherwise
signals can be too easily swallowed without notification to the rest of your
process.
This commit takes withSignalHandlers out of `runGhc/runGhcT` and provides
`runGhcWithSignalHandlers` for the old behaviour.
- - - - -
c56aa417 by Andreas Klebinger at 2026-10-14T17:44:30+02:00
x86 ncg: Track the current block in NatM to keep the CFG in sync
The x86 NCG maintains a CFG that is used for code layout. Whenever
instruction selection introduces new basic blocks (for instance for
word-to-float conversions, cmpxchg based atomic operations, ctz on
i386 or 64-bit shifts on i386) it must update this CFG, and the code
that follows must use the new block as its origin.
Before this patch the current block was threaded explicitly through
some statement-level functions only. Block-introducing primops that
were dispatched as "simple" (word2Float#, #27915) and the 64-bit shifts
in iselExpr64 did not update the CFG at all. This resulted in an
out-of-sync CFG, bad code layout and the "Can't find weight for edge
that should have one" panic once the register allocator inserted
fixup blocks (#27694, #27915).
We now keep the current block in the NatM state and provide a few
combinators (continueInNewBlock, addCondBlock, addDiamondFlow,
addColdSelfLoop, increaseEdgeWeight) that update the CFG and the
current block at the same time. See Note [Updating the CFG during
CodeGen] in GHC.CmmToAsm.Monad.
The cfg sanity check (-dasm-lint) now also checks that all branches in
the generated code have a matching edge in the CFG, which catches this
class of bug early.
Fixes #27694, #27915
Co-authored-by: Sylvain Henry <sylvain(a)haskus.fr>
Co-Authored-By: Claude Fable 5.1 <noreply(a)anthropic.com>
- - - - -
393 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- .gitlab/hello.hs
- + changelog.d/24450
- changelog.d/26616
- + changelog.d/26761
- + changelog.d/27641
- + changelog.d/27694
- + changelog.d/27845
- + changelog.d/T23785
- + changelog.d/T26543
- changelog.d/T27329
- changelog.d/T27455
- + changelog.d/T27545
- + changelog.d/T27731
- + changelog.d/T27828
- + changelog.d/T27830-rts-flag-bounds
- + changelog.d/T27844
- + changelog.d/T27916
- + changelog.d/T4162
- changelog.d/boxing-family
- + changelog.d/clc-341-strict-mutable-data-types
- + changelog.d/fix-cross-ghc-info-unit-id
- changelog.d/fix-parallel-gc-selector-race-27477
- + changelog.d/hadrian-persist-flavour
- + changelog.d/hpc-classinsts
- changelog.d/semaphore-v2
- changelog.d/tcplugin_init.md
- changelog.d/unit-index
- compiler/GHC.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/Builtin/WiredIn/Ids.hs
- compiler/GHC/Builtin/WiredIn/Prim.hs
- compiler/GHC/Builtin/WiredIn/Types/Box.hs
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Cmm/GenericOpt.hs
- compiler/GHC/Cmm/UniqueRenamer.hs
- compiler/GHC/CmmToAsm.hs
- compiler/GHC/CmmToAsm/BlockLayout.hs
- compiler/GHC/CmmToAsm/CFG.hs
- compiler/GHC/CmmToAsm/Dwarf/Types.hs
- compiler/GHC/CmmToAsm/Monad.hs
- compiler/GHC/CmmToAsm/Reg/Graph/Spill.hs
- compiler/GHC/CmmToAsm/Reg/Graph/SpillClean.hs
- compiler/GHC/CmmToAsm/Reg/Graph/SpillCost.hs
- compiler/GHC/CmmToAsm/Reg/Graph/Stats.hs
- compiler/GHC/CmmToAsm/Reg/Linear/State.hs
- compiler/GHC/CmmToAsm/Reg/Linear/Stats.hs
- compiler/GHC/CmmToAsm/Reg/Liveness.hs
- compiler/GHC/CmmToAsm/Wasm/Asm.hs
- compiler/GHC/CmmToAsm/Wasm/Types.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToC.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Core/Coercion.hs-boot
- compiler/GHC/Core/Coercion/Axiom.hs
- compiler/GHC/Core/Coercion/Opt.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/FVs.hs
- compiler/GHC/Core/FamInstEnv.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Make.hs
- compiler/GHC/Core/Map/Type.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/Opt/CallerCC.hs
- compiler/GHC/Core/Opt/CallerCC/Types.hs
- compiler/GHC/Core/Opt/Exitify.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Reduction.hs
- compiler/GHC/Core/Seq.hs
- compiler/GHC/Core/TyCo/Compare.hs
- compiler/GHC/Core/TyCo/FVs.hs
- + compiler/GHC/Core/TyCo/Make.hs
- + compiler/GHC/Core/TyCo/Make.hs-boot
- compiler/GHC/Core/TyCo/Ppr.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Core/TyCo/Rep.hs-boot
- compiler/GHC/Core/TyCo/Subst.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Type.hs-boot
- compiler/GHC/Core/Unify.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToIface.hs-boot
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Pipeline.hs-boot
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/Phases.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Lit.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Match/Literal.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Errors/Types.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Debug.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Iface/Ext/Utils.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Iface/Type.hs
- compiler/GHC/Iface/Type.hs-boot
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/Lexer.x
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Parser/String.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Unbound.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Settings.hs
- compiler/GHC/Settings/IO.hs
- compiler/GHC/Stg/BcPrep.hs
- compiler/GHC/Stg/Debug.hs
- compiler/GHC/Stg/EnforceEpt/Rewrite.hs
- compiler/GHC/Stg/EnforceEpt/TagSig.hs
- compiler/GHC/Stg/Subst.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/Tc/Deriv/Functor.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Tc/Instance/Family.hs
- compiler/GHC/Tc/Instance/FunDeps.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Solver/Default.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Types/ErrCtxt.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Tc/Utils/TcMType.hs
- compiler/GHC/Tc/Utils/TcType.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Tc/Zonk/Env.hs
- compiler/GHC/Tc/Zonk/Monad.hs
- compiler/GHC/Tc/Zonk/TcType.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Arity.hs
- compiler/GHC/Types/Basic.hs
- compiler/GHC/Types/Cpr.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Name/Env.hs
- compiler/GHC/Types/Name/Occurrence.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/Unique/DSM.hs
- compiler/GHC/Types/Unique/FM.hs
- compiler/GHC/Types/Unique/Map.hs
- compiler/GHC/Types/Unique/Set.hs
- compiler/GHC/Types/Unique/Supply.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Types/Var.hs-boot
- compiler/GHC/Types/Var/FV.hs
- compiler/GHC/Types/Var/Set.hs
- compiler/GHC/Unit/External/Wired.hs
- compiler/GHC/Unit/Home.hs
- compiler/GHC/Unit/Module.hs
- compiler/GHC/Unit/Module/Env.hs
- + compiler/GHC/Unit/Module/Name.hs
- compiler/GHC/Unit/Parser.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Unit/Types.hs-boot
- compiler/GHC/Utils/Binary.hs
- + compiler/GHC/Utils/Monad/StrictIdentity.hs
- + compiler/GHC/Utils/Monad/StrictReader.hs
- compiler/GHC/Utils/Monad/State/Strict.hs → compiler/GHC/Utils/Monad/StrictState.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/GHC/Utils/Panic.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Lit.hs
- compiler/Language/Haskell/Syntax/Module/Name.hs
- compiler/Language/Haskell/Syntax/Specificity.hs
- compiler/Language/Haskell/Syntax/Text.hs
- compiler/ghc.cabal.in
- distrib/configure.ac.in
- docs/users_guide/exts/literals.rst
- docs/users_guide/runtime_control.rst
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Info.hs
- ghc/Main.hs
- hadrian/bindist/Makefile
- hadrian/bindist/config.mk.in
- hadrian/doc/flavours.md
- hadrian/src/Rules.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Settings.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- libraries/base/src/Control/Concurrent/Chan.hs
- + libraries/base/src/Control/Concurrent/Chan/Strict.hs
- libraries/base/src/Control/Concurrent/MVar.hs
- + libraries/base/src/Control/Concurrent/MVar/Strict.hs
- libraries/base/src/Data/IORef.hs
- + libraries/base/src/Data/IORef/Strict.hs
- libraries/containers
- libraries/ghc-internal/src/GHC/Internal/Bits.hs
- linters/lint-codes/LintCodes/Static.hs
- m4/subst_tooldir.m4
- rts/RtsFlags.c
- rts/RtsStartup.c
- rts/include/rts/Flags.h
- rts/include/rts/storage/GC.h
- rts/sm/GC.c
- rts/sm/Storage.c
- testsuite/driver/junit.py
- testsuite/driver/runtests.py
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- + testsuite/tests/backpack/should_compile/T27835.bkp
- + testsuite/tests/backpack/should_compile/T27835.stderr
- testsuite/tests/backpack/should_compile/all.T
- + testsuite/tests/codeGen/should_run/T27915.hs
- + testsuite/tests/codeGen/should_run/T27915.stdout-ws-32
- + testsuite/tests/codeGen/should_run/T27915.stdout-ws-64
- testsuite/tests/codeGen/should_run/all.T
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/default/default-fail05.stderr
- + testsuite/tests/dmdanal/should_run/T27845.hs
- + testsuite/tests/dmdanal/should_run/T27845.stdout
- + testsuite/tests/dmdanal/should_run/T27845_Lib.hs
- + testsuite/tests/dmdanal/should_run/T27845b.hs
- + testsuite/tests/dmdanal/should_run/T27845b.stdout
- + testsuite/tests/dmdanal/should_run/T27845b_Lib.hs
- testsuite/tests/dmdanal/should_run/all.T
- + testsuite/tests/ghc-api/T4162.hs
- + testsuite/tests/ghc-api/T4162.stdout
- testsuite/tests/ghc-api/all.T
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/ghci.debugger/scripts/T14628.stderr
- testsuite/tests/ghci/prog020/ghci.prog020a.stderr
- testsuite/tests/ghci/prog020/ghci.prog020b.stderr
- testsuite/tests/ghci/prog021/prog021a.stderr
- testsuite/tests/ghci/prog021/prog021b.stderr
- testsuite/tests/ghci/prog022/ghci.prog022a.stderr
- testsuite/tests/ghci/prog022/ghci.prog022b.stderr
- testsuite/tests/ghci/prog022/ghci.prog022c.stderr
- testsuite/tests/ghci/prog022/ghci.prog022d.stderr
- testsuite/tests/ghci/prog022/ghci.prog022e.stderr
- testsuite/tests/ghci/prog022/ghci.prog022f.stderr
- testsuite/tests/ghci/scripts/Defer02.stderr
- + testsuite/tests/ghci/scripts/T27641.script
- + testsuite/tests/ghci/scripts/T27641.stderr
- + testsuite/tests/ghci/scripts/T27641.stdout
- + testsuite/tests/ghci/scripts/T27834.hs
- + testsuite/tests/ghci/scripts/T27834.script
- + testsuite/tests/ghci/scripts/T27834.stdout
- testsuite/tests/ghci/scripts/all.T
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/hiefile/should_run/T24544.hs
- + testsuite/tests/hpc/instmeths/instmeths.hs
- + testsuite/tests/hpc/instmeths/instmeths.stdout
- + testsuite/tests/hpc/instmeths/test.T
- + testsuite/tests/impredicative/T27917.hs
- testsuite/tests/impredicative/all.T
- + testsuite/tests/indexed-types/should_compile/T27828.hs
- + testsuite/tests/indexed-types/should_compile/T27828a.hs
- + testsuite/tests/indexed-types/should_compile/T27828b.hs
- testsuite/tests/indexed-types/should_compile/all.T
- testsuite/tests/indexed-types/should_fail/T1897b.stderr
- testsuite/tests/indexed-types/should_fail/T9662.stderr
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/module/mod130.stderr
- testsuite/tests/module/mod185.stderr
- + testsuite/tests/numeric/should_run/T27545.hs
- + testsuite/tests/numeric/should_run/T27545.stdout
- testsuite/tests/numeric/should_run/all.T
- testsuite/tests/overloadedstrings/should_fail/T25926.stderr
- testsuite/tests/overloadedstrings/should_fail/T27124.stderr
- testsuite/tests/package/T4806a.stderr
- testsuite/tests/package/all.T
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- testsuite/tests/patsyn/should_fail/records-nofieldselectors.stderr
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/plugins/T26761.hs
- + testsuite/tests/plugins/T26761.stderr
- + testsuite/tests/plugins/T26761Plugin.hs
- + testsuite/tests/plugins/T26761a.hs
- testsuite/tests/plugins/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- testsuite/tests/printer/T17697.stderr
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/profiling/should_run/caller-cc/CallerCcParser.hs
- testsuite/tests/rename/should_fail/T15607.stderr
- testsuite/tests/rename/should_fail/T19843b.stderr
- + testsuite/tests/rts/T24450.hs
- + testsuite/tests/rts/T24450.stderr
- testsuite/tests/rts/all.T
- testsuite/tests/rts/rtsflags001.stderr-ws-32
- testsuite/tests/rts/rtsflags001.stderr-ws-64
- testsuite/tests/safeHaskell/ghci/p4.stderr
- testsuite/tests/safeHaskell/ghci/p6.stderr
- testsuite/tests/simplCore/should_compile/T22375.stderr
- testsuite/tests/simplCore/should_compile/T22375DataFamily.stderr
- testsuite/tests/simplCore/should_compile/T22611.hs
- testsuite/tests/simplCore/should_compile/T22611.stderr
- + testsuite/tests/simplCore/should_compile/T22611a.hs
- + testsuite/tests/simplCore/should_compile/T7803.hs
- + testsuite/tests/simplCore/should_compile/T7803.stderr
- + testsuite/tests/simplCore/should_compile/T7803a.hs
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/typecheck/should_compile/LocalGivenEqs.hs
- testsuite/tests/typecheck/should_compile/T11254.stderr
- testsuite/tests/typecheck/should_compile/T15370.stderr
- + testsuite/tests/typecheck/should_compile/T26543_orig.hs
- + testsuite/tests/typecheck/should_compile/T27916.hs
- testsuite/tests/typecheck/should_compile/all.T
- testsuite/tests/typecheck/should_compile/valid_hole_fits.stderr
- testsuite/tests/typecheck/should_fail/ClassOperator.stderr
- testsuite/tests/typecheck/should_fail/DoExpansion2.stderr
- testsuite/tests/typecheck/should_fail/DoExpansion3.stderr
- testsuite/tests/typecheck/should_fail/FunDepOrigin1b.hs
- testsuite/tests/typecheck/should_fail/T12785b.stderr
- testsuite/tests/typecheck/should_fail/T18851.stderr
- + testsuite/tests/typecheck/should_fail/T18851e.hs
- + testsuite/tests/typecheck/should_fail/T18851e.stderr
- + testsuite/tests/typecheck/should_fail/T18851f.hs
- + testsuite/tests/typecheck/should_fail/T18851f.stderr
- testsuite/tests/typecheck/should_fail/T22707.stderr
- testsuite/tests/typecheck/should_fail/T22940.stderr
- + testsuite/tests/typecheck/should_fail/T26543a.hs
- + testsuite/tests/typecheck/should_fail/T26543a.stderr
- testsuite/tests/typecheck/should_fail/T26823.stderr
- + testsuite/tests/typecheck/should_fail/T27731.hs
- + testsuite/tests/typecheck/should_fail/T27731.stderr
- + testsuite/tests/typecheck/should_fail/T27844.hs
- + testsuite/tests/typecheck/should_fail/T27844.stderr
- testsuite/tests/typecheck/should_fail/T5691.stderr
- testsuite/tests/typecheck/should_fail/T7437.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_fail/mc24.stderr
- testsuite/tests/typecheck/should_fail/tcfail019.stderr
- testsuite/tests/typecheck/should_fail/tcfail189.stderr
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Utils.hs
- utils/check-ppr/Main.hs
- utils/count-deps/Main.hs
- utils/dump-decls/Main.hs
- utils/haddock/haddock-api/src/Haddock.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker/Renderer.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs
- utils/haddock/haddock-api/test/Haddock/Backends/Hyperlinker/ParserSpec.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1f04ec48b4918a8d3e2f98ba32f11c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1f04ec48b4918a8d3e2f98ba32f11c…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/async-rpc] 4 commits: fixup: forkIOWithUnmask
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
65b04ffc by Rodrigo Mesquita at 2026-10-14T15:59:25+01:00
fixup: forkIOWithUnmask
- - - - -
bb069e99 by Rodrigo Mesquita at 2026-10-14T16:12:51+01:00
fixup: mask handleAsyncReq to not throw before replying
- - - - -
16e8f30a by Rodrigo Mesquita at 2026-10-14T16:15:10+01:00
fixup Binary/fail
- - - - -
e1e7a528 by Rodrigo Mesquita at 2026-10-14T16:16:21+01:00
clean up pending replies on exc. in takeTMVar
- - - - -
1 changed file:
- libraries/ghci/GHCi/Message.hs
Changes:
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -835,6 +835,8 @@ remoteAsyncReq pipe msg = do
-- Block waiting for reply
atomically (takeTMVar wv)
+ `onException`
+ atomically (modifyTVar pendingReplies (IntMap.delete uq))
-- | Receive a single asynchronous request and send a matching reply
-- asynchronously (the handler may fork a computation that eventually replies
@@ -845,7 +847,7 @@ remoteAsyncReq pipe msg = do
-- reply more than once). Linear types enforce the handler replies in every
-- branch, and if the handler otherwise throws, it is handled by 'withAsyncHandler'.
handleAsyncReq :: Pipe -> Get a -> (WriteReply Put %1 -> a -> IO r) -> IO r
-handleAsyncReq pipe getit k = do
+handleAsyncReq pipe getit k = mask $ \restore -> do
ack <- newEmptyMVar
-- Block waiting for request
@@ -870,7 +872,7 @@ handleAsyncReq pipe getit k = do
reply (Left (toException exc))
throwIO exc
Right (_, _, it) ->
- k (WriteReply reply) it
+ restore (k (WriteReply reply) it)
-- If there's an exception before the reply is sent, reply with an error instead.
`catch` \(e :: SomeException) -> do
@@ -934,7 +936,7 @@ instance Binary IddMsg where
case b of
0 -> Request <$> get <*> get
1 -> Reply <$> get <*> get
- _ -> error $ "impossible: Binary IddMsg - " ++ show b
+ _ -> fail $ "impossible: Binary IddMsg - " ++ show b
newAsyncManager :: Pipe -> IO AsyncManager
newAsyncManager pipe = do
@@ -944,7 +946,7 @@ newAsyncManager pipe = do
-- Fork thread to drain read end.
-- (cleaned up on 'closePipe')
- asyncDrainTid <- forkIO (drainPipe pipe)
+ asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
let am = AsyncManager{..}
pure am
@@ -954,8 +956,8 @@ newAsyncManager pipe = do
-- In pratice, we'll have both a thread draining the read end of the host's
-- 'Pipe' and a thread draining the read end of the interpreter's 'Pipe', since
-- both processes construct a 'Pipe' and send and receive messages
-drainPipe :: Pipe -> IO ()
-drainPipe p = catch loop replyToPendings where
+drainPipe :: Pipe -> (forall x. IO x -> IO x) -> IO ()
+drainPipe p unmask = catch (unmask loop) replyToPendings where
loop = do
readPipe p >>= \case
-- Receive a reply to a pending notification
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0ed97f29fd6e635ad3d44b3be587be…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0ed97f29fd6e635ad3d44b3be587be…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/jeltsch/smaller-pit] 21 commits: [CLC 341] Strict variants of mutable types
by Wolfgang Jeltsch (@jeltsch) 14 Oct '26
by Wolfgang Jeltsch (@jeltsch) 14 Oct '26
14 Oct '26
Wolfgang Jeltsch pushed to branch wip/jeltsch/smaller-pit at Glasgow Haskell Compiler / GHC
Commits:
f8cfb1a5 by Hécate Kleidukos at 2026-10-04T19:36:39+02:00
[CLC 341] Strict variants of mutable types
This commit is the implementation of CLC Proposal 341
https://github.com/haskell/core-libraries-committee/issues/341
- - - - -
575c4bc6 by Rodrigo Mesquita at 2026-10-05T12:32:02-04:00
ttg: HsModuleName in the AST, ModuleName in GHC
This commit makes the AST fully independent from GHC by parsing a
"module name" as an `HsModuleName (= HText)` rather than constructing an
`ModuleName (= FastString)` straight away.
In the GHC side, everything remains as a `ModuleName`. We turn AST
`HsModuleName`s into fast strings with the cheap `mkFastStringShortText`
when we process the AST for manipulation within the compiler.
There are no performance regressions introduced by this approach.
This is the final piece of #21592
Alternative plans were discussed in
gitlab.haskell.org/ghc/ghc/-/merge_requests/16546#note_694647,
but the Simon suggested one won in simplicity and performance when
compared against my attempt at implementing my original plan of having
a family `ModuleNameP`.
- - - - -
b3b89d32 by mangoiv at 2026-10-05T22:03:32-04:00
hadrian: Persist the used flavour name in the build root (#20650)
This executes the idea proposed in #20650, teaching Hadrian to persist
the flavour used to build a build root. This both makes use of multiple
build roots more ergonomic (as the user no longer needs to repeat
themselves) and avoids a tiresome foot-gun (forgetting to specify a
flavour, often wholly invalidating a build root).
Closes #20650.
Co-authored-by: Sylvain Henry <sylvain(a)haskus.fr>
- - - - -
f977837b by mangoiv at 2026-10-05T22:04:21-04:00
configure: fix sed script and subst tooldir script
Fixes #27672
Adds sanity checking for SUBST_TOOLDIR
Metric Decrease:
LinkableUsage01
MultiLayerModulesTH_OneShot
T10547
T12234
T12425
T13035
T13820
T18140
T18730
T18923
T9198
TcPlugin_RewritePerf
mhu-perf
- - - - -
f1cb08be by Hannes Siebenhandl at 2026-10-05T22:05:03-04:00
Prepend in-memory unit database for backpack
The packageDbFlags are stored in reverse order. Thus, prepending will
ensure that the in-memory unit database is the last item on the unit
database stack.
Further, the in-memory unit database needs to be able to shadow units
from earlier unit databases in the unit database stack.
Moreover, a `-clear-package-db` would remove the in-memory database if
it weren't the last item on the stack.
Closes #27835
- - - - -
8d49ed42 by Kazu Yamamoto at 2026-10-06T19:37:25-04:00
Do not leave standard file descriptors free at startup (#24450)
A process may be started with stdout closed -- `prog >&-` from a shell does
exactly that. Nothing then reserves descriptor 1, and open(), pipe() and
socket() hand out the lowest free descriptor, so the next descriptor the
runtime creates for itself takes it: the ticker's pipe, or a capability's IO
manager control pipe.
'GHC.Internal.IO.StdHandles.stdout' is a CAF, built the first time the program
writes to stdout, which is long after that has happened. It builds a 'Handle'
over descriptor 1, which now belongs to the runtime, and nothing reports an
error because the descriptor is perfectly good. The program then writes its
output into the runtime's own plumbing.
What that looks like from the outside is a hang. The descriptor the Handle has
taken over is the read end of a pipe, so poll() for POLLOUT on it never says
yes and epoll_ctl() accepts a registration for it; the thread waiting to write
blocks for ever. Closing the Handle at exit closes the runtime's descriptor,
which is worse: the ticker then fails on every tick.
'hs_init_ghc' now opens /dev/null onto whichever of descriptors 0, 1 and 2 are
closed, before the runtime creates any descriptor of its own. Each is opened
for the access that descriptor would normally have, read-only for 0 and
write-only for 1 and 2, so that writing to 'stdin' or reading from 'stdout'
still fails rather than quietly succeeding against /dev/null. See
Note [Standard file descriptors must be open].
Moving only the descriptors the runtime opens would not do: the first file the
program itself opened would land on the free number instead. The fix has to
leave no free number. A program run this way therefore finds stdout connected
to /dev/null rather than failing with EBADF on the first write, which is what
such a program would get from most other language runtimes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply(a)anthropic.com>
- - - - -
65bcf9e9 by Simon Jakobi at 2026-10-06T19:38:09-04:00
changelog: Repair the sentence describing #27477
The description read "programs using could crash", the object of
"using" having been lost. The synopsis already says which programs are
affected, so drop the dangling word.
Assisted-by: Claude Fable 5.1
- - - - -
15ece296 by mangoiv at 2026-10-08T15:14:56-04:00
ci: move hadrian/ghci into the lint category so it can be picked up by more runners
Fixes #27817
- - - - -
c0823135 by mangoiv at 2026-10-08T15:14:56-04:00
ci: make lint-adjacent jobs into platform independent
- move lint jobs into lint-anyarch tag
- unify lint jobs under the same platform independent container
- - - - -
f76464cf by Zubin Duggal at 2026-10-08T15:15:39-04:00
DmdAnal: Make AbsDmd neutral in maxDmd
fixes #27845
- - - - -
ffa56ae4 by sheaf at 2026-10-09T17:15:59+02:00
Preserve sign of zero when parsing -0.0#
This commit leverages the recently added GHC.Types.Literal.Floating
module in order to preserve the sign of negative zero from
source-written floating-point literals.
Fixes #27545
- - - - -
d538cac9 by Facundo Domínguez at 2026-10-09T20:26:38-04:00
Propagate annotations to simple interfaces
This allows annotations to be visible to plugins when run by
haddock. The LiquidHaskell plugin needs the annotations, in particular.
Fixes #26761
- - - - -
32c4f4ab by meooow25 at 2026-10-09T20:27:28-04:00
Bump containers submodule to 0.8.1
And fix affected tests.
* T4086a: containers depends on template-haskell-lift now instead of
template-haskell.
* T22611: Updated to be less dependent on containers internals.
- - - - -
c786065e by sheaf at 2026-10-09T20:28:20-04:00
Use an explicit pattern match in toIfaceIdDetails
The old wildcard pattern let this code bit rot: it did not properly account
for recently added 'IdDetails' such as 'RepPolyId', which resulted in
spurious 'pprTrace' messages that could even pollute HLS sessions.
Fixes #23785
- - - - -
e7cacf59 by sheaf at 2026-10-09T20:29:09-04:00
Take into account ifKind in freeNamesIfDecl
Commit 3c2f4bb4 added the 'ifKind' field to various constructors
of 'IfaceDecl', which preserves the original kind (before type synonym
expansion) for better error messages.
However, that commit forgot to update 'freeNamesIfDecl' correspondingly.
This could result in a "urk! lookup local fingerpring" crash when e.g.
a type's kind uses a type synonym defined later, as we failed to add a
dependency on the type synonym during dependency analysis.
Fixes #27916
- - - - -
41e80f41 by Simon Peyton Jones at 2026-10-11T14:38:51-04:00
Simplify the error-suppression mechanism in GHC.Tc.Errors
In `GHC.Tc.Errors.reportWanteds` we suppress some errors in favour of
others. But the mechanism had grown crufty, and #27331 exposed a bug.
This MR makes it simpler and more uniform, by doing everything via
the `ei_suppress` field of the ErrorItem.
Claude then found another closely-related bug (see the review linked
on !16564). That bug is immortalised in new test T18851d.
I'm still not happy with `ignoreConstraint` but we can worry about
that another day; I have not touched it.
Main changes
* Instead of /predicting/ when an error will be reported, we set
`cec_error_reported` when an error is /actually/ reported. (I renamed
it from `cec_suppress` to reflect this change.)
Now errors are suppressed only when a preceding error has actually been
reported. Simpler and more robust.
See `Note [cec_error_reported: suppressing less-serious errors]`
The big changes are in `reportWanteds` which looks very different.
* New `Note [ei_suppress: suppressing confusing errors]` describes the
(quite separate) `ei_suppress` mechanism for suppressing confusing
errors, mainly arising from non-empty rewriter sets
* In `tryTypeClassDefaulting`, instead of switching off type-class defaulting
if there is an insoluble /anywhere/, we just switch it off locally when
we find an insoluble sub-tree. See Note [When to do type-class defaulting]
and (W5) in Note [ApproximateWC]
* Greatly simplify `insolubleWantedCt`.
* I made type-class-defaulting be a bit more eager, only switched off when
a peer or ancestor (but not child) constraint is insoluble. See
Note [When to do type-class defaulting].
Small refactorings
* Rename `askNoErrs` to `askErrsFound` and flip its polarity
* The code for "check if ei_suppress is true of all items" code is
moved to `mkErrorItems` rather than in the large `reportWanteds`.
* Code for `has_gadt_match` moves to `mkGivenErrorReporter`, rather than
polluting the large `reportWanteds`.
* Many tests with `-fdefer-type-errors` get more warnings, especially
about ambiguous (Num a) constraints. This is by-design: see
Note [Always warn with -fdefer-type-errors]. Previously GHC was not
honouring this Note but now it is, hence the extra errors.
- - - - -
f217fc31 by Cheng Shao at 2026-10-11T14:39:33-04:00
compiler: ensure cross ghc --info output target unit ids
Previously, for cross ghcs, the base/ghc/ghc-internal unit ids in
--info output do not match the target unit ids reported by ghc-pkg,
breaking cross ghc release builds with hash-unit-ids when being used
with cabal 3.18. This patch fixes it by making --info output target
unit ids. Fixes #27919.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
8511ab91 by Wolfgang Jeltsch at 2026-10-13T01:18:33+03:00
Make Haddock avoid the PIT
To this end, Haddock has been using `loadSysInterface` to fetch module
interfaces. The `loadSysInterface` operation is very complex, containing
many bits that do not seem to be needed by Haddock. In particular,
storing interface data in and potentially retrieving it from the PIT
seems to be superfluous. This interaction with the PIT limits how much
we can statically shrink the amount of information that is stored in the
PIT. Therefore, we drop the use of `loadSysInterface` and implement the
necessary bits of interface fetching directly in `loadHiFile`, where
`loadSysInterface` use to be invoked.
- - - - -
41381402 by Wolfgang Jeltsch at 2026-10-13T01:18:33+03:00
Statically enforce module interface reduction for the PIT
`GHC.Iface.Load.loadInterface` used to replace certain fields of each
fetched module interface by `panic` applications before storing it in
the PIT. With the present change, the absence of those fields in the PIT
is guaranteed statically.
See #27044 for the bigger picture.
- - - - -
46029e3b by Wolfgang Jeltsch at 2026-10-13T01:18:33+03:00
Make `IfaceBinHandle` a type synonym family
This brings the handling of `IfaceBinHandle` somewhat in line with how
other phase-dependent data is handled. Note that `mi_hi_bytes_` now
includes the `Strict.Maybe` layer itself, independently of the phase.
This is necessary, because with a type family instead of a GADT
`clear_mi_hi_bytes` cannot determine the phase at runtime anymore and
thus has to set `mi_hi_bytes_` to `Strict.Nothing` for every phase.
- - - - -
8869f526 by Wolfgang Jeltsch at 2026-10-14T18:08:05+03:00
Improve the error message about non-found interfaces
- - - - -
236 changed files:
- .gitlab-ci.yml
- .gitlab/hello.hs
- + changelog.d/24450
- + changelog.d/26761
- + changelog.d/27845
- + changelog.d/T23785
- + changelog.d/T27545
- + changelog.d/T27731
- + changelog.d/T27916
- + changelog.d/clc-341-strict-mutable-data-types
- + changelog.d/fix-cross-ghc-info-unit-id
- changelog.d/fix-parallel-gc-selector-race-27477
- + changelog.d/hadrian-persist-flavour
- changelog.d/unit-index
- compiler/GHC.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/Builtin/WiredIn/Ids.hs
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/Make.hs
- compiler/GHC/Core/Opt/CallerCC/Types.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Pipeline.hs-boot
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/Phases.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Driver/Session/Inspect.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Lit.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Match/Literal.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Binary.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Errors/Types.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Debug.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Load.hs-boot
- compiler/GHC/Iface/Make.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/Lexer.x
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/String.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Unbound.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Settings.hs
- compiler/GHC/Settings/IO.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Instance/Family.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Solver/Default.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Types/ErrCtxt.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Unit/External.hs
- compiler/GHC/Unit/Home.hs
- compiler/GHC/Unit/Module.hs
- compiler/GHC/Unit/Module/Env.hs
- compiler/GHC/Unit/Module/ModIface.hs
- + compiler/GHC/Unit/Module/Name.hs
- compiler/GHC/Unit/Parser.hs
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Unit/Types.hs-boot
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Lit.hs
- compiler/Language/Haskell/Syntax/Module/Name.hs
- compiler/Language/Haskell/Syntax/Text.hs
- compiler/ghc.cabal.in
- distrib/configure.ac.in
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Info.hs
- ghc/Main.hs
- hadrian/bindist/Makefile
- hadrian/bindist/config.mk.in
- hadrian/doc/flavours.md
- hadrian/src/Rules.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Settings.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- libraries/base/src/Control/Concurrent/Chan.hs
- + libraries/base/src/Control/Concurrent/Chan/Strict.hs
- libraries/base/src/Control/Concurrent/MVar.hs
- + libraries/base/src/Control/Concurrent/MVar/Strict.hs
- libraries/base/src/Data/IORef.hs
- + libraries/base/src/Data/IORef/Strict.hs
- libraries/containers
- linters/lint-codes/LintCodes/Static.hs
- m4/subst_tooldir.m4
- rts/RtsStartup.c
- + testsuite/tests/backpack/should_compile/T27835.bkp
- + testsuite/tests/backpack/should_compile/T27835.stderr
- testsuite/tests/backpack/should_compile/all.T
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/dmdanal/should_run/T27845.hs
- + testsuite/tests/dmdanal/should_run/T27845.stdout
- + testsuite/tests/dmdanal/should_run/T27845_Lib.hs
- + testsuite/tests/dmdanal/should_run/T27845b.hs
- + testsuite/tests/dmdanal/should_run/T27845b.stdout
- + testsuite/tests/dmdanal/should_run/T27845b_Lib.hs
- testsuite/tests/dmdanal/should_run/all.T
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/ghci/prog020/ghci.prog020a.stderr
- testsuite/tests/ghci/prog020/ghci.prog020b.stderr
- testsuite/tests/ghci/prog021/prog021a.stderr
- testsuite/tests/ghci/prog021/prog021b.stderr
- testsuite/tests/ghci/prog022/ghci.prog022a.stderr
- testsuite/tests/ghci/prog022/ghci.prog022b.stderr
- testsuite/tests/ghci/prog022/ghci.prog022c.stderr
- testsuite/tests/ghci/prog022/ghci.prog022d.stderr
- testsuite/tests/ghci/prog022/ghci.prog022e.stderr
- testsuite/tests/ghci/prog022/ghci.prog022f.stderr
- testsuite/tests/ghci/scripts/Defer02.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/hiefile/should_run/T24544.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/module/mod130.stderr
- testsuite/tests/module/mod185.stderr
- + testsuite/tests/numeric/should_run/T27545.hs
- + testsuite/tests/numeric/should_run/T27545.stdout
- testsuite/tests/numeric/should_run/all.T
- testsuite/tests/overloadedstrings/should_fail/T25926.stderr
- testsuite/tests/overloadedstrings/should_fail/T27124.stderr
- testsuite/tests/package/T4806a.stderr
- testsuite/tests/package/all.T
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- testsuite/tests/patsyn/should_fail/records-nofieldselectors.stderr
- + testsuite/tests/plugins/T26761.hs
- + testsuite/tests/plugins/T26761.stderr
- + testsuite/tests/plugins/T26761Plugin.hs
- + testsuite/tests/plugins/T26761a.hs
- testsuite/tests/plugins/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- testsuite/tests/printer/T17697.stderr
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/profiling/should_run/caller-cc/CallerCcParser.hs
- testsuite/tests/rename/should_fail/T15607.stderr
- testsuite/tests/rename/should_fail/T19843b.stderr
- + testsuite/tests/rts/T24450.hs
- + testsuite/tests/rts/T24450.stderr
- testsuite/tests/rts/all.T
- testsuite/tests/safeHaskell/ghci/p4.stderr
- testsuite/tests/safeHaskell/ghci/p6.stderr
- testsuite/tests/simplCore/should_compile/T22611.hs
- testsuite/tests/simplCore/should_compile/T22611.stderr
- + testsuite/tests/simplCore/should_compile/T22611a.hs
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/typecheck/should_compile/T11254.stderr
- testsuite/tests/typecheck/should_compile/T15370.stderr
- + testsuite/tests/typecheck/should_compile/T27916.hs
- testsuite/tests/typecheck/should_compile/all.T
- testsuite/tests/typecheck/should_compile/valid_hole_fits.stderr
- testsuite/tests/typecheck/should_fail/ClassOperator.stderr
- testsuite/tests/typecheck/should_fail/DoExpansion2.stderr
- testsuite/tests/typecheck/should_fail/DoExpansion3.stderr
- testsuite/tests/typecheck/should_fail/FunDepOrigin1b.hs
- testsuite/tests/typecheck/should_fail/T18851.stderr
- + testsuite/tests/typecheck/should_fail/T18851e.hs
- + testsuite/tests/typecheck/should_fail/T18851e.stderr
- + testsuite/tests/typecheck/should_fail/T18851f.hs
- + testsuite/tests/typecheck/should_fail/T18851f.stderr
- testsuite/tests/typecheck/should_fail/T22940.stderr
- testsuite/tests/typecheck/should_fail/T26823.stderr
- + testsuite/tests/typecheck/should_fail/T27731.hs
- + testsuite/tests/typecheck/should_fail/T27731.stderr
- testsuite/tests/typecheck/should_fail/T5691.stderr
- testsuite/tests/typecheck/should_fail/T7437.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_fail/mc24.stderr
- testsuite/tests/typecheck/should_fail/tcfail019.stderr
- testsuite/tests/typecheck/should_fail/tcfail189.stderr
- utils/check-exact/ExactPrint.hs
- utils/count-deps/Main.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker/Renderer.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/969e7ea7de61ae6ad2976042e4449e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/969e7ea7de61ae6ad2976042e4449e…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/async-rpc] fixup: Close pipe cleans up pending requests and the other process can handle...
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
0ed97f29 by Rodrigo Mesquita at 2026-10-14T15:44:04+01:00
fixup: Close pipe cleans up pending requests and the other process can handle EOF and raise correct exceptions when pipe is read or written when closed
More race fixes
- - - - -
3 changed files:
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Server.hs
- testsuite/tests/ghci/custom-external-interpreter-commands/Main.hs
Changes:
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -27,7 +27,7 @@ module GHCi.Message
, Pipe(..), mkPipeFromHandles, mkPipeFromContinuations, closePipe
, remoteCall, remoteTHCall
, handleRemoteCall, handleRemoteTHCall
- , RemoteProcessException(..)
+ , RemoteProcessException(..), PipeClosedException(..)
, BreakModule
, BreakUnitId
@@ -89,7 +89,6 @@ import qualified GHC.Boot.TH.Syntax as TH
import qualified GHC.Boot.TH.Monad as TH
import System.Exit
import System.IO
-import System.IO.Error
import Control.Monad
import Control.Concurrent.STM
import Data.Bifunctor
@@ -722,38 +721,54 @@ data Pipe = Pipe
, pipeLeftovers :: !(IORef (Maybe ByteString))
, pipeLock :: !(MVar ()) -- ^ Lock to prevent concurrent writes to the stream
, pipeAsyncMngr :: !AsyncManager
- , pipeCleanupRW :: !(IO ())
- -- ^ Clean up the read/write resources, e.g. closes the handles if 'mkPipeFromHandles'
+ , pipeIsClosed :: !(TVar Bool)
+ -- ^ After the pipe is closed, requests can no longer be received nor sent
}
-- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
--- Must call 'closePipe' at the end to clean up and close the handles.
+-- Must call 'closePipe' at the end to clean up.
mkPipeFromHandles :: Handle -> Handle -> IO Pipe
mkPipeFromHandles pipeRead pipeWrite = do
let getSome = B.hGetSome pipeRead (32*1024)
putAll b = do
B.hPutBuilder pipeWrite b
hFlush pipeWrite
- mkPipeFromContinuations getSome putAll (hClose pipeRead >> hClose pipeWrite)
+ mkPipeFromContinuations getSome putAll
--- | Make a 'Pipe' from a reader function and a writer function, plus an action
--- to clean up the reader and writer resources. The pipe should be closed with
--- 'closePipe', which will call this clean up action too.
-mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO () -> IO Pipe
-mkPipeFromContinuations getSome putAll pipeCleanupRW = mdo
+-- | Make a 'Pipe' from a reader function and a writer function.
+-- The pipe should be closed with 'closePipe'.
+mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO Pipe
+mkPipeFromContinuations getSome putAll = mdo
pipeLeftovers <- newIORef Nothing
pipeLock <- newMVar ()
+ pipeIsClosed <- newTVarIO False
mdo
pipeAsyncMngr <- newAsyncManager p
let p = Pipe { .. }
pure p
--- | Cleanly close this pipe, terminating the async manager's drain-read-end thread.
--- This doesn't close the handles
+-- | Cleanly close our end of the Pipe, terminating the async manager's
+-- drain-read-end thread. Calling 'closePipe' on an already closed pipe is a no-op.
+--
+-- If the pipe was created using 'mkPipeFromHandles', the handles must only by
+-- closed after calling 'closePipe'.
+--
+-- The requests we've sent that are pending are all canceled with
+-- 'PipeClosedException' by ourselves.
+--
+-- The remote process does not necessarily detect that this end of the pipe was
+-- closed, but it must handle cleanly and clean up after exceptions caused from
+-- it such as a handle being closed.
closePipe :: Pipe -> IO ()
closePipe Pipe{..} = do
- killThread (asyncDrainTid pipeAsyncMngr) -- releases handle lock taken by reading
- pipeCleanupRW -- hClose won't deadlock now that the drainThread is killed
+
+ isAlreadyClosed <- atomically $ swapTVar pipeIsClosed True
+
+ unless isAlreadyClosed $ do
+
+ -- releases handle lock taken by reading, and cleans up by replying to every
+ -- pending request with 'PipeClosedException'.
+ throwTo (asyncDrainTid pipeAsyncMngr) PipeClosedException
-- | Send a 'Message' request to and wait for a reply from the interpreter
-- (blocks on the calling side, doesn't necessarily block the interpreter)
@@ -798,20 +813,28 @@ remoteAsyncReq pipe msg = do
-- Mark pending request
uq <- freshReqId (pipeAsyncMngr pipe)
- wv <- newEmptyMVar
- atomicModifyIORef' pendingReplies $
- \m -> (IntMap.insert uq wv m, ())
+ wv <- newEmptyTMVarIO
+
+ isClosed <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ if isClosed
+ then pure True
+ else do
+ modifyTVar pendingReplies (IntMap.insert uq wv)
+ pure False
+
+ when isClosed $ do
+ throwIO PipeClosedException
-- Write request
writePipe pipe (Request uq (runPut msg))
`onException`
-- the req wasn't done, or no one will be reading the reply:
-- in either case, drop it from the pending map.
- atomicModifyIORef' pendingReplies
- (\m -> (IntMap.delete uq m, ()))
+ atomically (modifyTVar pendingReplies (IntMap.delete uq))
-- Block waiting for reply
- takeMVar wv
+ atomically (takeTMVar wv)
-- | Receive a single asynchronous request and send a matching reply
-- asynchronously (the handler may fork a computation that eventually replies
@@ -826,32 +849,40 @@ handleAsyncReq pipe getit k = do
ack <- newEmptyMVar
-- Block waiting for request
- (uq, bs) <- atomically (readTQueue (requestsQueue (pipeAsyncMngr pipe)))
-
- let reply p = do
- writePipe pipe (Reply uq (bimap toSerializableException runPut p))
- putMVar ack ()
-
- -- Run the continuation which must reply exactly once with 'writeReply'
- case runGetOrFail getit bs of
- Left (_, _, err) -> do
- let exc = DecodeAsyncReqException err
- reply (Left (toException exc))
- throwIO exc
- Right (_, _, it) ->
- k (WriteReply reply) it
-
- -- If there's an exception before the reply is sent, reply with an error instead.
- `catch` \(e :: SomeException) -> do
-
- tryTakeMVar ack >>= \case
- Just () ->
- -- Already replied
- throwIO e
- Nothing -> do
- -- Reply with an error
- reply (Left e)
- throwIO e
+ mbreq <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ isEmpty <- isEmptyTQueue (requestsQueue (pipeAsyncMngr pipe))
+ if isClosed && isEmpty
+ then return Nothing
+ else Just <$> readTQueue (requestsQueue (pipeAsyncMngr pipe))
+
+ case mbreq of
+ Nothing -> throwIO PipeClosedException
+ Just (uq, bs) -> do
+ let reply p = do
+ writePipe pipe (Reply uq (bimap toSerializableException runPut p))
+ putMVar ack ()
+
+ -- Run the continuation which must reply exactly once with 'writeReply'
+ case runGetOrFail getit bs of
+ Left (_, _, err) -> do
+ let exc = DecodeAsyncReqException err
+ reply (Left (toException exc))
+ throwIO exc
+ Right (_, _, it) ->
+ k (WriteReply reply) it
+
+ -- If there's an exception before the reply is sent, reply with an error instead.
+ `catch` \(e :: SomeException) -> do
+
+ tryTakeMVar ack >>= \case
+ Just () ->
+ -- Already replied
+ throwIO e
+ Nothing -> do
+ -- Reply with an error
+ reply (Left e)
+ throwIO e
-- | An exception that can be thrown on the host process representing an
-- expection that happened on the remote one.
@@ -862,12 +893,16 @@ instance Exception RemoteProcessException
newtype DecodeAsyncReqException = DecodeAsyncReqException String deriving Show
instance Exception DecodeAsyncReqException
+-- | We've closed the pipe on our end and we terminate all pending requests with this.
+data PipeClosedException = PipeClosedException deriving Show
+instance Exception PipeClosedException
+
-- ** Async matching "internals" -----------------------------------------------
data AsyncManager = AsyncManager
- { pendingReply :: !(IORef (IntMap (MVar (Either SomeException LB.ByteString))))
+ { pendingReply :: !(TVar (IntMap (TMVar (Either SomeException LB.ByteString))))
-- ^ When a request is sent, keep track that we're expecting a reply. The
- -- MVar is signaled when the reply arrives. The exception can originate
+ -- TMVar is signaled when the reply arrives. The exception can originate
-- from the host process, or from the remote one (with 'RemoteProcessException').
, requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
-- ^ A queue of pending received requests. When a request is received,
@@ -904,45 +939,55 @@ instance Binary IddMsg where
newAsyncManager :: Pipe -> IO AsyncManager
newAsyncManager pipe = do
asyncNextReq <- newIORef 0
- pendingReply <- newIORef IntMap.empty
+ pendingReply <- newTVarIO IntMap.empty
requestsQueue <- newTQueueIO
- mdo
- -- Fork thread to drain read end.
- -- (cleaned up on 'closePipe')
- asyncDrainTid <- forkIO (drainPipe pipe am)
+ -- Fork thread to drain read end.
+ -- (cleaned up on 'closePipe')
+ asyncDrainTid <- forkIO (drainPipe pipe)
- let am = AsyncManager{..}
- pure am
+ let am = AsyncManager{..}
+ pure am
-- | Drains the read end of the Pipe and match 'Reply'es against pending 'Request's.
--
-- In pratice, we'll have both a thread draining the read end of the host's
-- 'Pipe' and a thread draining the read end of the interpreter's 'Pipe', since
--- both processes construct a 'Pipe' and send and receive messages:
-drainPipe :: Pipe -> AsyncManager -> IO ()
-drainPipe p am = flip catch cancelPending $ forever $ do
- readPipe p >>= \case
- -- Receive a reply to a pending notification
- Reply uq bs -> do
- mwv <- atomicModifyIORef' (pendingReply am) $ \m -> do
- case IntMap.lookup uq m of
- Nothing -> (m, Nothing)
- Just wv -> (IntMap.delete uq m, Just wv)
- case mwv of
- Just wv ->
- putMVar wv (bimap (toException . RemoteProcessException) id bs) -- signal matching request
- Nothing ->
- -- the req was dropped from pending bc there was an exception, and no
- -- one's listening any more. the reply is ignored
- pure ()
- -- Receive a request and queue it
- Request uq bs ->
- atomically $ writeTQueue (requestsQueue am) (uq,bs)
- where
- cancelPending (e::SomeException) = do
- pendings <- readIORef (pendingReply am)
- mapM_ (\p -> putMVar p (Left e {-host exception-})) pendings
+-- both processes construct a 'Pipe' and send and receive messages
+drainPipe :: Pipe -> IO ()
+drainPipe p = catch loop replyToPendings where
+ loop = do
+ readPipe p >>= \case
+ -- Receive a reply to a pending notification
+ Just (Reply uq bs) -> do
+ atomically $ do
+ m <- readTVar (pendingReply am)
+ case IntMap.lookup uq m of
+ Nothing ->
+ -- the req was dropped from pending bc there was an exception, and no
+ -- one's listening any more. the reply is ignored
+ pure ()
+ Just wv -> do
+ writeTVar (pendingReply am) (IntMap.delete uq m)
+ -- signal matching request
+ putTMVar wv (bimap (toException . RemoteProcessException) id bs)
+ loop
+ -- Receive a request and queue it
+ Just (Request uq bs) -> do
+ atomically $ writeTQueue (requestsQueue am) (uq,bs)
+ loop
+ -- EOF on read handle
+ Nothing -> do
+ -- clean up pipe then exit loop
+ replyToPendings (toException PipeClosedException)
+ pure ()
+
+ replyToPendings e = atomically $ do
+ writeTVar (pipeIsClosed p) True
+ pendings <- swapTVar (pendingReply am) IntMap.empty -- take pending
+ mapM_ (\p -> putTMVar p (Left e {-host exception-})) pendings
+
+ am = pipeAsyncMngr p
freshReqId :: AsyncManager -> IO CorrelationId
freshReqId AsyncManager{asyncNextReq} =
@@ -960,16 +1005,17 @@ writePipe p@Pipe{..} = withLock p . putAll . execPut . put
-- | Only the 'drainPipe' thread should read from the pipe directly. Other
-- functions should wait for the matching reply to be delivered asynchronously.
-- (see 'withAsyncReq'). It doesn't lock to read bc it assumes single reader is 'drainPipe'.
-readPipe :: Pipe -> IO IddMsg
+--
+-- Returns @Nothing@ on EOF.
+readPipe :: Pipe -> IO (Maybe IddMsg)
readPipe Pipe{..} = do
leftovers <- readIORef pipeLeftovers
m <- getBin getSome get leftovers
case m of
- Nothing -> throw $
- mkIOError eofErrorType "GHCi.Message.readPipe" Nothing Nothing
+ Nothing -> return Nothing
Just (result, new_leftovers) -> do
writeIORef pipeLeftovers new_leftovers
- return result
+ return (Just result)
getBin
:: (IO ByteString) -> Get a -> Maybe ByteString
=====================================
libraries/ghci/GHCi/Server.hs
=====================================
@@ -79,7 +79,7 @@ servWithCustom verbose hook pipe restore customHandler = newIORef False >>= loop
_other -> run msg >>= reply
isDone <- readIORef doneRef
- if isDone then return ()
+ if isDone then closePipe pipe
else loop doneRef
handleCustom tag payload = do
@@ -184,7 +184,7 @@ defaultServerWithCustom customHandler = do
#if defined(wasm32_HOST_ARCH)
-- See Note [wasm ghci signal handlers] for details
installSignalHandlers $ js_register_signal_handler cb_sig
- pipe <- mkPipeFromContinuations (recv_buf cb_recv) (send_buf cb_send) (pure ())
+ pipe <- mkPipeFromContinuations (recv_buf cb_recv) (send_buf cb_send)
#else
when verbose $
printf "GHC iserv starting (in: %s; out: %s)\n" (show inh) (show outh)
=====================================
testsuite/tests/ghci/custom-external-interpreter-commands/Main.hs
=====================================
@@ -171,12 +171,14 @@ withServer serverExe action = do
, std_out = Inherit
, std_err = Inherit
}
- bracket (mkHandles ghcRead ghcWrite >>= uncurry mkPipeFromHandles)
- (\pipe -> do
+ bracket (mkHandles ghcRead ghcWrite >>= \hds -> (hds,) <$> uncurry mkPipeFromHandles hds)
+ (\((hFromServer, hToServer), pipe) -> do
closePipe pipe
+ hClose hFromServer
+ hClose hToServer
terminateProcess ph
void (waitForProcess ph))
- (\pipe -> action pipe)
+ (\(_, pipe) -> action pipe)
where
mkHandles r w = do
hR <- fdToHandle r
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0ed97f29fd6e635ad3d44b3be587be6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0ed97f29fd6e635ad3d44b3be587be6…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/async-rpc] Close pipe cleans up pending requests and the other process can handle EOF and...
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
by Rodrigo Mesquita (@alt-romes) 14 Oct '26
14 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/async-rpc at Glasgow Haskell Compiler / GHC
Commits:
1c251824 by Rodrigo Mesquita at 2026-10-14T15:35:42+01:00
Close pipe cleans up pending requests and the other process can handle EOF and raise correct exceptions when pipe is read or written when closed
More race fixes
- - - - -
3 changed files:
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Server.hs
- testsuite/tests/ghci/custom-external-interpreter-commands/Main.hs
Changes:
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -27,7 +27,7 @@ module GHCi.Message
, Pipe(..), mkPipeFromHandles, mkPipeFromContinuations, closePipe
, remoteCall, remoteTHCall
, handleRemoteCall, handleRemoteTHCall
- , RemoteProcessException(..)
+ , RemoteProcessException(..), PipeClosedException(..)
, BreakModule
, BreakUnitId
@@ -89,7 +89,6 @@ import qualified GHC.Boot.TH.Syntax as TH
import qualified GHC.Boot.TH.Monad as TH
import System.Exit
import System.IO
-import System.IO.Error
import Control.Monad
import Control.Concurrent.STM
import Data.Bifunctor
@@ -722,38 +721,54 @@ data Pipe = Pipe
, pipeLeftovers :: !(IORef (Maybe ByteString))
, pipeLock :: !(MVar ()) -- ^ Lock to prevent concurrent writes to the stream
, pipeAsyncMngr :: !AsyncManager
- , pipeCleanupRW :: !(IO ())
- -- ^ Clean up the read/write resources, e.g. closes the handles if 'mkPipeFromHandles'
+ , pipeIsClosed :: !(TVar Bool)
+ -- ^ After the pipe is closed, requests can no longer be received nor sent
}
-- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
--- Must call 'closePipe' at the end to clean up and close the handles.
+-- Must call 'closePipe' at the end to clean up.
mkPipeFromHandles :: Handle -> Handle -> IO Pipe
mkPipeFromHandles pipeRead pipeWrite = do
let getSome = B.hGetSome pipeRead (32*1024)
putAll b = do
B.hPutBuilder pipeWrite b
hFlush pipeWrite
- mkPipeFromContinuations getSome putAll (hClose pipeRead >> hClose pipeWrite)
+ mkPipeFromContinuations getSome putAll
--- | Make a 'Pipe' from a reader function and a writer function, plus an action
--- to clean up the reader and writer resources. The pipe should be closed with
--- 'closePipe', which will call this clean up action too.
-mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO () -> IO Pipe
-mkPipeFromContinuations getSome putAll pipeCleanupRW = mdo
+-- | Make a 'Pipe' from a reader function and a writer function.
+-- The pipe should be closed with 'closePipe'.
+mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO Pipe
+mkPipeFromContinuations getSome putAll = mdo
pipeLeftovers <- newIORef Nothing
pipeLock <- newMVar ()
+ pipeIsClosed <- newTVarIO False
mdo
pipeAsyncMngr <- newAsyncManager p
let p = Pipe { .. }
pure p
--- | Cleanly close this pipe, terminating the async manager's drain-read-end thread.
--- This doesn't close the handles
+-- | Cleanly close our end of the Pipe, terminating the async manager's
+-- drain-read-end thread. Calling 'closePipe' on an already closed pipe is a no-op.
+--
+-- If the pipe was created using 'mkPipeFromHandles', the handles must only by
+-- closed after calling 'closePipe'.
+--
+-- The requests we've sent that are pending are all canceled with
+-- 'PipeClosedException' by ourselves.
+--
+-- The remote process does not necessarily detect that this end of the pipe was
+-- closed, but it must handle cleanly and clean up after exceptions caused from
+-- it such as a handle being closed.
closePipe :: Pipe -> IO ()
closePipe Pipe{..} = do
- killThread (asyncDrainTid pipeAsyncMngr) -- releases handle lock taken by reading
- pipeCleanupRW -- hClose won't deadlock now that the drainThread is killed
+
+ isAlreadyClosed <- atomically $ swapTVar pipeIsClosed True
+
+ unless isAlreadyClosed $ do
+
+ -- releases handle lock taken by reading, and cleans up by replying to every
+ -- pending request with 'PipeClosedException'.
+ throwTo (asyncDrainTid pipeAsyncMngr) PipeClosedException
-- | Send a 'Message' request to and wait for a reply from the interpreter
-- (blocks on the calling side, doesn't necessarily block the interpreter)
@@ -798,20 +813,28 @@ remoteAsyncReq pipe msg = do
-- Mark pending request
uq <- freshReqId (pipeAsyncMngr pipe)
- wv <- newEmptyMVar
- atomicModifyIORef' pendingReplies $
- \m -> (IntMap.insert uq wv m, ())
+ wv <- newEmptyTMVarIO
+
+ isClosed <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ if isClosed
+ then pure True
+ else do
+ modifyTVar pendingReplies (IntMap.insert uq wv)
+ pure False
+
+ when isClosed $ do
+ throwIO PipeClosedException
-- Write request
writePipe pipe (Request uq (runPut msg))
`onException`
-- the req wasn't done, or no one will be reading the reply:
-- in either case, drop it from the pending map.
- atomicModifyIORef' pendingReplies
- (\m -> (IntMap.delete uq m, ()))
+ atomically (modifyTVar pendingReplies (IntMap.delete uq))
-- Block waiting for reply
- takeMVar wv
+ atomically (takeTMVar wv)
-- | Receive a single asynchronous request and send a matching reply
-- asynchronously (the handler may fork a computation that eventually replies
@@ -826,32 +849,40 @@ handleAsyncReq pipe getit k = do
ack <- newEmptyMVar
-- Block waiting for request
- (uq, bs) <- atomically (readTQueue (requestsQueue (pipeAsyncMngr pipe)))
-
- let reply p = do
- writePipe pipe (Reply uq (bimap toSerializableException runPut p))
- putMVar ack ()
-
- -- Run the continuation which must reply exactly once with 'writeReply'
- case runGetOrFail getit bs of
- Left (_, _, err) -> do
- let exc = DecodeAsyncReqException err
- reply (Left (toException exc))
- throwIO exc
- Right (_, _, it) ->
- k (WriteReply reply) it
-
- -- If there's an exception before the reply is sent, reply with an error instead.
- `catch` \(e :: SomeException) -> do
-
- tryTakeMVar ack >>= \case
- Just () ->
- -- Already replied
- throwIO e
- Nothing -> do
- -- Reply with an error
- reply (Left e)
- throwIO e
+ mbreq <- atomically $ do
+ isClosed <- readTVar (pipeIsClosed pipe)
+ isEmpty <- isEmptyTQueue (requestsQueue (pipeAsyncMngr pipe))
+ if isClosed && isEmpty
+ then return Nothing
+ else Just <$> readTQueue (requestsQueue (pipeAsyncMngr pipe))
+
+ case mbreq of
+ Nothing -> throwIO PipeClosedException
+ Just (uq, bs) -> do
+ let reply p = do
+ writePipe pipe (Reply uq (bimap toSerializableException runPut p))
+ putMVar ack ()
+
+ -- Run the continuation which must reply exactly once with 'writeReply'
+ case runGetOrFail getit bs of
+ Left (_, _, err) -> do
+ let exc = DecodeAsyncReqException err
+ reply (Left (toException exc))
+ throwIO exc
+ Right (_, _, it) ->
+ k (WriteReply reply) it
+
+ -- If there's an exception before the reply is sent, reply with an error instead.
+ `catch` \(e :: SomeException) -> do
+
+ tryTakeMVar ack >>= \case
+ Just () ->
+ -- Already replied
+ throwIO e
+ Nothing -> do
+ -- Reply with an error
+ reply (Left e)
+ throwIO e
-- | An exception that can be thrown on the host process representing an
-- expection that happened on the remote one.
@@ -862,12 +893,16 @@ instance Exception RemoteProcessException
newtype DecodeAsyncReqException = DecodeAsyncReqException String deriving Show
instance Exception DecodeAsyncReqException
+-- | We've closed the pipe on our end and we terminate all pending requests with this.
+data PipeClosedException = PipeClosedException deriving Show
+instance Exception PipeClosedException
+
-- ** Async matching "internals" -----------------------------------------------
data AsyncManager = AsyncManager
- { pendingReply :: !(IORef (IntMap (MVar (Either SomeException LB.ByteString))))
+ { pendingReply :: !(TVar (IntMap (TMVar (Either SomeException LB.ByteString))))
-- ^ When a request is sent, keep track that we're expecting a reply. The
- -- MVar is signaled when the reply arrives. The exception can originate
+ -- TMVar is signaled when the reply arrives. The exception can originate
-- from the host process, or from the remote one (with 'RemoteProcessException').
, requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
-- ^ A queue of pending received requests. When a request is received,
@@ -904,45 +939,58 @@ instance Binary IddMsg where
newAsyncManager :: Pipe -> IO AsyncManager
newAsyncManager pipe = do
asyncNextReq <- newIORef 0
- pendingReply <- newIORef IntMap.empty
+ pendingReply <- newTVarIO IntMap.empty
requestsQueue <- newTQueueIO
- mdo
- -- Fork thread to drain read end.
- -- (cleaned up on 'closePipe')
- asyncDrainTid <- forkIO (drainPipe pipe am)
+ -- Fork thread to drain read end.
+ -- (cleaned up on 'closePipe')
+ asyncDrainTid <- forkIO (drainPipe pipe)
- let am = AsyncManager{..}
- pure am
+ let am = AsyncManager{..}
+ pure am
-- | Drains the read end of the Pipe and match 'Reply'es against pending 'Request's.
--
-- In pratice, we'll have both a thread draining the read end of the host's
-- 'Pipe' and a thread draining the read end of the interpreter's 'Pipe', since
--- both processes construct a 'Pipe' and send and receive messages:
-drainPipe :: Pipe -> AsyncManager -> IO ()
-drainPipe p am = flip catch cancelPending $ forever $ do
- readPipe p >>= \case
- -- Receive a reply to a pending notification
- Reply uq bs -> do
- mwv <- atomicModifyIORef' (pendingReply am) $ \m -> do
- case IntMap.lookup uq m of
- Nothing -> (m, Nothing)
- Just wv -> (IntMap.delete uq m, Just wv)
- case mwv of
- Just wv ->
- putMVar wv (bimap (toException . RemoteProcessException) id bs) -- signal matching request
- Nothing ->
- -- the req was dropped from pending bc there was an exception, and no
- -- one's listening any more. the reply is ignored
- pure ()
- -- Receive a request and queue it
- Request uq bs ->
- atomically $ writeTQueue (requestsQueue am) (uq,bs)
- where
- cancelPending (e::SomeException) = do
- pendings <- readIORef (pendingReply am)
- mapM_ (\p -> putMVar p (Left e {-host exception-})) pendings
+-- both processes construct a 'Pipe' and send and receive messages
+drainPipe :: Pipe -> IO ()
+drainPipe p = catch loop replyToPendings where
+ loop = do
+ readPipe p >>= \case
+ -- Receive a reply to a pending notification
+ Just (Reply uq bs) -> do
+ mwv <- atomically $ do
+ m <- readTVar (pendingReply am)
+ case IntMap.lookup uq m of
+ Nothing -> pure Nothing
+ Just wv -> do
+ writeTVar (pendingReply am) (IntMap.delete uq m)
+ pure (Just wv)
+ case mwv of
+ Just wv -> atomically $
+ putTMVar wv (bimap (toException . RemoteProcessException) id bs) -- signal matching request
+ Nothing ->
+ -- the req was dropped from pending bc there was an exception, and no
+ -- one's listening any more. the reply is ignored
+ pure ()
+ loop
+ -- Receive a request and queue it
+ Just (Request uq bs) -> do
+ atomically $ writeTQueue (requestsQueue am) (uq,bs)
+ loop
+ -- EOF on read handle
+ Nothing -> do
+ -- clean up pipe then exit loop
+ replyToPendings (toException PipeClosedException)
+ pure ()
+
+ replyToPendings e = atomically $ do
+ writeTVar (pipeIsClosed p) True
+ pendings <- swapTVar (pendingReply am) IntMap.empty -- take pending
+ mapM_ (\p -> putTMVar p (Left e {-host exception-})) pendings
+
+ am = pipeAsyncMngr p
freshReqId :: AsyncManager -> IO CorrelationId
freshReqId AsyncManager{asyncNextReq} =
@@ -960,16 +1008,17 @@ writePipe p@Pipe{..} = withLock p . putAll . execPut . put
-- | Only the 'drainPipe' thread should read from the pipe directly. Other
-- functions should wait for the matching reply to be delivered asynchronously.
-- (see 'withAsyncReq'). It doesn't lock to read bc it assumes single reader is 'drainPipe'.
-readPipe :: Pipe -> IO IddMsg
+--
+-- Returns @Nothing@ on EOF.
+readPipe :: Pipe -> IO (Maybe IddMsg)
readPipe Pipe{..} = do
leftovers <- readIORef pipeLeftovers
m <- getBin getSome get leftovers
case m of
- Nothing -> throw $
- mkIOError eofErrorType "GHCi.Message.readPipe" Nothing Nothing
+ Nothing -> return Nothing
Just (result, new_leftovers) -> do
writeIORef pipeLeftovers new_leftovers
- return result
+ return (Just result)
getBin
:: (IO ByteString) -> Get a -> Maybe ByteString
=====================================
libraries/ghci/GHCi/Server.hs
=====================================
@@ -79,7 +79,7 @@ servWithCustom verbose hook pipe restore customHandler = newIORef False >>= loop
_other -> run msg >>= reply
isDone <- readIORef doneRef
- if isDone then return ()
+ if isDone then closePipe pipe
else loop doneRef
handleCustom tag payload = do
@@ -184,7 +184,7 @@ defaultServerWithCustom customHandler = do
#if defined(wasm32_HOST_ARCH)
-- See Note [wasm ghci signal handlers] for details
installSignalHandlers $ js_register_signal_handler cb_sig
- pipe <- mkPipeFromContinuations (recv_buf cb_recv) (send_buf cb_send) (pure ())
+ pipe <- mkPipeFromContinuations (recv_buf cb_recv) (send_buf cb_send)
#else
when verbose $
printf "GHC iserv starting (in: %s; out: %s)\n" (show inh) (show outh)
=====================================
testsuite/tests/ghci/custom-external-interpreter-commands/Main.hs
=====================================
@@ -171,12 +171,14 @@ withServer serverExe action = do
, std_out = Inherit
, std_err = Inherit
}
- bracket (mkHandles ghcRead ghcWrite >>= uncurry mkPipeFromHandles)
- (\pipe -> do
+ bracket (mkHandles ghcRead ghcWrite >>= \hds -> (hds,) <$> uncurry mkPipeFromHandles hds)
+ (\((hFromServer, hToServer), pipe) -> do
closePipe pipe
+ hClose hFromServer
+ hClose hToServer
terminateProcess ph
void (waitForProcess ph))
- (\pipe -> action pipe)
+ (\(_, pipe) -> action pipe)
where
mkHandles r w = do
hR <- fdToHandle r
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1c2518249550c0cf00e8a3fa61ef282…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1c2518249550c0cf00e8a3fa61ef282…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/andreask/cfg-fix] 111 commits: Avoid wasteful allocations in mkTyConAppCo
by Sylvain Henry (@hsyl20) 14 Oct '26
by Sylvain Henry (@hsyl20) 14 Oct '26
14 Oct '26
Sylvain Henry pushed to branch wip/andreask/cfg-fix at Glasgow Haskell Compiler / GHC
Commits:
1aac7095 by sheaf at 2026-08-16T04:37:16-04:00
Avoid wasteful allocations in mkTyConAppCo
The idiom "traverse isReflCo_maybe" followed by "map fst" used in
'GHC.Core.Coercion.mkTyConAppCo' was allocating a lot of waste.
This commit uses 'GHC.Data.Unboxed.traverseMaybeUB' to avoid all these
unnecessary intermediate allocations.
In a quick microbenchmark for 'mkTyConAppCo', this change resulted in:
- refl case (all argument coercions are reflexive):
- -60% runtime
- -80% allocations
- non-refl case:
- from 0% to -12% runtime (depending on which argument is non-refl)
- from 0% to -70% allocations ( -- '' -- )
Fixes #27648
-------------------------
Metric Decrease:
FamAppCachePerf
SimplCastPerf
T12425
T15703
T26426
T3064
T9872a
T9872b
T9872b_defer
T9872c
T9872d
T5321Fun
T9020
T9630
TcPlugin_RewritePerf
Metric Increase:
LinkableUsage02
-------------------------
- - - - -
3915e982 by Alan Zimmerman at 2026-08-16T04:37:58-04:00
EPA: Remove al_trailing from AnnList
It was not being used
- - - - -
fed942ac by Andreas Klebinger at 2026-08-17T12:11:57-04:00
testsuite: Use sigkill in process009.
SIGHUB might have been blocked by a (grand)*parent of the test.
In such cases the test would fail as the python process would simply
exist gracefully instead of committing to a premature end.
By using SIGKILL we can rely on the signal not being blocked, fixing #27578
in the process.
- - - - -
574c875f by Andreas Klebinger at 2026-08-17T12:12:39-04:00
Simplify comparison in DFM.hs
Fixes #27669
- - - - -
d8f1a2a3 by Alan Zimmerman at 2026-08-17T12:13:18-04:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print
annotations next, allowing us to get rid of LocatedBF / SrcSpanAnnBF
- - - - -
93a2b20f by Andreas Klebinger at 2026-08-18T04:31:04-04:00
Fix a number of incorrect module references:
Fix module reference in Note [DataCon wrappers are conlike].
Fix module reference in Note [Detailed InertCans Invariants].
Fix module reference in Note [GHC's data format representations].
Fix module reference in Note [Grand plan for static forms].
Fix module reference in Note [How tuples work].
Fix module reference in Note [Solved dictionaries].
Fix module reference in Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)].
Fix module reference in Note [The VarBndr type and its uses].
Found the incorrect references with a llm.
- - - - -
eb0dfb01 by Simon Jakobi at 2026-08-18T04:31:44-04:00
ci: Run stack-hadrian-build only in full-ci pipelines
The job exists to catch changes that break hadrian/build-stack (#18726),
but nothing in the pipeline depends on it, and it can only break when
hadrian's dependencies change. Restricting it to full-ci (like
hadrian-multi) still covers marge-bot merge batches, so such breakage
cannot reach master unnoticed, while ordinary validate pipelines skip
the job.
Assisted-by: Claude Fable 5
- - - - -
b9160962 by Simon Jakobi at 2026-08-20T14:57:52-04:00
testsuite: Show baseline sample count and range in perf failures
A perf baseline is the mean of all samples recorded for a commit, and
it prints as a single number, hiding how far the samples spread. When
the spread is wide, this can indicate an unstable metric that isn't
actually useful as a signal for the perf tests.
For example, in #27602, T27336's peak_megabytes_allocated baseline
showed as 757 when the underlying samples were 605 and 909.
When the baseline is averaged from more than one sample, say so in the
failure output: the one-line stat-failure reason shows the sample
range, and the detail block lists the raw samples. Single-sample
baselines print exactly as before.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
a4979877 by Simon Jakobi at 2026-08-20T14:57:52-04:00
testsuite: Fold Baseline into CommitMetric
A Baseline was just a CommitMetric plus the commit it came from, built
by copying fields across. Since get_commit_metric already knows that
commit, record it on CommitMetric itself and drop Baseline. This also
collapses both branches of find_baseline into plain returns.
Assisted-by: Claude Fable 5
- - - - -
99fb8d68 by Simon Jakobi at 2026-08-20T14:57:52-04:00
ci: Clarify comment on pushing perf notes after failures
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
2ca87972 by Alan Zimmerman at 2026-08-20T14:58:36-04:00
EPA: Remove LocatedBC / SrcSpanBF
The custom annotations are now in the BooleanFormula TTG extension
points, so LBooleanFormula can now use the standard LocatedA.
- - - - -
d2bc32aa by Simon Peyton Jones at 2026-08-21T12:59:26-04:00
Better handling of serialisation of wired-in names
Fixes #27501
- - - - -
d2795ffc by Alan Zimmerman at 2026-08-21T13:00:05-04:00
EPA: Remove NoEpTok/NoEpUniTok, using an unhelpful SrcSpan instead
Also introduce helper functions noEpTok and noEpUniTok to serve
as simple replacements in code inserting an token annotation without
location information.
- - - - -
b5d29ab8 by Brandon Chinn at 2026-08-25T18:42:08-04:00
Add Data.RealFloat and Infinity/NegInfinity/NaN pattern synonyms (#26961)
- - - - -
e60eb3bc by Andreas Klebinger at 2026-08-25T18:42:59-04:00
rts linker: Fix pointer arithmetic issue in flushInstructionCacheRISCV64
We accidentally operated over `uint64_t*` when we should use `uint8_t`.
Fixes #27569
- - - - -
e9bbe8f9 by Andreas Klebinger at 2026-08-26T15:09:23-04:00
cmm dumps: Add machop width info with -dppr-debug for infix ops.
- - - - -
86e3a9d8 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
CmmLint: Check for unsupported MachOp widths
machOpArgReps now maps MachOp + Width to a list of supported
argument widths or Nothing if the given operation is not supported
at the given width.
This allows us to check for nonsensical combinations like FloatToInt
at Word16.
Similarly we now check that every address is actually wordwidth.
- - - - -
13781cca by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: The big subword truncation fix.
A set of slightly related fixes to arm subword handling:
Bitmask immediates:
Don't produce overflowing assembly literals.
There is still another bug here that causes us to miss some valid
literals but we will fix that later.
Improve subword truncation handling:
We now use a small set of helpers to truncate `Register` values rather
than truncating immediate `Reg` values which greatly simplifies the code
structure. This fixes a great many bugs to do with sign/zero extending subwords
or the lack thereof.
We now establish the invariant that subword values are zero-extended at
every site at which they come into "scope" of the ncg, and rely on the
invariant throughout rather than pessimistically inserting redundant
extensions in a hodgepodge manner at the use sites of these values.
This fixes at least the bugs described in issues #27533, #27430
#27537, #27538, #27539, and #27550. But likely more bugs yet not
found.
Subword ffi results:
Apply truncations when calling functions returning
subword values.
genCondJump:
Don't sign extend signed values in the input register as
it might map to a local variable, corrupting the value stored within.
Fix subword store/load instructions.:
We used to read those at 32bit width even for smaller values possibly
resulting in invalid memory access. Now we construct the suffix for
subword variants based on the instruction format for these.
- - - - -
d8fa5d7c by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: Fix MO_V_Broadcast for non-literals.
We now use OpReg instead of OpScalarAsVec as required since we broadcast a gp register.
Also adds a test. Fixes #27565.
- - - - -
94822c95 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
Add some test cases covering bugs in the arm ncg.
* Test for #27430 (subword ffi results)
* #27537 - subword conversions
* #27538 - subwords used in conditional
* #27533 - single byte read
- - - - -
dd1ba88a by Andreas Klebinger at 2026-08-26T15:09:24-04:00
cmmLint: Lint against MO_FS_Truncate subword use.
- - - - -
fd22f71e by Zubin Duggal at 2026-08-26T15:10:20-04:00
ghc-internal: annotateSTM should use catchSTM# rather than catch#
A catch# frame inside a transaction breaks retry and async exception
delivery.
Fixes #27657
- - - - -
bb324171 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: refactor to reduce THREADED_RTS in MSG_UPD_TSO_FLAGS
- No behavior change in this commit (well, a small optimization here
makes us do less work if the target TSO owned by the curr. capability)
- Move all THREADED_RTS CPP needed into `updThreadFlag`
- Merge MSG_SET_TSO_FLAGS and MSG_UNSET_TSO_FLAGS into MSG_UPD_TSO_FLAGS
plus a `set` bool field in the MessageUpdTSOFlag struct
Towards #27729
- - - - -
ed99b7b7 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: Fix race condition in MSG_UPD_TSO_FLAGS execution
The code for processing the MSG_UPD_TSO_FLAGS message was not taking
into consideration that the TSO's owner might have moved in between that
capability receiving the message (since it was its previous owner) and
starting to process its inbox (a point at which it was no longer the
owner)
Added Note [TSO owner may change in between Msg being sent and received]
to explain this race and the pattern used to fix this, where we just
forward the message to the new owner.
Fixes #27729
- - - - -
cd653714 by Alan Zimmerman at 2026-08-26T15:11:49-04:00
EPA: Uses Parsers.parseModule for exactprint tests
Parsers.parseModule is the advertised way to parse for use for exact
printing in the ghc-exactprint library. This commit updates the GHC
exact print testing to use it.
This requires moving the comment balancing that was occurring
only in the test path into the advertising parser path, so it moves
from Transforms.hs to Utils.hs.
Also update the comment adding to honour trailing annotations
- - - - -
d1d01fa5 by Wolfgang Jeltsch at 2026-08-27T13:17:59+03:00
Add `rethrowSTM` and improve STM-related documentation
Adding `rethrowSTM` resolves #26758.
The implementation of `rethrowSTM` is completely analogous to the one of
`rethrowIO`.
The following is established for the documentation of `throwSTM` and
`catchSTM`:
* Both operations are directly described as analogs of their `IO`
counterparts.
* There is no reference to `throw` in the documentation of `throwSTM`,
because, although such a reference is great in the documentation of
`throwIO`, it is somewhat out of place in the documentation of
`throwSTM`.
* Instead of repeating part of `throwIO`’s documentation, the
documentation of `throwSTM` just recommends using `throwSTM` instead
of `throw` and references the corresponding arguments in the
documentation of `throwIO`.
- - - - -
06fde293 by fendor at 2026-08-28T06:06:44-04:00
GHCi: Fix order of `PackageDBFlag`s for interactive home unit
`PackageDBFlag`s are stored in reverse order of cli specification.
When sorting the `PackageDBFlag`s by longest common prefix, we need thus
to reverse the package db stacks before calculating the prefix.
We make sure to reverse the package db stack for the interactive home
unit to uphold that later specified package dbs overwrite earlier ones.
Resolved and adds regression test for #27640
- - - - -
024c4d04 by fendor at 2026-08-28T06:07:23-04:00
Reuse the UnitIndexCache after initialising multiple home units
- - - - -
55326fa0 by Alan Zimmerman at 2026-08-28T06:08:03-04:00
EPA: Some Haddock processing tweaks
These changes to the Haddock postprocessing should not change
behaviour, but just bring it more closely in line with the
original, changed at 44309cd377f
And add some haddock exactprint tests to show they work.
- - - - -
b3ddee95 by Andreas Klebinger at 2026-08-28T13:57:46-04:00
hadrian: Deprecate quickest flavour.
It was more of a trap for new users than actually beneficial so we
deprecate it and suggest quick+no_dynamic_libs to users instead.
- - - - -
a1d81390 by Andreas Klebinger at 2026-08-28T13:58:37-04:00
cmm: Always favour entry block during block deduplication.
We now always keep the first block in the CmmGraph. This way we avoid
the need to update the entry info table.
Failing to do so caused #27722.
Fixes #27722.
- - - - -
5bd65f00 by Andreas Klebinger at 2026-08-28T13:59:16-04:00
test: FamAppCachePerf - Only collect bytes allocated. Fixes 27747
- - - - -
ced53ce6 by mangoiv at 2026-08-29T07:15:24-04:00
nightlies: output yaml to file only
Previously we would just output the metadata to stdout
which risks that it's clobbered by incidental debugt output.
We now output to file only.
Fixes #27511
- - - - -
578bd185 by Andreas Klebinger at 2026-08-29T07:16:05-04:00
Specialise: Stop looping on recursive dictionaries in interestingDict
interestingDict now doesn't look through loopbreaker unfoldings.
Doing so would cause infinite loops on certain dictionaries.
Fixes #27705.
- - - - -
7bf546fc by Simon Peyton Jones at 2026-08-31T23:48:53-04:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
type family F a :: Constraint
type instance F W = TC W
a :: F W => Int -> Int -- (F W) argument is absent
Oops! Entered absent arg Arg: irred
Type: F W
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
- - - - -
5f474953 by Zubin Duggal at 2026-08-31T23:48:53-04:00
Add tests for absent fillers at dictionary types
T27627 a unary class whose superclass is a non-unary class
T27627a ...whose superclass is a Constraint-kinded type family
T27627b ...whose superclass is a quantified constraint
T27627c a unary class applied to itself, (UC (UC (TC a)))
T27627e a (forall b. P b) dictionary that loops
- - - - -
cd5c6bcc by Zubin Duggal at 2026-08-31T23:48:53-04:00
An abstract TyCon may hide a unary class
A class declared in an hs-boot file is an AbstractTyCon inside the
module loop, and compiling the real declaration may reveal it to be a
UnaryClassTyCon.
- isTerminatingType returned True for such AbstractTyCons
- IfaceToCore set the unary flag to False in the DFunId
So we could end up speculating bottom dictionaries because inside a module
loop we see an UnaryClassTyCon as an AbstractTyCon
Use isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
- - - - -
abfc224a by Zubin Duggal at 2026-08-31T23:48:53-04:00
Specialise: don't replace dead args with absent fillers
specHeader decides an argument is dead by calling isDeadBinder on a binder of
the /optimised RHS/, then applies the filler to the /stable unfolding/
template instead. The two may differ, so the argument can be dead in
the RHS and not in the template.
The specialised function's unfolding then has an absent filler, and any call
site that inlines it evaluates the error thunk.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
- - - - -
1557fd1c by Zubin Duggal at 2026-08-31T23:48:53-04:00
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
- - - - -
4117e5ae by Wolfgang Jeltsch at 2026-08-31T23:49:35-04:00
Incorporate the `rethrowSTM` reexport into the `stm` submodule
- - - - -
4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T3586
unsafeRead and unsafeWrite use 0-based index.
Looking at #3586, the expected output seems to be 2.8e8.
Addresses #27596
- - - - -
3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T21305
writeInt64Array# takes an index measured in units of Int64 elements.
Fixes #27596
- - - - -
44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00
Fix buglet in INLINE-arity calculation for pattern synonyms
This fixes #27744.
The buglet was accidentally introduced by
commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sat Aug 1 00:13:02 2026 +0100
Fix three bugs related to required type args and INLINE pragmas
I failed to find all the calls to `addInlinePragArity`!
- - - - -
df058f1d by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, although their
noise profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates the remaining users of 'all' (and of the 'all'
default) to explicit per-metric collection, ahead of removing 'all'
from the driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved
by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425), residency remains gated via max_bytes_used,
now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all. One is a simplifier-ticks blowup and
the other is stated entirely in allocation numbers, so the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric. They are already gated at 1% via
collect_compiler_runtime. The ghc/max gate had previously broken CI
spuriously (9fd11585eb widened it from 1% to 10% for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
72dd2432 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated
at a single tolerance, although their noise profiles are incompatible,
making such tests either flaky or toothless (#27653).
Closes #27653.
Assisted-by: Claude Fable 5
- - - - -
2228cb30 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Make the deviation argument of collect_stats mandatory
Almost every caller passes an explicit tolerance matched to the
metric's noise profile, and the silent 20% default is far slacker than
'bytes allocated' merits. Only two tests relied on it. They now state
their tolerance explicitly:
large-project gets 10%, in line with other large compile-time tests.
T9848 gets 2%: its metric is byte-for-byte deterministic across CI
jobs and platforms of a given test_env, has drifted only about 2.5%
since 2015, and the fusion failure it guards against would show up as
a roughly +30000% jump.
Assisted-by: Claude Fable 5
- - - - -
56291fc5 by Cheng Shao at 2026-09-02T07:18:45-04:00
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
- - - - -
20eb3f41 by Cheng Shao at 2026-09-02T07:18:45-04:00
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
- - - - -
9e7739ab by Simon Jakobi at 2026-09-03T13:45:43-04:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (`!` or `~`). It complements the
LazyFieldAnnotations extension (4762a8bf30f) from GHC proposal 752,
which makes `~` annotations available for this purpose.
Deciding which fields to report requires their levity, so the check
runs after typechecking. To keep the noise down, the diagnostic is
emitted once per data declaration, grouped by constructor.
Closes #16836.
Assisted-by: Claude Fable 5
- - - - -
6a44d591 by mangoiv at 2026-09-03T13:46:22-04:00
simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
Prior to this commit
commit 08bc245be70d95801bc1138804ed1de9474fbdc0
Author: sheaf <sam.derbyshire(a)gmail.com>
Date: Sat Feb 28 16:30:43 2026 +0100
Clean up join points, casts & ticks
This commit shores up the logic dealing with casts and ticks occurring
in between a join point binding and a jump
any `PlaceRuntime` ticks we observed were profiling ticks, even though that
isn't necessary.
The more liberal rules in the above commit allow e.g. breakpoint ticks
(which are valid PlaceRuntime ticks) to legitimately appear in an argument
position.
Fixes #27556
- - - - -
eaa95a6f by Andrei Borzenkov at 2026-09-04T08:08:40-04:00
Parentheses in prefix GADT constructors (#27423)
Updated `splitLHsGadtTy` to allow looking
through the parentheses for inner binders. General example
of a code pattern that's allowed now:
data S a where
MkS :: (forall a. S a)
That should work now with any combination of nested
foralls and parentheses.
We don't perform parenthesis unwrapping for record
GADT constructors in accordance with GHC Proposal #402.
To this end `con_inner_bndrs` no longer stores plain forall
telescopes: `[HsForAllTelescope pass]` is replaced with
`[LHsGadtTelescope pass]`, a new `HsArg`-style type whose
`HsGadtForAll` holds an inner telescope and whose `HsGadtPar`
holds a pair of parentheses. The parentheses carry no meaning
for renaming or type checking; the only reason to record them
is exact-printing.
Updated `pprConDecl` to improve the `parse == parse . ppr . parse`
property of GADT pretty-printing.
The pretty printer can now output code that's similar to this:
data T a where
MkT1 :: (forall a. T a)
MkT2 :: forall . forall a. T a
These are special cases of inner forall binders
for prefix GADT constructors, when we have either
implicit or zero explicit outer binders.
- - - - -
639456a8 by Evgeny Malyshev at 2026-09-04T08:09:25-04:00
HsPat: Add spacing to unary tuple patterns
Use hsep when printing unary boxed TuplePat applications so that the
MkSolo constructor is separated from its argument. The old hcat rendered
wildcard and parenthesized arguments as MkSolo_ and MkSolo().
Add T27034 to exercise the HsPat splice-dump path with wildcard and
variable arguments, and update existing affected golden output.
Fixes #27034
Assisted-By: OpenAI Codex
- - - - -
2908ab36 by Simon Jakobi at 2026-09-05T07:22:17-04:00
X86 NCG: use btr/bts/btc for single-bit operations
Previously the Cmm patterns
x & ~(1 << i)
x | (1 << i)
x ^ (1 << i)
compiled to mov/shl/not/and-style sequences of 3-4 instructions. Now
they compile to a single btr, bts or btc, matching what C compilers
produce.
When the bit index is a literal, constant folding has already collapsed
these patterns into ones with a literal mask, such as
x & 0xfffffeffffffffff for x & ~(1 << 40). Such masks are now also
compiled to a bit-test instruction when they don't fit in an imm32 and
would otherwise have to be loaded into a register first.
For a variable bit index, this applies only when the shift is unchecked
(uncheckedShiftL#, Data.Bits.unsafeShiftL): the bounds-checked shiftL
used by e.g. the default clearBit/setBit/complementBit implementations
wraps the shift in a bounds mask that this optimisation does not see
through. With a literal index, the bounds mask is constant-folded away,
so the checked operations benefit too.
See Note [Bit-test instructions] in GHC.CmmToAsm.X86.CodeGen.
Fixes #25233.
Assisted-by: Claude Fable 5
- - - - -
c673ecf0 by Simon Peyton Jones at 2026-09-05T07:22:59-04:00
Move HsStatic free-var test to typechecker
A `static` form should have no free *term* variables, but it
can have free *type* variables. Alas, the renamer does not really know what
is a term variable and what is a type variable, because of required type
arguments. This patch moves the test to the typechecker, which does know.
Addresses #27664
- - - - -
5939ceaf by sheaf at 2026-09-05T22:06:18-04:00
Windows: enforce path convention in ./configure
As detailed in Note [MSYS paths] in Hadrian.Utilities, the standing
convention (using Windows-style paths with forward slashes) is now
enforced in ./configure instead of within Hadrian, removing the need for
'cygpath' calls within Hadrian.
Fixes #26683
- - - - -
a6061455 by sheaf at 2026-09-05T22:06:18-04:00
Hadrian: introduce ExeSpawnPath
Specific details about the filepath used to specify the executable to
spawn with CreateProcess matters on Windows: whether we use forward or
backward slashes, a leading ./, or an absolute path changes how the
executable is found.
This commit introduces 'ExeSpawnPath' which is a path that is guaranteed
to be found when spawning a process. All command invocations now go
through this type to ensure the path has been properly sanitised.
See Note [NeedCurrentDirectoryForExePath] in Hadrian.Utilities.
The same treatment is applied to hsc2hs. Updates hsc2hs submodule.
- - - - -
e28313e3 by sheaf at 2026-09-05T22:07:06-04:00
Preserve tick ordering in 'tickTickedExpr'
'GHC.Core.Utils.tickTickedExpr' tries to combine a tick 't1' into an
existing stack of ticks 't2s'. There are two situations:
1. 't1' is subsumed by a tick in 't2s': drop it.
2. A tick in 't2s' is subsumed by 't1', say 't2'.
This commit ensures that in case (2) we keep 't1' on the outside instead
of replacing 't2' at its position in the stack. This avoids re-ordering
source notes (which was the cause of #27749).
This fixes a regression introduced in 2dadf3b0d05.
Fixes #27749
- - - - -
3172f557 by sheaf at 2026-09-05T22:07:06-04:00
Consistently prefer local source note ticks
GHC.Cmm.DebugBlock.cmmDebugGen (DWARF annotations) and
GHC.Stg.Debug.quickSourcePos (-finfo-table-map) both contained logic to
prioritise source note ticks from the current module.
This commit commons up this logic and propagates it to a third consumer:
IPE stack frames, in GHC.Driver.GenerateCgIPEStub.
See the new function GHC.Types.Tickish.bestSourceNote.
- - - - -
192be0b6 by Luite Stegeman at 2026-09-07T19:43:36-04:00
rts: fix ctoi_tuple_spill_words getting out of sync
Fix a few places that were not updating ctoi_tuple_spill_words
correctly, leading to corruption/crashes when dealing with large
unboxed tuples in bytecode:
- captureContinuationAndAbort
- findRetryFrameHelper/findAtomicallyFrameHelper
- interpretBCO bci_BRK_FUN
fixes #27633
- - - - -
604eb43c by Simon Jakobi at 2026-09-07T19:44:15-04:00
Hadrian: don't capture the testsuite driver's output (#27780)
a6061455d54 switched the Testsuite RunTest case from Shake's cmd to the
cmd' wrapper. cmd' always captures stdout and stderr, and since the
caller asks for Exit, it returns without dumping what it captured. As a
result the testsuite output no longer appears in CI job logs: failures,
performance metrics and the summary were lost with the job.
Use cmdExe, the uncaptured cmd, as the other plain-cmd sites in that
commit do.
Fixes #27780.
Assisted-by: Claude Fable 5.1
- - - - -
06eee015 by Luite Stegeman at 2026-09-08T06:03:06+02:00
rts: Fix missing memory barrier in eval_thunk_selector (#27477)
unchain_thunk_selectors() was missing an ACQUIRE_LOAD for the
indirectee, leading to segfaults and corruption during GC on
weakly-ordered architectures.
Fixes #27477
- - - - -
f8b2bd8f by Alan Zimmerman at 2026-09-08T09:39:12-04:00
EPA Fix HsCmdDo exact print with comments
Exact printing of HsCmdDo was ignoring the location for the do
statements, and this is an annotation that can have comments in it.
Update it so we print the statements as a unit, including any
comments.
Also add the result of auditing that we capture comments in all needed
places, noting that the remaining Anno SrcSpan instances are benign.
- - - - -
430967ab by Duncan Coutts at 2026-09-09T11:07:01+01:00
Reorder cmm decls in HeapStackCheck for a better logical grouping
And put more section headers in to deliniate the groups.
We're about to add more here, so better to organise it first.
- - - - -
344080fd by Duncan Coutts at 2026-09-09T11:07:01+01:00
Add raisePrimIOException and add it to RTS<->ghc-internal API
The raisePrimIOException is a new helper function that I/O primops will
use to help them report I/O errors. This is implemented in Haskell
(since that's a lot easier), but has a calling convention that is
easy(ish) to use from Cmm in the I/O primops.
So we add it to the RTS API struct, and since we'll use it from Cmm we
also need a field accessor macro for cmm (in deriveConstants).
See the Note about how we cannot have nice things due to async
exceptions and thunks preventing us from using catch.
- - - - -
cfdb3399 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Add new blocking functions for I/O primops
See the Note [Thread blocking for new I/O primops],
and the Note [Calling convention for raisePrimIOException].
The point is, it will allow us to report synchronous exceptions from I/O
primops, and do so much more flexibly.
Previously the I/O managers could only report async exceptions and only
nullary exceptions. This was OK historically, but no good as we add
more I/O managers and expand the range of I/O operations we support.
- - - - -
f09c09d8 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Change the encoding of results from the I/O manager to I/O primops
Previously we just had async continue or heap overflow.
We now extend what we can report with synchronous success, and
synchronous failure with an errno.
See Note [Encoding of result of I/O manager operations]
We don't use these two new cases yet, but we will. In particular an
epoll I/O manager needs to be able to report synchronous success or
failure for waitRead#/waitWrite#.
- - - - -
30f074d9 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Switch waitRead/Write# to use new blocking return frames
and update the I/O managers to set the result before resuming the
blocked threads.
This makes it possible for I/O managers to report synchronous exceptions
from the I/O primops, but that will be done in a subsequent commit.
- - - - -
a8786ced by Duncan Coutts at 2026-09-09T11:07:01+01:00
Switch Poll and Select I/O managers to report sync exceptions
rather than using raiseAsync with blockedOnBadFD_closure.
This uses the new mechanism in the blocking frame return code to report
synchronous exceptions.
- - - - -
7a9df79a by Duncan Coutts at 2026-09-09T11:07:01+01:00
Remove now-unused blockedOnBadFD
It was previously thrown by the select and poll I/O managers, but now
they use raisePrimIOException (with an EBADF errno).
- - - - -
6b0424e3 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Improve the docs for delay# waitRead# and waitWrite#
Document that the waitRead/Write# can throw exceptions (this was true
before too), and that all of them are async exception cancellation
points.
- - - - -
afeb2c30 by Duncan Coutts at 2026-09-09T20:08:58-04:00
Enable printf warnings for trace functions and fix resulting warnings
Most of the existing printf-style functions are annotated with
attributes to enable gcc/clang warnings for the printf format string,
but several trace functions in Trace.h were missing this annotation.
Enable them, and fix the resulting warnings.
- - - - -
9a442c93 by Alan Zimmerman at 2026-09-09T20:09:36-04:00
EPA: Exact print ConDeclGADT without custom enterAnn
!16321 brought in explicit capture of parens in a ConDeclGADT.
The ExactPrint update introduced a modification of the fundamental
function in exact printing, `enterAnn`, by splitting it into a version
allowing injection of functionality normally handled by the
ExactPrint class methods.
This commit refactors that code, to restore the prior `enterAnn`
version, by following the convention in ExactPrint of introducing a
helper data structure with its own `ExactPrint` instance to achieve
the same effect.
- - - - -
4e9297ea by Simon Jakobi at 2026-09-11T08:32:45-04:00
ci: Actually pass --ignore-perf-failures to the testsuite driver
684c0018d9 changed the IGNORE_PERF_FAILURES handling to append to
RUNTEST_ARGS with bash array syntax, but the three use sites still
expand RUNTEST_ARGS as a scalar, which yields only the first array
element. The appended flag was therefore dropped in every job, so
perf improvements kept failing Marge Bot batches and master pipelines,
contrary to #19562.
Append as a string instead, matching the existing --force-colors
append in test_hadrian.
Fixes #27563.
Assisted-by: Claude Fable 5.1
- - - - -
4f78dfdf by Simon Jakobi at 2026-09-11T08:33:24-04:00
testsuite: Give T26537 a 1.5x compile timeout
Its -fregs-graph compile step tends to time out in unoptimized builds
(#27188, #27776).
Assisted-by: Claude Fable 5.1
- - - - -
130286e3 by Alan Zimmerman at 2026-09-11T08:34:08-04:00
EPA: Remove ListBanana / ListParens from AnnListBrackets
This is a step towards making AnnList only represent
lists of items which can have either braces or layout.
- - - - -
a9827d13 by Simon Jakobi at 2026-09-11T18:53:32-04:00
Reject ~, UNPACK and multiplicity annotations on type data fields
Only `!` was rejected so far. `~`, UNPACK and multiplicity annotations
were silently accepted. None of them make sense at the type level, so
restriction (R3) of Note [Type data declarations] now covers all of
them.
Fixes #27732.
Assisted-by: Claude Fable 5.1
- - - - -
c71d5e23 by Alan Zimmerman at 2026-09-11T18:54:11-04:00
EPA: Add a Note for the exact print main loop
- - - - -
7a108e43 by Simon Jakobi at 2026-09-12T18:35:59-04:00
FastString: Drop mkFastStringWith's constructor callback
All three callers passed the same callback, a partial application of
mkNewFastStringShortByteString to the string being interned. That
partial application is allocated as a closure before the table lookup,
on the common hit path too, although the callback is needed only after
a miss.
Drop the parameter and call mkNewFastStringShortByteString directly
after a miss. Since nothing is passed "with" anymore, rename the
function to internSB.
Suggested by Simon PJ in #27528:
https://gitlab.haskell.org/ghc/ghc/-/work_items/27528#note_687031
Assisted-by: Claude Fable 5
- - - - -
82c73b22 by Alan Zimmerman at 2026-09-12T18:36:38-04:00
EPA: More targeted HsDo exact print annotation
HsDo is multi-purpose, as encoded in its HsDoFlavour field. Some of
these are in a layout context (DoExpr, MDoExpr), others are not
(ListComp, MonadComp).
We are moving towards using AnnList only in layout contexts, so we
switch the HsDo TTG annotation from holding an AnnList for this, to
holding
Either (EpToken "[", EpToken "]") AnnList
This also allows us to trim down AnnListBrackets to only have braces
or None, thereby opening the door for unification with the existing
layout context data type EpLayout.
- - - - -
749d2448 by Luite Stegeman at 2026-09-15T14:07:01-04:00
rts: handle large CONSTR closures in compacting GC
The function update_fwd_large in the compacting GC could run into
an unexpected object with the following error:
internal error: update_fwd_large: unknown/strange object 1
Closure type 1 is CONSTR. This patch adds cases for CONSTR and
CONSTR_NOCAF.
This is the same problem as #27434 (large AP closures, fixed by
cca0d58963), which missed this case.
Fixes #27649
- - - - -
177d6155 by Luite Stegeman at 2026-09-15T14:07:02-04:00
testsuite: fix T27434 test with compacting GC
Ensure that the interpreter is actually run with the compacting GC
- - - - -
5796aa63 by Luite Stegeman at 2026-09-15T14:07:49-04:00
rts: initialise the stack frame header for mask_frame and apply_mask_frame
We must leave the stack in consistent state before jumping to
mask_frame or apply_mask_frame because they may result. Failing
to do so could lead to a crash if there were waiting exceptions.
Fixes #27651
- - - - -
2486cc92 by Simon Peyton Jones at 2026-09-15T14:08:36-04:00
Re-introduce the "weird SpecDict" case
In this commit
commit f80375dd4945350a1d784e436975b48b9c736eaf
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sun Jun 29 15:26:58 2025 +0100
Refactor of Specialise.hs
I removed a test for a dictionary with unbound type variables. This
turned out to be wrong; see #27629.
So this MR re-introduces it.
- - - - -
8e09a46f by Duncan Coutts at 2026-09-15T20:11:35-04:00
Refactor (and rename) removeFromQueues, to simplify I/O managers
Rename it to unblockAndAppendToRunQueue which better reflects what it is
intended to do.
The post-condition for unblockAndAppendToRunQueue is that the TSO is on
the run queue or it is in the process of migrating to another cap.
Previously it achieved that by always directly adding the TSO to the run
queue itself.
But this actually made things more complicated for the I/O managers,
because it meant they needed a separate code path for notifying for
cancellation compared to notifying for completion. The general
notification code would always add the TSO to the run queue itself.
So the improvement is to allow different cases in
unblockAndAppendToRunQueue to achieve the same outcome in different
ways: either directly adding to the run queue or calling helper
functions that do so themselves.
This then allows the new I/O managers to share code between the sync and
async cancellation, and to reuse their notifyIOCompletion helpers for
cancellation. This avoids a source of bugs where the completion path may
be updated but the cancellation path may be forgotten, or similarly in
future for sync/async operations.
Update all the existing in-RTS I/O managers, and the posix timeout code.
- - - - -
d668782c by Duncan Coutts at 2026-09-15T20:11:35-04:00
Document that awaitCompletedTimeoutsOrIO expects an empty run queue
This was true before but implicit and not relied on much. It's better to
be explicit, and allow things to depend on it.
- - - - -
11d938bb by Duncan Coutts at 2026-09-15T20:11:35-04:00
Store the I/O opcode and fd in the StgAsyncIOOp
This will be useful in several I/O managers and it is handy for logging
and debugging.
It also doesn't increase the size of the StgAsyncIOOp structure. There
was enough spare padding space already.
Update the poll I/O manager to set the new fields.
Add a helper function to convert the enum IOReadOrWrite into the enum
IOOpCode. Also change IOReadOrWrite to be an enum without a typedef, for
consistency with other enumerations in IOManager.h
- - - - -
bfa87882 by Duncan Coutts at 2026-09-15T20:11:36-04:00
Add a new I/O manager based on select()
Yes, this is the second such I/O manager, but it is a modern
re-implementation based on the new in-RTS I/O manager infrastructure. So
it is cleaner and faster than the old select I/O manager.
Why do we need another I/O manager based on select? Why isn't the poll()
one good enough as a baseline portable unix I/O manager? Because macOS.
Apple Inc. is why we cannot have nice things.
The man page for poll on macOS documents the fact that it does not work.
At least, it does not work for all files. Specifically, it does not work
for device files. Whereas macOS select() does work for device files.
Aaaaarg!
We _do_ want to deprecate and remove the old select I/O manager, but due to
macOS we cannot do that until we have a replacement. This is that
replacement. Until of course a nice new k-queue I/O manager arrives,
which could become the new default for macOS and FreeBSD.
Interestingly, this select I/O manager is actually faster than the poll
one, on Linix, in some circumstances: specifically when many Haskell
threads are waiting on the same fd. The poll I/O manager does O(n) work
for n threads waiting on I/O, whereas the select one does O(fds) work
for the number of fds that threads are waiting on. Usually this is 1:1,
so it's not noticable, but one can concoct extreme benchmarks to show
the difference.
- - - - -
8e9de15a by Duncan Coutts at 2026-09-15T20:11:36-04:00
Minor updates in the poll I/O manager to keep in sync with select
This keeps it in sync with select one. The changes are based on code review
while implementing the new select I/O manager. The two I/O managers are so
similar in structure that it makes sense to try to minimise the diff between
them. This should aid understanding, and fixes to both in future.
- - - - -
222668bb by Duncan Coutts at 2026-09-15T20:11:36-04:00
Document the new select I/O manager in the user guide
in the RTS section about I/O managers.
And add a changelog entry.
- - - - -
0f48735b by Cheng Shao at 2026-09-15T20:12:23-04:00
Revert "wasm: workaround WebKit bug in dyld"
This reverts commit c9b8465c2c338176fcab9d197e9d31f4aee11f68. The
underlying webkit bug has been fixed in
https://commits.webkit.org/314219@main. Closes #27564.
- - - - -
809294f1 by Luite Stegeman at 2026-09-15T20:13:08-04:00
rts: make stg_threadLabelzh return a valid pointer for unlabeled threads.
This fixes a segfault in the GC caused by stg_threadLabelzh returning
a 0 pointer in a GC pointer field.
stg_threadLabelzh returns a tuple of type (# Int#, ByteArray# #). If a
thread has no label, the second field is unused. We must still return
a valid heap object pointer.
Instead of returning 0, we now return stg_DEAD_SLOT_closure.
fixes #27618
- - - - -
e2d57026 by Luite Stegeman at 2026-09-15T20:13:54-04:00
JS: fix selector thunk handling
fix a bug where entering a selector thunk could lead to a crash
if the selected field was still a thunk.
fixes #27788
- - - - -
1b4a622f by sheaf at 2026-09-17T02:39:13-04:00
Improvements to type/data family handling
This commit makes several improvements to the treatment of type and data
families in GHC:
- We consistently use "type family" instead of "type synonym family"
to avoid a confusion with type synonyms. For example:
- OpenSynFamilyTyCon ==> OpenTypeFamilyTyCon
- ClosedSynFamilyTyCon => ClosedTypeFamilyTyCon
- Various functions that were specific to data families have been
renamed to make this clear. For example:
- tyConFamInst_maybe ==> tyConDataFamInst_maybe
- isFamInstTyCon ==> isDataFamInstTyCon
- 'FamTyConFlav' has been refactored so that all closed type families
share a single 'ClosedTypeFamilyTyCon' constructor.
The corresponding 'IfaceFamTyConFlav' type was updated similarly.
This is then used to tidy up the implementation of
GHC.Core.FamInstEnv.reduceTyFamApp_maybe.
- - - - -
c5aa9098 by sheaf at 2026-09-17T02:39:13-04:00
Small refactor of lookupFamInstEnv
This commit reworks the code for looking up family instances in
GHC.Core.FamInstEnv, reducing duplication a little bit.
No change in behaviour.
- - - - -
a3ad4588 by sheaf at 2026-09-17T02:39:14-04:00
Use a type family for boxing unboxed types
This commit overhauls the way in which unboxed types are boxed, as per
the updated Note [Boxing constructors] in GHC.Builtin.WiredIn.Types.Box.
The main change is to allow boxing to be parametrised by representation,
e.g. making 'Box ty' a valid type. We achieve this by implementing 'Box'
using a closed type family:
newtype Box (a :: TYPE r) = Box (BoxTF r)
type BoxTF :: RuntimeRep -> Type
type family BoxTF r where
BoxTF IntRep = BoxInt
BoxTF Int8Rep = BoxInt8
BoxTF WordRep = BoxWord
BoxTF LiftedRep = Any @Type
BoxTF (TupleRep [r1, r2]) = (BoxTF r1, BoxTF r2)
...
BoxTF (SumRep rs) = BoxSum rs
This new design solves the following problems:
- It allows boxing a type whose 'RuntimeRep' has not yet been
determined (e.g. because we only learn the 'RuntimeRep' after
constraint solving). That is, 'Box @r a' is a perfectly valid type
even if 'r' is an unfilled metavariable. This is a requirement
to fix #25065 (in which we need to box a type whose 'RuntimeRep'
is only filled in during constraint solving).
- It handles boxing of unboxed tuples and unboxed sums, via recursive
boxing. See Note [Boxing TupleRep and SumRep] in
GHC.Builtin.WiredIn.Types.Box.
'Box', 'BoxTF' and all of the boxing data constructors are defined in
a new 'ghc-internal' module, 'GHC.Internal.Box'. This module contains
nothing but wired-in declarations, and GHC never loads its interface on
account of a wired-in thing (GHC.Builtin.Modules.isWiredInOnlyModule);
see Note [Loading instances for wired-in things] in GHC.Iface.Load.
The individual boxing data constructors are deliberately left as an
internal implementation detail, and not exported. The intended API is
via the two new magic 'Id's:
box :: forall {r} (a :: TYPE r). a -> Box a
unbox :: forall {r} (a :: TYPE r). Box a -> a
exported from 'GHC.PrimOps' (in 'ghc-experimental'). The old boxing
data types (IntBox, WordBox, FloatBox, DoubleBox, DictBox) are no longer
exported from base (CLC proposal #421).
We use this new design to consistently desugar:
- parallel comprehensions (GHC.HsToCore.ListComp.{deListComp,dsMcStmt})
- then/group/using statements (GHC.HsToCore.ListComp.{dsTransStmt,dsMcStmt})
- recursive do blocks (mdo/rec) (GHC.HsToCore.Expr.dsDo)
- arrows (GHC.HsToCore.Arrows)
by consistently using 'box'/'unbox' to deal with unboxed types being put
into big tuples. We also adapt 'GHC.Tc.Gen.Match.{tcMcStmt,tcDoStmt}'
to create fresh /representation-polymorphic/ type variables for the
binders that we are going to put into a big tuple (previously, GHC only
allowed lifted types in that position).
These desugaring of box and unbox is explained in
Note [Desugaring box & unbox] in GHC.Core.Make.Box. Roughly speaking,
the desugaring looks like:
box @r @ty e ==> K e
unbox @r @ty e ==> case e of K x -> x
where 'K' is the boxing data constructor at RuntimeRep 'r'. It's
slightly more complicated than that in practice: we first need to
insert a cast (see Note [The canonical type of a RuntimeRep]) using the
new 'CanonicalProv' 'UnivCoProvenance', and unboxed tuples/sums need
recursive logic explained in Note [box/unbox for unboxed tuples and sums],
including a specific 'BoxSum' datatypes for unboxed sum (which also
needed a wired-in type-level list indexing type family (!!)).
The desugaring happens in two ways:
- Occurrences of 'box'/'unbox' emitted by the typechecker are
eliminated in the 'ds_app_var' case of GHC.HsToCore.Expr.
- Other code that needs to box/unbox emits the appropriate code inline.
This includes the big tuple machinery of GHC.Core.Make used by the
desugarer (GHC.HsToCore.{Utils,Binds,Arrows,ListComp}) as well as
the float-out pass (GHC.Core.Opt.SetLevels).
Core Lint checks that no occurrence of 'box'/'unbox' survives desugaring.
On the way, we overhaul the litany of big tuple functions by introducing
data BigTupleLayout = BoxedElements | BareElements
to distinguish the two kinds of "big tuples" that GHC routinely deals
with; for example 'mkBigCoreVarTupSolo' becomes 'mkBigCoreVarTup BareElements'.
All of this code is moved to the new module GHC.Core.Make.BigTuple.
Fixes #20864 #22336 #22473 #25065 #27331
-------------------------
Metric Decrease:
T9872a
-------------------------
- - - - -
4cf7628a by Evgeny Malyshev at 2026-09-17T02:39:56-04:00
Fix the CompactionFailed documentation link to compact
The old Haddock reference links into base instead of ghc-compact.
Use an explicit URL because ghc-compact depends on base, not vice versa.
Fixes #27558
Assisted-By: OpenAI Codex
- - - - -
098bba70 by Facundo Domínguez at 2026-09-17T14:57:41-04:00
Remove the call to hoistFlag from processCmdLineP
This call was undesirable because it was causing a reconstruction
of the list of flag processors.
Even relying on list fusion would not be sufficient because we want
to replace the list with a data structure with faster lookups to
address #25763.
The removal is done in two moves:
* We move CmdLineP to CmdLine.hs
* Then we move the effect of hoisting to processArgs via a new function
hoistEwM
- - - - -
e01799c7 by Facundo Domínguez at 2026-09-17T14:57:42-04:00
Do not create duplicate flag processors for the -W flag
This makes simpler implementing the command line parser with a trie map
holding flag processors. If we were to allow duplicate flag processors
for any flag, the trie map would need to be a multimap.
- - - - -
3875b257 by Facundo Domínguez at 2026-09-17T14:57:42-04:00
Replace the list of flag processors with a trie map in `processArgs`
Previously `GHC.Driver.CmdLine.findArg` was called for each
command-line argument, doing a linear search on the list of flag
processors each time. This was somewhat costly as the list of flag
processor has near 2000 elements.
A lookup with a trie map is faster in general, but the speed up is
only noticeable when calling `GHC.Driver.CmdLine.processArgs` sufficient
times, so the amount of lookups piles up. This could happen in large
projects with many units each having many dependencies. If only a
module needs to be recompiled, the user still needs to wait for all of
the flag parsing to complete.
The benchmark at testsuite/tests/perf/compiler/cmdline-parser shows a
10x speed up when using the trie.
The trie map is implemented in `GHC.Data.StringTrie`.
The design is explained in Note [Processing command line argument fast]
in GHC.Driver.Session.
The commit also:
* adds two property-based tests for trie map operations,
* adds a benchmark of the command line parser, and
* renames the predicate `arg_ok` to `valid_flag_suffix` to decide what
flag processor should be used when there are multiple matching
prefixes on a flag.
Fixes #25763.
- - - - -
5236634a by Andreas Klebinger at 2026-09-17T14:58:24-04:00
Compile Word64{Map,Set} with -O2 always.
This is the hottest code and even if we build GHC with -O0 otherwise -O2
pays off for these modules.
Fixes #27814.
- - - - -
545fd4fb by ARATA Mizuki at 2026-09-22T15:03:24-04:00
Change the C types of hs_{bitrev,bswap}{16,32} to match the Haskell types
bitReverse{16,32}# and byteSwap{16,32}# have the type
Word# -> Word#, but their C correspondents took and
returned StgWord{16,32}.
The mismatch on bitReverse16# led to the problem reported
in #27299. Although no similar issue is reported for
bitReverse32# and byteSwap{16,32}#, it seemed wise to
change them proactively.
This commit fixes the mismatch by changing the C type.
Fixes #27299
- - - - -
bf17f289 by fendor at 2026-09-22T15:04:05-04:00
Don't lose instances from shadowed imported modules with the same name
When we look for instances below the current module, we need to check
the unit id as well when we check for whether the module is the current
module, otherwise we ignore modules we import that have the same name as
the import module.
Adds regression test.
- - - - -
53eead15 by Wolfgang Jeltsch at 2026-09-24T20:12:03-04:00
Correct the documentation of `explicitUnits`
Resolves #27787.
- - - - -
35bf6f4b by Simon Jakobi at 2026-09-24T20:12:49-04:00
Remove stale specHeader bullet from Note [Don't make fillers for constraint types]
The Note said that Specialise.specHeader also calls mkAbsentFiller.
abfc224a27c removed that call: the specialiser no longer drops dead
arguments at all, see Note [Do not drop dead args from specialisations]
in GHC.Core.Opt.Specialise.
Assisted-by: Claude Fable 5.1
- - - - -
1abef1ef by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Improve the `mhu-closure` makefile
- - - - -
a79a68ea by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Make `ghci-multi` hide all unneeded packages
- - - - -
d6e72450 by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Improve the definition of the home unit closure property
- - - - -
c9a2766d by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Re-implement the home unit closure check
Compared to the previous implementation, the new one has the following
advantages:
* It is correct.
- It distinguishes between units that have the same unit ID but
different ABI hashes.
- When `-hide-all-packages` is not used, it considers as home unit
dependencies also units that are made implicitly available
because they are in the package database.
* It seems to be faster in usual settings.
- In particular, it does not have a preparation phase in which it
merges the dependency information from all home units, so that
its running time does not grow linearly with the size of the
package database in normal Cabal scenarios.
* It is (hopefully) clearer.
* It is better documented.
- - - - -
fa96acf8 by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Remove erroneous GHC flag in `hadrian-multi`
The flag was `-Werror=-Wno-error=incomplete-record-selectors`. Its
purpose was to permit incomplete record selectors, but because of the
illegal combination of `-Werror=` and `-Wno-error` it was ignored,
without any negative consequences.
- - - - -
9f48a5b9 by Alan Zimmerman at 2026-09-25T17:02:50-04:00
EPA: Add specific layout field to AnnList
Up to now AnnList has effectively served to represent lists of things
having layout, regardless of whether they do or not.
In Haskell, layout is optional in many places, as there is also an
alternative syntax using braces and semi-colons, which is free-form.
This commit introduces AnnListLayout, a data structure capturing this
distinction.
It is deliberately set up similarly to EpLayout, so that these two can
be merged in time.
The transition is done mechanically, where AnnListBraces is not used
yet, the original Nothing case maps to AnnListNoLayout, and the Just
case to AnnListLayout.
Later commits will fine tune these against the actual parser layout.
- - - - -
1f04ec48 by Andreas Klebinger at 2026-10-14T16:26:28+02:00
x86 ncg: Track the current block in NatM to keep the CFG in sync
The x86 NCG maintains a CFG that is used for code layout. Whenever
instruction selection introduces new basic blocks (for instance for
word-to-float conversions, cmpxchg based atomic operations, ctz on
i386 or 64-bit shifts on i386) it must update this CFG, and the code
that follows must use the new block as its origin.
Before this patch the current block was threaded explicitly through
some statement-level functions only. Block-introducing primops that
were dispatched as "simple" (word2Float#, #27915) and the 64-bit shifts
in iselExpr64 did not update the CFG at all. This resulted in an
out-of-sync CFG, bad code layout and the "Can't find weight for edge
that should have one" panic once the register allocator inserted
fixup blocks (#27694, #27915).
We now keep the current block in the NatM state and provide a few
combinators (continueInNewBlock, addCondBlock, addDiamondFlow,
addColdSelfLoop, increaseEdgeWeight) that update the CFG and the
current block at the same time. See Note [Updating the CFG during
CodeGen] in GHC.CmmToAsm.Monad.
The cfg sanity check (-dasm-lint) now also checks that all branches in
the generated code have a matching edge in the CFG, which catches this
class of bug early.
Fixes #27694, #27915
Co-authored-by: Sylvain Henry <sylvain(a)haskus.fr>
Co-Authored-By: Claude Fable 5.1 <noreply(a)anthropic.com>
- - - - -
583 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- .gitlab/rel_eng/mk-ghcup-metadata/mk_ghcup_metadata.py
- + changelog.d/25763-cmdline-opt
- + changelog.d/26610
- + changelog.d/27556
- + changelog.d/27627
- + changelog.d/27694
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- + changelog.d/27732
- changelog.d/T27202
- + changelog.d/T27629
- + changelog.d/T27657
- + changelog.d/T27705
- + changelog.d/T27722-cbe-entry-block.md
- + changelog.d/T27744
- + changelog.d/T27749
- + changelog.d/T27764
- + changelog.d/T27788-js-selector
- + changelog.d/allow-gadt-prefix-con-parens
- + changelog.d/arm_ncg_fixes_T27430
- + changelog.d/boxing-family
- + changelog.d/clc-421
- + changelog.d/fix-compacting-gc-constr-27649
- + changelog.d/fix-control0-mask-trampoline
- + changelog.d/fix-parallel-gc-selector-race-27477
- + changelog.d/fix-threadlabel-segfault-27618
- + changelog.d/implicit-field-strictness-warning
- changelog.d/lazy-field-annotations
- + changelog.d/llvm-23
- + changelog.d/ncg-x86-bit-test-instructions
- + changelog.d/rethrow-stm
- + changelog.d/select-io-manager
- changelog.d/unit-index
- compiler/GHC/Builtin.hs
- compiler/GHC/Builtin/KnownKeys.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/Builtin/Uniques.hs
- compiler/GHC/Builtin/WiredIn/Ids.hs
- compiler/GHC/Builtin/WiredIn/Prim.hs
- compiler/GHC/Builtin/WiredIn/TypeLits.hs
- compiler/GHC/Builtin/WiredIn/Types.hs
- + compiler/GHC/Builtin/WiredIn/Types/Box.hs
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/Cmm/CommonBlockElim.hs
- compiler/GHC/Cmm/DebugBlock.hs
- compiler/GHC/Cmm/Expr.hs
- compiler/GHC/Cmm/Lint.hs
- compiler/GHC/Cmm/MachOp.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm.hs
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs
- compiler/GHC/CmmToAsm/BlockLayout.hs
- compiler/GHC/CmmToAsm/CFG.hs
- compiler/GHC/CmmToAsm/Format.hs
- compiler/GHC/CmmToAsm/Monad.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Core/Coercion/Axiom.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/FamInstEnv.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Make.hs
- + compiler/GHC/Core/Make/BigTuple.hs
- + compiler/GHC/Core/Make/Box.hs
- compiler/GHC/Core/Opt/SetLevels.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCo/Ppr.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Data/FastString.hs
- + compiler/GHC/Data/StringTrie.hs
- compiler/GHC/Data/Unboxed.hs
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Lazy.hs
- compiler/GHC/Data/Word64Map/Strict.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Data/Word64Set/Internal.hs
- compiler/GHC/Driver/CmdLine.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Binds.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/ListComp.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/Iface/Binary.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Rename.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Iface/Tidy/StaticPtrTable.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Stg/Debug.hs
- compiler/GHC/Stg/Lint.hs
- compiler/GHC/StgToJS/Apply.hs
- compiler/GHC/StgToJS/CodeGen.hs
- compiler/GHC/StgToJS/Symbols.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Instance/Family.hs
- compiler/GHC/Tc/Instance/Typeable.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Solver/Default.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Types/CompleteMatch.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Types/Id.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Types/TyThing.hs
- compiler/GHC/Types/TyThing/Ppr.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/Unique/DFM.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/State.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/Language/Haskell/Syntax/Type.hs
- compiler/ghc.cabal.in
- configure.ac
- docs/users_guide/exts/gadt_syntax.rst
- docs/users_guide/exts/strict.rst
- docs/users_guide/exts/type_data.rst
- docs/users_guide/runtime_control.rst
- docs/users_guide/using-warnings.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/GHCi/UI.hs
- hadrian/README.md
- hadrian/doc/cross-compile.md
- hadrian/doc/flavours.md
- hadrian/doc/make.md
- hadrian/doc/windows.md
- hadrian/ghci-multi-cabal.in
- hadrian/hadrian.cabal
- hadrian/src/Base.hs
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Flavour.hs
- hadrian/src/Hadrian/Builder.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Oracles/Path.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Oracles/Setting.hs
- hadrian/src/Oracles/TestSettings.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Changelog.hs
- hadrian/src/Rules/Codes.hs
- hadrian/src/Rules/Lint.hs
- hadrian/src/Rules/Nofib.hs
- hadrian/src/Rules/Test.hs
- hadrian/src/Settings.hs
- − hadrian/src/Settings/Flavours/Quickest.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- + libraries/base/src/Data/RealFloat.hs
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/Conc.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/base/tests/all.T
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- + libraries/ghc-internal/src/GHC/Internal/Box.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/IO.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Thread.hs
- libraries/ghc-internal/src/GHC/Internal/Exts.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/ghc-internal/src/GHC/Internal/TypeNats.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
- libraries/stm
- linters/lint-codes/LintCodes/Static.hs
- m4/find_python.m4
- + m4/fp_canonicalise_win_path.m4
- m4/fp_find_nm.m4
- m4/fp_find_root.m4
- m4/fp_prog_ar.m4
- m4/fp_prog_ar_args.m4
- m4/fp_prog_sh.m4
- m4/fptools_alex.m4
- m4/fptools_happy.m4
- rts/Capability.c
- rts/CloneStack.c
- rts/Continuation.c
- rts/ContinuationOps.cmm
- rts/HeapStackCheck.cmm
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/Interpreter.c
- rts/Messages.c
- rts/Prelude.h
- rts/PrimOps.cmm
- rts/Printer.c
- rts/ProfHeap.c
- rts/RaiseAsync.c
- rts/STM.c
- rts/Schedule.c
- rts/Sparks.c
- rts/StableName.c
- rts/StgMiscClosures.cmm
- rts/ThreadPaused.c
- rts/Threads.c
- rts/Threads.h
- rts/Trace.h
- rts/configure.ac
- rts/eventlog/EventLog.c
- rts/include/rts/Flags.h
- rts/include/rts/RtsToHsIface.h
- rts/include/rts/storage/Closures.h
- rts/include/stg/MiscClosures.h
- rts/include/stg/Prim.h
- rts/linker/elf_reloc_riscv64.c
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Select.c
- + rts/posix/SelectBis.c
- + rts/posix/SelectBis.h
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/prim/atomic.c
- rts/prim/bitrev.c
- rts/prim/bswap.c
- rts/rts.cabal
- rts/sm/Compact.c
- rts/sm/Evac.c
- rts/sm/GC.c
- rts/sm/GCUtils.c
- rts/sm/MBlock.c
- rts/sm/NonMoving.c
- rts/sm/NonMovingMark.c
- rts/sm/Storage.c
- rts/sm/Sweep.c
- testsuite/driver/README.md
- testsuite/driver/perf_notes.py
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- testsuite/tests/MiniQuickCheck.hs
- testsuite/tests/arrows/should_run/all.T
- + testsuite/tests/arrows/should_run/arrowrun005.hs
- + testsuite/tests/arrows/should_run/arrowrun005.stdout
- + testsuite/tests/bytecode/T27633/Obj.hs
- + testsuite/tests/bytecode/T27633/T27633a.hs
- + testsuite/tests/bytecode/T27633/T27633a.stdout
- + testsuite/tests/bytecode/T27633/T27633b.hs
- + testsuite/tests/bytecode/T27633/T27633b.stdout
- + testsuite/tests/bytecode/T27633/all.T
- testsuite/tests/cmm/should_compile/Makefile
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.stderr
- − testsuite/tests/cmm/should_compile/T27368-ppr-debug.stdout
- testsuite/tests/cmm/should_compile/all.T
- + testsuite/tests/codeGen/should_gen_asm/T25233.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233.hs
- + testsuite/tests/codeGen/should_gen_asm/T25233b.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233b.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
- + testsuite/tests/codeGen/should_run/T27430.hs
- + testsuite/tests/codeGen/should_run/T27430.stdout
- + testsuite/tests/codeGen/should_run/T27430_c.c
- + testsuite/tests/codeGen/should_run/T27533.hs
- + testsuite/tests/codeGen/should_run/T27533.stdout
- + testsuite/tests/codeGen/should_run/T27533_cmm.cmm
- + testsuite/tests/codeGen/should_run/T27537.hs
- + testsuite/tests/codeGen/should_run/T27537.stdout
- + testsuite/tests/codeGen/should_run/T27538.hs
- + testsuite/tests/codeGen/should_run/T27538.stdout
- + testsuite/tests/codeGen/should_run/T27915.hs
- + testsuite/tests/codeGen/should_run/T27915.stdout-ws-32
- + testsuite/tests/codeGen/should_run/T27915.stdout-ws-64
- testsuite/tests/codeGen/should_run/all.T
- + testsuite/tests/concurrent/should_run/T27657a.hs
- + testsuite/tests/concurrent/should_run/T27657a.stdout
- + testsuite/tests/concurrent/should_run/T27657b.hs
- + testsuite/tests/concurrent/should_run/T27657b.stdout
- testsuite/tests/concurrent/should_run/all.T
- + testsuite/tests/core-to-stg/T27627/Callee.hs
- + testsuite/tests/core-to-stg/T27627/Caller.hs
- + testsuite/tests/core-to-stg/T27627/Main.hs
- + testsuite/tests/core-to-stg/T27627/T27627.stdout
- + testsuite/tests/core-to-stg/T27627/all.T
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
- + testsuite/tests/core-to-stg/T27627c/Callee.hs
- + testsuite/tests/core-to-stg/T27627c/Caller.hs
- + testsuite/tests/core-to-stg/T27627c/Main.hs
- + testsuite/tests/core-to-stg/T27627c/T27627c.stdout
- + testsuite/tests/core-to-stg/T27627c/all.T
- + testsuite/tests/core-to-stg/T27627e.hs
- + testsuite/tests/core-to-stg/T27627e.stdout
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- testsuite/tests/core-to-stg/all.T
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/deSugar/should_compile/T25065a.hs
- testsuite/tests/deSugar/should_compile/all.T
- + testsuite/tests/deSugar/should_run/T20864.hs
- + testsuite/tests/deSugar/should_run/T20864.stdout
- + testsuite/tests/deSugar/should_run/T25065b.hs
- + testsuite/tests/deSugar/should_run/T25065b.stdout
- testsuite/tests/deSugar/should_run/all.T
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/Makefile
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
- + testsuite/tests/driver/stringtrie.hs
- + testsuite/tests/driver/stringtrie.stdout
- testsuite/tests/ffi/should_run/T21305.hs
- − testsuite/tests/gadt/T14320.stderr
- testsuite/tests/gadt/T18191.hs
- testsuite/tests/gadt/T18191.stderr
- + testsuite/tests/gadt/T27423a.hs
- + testsuite/tests/gadt/T27423b.hs
- + testsuite/tests/gadt/T27423b.stderr
- testsuite/tests/gadt/all.T
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- + testsuite/tests/ghci.debugger/scripts/T27633c.hs
- + testsuite/tests/ghci.debugger/scripts/T27633c.script
- + testsuite/tests/ghci.debugger/scripts/T27633c.stdout
- + testsuite/tests/ghci.debugger/scripts/T27633c_obj.hs
- testsuite/tests/ghci.debugger/scripts/all.T
- + testsuite/tests/ghci/prog-mhu007/Makefile
- + testsuite/tests/ghci/prog-mhu007/a/A.hs
- + testsuite/tests/ghci/prog-mhu007/all.T
- + testsuite/tests/ghci/prog-mhu007/b/B.hs
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.script
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.stdout
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/Bar.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/testpkg-bar.pkg
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/Foo.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/testpkg-foo.pkg
- + testsuite/tests/ghci/prog-mhu007/unitA
- + testsuite/tests/ghci/prog-mhu007/unitB
- + testsuite/tests/ghci/prog-mhu008/Makefile
- + testsuite/tests/ghci/prog-mhu008/a/M.hs
- + testsuite/tests/ghci/prog-mhu008/all.T
- + testsuite/tests/ghci/prog-mhu008/b/M.hs
- + testsuite/tests/ghci/prog-mhu008/prog-mhu008.script
- + testsuite/tests/ghci/prog-mhu008/prog-mhu008.stdout
- + testsuite/tests/ghci/prog-mhu008/unitA
- + testsuite/tests/ghci/prog-mhu008/unitB
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- + testsuite/tests/indexed-types/should_run/T27331.hs
- + testsuite/tests/indexed-types/should_run/T27331.stdout
- + testsuite/tests/indexed-types/should_run/T27331b.hs
- + testsuite/tests/indexed-types/should_run/T27331b.stdout
- + testsuite/tests/indexed-types/should_run/T27331b_Plugin.hs
- testsuite/tests/indexed-types/should_run/all.T
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-prim-exports.stdout
- testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32
- + testsuite/tests/javascript/T27788.hs
- + testsuite/tests/javascript/T27788.stdout
- testsuite/tests/javascript/all.T
- testsuite/tests/linters/notes.stdout
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- + testsuite/tests/patsyn/should_compile/T27744.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/perf/compiler/T25763.stderr
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/compiler/large-project/all.T
- testsuite/tests/perf/should_run/T3586.hs
- testsuite/tests/perf/should_run/T3586.stdout
- testsuite/tests/perf/space_leaks/all.T
- + testsuite/tests/primops/should_run/T27299.hs
- + testsuite/tests/primops/should_run/T27299.stdout
- testsuite/tests/primops/should_run/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- + testsuite/tests/printer/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- + testsuite/tests/printer/T27423c.hs
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- testsuite/tests/process/process009.hs
- testsuite/tests/process/process009.stdout
- testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr
- testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr
- testsuite/tests/rename/should_fail/T26545.stderr
- + testsuite/tests/rts/T27477.hs
- + testsuite/tests/rts/T27477.stdout
- + testsuite/tests/rts/T27618.hs
- + testsuite/tests/rts/T27618.stdout
- + testsuite/tests/rts/T27649.hs
- + testsuite/tests/rts/T27649.stdout
- testsuite/tests/rts/all.T
- + testsuite/tests/rts/continuations/T27651.hs
- + testsuite/tests/rts/continuations/T27651.stdout
- testsuite/tests/rts/continuations/all.T
- + testsuite/tests/simd/should_run/T27565.hs
- + testsuite/tests/simd/should_run/T27565.stdout
- testsuite/tests/simd/should_run/all.T
- testsuite/tests/simplCore/should_compile/Makefile
- testsuite/tests/simplCore/should_compile/T17966.stderr
- + testsuite/tests/simplCore/should_compile/T27556.hs
- + testsuite/tests/simplCore/should_compile/T27556.script
- + testsuite/tests/simplCore/should_compile/T27629.hs
- + testsuite/tests/simplCore/should_compile/T27629.stderr
- + testsuite/tests/simplCore/should_compile/T27629Plugin.hs
- + testsuite/tests/simplCore/should_compile/T27749.hs
- + testsuite/tests/simplCore/should_compile/T27749.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
- + testsuite/tests/simplCore/should_run/T27705.hs
- + testsuite/tests/simplCore/should_run/T27705.stdout
- + testsuite/tests/simplCore/should_run/T27705_Inst.hs
- testsuite/tests/simplCore/should_run/all.T
- testsuite/tests/th/T17380.stderr
- testsuite/tests/th/T18612.stderr
- + testsuite/tests/th/T27034.hs
- + testsuite/tests/th/T27034.stderr
- testsuite/tests/th/T8761.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/type-data/should_fail/T27732a.hs
- + testsuite/tests/type-data/should_fail/T27732a.stderr
- + testsuite/tests/type-data/should_fail/T27732b.hs
- + testsuite/tests/type-data/should_fail/T27732b.stderr
- + testsuite/tests/type-data/should_fail/T27732c.hs
- + testsuite/tests/type-data/should_fail/T27732c.stderr
- + testsuite/tests/type-data/should_fail/T27732d.hs
- + testsuite/tests/type-data/should_fail/T27732d.stderr
- + testsuite/tests/type-data/should_fail/T27732e.hs
- + testsuite/tests/type-data/should_fail/T27732e.stderr
- + testsuite/tests/type-data/should_fail/T27732f.hs
- + testsuite/tests/type-data/should_fail/T27732f.stderr
- testsuite/tests/type-data/should_fail/TDStrictnessGADT.stderr
- testsuite/tests/type-data/should_fail/TDStrictnessH98.stderr
- testsuite/tests/type-data/should_fail/all.T
- + testsuite/tests/typecheck/should_compile/T27664.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/warnings/should_compile/T16836a.hs
- + testsuite/tests/warnings/should_compile/T16836a.stderr
- + testsuite/tests/warnings/should_compile/T16836b.hs
- + testsuite/tests/warnings/should_compile/T16836c.hs
- + testsuite/tests/warnings/should_compile/T16836c.stderr
- + testsuite/tests/warnings/should_compile/T16836d.hs
- + testsuite/tests/warnings/should_compile/T16836d.stderr
- testsuite/tests/warnings/should_compile/all.T
- + testsuite/tests/warnings/should_fail/WExpectedNoArgError.stderr
- testsuite/tests/warnings/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/deriveConstants/Main.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Target.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
- utils/hsc2hs
- utils/jsffi/dyld.mjs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/601c8e8d21526db86e3fe44dbd09ce…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/601c8e8d21526db86e3fe44dbd09ce…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0