[Git][ghc/ghc][wip/primop-traits] 2 commits: Clear up PrimOp effect semantics

Sebastian Graf gitlab at gitlab.haskell.org
Wed May 27 17:31:33 UTC 2020



Sebastian Graf pushed to branch wip/primop-traits at Glasgow Haskell Compiler / GHC


Commits:
9616d2d1 by Sebastian Graf at 2020-05-27T19:31:24+02:00
Clear up PrimOp effect semantics

Previously we classified PrimOps with two boolean flags, `can_fail` and
`has_side_effects`. Although there is quite a slew of documentation
surrounding them, see `Note [PrimOp can_fail and has_side_effects]`
and `Note [Transformations affected by can_fail and has_side_effects]`,
I found it quite hard to understand and also was confused of
conservative misclassifications for some read-only primops like
`readMutVar#` (which is marked as `has_side_effect`, although it
actually shouldn't per semantics of `has_side_effect`, see #3207), but
not for others (`indexIntArr#`, which is just `can_fail`).

This patch defines a total order of 4 different `PrimOpEffect`s:

- `NoEffect`: A pure primop
- `ThrowsImprecise`: Possibly throws an imprecise exception (and may
  perform read effects)
- `WriteEffect`: May write to a mutable ref cell, array or the world (or
  read from them or throw an imprecise exception)
- `ThrowsPrecise`: May throw a precise exception, or do any of the
  aforementioned effects.

Each effect is strictly "stronger" than its predecessor in this list
wrt. to program transformation that are sound to apply to it.
For example, we may speculatively execute read effects (as long as their
data dependencies such as the state token are satisfied), but we may not
speculate division (for fear of imprecise division-by-zero errors).

Which `PrimOpEffect` inhibits which transformation, including examples,
is spelled out in the rewritten `Note [Transformations affected by
PrimOpEffect]`.

Fixes #17900.

- - - - -
6f871bba by Sebastian Graf at 2020-05-27T19:31:24+02:00
Modify primops.txt.pp to declare PrimOpEffects

- - - - -


7 changed files:

- compiler/GHC/Builtin/PrimOps.hs
- compiler/GHC/Builtin/primops.txt.pp
- compiler/ghc.mk
- distrib/hc-build
- hadrian/src/Rules/Generate.hs
- hadrian/src/Settings/Builders/GenPrimopCode.hs
- utils/genprimopcode/Main.hs


Changes:

=====================================
compiler/GHC/Builtin/PrimOps.hs
=====================================
@@ -312,158 +312,190 @@ perform a heap check or they block.
 primOpOutOfLine :: PrimOp -> Bool
 #include "primop-out-of-line.hs-incl"
 
+
 {-
 ************************************************************************
 *                                                                      *
             Failure and side effects
 *                                                                      *
 ************************************************************************
+-}
 
-Note [Checking versus non-checking primops]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-  In GHC primops break down into two classes:
-
-   a. Checking primops behave, for instance, like division. In this
-      case the primop may throw an exception (e.g. division-by-zero)
-      and is consequently is marked with the can_fail flag described below.
-      The ability to fail comes at the expense of precluding some optimizations.
-
-   b. Non-checking primops behavior, for instance, like addition. While
-      addition can overflow it does not produce an exception. So can_fail is
-      set to False, and we get more optimisation opportunities.  But we must
-      never throw an exception, so we cannot rewrite to a call to error.
-
-  It is important that a non-checking primop never be transformed in a way that
-  would cause it to bottom. Doing so would violate Core's let/app invariant
-  (see Note [Core let/app invariant] in GHC.Core) which is critical to
-  the simplifier's ability to float without fear of changing program meaning.
-
-
-Note [PrimOp can_fail and has_side_effects]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Both can_fail and has_side_effects mean that the primop has
-some effect that is not captured entirely by its result value.
-
-----------  has_side_effects ---------------------
-A primop "has_side_effects" if it has some *write* effect, visible
-elsewhere
-    - writing to the world (I/O)
-    - writing to a mutable data structure (writeIORef)
-    - throwing a synchronous Haskell exception
-
-Often such primops have a type like
-   State -> input -> (State, output)
-so the state token guarantees ordering.  In general we rely *only* on
-data dependencies of the state token to enforce write-effect ordering
-
- * NB1: if you inline unsafePerformIO, you may end up with
-   side-effecting ops whose 'state' output is discarded.
-   And programmers may do that by hand; see #9390.
-   That is why we (conservatively) do not discard write-effecting
-   primops even if both their state and result is discarded.
-
- * NB2: We consider primops, such as raiseIO#, that can raise a
-   (Haskell) synchronous exception to "have_side_effects" but not
-   "can_fail".  We must be careful about not discarding such things;
-   see the paper "A semantics for imprecise exceptions".
-
- * NB3: *Read* effects (like reading an IORef) don't count here,
-   because it doesn't matter if we don't do them, or do them more than
-   once.  *Sequencing* is maintained by the data dependency of the state
-   token.
-
-----------  can_fail ----------------------------
-A primop "can_fail" if it can fail with an *unchecked* exception on
-some elements of its input domain. Main examples:
-   division (fails on zero denominator)
-   array indexing (fails if the index is out of bounds)
-
-An "unchecked exception" is one that is an outright error, (not
-turned into a Haskell exception,) such as seg-fault or
-divide-by-zero error.  Such can_fail primops are ALWAYS surrounded
-with a test that checks for the bad cases, but we need to be
-very careful about code motion that might move it out of
-the scope of the test.
-
-Note [Transformations affected by can_fail and has_side_effects]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The can_fail and has_side_effects properties have the following effect
-on program transformations.  Summary table is followed by details.
-
-            can_fail     has_side_effects
-Discard        YES           NO
-Float in       YES           YES
-Float out      NO            NO
-Duplicate      YES           NO
+-- | A classification of primops by triggered side effects.
+-- See Note [Classification by PrimOpEffect].
+-- The total 'Ord' instance is significant (and hence the order of constructors
+-- is). A "stronger" effect means less transformations are sound to apply to
+-- them.
+data PrimOpEffect
+  = NoEffect
+  -- ^ Triggers no side-effects.
+  | ThrowsImprecise
+  -- ^ May throw an /imprecise/ exception.
+  -- See Note [Precise vs imprecise exceptions] in GHC.Types.Demand.
+  | WriteEffect
+  -- ^ May perform a write effect, such as writing out to a file or a mutable
+  -- ref cell. (Or any of the weaker effects.)
+  | ThrowsPrecise
+  -- ^ May throw a /precise/ exception. (Or any of the weaker effects.)
+  -- See Note [Precise vs imprecise exceptions] in GHC.Types.Demand.
+  deriving (Eq, Ord)
+
+-- | Can we discard a call to the primop, i.e. @case a `op` b of _ -> rhs@?
+-- This is a question that i.e. the Simplifier asks before dropping the @case at .
+-- See Note [Transformations affected by can_fail and has_side_effects].
+isDiscardablePrimOpEffect :: PrimOpEffect -> Bool
+isDiscardablePrimOpEffect eff = eff <= ThrowsImprecise
+
+-- | Can we duplicate a call to the primop?
+-- This is a question that i.e. the Simplifier asks when inlining definitions
+-- involving primops with multiple syntactic occurrences.
+-- See Note [Transformations affected by can_fail and has_side_effects].
+isDupablePrimOpEffect :: PrimOpEffect -> Bool
+-- isDupablePrimOpEffect eff = True -- #3207, see the Note
+isDupablePrimOpEffect eff = eff <= ThrowsImprecise
+
+-- | Can we perform other actions first before entering the primop?
+-- This is the question that i.e. @FloatIn@ asks.
+-- See Note [Transformations affected by can_fail and has_side_effects].
+isDeferrablePrimOpEffect :: PrimOpEffect -> Bool
+isDeferrablePrimOpEffect eff = eff <= WriteEffect
+
+-- | Can we speculatively execute this primop, before performing other actions
+-- that should come first according to evaluation strategy?
+-- This is the question that i.e. @FloatOut@ (of a @case@) asks.
+-- See Note [Transformations affected by can_fail and has_side_effects].
+isSpeculatablePrimOpEffect :: PrimOpEffect -> Bool
+isSpeculatablePrimOpEffect eff = eff <= NoEffect
+
+{- Note [Classification by PrimOpEffect]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Some primops have effects that are not captured entirely by their result value.
+We distinguish these cases:
+
+  * NoEffect: Pure primop, like `plusInt#`.
+  * ThrowsImprecise: Possibly throws an *imprecise* exception, like
+    division-by-zero or a segfault arising from an out of bounds array access.
+    An imprecise exception is an outright error and transformations may play
+    fast and loose by turning one imprecise exception into another, or bottom.
+    See Note [Precise vs imprecise exceptions] in GHC.Types.Demand.
+  * WriteEffect: A write side-effect, either writing to the RealWorld (IO) or
+    to a mutable variable (`writeMutVar#`).
+  * ThrowsPrecise: Possibly throws a *precise* exception. `raiseIO#` is the
+    only primop that does that.
+    See Note [Precise vs imprecise exceptions] in GHC.Types.Demand.
+
+Why is this classification necessary? Because the kind of effect a primop
+performs influences the transformations we are allowed to apply to it.
+For example let binding a division-by-zero (which `ThrowsImprecise`) might
+violate Core's let/app invariant (see Note [Core let/app invariant] in
+GHC.Core) which is critical to the simplifier's ability to float without fear
+of changing program meaning.
+
+See Note [Transformations affected by PrimOpEffect].
+
+Note [Transformations affected by PrimOpEffect]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The PrimOpEffect of a primop affects applicability of program transformations
+in the following way.
+Summary table is followed by details.
+
+Tranform. | Example  | NoEffect | ThrowsImprecise | WriteEffect | ThrowsPrecise
+----------+----------+----------+-----------------+-------------+--------------
+Defer     | FloatIn  | YES      | YES             | YES         | NO
+Discard   | DeadCode | YES      | YES             | NO          | NO
+Dupe      | Inlining | YES      | YES             | NO          | NO
+Speculate | FloatOut | YES      | NO              | NO          | NO
+
+Note how there is a total order on effects in terms of which program
+tranformations they inhibit. A "stronger" effect means less transformations
+are sound to apply to it. NoEffect means any tranformation is sound;
+ThrowsPrecise means none of the following is.
+Whether or not a primop is cheap to evaluate is an orthogonal concern.
 
 * Discarding.   case (a `op` b) of _ -> rhs  ===>   rhs
-  You should not discard a has_side_effects primop; e.g.
-     case (writeIntArray# a i v s of (# _, _ #) -> True
-  Arguably you should be able to discard this, since the
-  returned stat token is not used, but that relies on NEVER
-  inlining unsafePerformIO, and programmers sometimes write
-  this kind of stuff by hand (#9390).  So we (conservatively)
-  never discard a has_side_effects primop.
-
-  However, it's fine to discard a can_fail primop.  For example
-     case (indexIntArray# a i) of _ -> True
-  We can discard indexIntArray#; it has can_fail, but not
-  has_side_effects; see #5658 which was all about this.
-  Notice that indexIntArray# is (in a more general handling of
-  effects) read effect, but we don't care about that here, and
-  treat read effects as *not* has_side_effects.
-
-  Similarly (a `/#` b) can be discarded.  It can seg-fault or
-  cause a hardware exception, but not a synchronous Haskell
+  You should not discard a WriteEffect primop; e.g.
+    case (writeIntArray# a i v s of (# _, _ #) -> True
+  Arguably you should be able to discard this, since the returned state token
+  is not used, but that relies on NEVER inlining unsafePerformIO, and
+  programmers sometimes write this kind of stuff by hand (#9390).  So we
+  (conservatively) never discard such a primop.
+  The situation with (stronger) ThrowsPrecise primops such as raiseIO# is even
+  more restrictive: We may never discard a side effect throwing a precise
   exception.
 
-
-
-  Synchronous Haskell exceptions, e.g. from raiseIO#, are treated
-  as has_side_effects and hence are not discarded.
-
-* Float in.  You can float a can_fail or has_side_effects primop
-  *inwards*, but not inside a lambda (see Duplication below).
-
-* Float out.  You must not float a can_fail primop *outwards* lest
-  you escape the dynamic scope of the test.  Example:
-      case d ># 0# of
-        True  -> case x /# d of r -> r +# 1
-        False -> 0
-  Here we must not float the case outwards to give
-      case x/# d of r ->
-      case d ># 0# of
-        True  -> r +# 1
-        False -> 0
-
-  Nor can you float out a has_side_effects primop.  For example:
-       if blah then case writeMutVar# v True s0 of (# s1 #) -> s1
-               else s0
-  Notice that s0 is mentioned in both branches of the 'if', but
-  only one of these two will actually be consumed.  But if we
-  float out to
-      case writeMutVar# v True s0 of (# s1 #) ->
-      if blah then s1 else s0
-  the writeMutVar will be performed in both branches, which is
-  utterly wrong.
-
-* Duplication.  You cannot duplicate a has_side_effect primop.  You
-  might wonder how this can occur given the state token threading, but
-  just look at Control.Monad.ST.Lazy.Imp.strictToLazy!  We get
-  something like this
-        p = case readMutVar# s v of
-              (# s', r #) -> (S# s', r)
-        s' = case p of (s', r) -> s'
-        r  = case p of (s', r) -> r
-
+  However, it's fine to discard a ThrowsImprecise primop.  For example
+    case (indexIntArray# a i) of _ -> True
+  We can discard indexIntArray#; it might throw an imprecise segmentation
+  fault, but no precise exception, so we are OK with not observing it.
+  See #5658 which was all about this.
+  Similarly (a `/#` b) can be discarded.  It can seg-fault or cause a hardware
+  exception, but not a precise Haskell exception.
+  It's obviously fine to discard a NoEffect if its result aren't used.
+
+* Duplication.  Example: The Simplifier inlines a (multi occ) binding.
+  You cannot duplicate any effectful primop participating in state token
+  threading. Not even what is actually a read-only effect like `readMutVar#`,
+  see #3207.
+  You might wonder how that can be problematic, but just look at
+  Control.Monad.ST.Lazy.Imp.strictToLazy!  We get something like this
+    p = case readMutVar# s v of
+          (# s', r #) -> (S# s', r)
+    s' = case p of (s', r) -> s'
+    r  = case p of (s', r) -> r
   (All these bindings are boxed.)  If we inline p at its two call
   sites, we get a catastrophe: because the read is performed once when
   s' is demanded, and once when 'r' is demanded, which may be much
-  later.  Utterly wrong.  #3207 is real example of this happening.
-
-  However, it's fine to duplicate a can_fail primop.  That is really
-  the only difference between can_fail and has_side_effects.
+  later.  Utterly wrong.  #3207 is a real example of this happening.
+
+  If it wasn't for working around state token threading
+  (see https://gitlab.haskell.org/ghc/ghc/issues/3207#note_257470 for other
+  approaches), then duplication wouldn't be an issue at all, soundness-wise.
+  But for the time being, we mark primops that participate in state token
+  threading such as `readMutVar#` (which has NoEffect at heart) and
+  `readArray#` (ThrowsImprecise) as WriteEffect and say that we may not
+  duplicate WriteEffect.
+
+* Deferring (Float In).  Example, here inside a single-alt case:
+    case (a `op` b) of (# s, x #) -> case e of p -> rhs
+    ==>
+    case e of p -> case (a `op` b) of (# s, x #) -> rhs
+  Note that e might diverge (or throw an imprecise exception) and thus the
+  side-effect we would observe by evaluating op might not happen if we defer it
+  after e.
+
+  That is a problem if op ThrowsPrecise: If e diverges, the user can catch
+  the precise exception /before/ FloatIn, but not afterwards. Hence we may not
+  float in a ThrowsPrecise primop like raiseIO#.
+
+  But since e can never throw an imprecise exception, there is no
+  non-imprecise-exceptional control flow in which it is possible to observe
+  that a WriteEffect (and anything "weaker") didn't happen. So it's OK to
+  defer (every weaker than or equal to) write effects. So you can float a
+  WriteEffect *inwards*, but not inside a lambda (see Duplication below [SG: It
+  isn't obvious to me how that explains why we shouldn't float inside a lambda
+  at all]).
+
+* Speculating (Float out).
+  You must not float a ThrowsImprecise primop *outwards* lest you escape the
+  dynamic scope of the test.  Example:
+    case d ># 0# of
+      True  -> case x /# d of r -> r +# 1
+      False -> 0
+  Here we must not float the division outwards to give
+    case x/# d of r ->
+    case d ># 0# of
+      True  -> r +# 1
+      False -> 0
+  Now the potential division by zero will be performed in both branches.
+
+  Similarly you can't float out a (stronger) WriteEffect primop.  For example:
+    if blah then case writeMutVar# v True s0 of (# s1 #) -> s1
+            else s0
+  Notice that s0 is mentioned in both branches of the 'if', but only one of
+  these two will actually be consumed.  But if we float out to
+    case writeMutVar# v True s0 of (# s1 #) ->
+      if blah then s1 else s0
+  the writeMutVar will be performed in both branches, which is utterly wrong.
 
 Note [Implementation: how can_fail/has_side_effects affect transformations]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -488,11 +520,16 @@ Two main predicates on primpops test these flags:
     has_side_effects things (very very very) not-cheap!
 -}
 
-primOpHasSideEffects :: PrimOp -> Bool
-#include "primop-has-side-effects.hs-incl"
+primOpEffect :: PrimOp -> PrimOpEffect
+#include "primop-effect.hs-incl"
+
+primOpOkForSideEffects :: PrimOp -> Bool
+-- This is exactly @isDupablePrimOpEffect (primOpEffect op)@
+primOpOkForSideEffects op = primOpEffect op < WriteEffect
 
 primOpCanFail :: PrimOp -> Bool
-#include "primop-can-fail.hs-incl"
+-- This is exactly @isSpeculatablePrimOpEffect (primOpEffect op)@
+primOpCanFail op = primOpEffect op < ThrowsImprecise
 
 primOpOkForSpeculation :: PrimOp -> Bool
   -- See Note [PrimOp can_fail and has_side_effects]


=====================================
compiler/GHC/Builtin/primops.txt.pp
=====================================
The diff for this file was not included because it is too large.

=====================================
compiler/ghc.mk
=====================================
@@ -108,11 +108,10 @@ $(eval $(call compilerConfig,2))
 PRIMOP_BITS_NAMES = primop-data-decl.hs-incl        \
                     primop-tag.hs-incl              \
                     primop-list.hs-incl             \
-                    primop-has-side-effects.hs-incl \
+                    primop-effect.hs-incl \
                     primop-out-of-line.hs-incl      \
                     primop-commutable.hs-incl       \
                     primop-code-size.hs-incl        \
-                    primop-can-fail.hs-incl         \
                     primop-strictness.hs-incl       \
                     primop-fixity.hs-incl           \
                     primop-primop-info.hs-incl      \
@@ -143,16 +142,14 @@ compiler/stage$1/build/primop-tag.hs-incl: compiler/stage$1/build/primops.txt $$
 	"$$(genprimopcode_INPLACE)" --primop-tag         < $$< > $$@
 compiler/stage$1/build/primop-list.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
 	"$$(genprimopcode_INPLACE)" --primop-list        < $$< > $$@
-compiler/stage$1/build/primop-has-side-effects.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
-	"$$(genprimopcode_INPLACE)" --has-side-effects   < $$< > $$@
+compiler/stage$1/build/primop-effect.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
+	"$$(genprimopcode_INPLACE)" --effect   < $$< > $$@
 compiler/stage$1/build/primop-out-of-line.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
 	"$$(genprimopcode_INPLACE)" --out-of-line        < $$< > $$@
 compiler/stage$1/build/primop-commutable.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
 	"$$(genprimopcode_INPLACE)" --commutable         < $$< > $$@
 compiler/stage$1/build/primop-code-size.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
 	"$$(genprimopcode_INPLACE)" --code-size          < $$< > $$@
-compiler/stage$1/build/primop-can-fail.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
-	"$$(genprimopcode_INPLACE)" --can-fail           < $$< > $$@
 compiler/stage$1/build/primop-strictness.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)
 	"$$(genprimopcode_INPLACE)" --strictness         < $$< > $$@
 compiler/stage$1/build/primop-fixity.hs-incl: compiler/stage$1/build/primops.txt $$$$(genprimopcode_INPLACE)


=====================================
distrib/hc-build
=====================================
@@ -73,11 +73,10 @@ HappyCmd="$PWD/distrib/fake-happy" ./configure --with-ghc="$PWD/compiler/ghc-inp
 PRIMOP_BITS="primop-data-decl.hs-incl \
 	      primop-tag.hs-incl  \
 	      primop-list.hs-incl  \
-	      primop-has-side-effects.hs-incl  \
+	      primop-effect.hs-incl  \
 	      primop-out-of-line.hs-incl  \
 	      primop-commutable.hs-incl  \
 	      primop-needs-wrapper.hs-incl  \
-	      primop-can-fail.hs-incl  \
 	      primop-strictness.hs-incl  \
 	      primop-usage.hs-incl  \
 	      primop-primop-info.hs-incl"


=====================================
hadrian/src/Rules/Generate.hs
=====================================
@@ -59,12 +59,11 @@ compilerDependencies = do
             , notStage0 ? isGmp ? return [gmpPath -/- "include/ghc-gmp.h"]
             , notStage0 ? return ((rtsPath -/-) <$> libffiHeaderFiles)
             , return $ fmap (ghcPath -/-)
-                  [ "primop-can-fail.hs-incl"
-                  , "primop-code-size.hs-incl"
+                  [ "primop-code-size.hs-incl"
                   , "primop-commutable.hs-incl"
                   , "primop-data-decl.hs-incl"
+                  , "primop-effect.hs-incl"
                   , "primop-fixity.hs-incl"
-                  , "primop-has-side-effects.hs-incl"
                   , "primop-list.hs-incl"
                   , "primop-out-of-line.hs-incl"
                   , "primop-primop-info.hs-incl"


=====================================
hadrian/src/Settings/Builders/GenPrimopCode.hs
=====================================
@@ -9,11 +9,10 @@ genPrimopCodeBuilderArgs = builder GenPrimopCode ? mconcat
     , output "//primop-data-decl.hs-incl"          ? arg "--data-decl"
     , output "//primop-tag.hs-incl"                ? arg "--primop-tag"
     , output "//primop-list.hs-incl"               ? arg "--primop-list"
-    , output "//primop-has-side-effects.hs-incl"   ? arg "--has-side-effects"
+    , output "//primop-effect.hs-incl"             ? arg "--effect"
     , output "//primop-out-of-line.hs-incl"        ? arg "--out-of-line"
     , output "//primop-commutable.hs-incl"         ? arg "--commutable"
     , output "//primop-code-size.hs-incl"          ? arg "--code-size"
-    , output "//primop-can-fail.hs-incl"           ? arg "--can-fail"
     , output "//primop-strictness.hs-incl"         ? arg "--strictness"
     , output "//primop-fixity.hs-incl"             ? arg "--fixity"
     , output "//primop-primop-info.hs-incl"        ? arg "--primop-primop-info"


=====================================
utils/genprimopcode/Main.hs
=====================================
@@ -124,10 +124,10 @@ main = getArgs >>= \args ->
                       "--data-decl"
                          -> putStr (gen_data_decl p_o_specs)
 
-                      "--has-side-effects"
+                      "--effect"
                          -> putStr (gen_switch_from_attribs
-                                       "has_side_effects"
-                                       "primOpHasSideEffects" p_o_specs)
+                                       "effect"
+                                       "primOpEffect" p_o_specs)
 
                       "--out-of-line"
                          -> putStr (gen_switch_from_attribs
@@ -144,11 +144,6 @@ main = getArgs >>= \args ->
                                        "code_size"
                                        "primOpCodeSize" p_o_specs)
 
-                      "--can-fail"
-                         -> putStr (gen_switch_from_attribs
-                                       "can_fail"
-                                       "primOpCanFail" p_o_specs)
-
                       "--strictness"
                          -> putStr (gen_switch_from_attribs
                                        "strictness"
@@ -514,20 +509,25 @@ gen_latex_doc (Info defaults entries)
 
            mk_options o =
              "\\primoptions{"
-              ++ mk_has_side_effects o ++ "}{"
+              ++ mk_effect o ++ "}{"
               ++ mk_out_of_line o ++ "}{"
               ++ mk_commutable o ++ "}{"
               ++ mk_needs_wrapper o ++ "}{"
-              ++ mk_can_fail o ++ "}{"
               ++ mk_fixity o ++ "}{"
               ++ latex_encode (mk_strictness o) ++ "}{"
               ++ "}"
 
-           mk_has_side_effects o = mk_bool_opt o "has_side_effects" "Has side effects." "Has no side effects."
+           mk_effect o = case lookup_attrib "effect" o of
+             Just (OptionString _ "NoEffect")        -> ""
+             Just (OptionString _ "ThrowsImprecise") -> "May throw an imprecise exception."
+             Just (OptionString _ "WriteEffect")     -> "May throw an imprecise exception or perform a write effect."
+             Just (OptionString _ "ThrowsPrecise")   -> "May throw a precise exception (or any other effect)."
+             Just v                                  -> error "Wrong value for effect: '" ++ show v ++ "'"
+             Nothing                                 -> ""
+
            mk_out_of_line o = mk_bool_opt o "out_of_line" "Implemented out of line." "Implemented in line."
            mk_commutable o = mk_bool_opt o "commutable" "Commutable." "Not commutable."
            mk_needs_wrapper o = mk_bool_opt o "needs_wrapper" "Needs wrapper." "Needs no wrapper."
-           mk_can_fail o = mk_bool_opt o "can_fail" "Can fail." "Cannot fail."
 
            mk_bool_opt o opt_name if_true if_false =
              case lookup_attrib opt_name o of
@@ -774,6 +774,7 @@ gen_switch_from_attribs attrib_name fn_name (Info defaults entries)
          getAltRhs (OptionString _ s) = s
          getAltRhs (OptionVector _) = "True"
          getAltRhs (OptionFixity mf) = show mf
+         getAltRhs (OptionEffect eff) = show eff
 
          mkAlt po
             = case lookup_attrib attrib_name (opts po) of



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/80544f093969dc264d2f01de87056e2d0489e750...6f871bba28298024524ee7708b855aa0667a59da

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/80544f093969dc264d2f01de87056e2d0489e750...6f871bba28298024524ee7708b855aa0667a59da
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200527/ee2963c9/attachment-0001.html>


More information about the ghc-commits mailing list