Skip to content

Don't reconstruct sum types if the type subtly changes

Consider this test case:

module Main (main) where

fun :: Either Int String -> Either String String
fun x = case x of
    Left int -> Left (show int)
    Right str -> Right str
{-# NOINLINE fun #-}

main :: IO ()
main = do
    l <- getLine
    print $ fun (Right l)

The core we get for fun looks like:

Main.fun [InlPrag=NOINLINE]
  :: Data.Either.Either GHC.Types.Int GHC.Base.String
     -> Data.Either.Either GHC.Base.String GHC.Base.String
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType <S,1*U>]
Main.fun =
  \ (x_aur :: Data.Either.Either GHC.Types.Int GHC.Base.String) ->
    case x_aur of _ [Occ=Dead] {
      Data.Either.Left int_aus ->
        Data.Either.Left
          @ GHC.Base.String
          @ GHC.Base.String
          (GHC.Show.$fShowInt_$cshow int_aus);
      Data.Either.Right str_aCG ->
        Data.Either.Right @ GHC.Base.String @ GHC.Base.String str_aCG
    }

There would be less allocations and probably perform better if we just coerced x into the new type. Because the coercion is common code, this also means that if hypothetically some other sum type which had 15 constructors, and only 3 had subtle type changes, 12 of the case branches could be CSE'd into the single coerce greatly reducing code generated and also hinting the inliner better.

Trac metadata
Trac field Value
Version 7.8.2
Type FeatureRequest
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information