summaryrefslogtreecommitdiff
path: root/compiler/prelude/PrelRules.hs
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2019-06-10 09:25:57 -0400
committerBen Gamari <ben@smart-cactus.org>2019-06-13 12:08:28 -0400
commit0bd3b9dd0428855b6f72f757c1214b5253aa7753 (patch)
tree6d0445b54a09eee68047b78231482a4be6363a47 /compiler/prelude/PrelRules.hs
parent5ce63d52fed05371edb34b4f330c33bc85a45127 (diff)
downloadhaskell-wip/T16742.tar.gz
PrelRules: Don't break let/app invariant in shiftRulewip/T16742
Previously shiftRule would rewrite as invalid shift like ``` let x = I# (uncheckedIShiftL# n 80) in ... ``` to ``` let x = I# (error "invalid shift") in ... ``` However, this breaks the let/app invariant as `error` is not okay-for-speculation. There isn't an easy way to avoid this so let's not try. Instead we just take advantage of the undefined nature of invalid shifts and return zero. Fixes #16742.
Diffstat (limited to 'compiler/prelude/PrelRules.hs')
-rw-r--r--compiler/prelude/PrelRules.hs25
1 files changed, 22 insertions, 3 deletions
diff --git a/compiler/prelude/PrelRules.hs b/compiler/prelude/PrelRules.hs
index 6f77813785..6b93df5494 100644
--- a/compiler/prelude/PrelRules.hs
+++ b/compiler/prelude/PrelRules.hs
@@ -476,8 +476,7 @@ shiftRule shift_op
-> return e1
-- See Note [Guarding against silly shifts]
| shift_len < 0 || shift_len > wordSizeInBits dflags
- -> return $ mkRuntimeErrorApp rUNTIME_ERROR_ID wordPrimTy
- ("Bad shift length " ++ show shift_len)
+ -> return $ Lit $ mkLitNumberWrap dflags LitNumInt 0 (exprType e1)
-- Do the shift at type Integer, but shift length is Int
Lit (LitNumber nt x t)
@@ -702,7 +701,27 @@ can't constant fold it, but if it gets to the assember we get
Error: operand type mismatch for `shl'
So the best thing to do is to rewrite the shift with a call to error,
-when the second arg is stupid.
+when the second arg is large. However, in general we cannot do this; consider
+this case
+
+ let x = I# (uncheckedIShiftL# n 80)
+ in ...
+
+Here x contains an invalid shift and consequently we would like to rewrite it
+as follows:
+
+ let x = I# (error "invalid shift)
+ in ...
+
+This was originally done in the fix to #16449 but this breaks the let/app
+invariant (see Note [CoreSyn let/app invariant] in CoreSyn) as noted in #16742.
+For the reasons discussed in Note [Checking versus non-checking primops] (in
+the PrimOp module) there is no safe way rewrite the argument of I# such that
+it bottoms.
+
+Consequently we instead take advantage of the fact that large shifts are
+undefined behavior (see associated documentation in primops.txt.pp) and
+transform the invalid shift into an "obviously incorrect" value.
There are two cases: