Marge Bot pushed to branch master 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 - - - - - 12 changed files: - + changelog.d/clc-341-strict-mutable-data-types - 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 - 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 Changes: ===================================== changelog.d/clc-341-strict-mutable-data-types ===================================== @@ -0,0 +1,9 @@ +section: base +synopsis: Add strict variants to mutable types (IORef, Chan, MVar) +issues: #27343 +mrs: !15450 + +description: { + These new types prevent users from (easily) mis-using existing and dangerous APIs (writeIORef, modifyIORef, modifyMVar, etc) which can lead to space leaks when stored in mutable containers. + They also prevent invisible coercions to and from their lazy equivalents by not exposing their constructors. +} ===================================== libraries/base/base.cabal.in ===================================== @@ -41,6 +41,7 @@ Library Control.Applicative , Control.Concurrent , Control.Concurrent.Chan + , Control.Concurrent.Chan.Strict , Control.Concurrent.QSem , Control.Concurrent.QSemN , Control.Monad.IO.Class @@ -80,6 +81,7 @@ Library , Control.Arrow , Control.Category , Control.Concurrent.MVar + , Control.Concurrent.MVar.Strict , Control.Exception , Control.Exception.Annotation , Control.Exception.Backtrace @@ -109,6 +111,7 @@ Library , Data.Functor.Const , Data.Functor.Identity , Data.IORef + , Data.IORef.Strict , Data.Int , Data.Ix , Data.Kind ===================================== libraries/base/changelog.md ===================================== @@ -12,6 +12,7 @@ * Add `withEmptyCallStack` to `GHC.Stack`. ([CLC proposal #428](https://github.com/haskell/core-libraries-committee/issues/428)) * Add new `Data.RealFloat` module re-exporting `RealFloat` from `GHC.Float` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394)) * Add `Infinity`, `NegInfinity`, and `NaN` pattern synonyms to `Data.RealFloat` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394)) + * Add strict variants to mutable types (IORef, Chan, MVar). ([CLC Proposal #341](https://github.com/haskell/core-libraries-committee/issues/341)) ## 4.23.0.0 *TBA* * Add `System.IO.hGetNewlineMode`. ([CLC proposal #370](https://github.com/haskell/core-libraries-committee/issues/370)) ===================================== libraries/base/src/Control/Concurrent/Chan.hs ===================================== @@ -18,6 +18,8 @@ -- @stm@ (software transactional memory) library has a more robust implementation -- of channels called @TChan@s. -- +-- A variant that forces its contents to weak head normal form is available in +-- "Control.Concurrent.Chan.Strict". ----------------------------------------------------------------------------- module Control.Concurrent.Chan ===================================== libraries/base/src/Control/Concurrent/Chan/Strict.hs ===================================== @@ -0,0 +1,81 @@ +-- | Unbounded channels whose contents are always in weak head normal form +-- (WHNF). +-- +-- This module is a thin wrapper around "Control.Concurrent.Chan": each +-- operation has the same semantics as its counterpart there, except that the +-- value written to the channel is forced to WHNF first. +-- "Control.Concurrent.Chan" is the reference documentation for these +-- operations; only the differences are repeated here. +-- +-- The caveats of the lazy module apply unchanged. In particular a 'Chan' is +-- /unbounded/: a producer that outruns its consumer makes the channel grow +-- without limit. The @stm@ package offers channels with a different +-- concurrency model, and @TBQueue@ in particular is bounded. +-- +-- Note that WHNF is not deep: writing @Just <thunk>@ to a +-- @'Chan' (Maybe a)@ still writes a thunk inside the @Just@. +module Control.Concurrent.Chan.Strict + ( Chan + + -- * Operations + , newChan + , writeChan + , readChan + , dupChan + , getChanContents + , writeList2Chan + ) where + +import Data.Foldable (mapM_) +import Data.Functor +import GHC.Base +import qualified Control.Concurrent.Chan as Lazy + +-- | An unbounded FIFO channel, wrapping @Chan@ from "Control.Concurrent.Chan". +-- +-- The values it holds are always in WHNF: every operation of this module forces +-- a value before it is written. +newtype Chan a = Chan (Lazy.Chan a) + deriving Eq + +-- | Build and return a new instance of 'Chan'. +-- +-- See @newChan@ in "Control.Concurrent.Chan" for the full semantics. +newChan :: IO (Chan a) +newChan = Chan <$> Lazy.newChan + +-- | Write a value to a 'Chan'. +-- +-- See @writeChan@ in "Control.Concurrent.Chan" for the full semantics. +-- +-- The value is forced to WHNF before it is written. +writeChan :: Chan a -> a -> IO () +writeChan (Chan chan) a = Lazy.writeChan chan =<< (pure $! a) + +-- | Read the next value from the 'Chan'. Blocks when the channel is empty. +-- +-- See @readChan@ in "Control.Concurrent.Chan" for the full semantics, in +-- particular for the fairness guarantees and for the +-- 'Control.Exception.BlockedIndefinitelyOnMVar' exception. +readChan :: Chan a -> IO a +readChan (Chan chan) = Lazy.readChan chan + +-- | Duplicate a 'Chan'. +-- +-- See @dupChan@ in "Control.Concurrent.Chan" for the full semantics. +dupChan :: Chan a -> IO (Chan a) +dupChan (Chan chan) = Chan <$> Lazy.dupChan chan + +-- | Return a lazy list representing the contents of the supplied 'Chan'. +-- +-- See @getChanContents@ in "Control.Concurrent.Chan" for the full semantics. +getChanContents :: Chan a -> IO [a] +getChanContents (Chan chan) = Lazy.getChanContents chan + +-- | Write an entire list of items to a 'Chan'. +-- +-- See @writeList2Chan@ in "Control.Concurrent.Chan" for the full semantics. +-- +-- Evaluates the list elements to WHNF. +writeList2Chan :: Chan a -> [a] -> IO () +writeList2Chan = mapM_ . writeChan ===================================== libraries/base/src/Control/Concurrent/MVar.hs ===================================== @@ -65,6 +65,11 @@ -- in an 'MVar' to the appropriate normal form, or utilize a strict -- @MVar@ provided by the [strict-concurrency](https://hackage.haskell.org/package/strict-concurrency) package. -- +-- A variant that forces its contents to weak head normal form is available in +-- "Control.Concurrent.MVar.Strict". Note that weak head normal form is often +-- not the appropriate normal form: it does not force the fields of the +-- constructor it exposes. +-- -- === Ordering -- -- 'MVar' operations are always observed to take place in the order ===================================== libraries/base/src/Control/Concurrent/MVar/Strict.hs ===================================== @@ -0,0 +1,202 @@ +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE TupleSections #-} + +-- | Synchronising variables whose contents are always in weak head normal form +-- (WHNF). +-- +-- This module is a thin wrapper around "Control.Concurrent.MVar": each +-- operation has the same semantics as its counterpart there, except that the +-- value put in the variable is forced to WHNF first. +-- "Control.Concurrent.MVar" is the reference documentation for these +-- operations — for the fairness and ordering guarantees, for the atomicity +-- caveats of the \"bigger\" functions, and for worked examples; only the +-- differences are repeated here. +-- +-- Note that WHNF is not deep: putting @Just <thunk>@ in an +-- @'MVar' (Maybe a)@ still puts a thunk inside the @Just@. + +module Control.Concurrent.MVar.Strict + ( MVar + + -- * Operations + , newEmptyMVar + , newMVar + , takeMVar + , putMVar + , readMVar + , swapMVar + , tryTakeMVar + , tryPutMVar + , tryReadMVar + , isEmptyMVar + , withMVar + , withMVarMasked + , modifyMVar_ + , modifyMVar + , modifyMVarMasked_ + , modifyMVarMasked + , mkWeakMVar + ) where + +import Data.Functor +import GHC.Base +import GHC.Weak (Weak) +import qualified Control.Concurrent.MVar as Lazy + +-- | A synchronising variable, wrapping @MVar@ from "Control.Concurrent.MVar". +-- It can be thought of as a box, which may be empty or full. +-- +-- The value it holds is always in WHNF: every operation of this module forces +-- the value before it is put in the box. +newtype MVar a = MVar (Lazy.MVar a) + deriving stock (Eq) + +-- | Create an 'MVar' which is initially empty. +-- +-- See @newEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics. +newEmptyMVar :: IO (MVar a) +newEmptyMVar = MVar <$> Lazy.newEmptyMVar + +-- | Create an 'MVar' which contains the supplied value. +-- +-- See @newMVar@ in "Control.Concurrent.MVar" for the full semantics. +-- +-- The initial value is forced to WHNF before it is stored. +newMVar :: a -> IO (MVar a) +newMVar a = fmap MVar . Lazy.newMVar =<< (pure $! a) + +-- | Return the contents of the 'MVar', leaving it empty. Blocks while the +-- 'MVar' is empty. +-- +-- See @takeMVar@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for the single-wakeup and FIFO guarantees. +takeMVar :: MVar a -> IO a +takeMVar (MVar var) = Lazy.takeMVar var + +-- | Atomically read the contents of an 'MVar'. Blocks while the 'MVar' is +-- empty. +-- +-- See @readMVar@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for the multiple-wakeup guarantee. +readMVar :: MVar a -> IO a +readMVar (MVar var) = Lazy.readMVar var + +-- | Put a value into an 'MVar'. Blocks while the 'MVar' is full. +-- +-- See @putMVar@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for the single-wakeup and FIFO guarantees. +-- +-- The value is forced to WHNF before it is put. +putMVar :: MVar a -> a -> IO () +putMVar (MVar var) a = Lazy.putMVar var =<< (pure $! a) + +-- | A non-blocking version of 'takeMVar'. +-- +-- See @tryTakeMVar@ in "Control.Concurrent.MVar" for the full semantics. +tryTakeMVar :: MVar a -> IO (Maybe a) +tryTakeMVar (MVar var) = Lazy.tryTakeMVar var + +-- | A non-blocking version of 'putMVar'. +-- +-- See @tryPutMVar@ in "Control.Concurrent.MVar" for the full semantics. +-- +-- The value is forced to WHNF before it is put. Note that it is forced whether +-- or not the 'MVar' turns out to be empty. +tryPutMVar :: MVar a -> a -> IO Bool +tryPutMVar (MVar var) a = Lazy.tryPutMVar var =<< (pure $! a) + +-- | Take a value from an 'MVar', put a new value into the 'MVar' and return the +-- value taken. +-- +-- See @swapMVar@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for its atomicity caveat. +-- +-- The new value is forced to WHNF before it is put. +swapMVar :: MVar a -> a -> IO a +swapMVar (MVar var) a = Lazy.swapMVar var =<< (pure $! a) + +-- | A non-blocking version of 'readMVar'. +-- +-- See @tryReadMVar@ in "Control.Concurrent.MVar" for the full semantics. +tryReadMVar :: MVar a -> IO (Maybe a) +tryReadMVar (MVar var) = Lazy.tryReadMVar var + +-- | Check whether a given 'MVar' is empty. +-- +-- See @isEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics. The +-- result is only a snapshot, so prefer 'tryTakeMVar' where possible. +isEmptyMVar :: MVar a -> IO Bool +isEmptyMVar (MVar var) = Lazy.isEmptyMVar var + +-- | An exception-safe wrapper for operating on the contents of an 'MVar'. +-- +-- See @withMVar@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for its atomicity caveat. +withMVar :: MVar a -> (a -> IO b) -> IO b +withMVar (MVar var) action = Lazy.withMVar var action +{-# INLINE withMVar #-} + +-- | Like 'withMVar', but the @IO@ action in the second argument is executed +-- with asynchronous exceptions masked. +-- +-- See @withMVarMasked@ in "Control.Concurrent.MVar" for the full semantics. +withMVarMasked :: MVar a -> (a -> IO b) -> IO b +withMVarMasked (MVar var) action = Lazy.withMVarMasked var action +{-# INLINE withMVarMasked #-} + +-- | An exception-safe wrapper for modifying the contents of an 'MVar'. +-- +-- See @modifyMVar_@ in "Control.Concurrent.MVar" for the full semantics, in +-- particular for its atomicity caveat. +-- +-- The new value is forced to WHNF before it is put back. +modifyMVar_ :: MVar a -> (a -> IO a) -> IO () +modifyMVar_ (MVar var) action = Lazy.modifyMVar_ var $ \a0 -> do + a <- action a0 + pure $! a +{-# INLINE modifyMVar_ #-} + +-- | A slight variation on 'modifyMVar_' that allows a value to be returned +-- (@b@) in addition to the modified value of the 'MVar'. +-- +-- See @modifyMVar@ in "Control.Concurrent.MVar" for the full semantics. +-- +-- The new value is forced to WHNF before it is put back. The returned value is +-- not forced. +modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b +modifyMVar (MVar var) action = Lazy.modifyMVar var $ \a0 -> do + (a, b) <- action a0 + (, b) <$> (pure $! a) +{-# INLINE modifyMVar #-} + +-- | Like 'modifyMVar_', but the @IO@ action in the second argument is executed +-- with asynchronous exceptions masked. +-- +-- See @modifyMVarMasked_@ in "Control.Concurrent.MVar" for the full semantics. +-- +-- The new value is forced to WHNF before it is put back. +modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO () +modifyMVarMasked_ (MVar var) action = Lazy.modifyMVarMasked_ var $ \a0 -> do + a <- action a0 + pure $! a +{-# INLINE modifyMVarMasked_ #-} + +-- | Like 'modifyMVar', but the @IO@ action in the second argument is executed +-- with asynchronous exceptions masked. +-- +-- See @modifyMVarMasked@ in "Control.Concurrent.MVar" for the full semantics. +-- +-- The new value is forced to WHNF before it is put back. The returned value is +-- not forced. +modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b +modifyMVarMasked (MVar var) action = Lazy.modifyMVarMasked var $ \a0 -> do + (a, b) <- action a0 + (, b) <$> (pure $! a) +{-# INLINE modifyMVarMasked #-} + +-- | Make a 'Weak' pointer to an 'MVar', using the second argument as a +-- finalizer to run when the 'MVar' is garbage-collected. +-- +-- See @mkWeakMVar@ in "Control.Concurrent.MVar" for the full semantics. +mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a)) +mkWeakMVar (MVar var) finalizer = coerce <$> Lazy.mkWeakMVar var finalizer ===================================== libraries/base/src/Data/IORef.hs ===================================== @@ -12,6 +12,8 @@ -- -- Mutable references in the IO monad. -- +-- A variant that forces its contents to weak head normal form is available in +-- "Data.IORef.Strict". module Data.IORef (-- * IORefs ===================================== libraries/base/src/Data/IORef/Strict.hs ===================================== @@ -0,0 +1,93 @@ +{-# LANGUAGE DerivingStrategies #-} + +-- | Mutable references in the IO monad whose contents are always in weak head +-- normal form (WHNF). +-- +-- This module is a thin wrapper around "Data.IORef": each operation has the +-- same semantics as its counterpart there, except that the value put in the +-- reference is forced to WHNF first. "Data.IORef" is the reference +-- documentation for these operations; only the differences are repeated here. +-- +-- Note that WHNF is not deep: storing @Just <thunk>@ in an +-- @'IORef' (Maybe a)@ still stores a thunk inside the @Just@. + +module Data.IORef.Strict + ( IORef + + -- * Operations + , newIORef + , readIORef + , writeIORef + , modifyIORef + , atomicModifyIORef + , atomicWriteIORef + , mkWeakIORef + ) where + +import GHC.Base +import GHC.Weak (Weak) +import qualified Data.IORef as Lazy + +-- | A mutable reference in the IO monad, wrapping @IORef@ from "Data.IORef". +-- +-- The value it holds is always in WHNF: every operation of this module forces +-- the value before it is written. +newtype IORef a = IORef (Lazy.IORef a) + deriving stock (Eq) + +-- | Build a new 'IORef'. +-- +-- See @newIORef@ in "Data.IORef" for the full semantics. +-- +-- The initial value is forced to WHNF before it is stored. +newIORef :: a -> IO (IORef a) +newIORef a = fmap IORef . Lazy.newIORef =<< (pure $! a) + +-- | Read the value of an 'IORef'. +-- +-- See @readIORef@ in "Data.IORef" for the full semantics, in particular for the +-- memory model that applies to reads and writes. +readIORef :: IORef a -> IO a +readIORef (IORef var) = Lazy.readIORef var + +-- | Write a new value into an 'IORef'. +-- +-- See @writeIORef@ in "Data.IORef" for the full semantics. This function does +-- not create a memory barrier; use 'atomicWriteIORef' when that matters. +-- +-- The new value is forced to WHNF before it is stored. +writeIORef :: IORef a -> a -> IO () +writeIORef (IORef var) a = Lazy.writeIORef var =<< (pure $! a) + +-- | Mutate the contents of an 'IORef', combining 'readIORef' and 'writeIORef'. +-- +-- See @modifyIORef@ in "Data.IORef" for the full semantics. This is not an +-- atomic update; use 'atomicModifyIORef' in a multithreaded environment. +-- +-- The new value is forced to WHNF before it is stored. +modifyIORef :: IORef a -> (a -> a) -> IO () +modifyIORef (IORef var) f = Lazy.modifyIORef' var f + +-- | Atomically modify the contents of an 'IORef'. +-- +-- See @atomicModifyIORef@ in "Data.IORef" for the full semantics, in particular +-- for the atomicity and memory-barrier guarantees. +-- +-- Both the new value and the returned value are forced to WHNF. +atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b +atomicModifyIORef (IORef var) f = Lazy.atomicModifyIORef' var f + +-- | Variant of 'writeIORef' that imposes a reordering barrier. +-- +-- See @atomicWriteIORef@ in "Data.IORef" for the full semantics. +-- +-- The new value is forced to WHNF before it is stored. +atomicWriteIORef :: IORef a -> a -> IO () +atomicWriteIORef (IORef var) a = Lazy.atomicWriteIORef var =<< (pure $! a) + +-- | Make a 'Weak' pointer to an 'IORef', using the second argument as a +-- finalizer to run when the 'IORef' is garbage-collected. +-- +-- See @mkWeakIORef@ in "Data.IORef" for the full semantics. +mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a)) +mkWeakIORef (IORef var) finalizer = fmap coerce (Lazy.mkWeakIORef var finalizer) ===================================== testsuite/tests/interface-stability/base-exports.stdout ===================================== @@ -173,6 +173,17 @@ module Control.Concurrent.Chan where writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () +module Control.Concurrent.Chan.Strict where + -- Safety: None + type Chan :: * -> * + newtype Chan a = ... + dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a) + getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a] + newChan :: forall a. GHC.Internal.Types.IO (Chan a) + readChan :: forall a. Chan a -> GHC.Internal.Types.IO a + writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () + writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () + module Control.Concurrent.MVar where -- Safety: Safe type MVar :: * -> * @@ -196,6 +207,28 @@ module Control.Concurrent.MVar where withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b +module Control.Concurrent.MVar.Strict where + -- Safety: None + type MVar :: * -> * + newtype MVar a = ... + isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a)) + modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a) + newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a) + putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO () + readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a + takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + module Control.Concurrent.QSem where -- Safety: Safe type QSem :: * @@ -1286,6 +1319,18 @@ module Data.IORef where readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () +module Data.IORef.Strict where + -- Safety: None + type IORef :: * -> * + newtype IORef a = ... + atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b + atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a)) + modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO () + newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a) + readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a + writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + module Data.Int where -- Safety: Safe type Int :: * @@ -11232,6 +11277,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’ @@ -11291,6 +11338,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’ instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’ +instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs ===================================== @@ -173,6 +173,17 @@ module Control.Concurrent.Chan where writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () +module Control.Concurrent.Chan.Strict where + -- Safety: None + type Chan :: * -> * + newtype Chan a = ... + dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a) + getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a] + newChan :: forall a. GHC.Internal.Types.IO (Chan a) + readChan :: forall a. Chan a -> GHC.Internal.Types.IO a + writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () + writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () + module Control.Concurrent.MVar where -- Safety: Safe type MVar :: * -> * @@ -196,6 +207,28 @@ module Control.Concurrent.MVar where withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b +module Control.Concurrent.MVar.Strict where + -- Safety: None + type MVar :: * -> * + newtype MVar a = ... + isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a)) + modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a) + newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a) + putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO () + readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a + takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + module Control.Concurrent.QSem where -- Safety: Safe type QSem :: * @@ -1286,6 +1319,18 @@ module Data.IORef where readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () +module Data.IORef.Strict where + -- Safety: None + type IORef :: * -> * + newtype IORef a = ... + atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b + atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a)) + modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO () + newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a) + readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a + writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + module Data.Int where -- Safety: Safe type Int :: * @@ -11264,6 +11309,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’ @@ -11323,6 +11370,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’ instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’ +instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-mingw32 ===================================== @@ -173,6 +173,17 @@ module Control.Concurrent.Chan where writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () +module Control.Concurrent.Chan.Strict where + -- Safety: None + type Chan :: * -> * + newtype Chan a = ... + dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a) + getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a] + newChan :: forall a. GHC.Internal.Types.IO (Chan a) + readChan :: forall a. Chan a -> GHC.Internal.Types.IO a + writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO () + writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO () + module Control.Concurrent.MVar where -- Safety: Safe type MVar :: * -> * @@ -196,6 +207,28 @@ module Control.Concurrent.MVar where withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b +module Control.Concurrent.MVar.Strict where + -- Safety: None + type MVar :: * -> * + newtype MVar a = ... + isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a)) + modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b + modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO () + newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a) + newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a) + putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO () + readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a + takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a + tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool + tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a) + withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b + module Control.Concurrent.QSem where -- Safety: Safe type QSem :: * @@ -1286,6 +1319,18 @@ module Data.IORef where readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () +module Data.IORef.Strict where + -- Safety: None + type IORef :: * -> * + newtype IORef a = ... + atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b + atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a)) + modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO () + newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a) + readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a + writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO () + module Data.Int where -- Safety: Safe type Int :: * @@ -11490,6 +11535,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’ instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’ +instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’ instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’ @@ -11549,6 +11596,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’ instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’ +instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’ instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’ View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8cfb1a5eed7fce615b42bd783631c0f... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8cfb1a5eed7fce615b42bd783631c0f... 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