diff options
author | Ben Gamari <ben@smart-cactus.org> | 2019-06-10 09:25:57 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2019-06-13 12:08:28 -0400 |
commit | 0bd3b9dd0428855b6f72f757c1214b5253aa7753 (patch) | |
tree | 6d0445b54a09eee68047b78231482a4be6363a47 /compiler/prelude/PrelRules.hs | |
parent | 5ce63d52fed05371edb34b4f330c33bc85a45127 (diff) | |
download | haskell-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.hs | 25 |
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: |