Skip to content

Simplifier wrongly discards erroring function call

Steps to reproduce

Compile with optimisations and run the following module:

{-# LANGUAGE GHC2021, UnboxedTuples #-}
module Main (main) where

newtype Tricky = TrickyCon { unTrickyCon :: (# #) -> Tricky }

main :: IO ()
main = do
  let
    tricky :: Tricky
    {-# OPAQUE tricky #-}
    tricky = TrickyCon $ \(# #) -> TrickyCon $ \(# #) ->
      error "tricky called with at least two args"

    applyToN :: Int -> Tricky -> Tricky
    {-# OPAQUE applyToN #-}
    applyToN n a | n == 0    = a
                 | otherwise = applyToN (n - 1) a `unTrickyCon` (# #)

  case applyToN 12345 tricky of
    !_ -> putStrLn "unreachable"

The function tricky is applied to far more than two args, so the error call in its body should be reached. But when run, the program prints "unreachable" and exits successfully instead of failing with "tricky called with at least two args". (This wrong behavior persists even with -O -fpedantic-bottoms -fno-state-hack.)

Inspecting the -ddump-simpl and -dverbose-core2core output suggests the simplifier is eliminating the applyToN stuff entirely.

Environment

  • GHC version used: 9.8.1
Edited by Matthew Craven
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information