st2 0.1.0.0 → 0.1.0.1
raw patch · 3 files changed
+66/−6 lines, 3 files
Files
- ChangeLog.md +6/−1
- src/Control/Monad/ST2.hs +59/−4
- st2.cabal +1/−1
ChangeLog.md view
@@ -1,5 +1,10 @@ # Revision history for st2 -## 0.1.0.0 -- YYYY-mm-dd+## 0.1.0.1 -- 2018-11-02++* Fix internal bug that could cause semantic differences under optimisations,+ related to implementation of runRegion#.++## 0.1.0.0 -- 2018-07-23 * First version. Released on an unsuspecting world.
src/Control/Monad/ST2.hs view
@@ -102,11 +102,12 @@ import Data.Semigroup (Semigroup((<>))) import GHC.IO (IO(IO),unsafeDupableInterleaveIO) import GHC.MVar (readMVar, putMVar, newEmptyMVar)-import GHC.Prim (State#, realWorld#, unsafeCoerce#, MutVar#, newMutVar#, readMutVar#, writeMutVar#, sameMutVar#, RealWorld, noDuplicate#)+import GHC.Prim (State#, unsafeCoerce#, MutVar#, newMutVar#, readMutVar#, writeMutVar#, sameMutVar#, RealWorld, noDuplicate#) import GHC.Show (Show(showsPrec, showList), showString, showList__) import GHC.Types (RuntimeRep, TYPE, Any, isTrue#) import Theory.Named (type (~~)) import Unsafe.Coerce (unsafeCoerce)+import qualified GHC.Magic as GHCMagic -- | Convert an ST2 to an ST toBaseST :: ST s a -> BaseST.ST s a@@ -119,7 +120,7 @@ fromBaseST = unsafeCoerce -- The state-transformer monad proper. By default the monad is strict;--- too many people got bitten by space leaks when it was lazy.+-- too many people got bit by space leaks when it was lazy. -- | The strict state-transformer monad. -- A computation of type @'ST' s a@ transforms an internal state indexed@@ -390,9 +391,64 @@ runRegion# :: forall (r :: RuntimeRep) (o :: TYPE r) s. (State# (Any ~~ s) -> o) -> o-runRegion# m = m (rwToAny# realWorld#)+runRegion# m = GHCMagic.runRW# (unsafeCoerce# m) -- m = m (rwToAny# realWorld#) {-# INLINE runRegion# #-} +{- Note [runRegion#]+ ~~~~~~~~~~~~~~~~~+Originally, `runRegion#` was defined quite similarly to runRW#:+ runRegion# :: forall (r :: RuntimeRep) (o :: TYPE r) s.+ (State# (Any ~~ s) -> o) -> o+ runRegion# m = m (rwToAny# realWorld#)++But this definition is extremely brittle under optimisations! You should never+define a function that performs as `runRW#` does without defining it in _terms_+of `runRW#`. You can get semantically undesirable floating - runRW# is treated+specially by Core and inlined only very late in compilation, after floating is+complete. Below, I will inline "Note [runRW magic]" which is written in ghc's+compiler/coreSyn/CorePrep.hs:++Some definitions, for instance @runST@, must have careful control over float out +of the bindings in their body. Consider this use of @runST@, + + f x = runST ( \ s -> let (a, s') = newArray# 100 [] s + (_, s'') = fill_in_array_or_something a x s' + in freezeArray# a s'' ) + +If we inline @runST@, we'll get: + + f x = let (a, s') = newArray# 100 [] realWorld#{-NB-} + (_, s'') = fill_in_array_or_something a x s' + in freezeArray# a s'' + +And now if we allow the @newArray#@ binding to float out to become a CAF, +we end up with a result that is totally and utterly wrong: + + f = let (a, s') = newArray# 100 [] realWorld#{-NB-} -- YIKES!!! + in \ x -> + let (_, s'') = fill_in_array_or_something a x s' + in freezeArray# a s'' + +All calls to @f@ will share a {\em single} array! Clearly this is nonsense and +must be prevented. + +This is what @runRW#@ gives us: by being inlined extremely late in the +optimization (right before lowering to STG, in CorePrep), we can ensure that +no further floating will occur. This allows us to safely inline things like +@runST@, which are otherwise needlessly expensive (see #10678 and #5916). + +'runRW' is defined (for historical reasons) in GHC.Magic, with a NOINLINE +pragma. It is levity-polymorphic. + + runRW# :: forall (r1 :: RuntimeRep). (o :: TYPE r) + => (State# RealWorld -> (# State# RealWorld, o #)) + -> (# State# RealWorld, o #) + +It needs no special treatment in GHC except this special inlining+in CorePrep (and in ByteCodeGen). ++-}+ rwToAny# :: forall s s'. State# s' -> State# (Any ~~ s) rwToAny# x# = unsafeCoerce# x# {-# INLINE rwToAny# #-}@@ -415,4 +471,3 @@ repFromAny# :: STRep (Any ~~ s) a -> (State# s -> (# State# s, a #)) repFromAny# = unsafeCoerce# {-# INLINE repFromAny# #-}-
st2.cabal view
@@ -1,5 +1,5 @@ name: st2-version: 0.1.0.0+version: 0.1.0.1 synopsis: shared heap regions between local mutable state threads description: This library implements the ST2 monad, a type using GDP (ghosts of departed proofs)