{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-unbox-small-strict-fields #-}
-- Makes f2 a bit more challenging
-- Tests inspired by Note [CPR examples] in DmdAnal, and Trac #10482
module Foo where
h :: Int -> Int -> Bool
h 0 y = y>0
h n y = h (n-1) y
-- The main point: all of these functions can have the CPR property
------- f1 -----------
-- x is used strictly by h, so it'll be available
-- unboxed before it is returned in the True branch
f1 :: Int -> Int
f1 x = case h x x of
True -> x
False -> f1 (x-1)
------- f2 -----------
-- x is a strict field of MkT2, so we'll pass it unboxed
-- to $wf2, so it's available unboxed. This depends on
-- the case expression analysing (a subcomponent of) one
-- of the original arguments to the function, so it's
-- a bit more delicate.
data T2 = MkT2 !Int Int
f2 :: T2 -> Int
f2 (MkT2 x y) | y>0 = f2 (MkT2 x (y-1))
| y>1 = 1
| otherwise = x
------- f3 -----------
-- h is strict in x, so x will be unboxed before it
-- is rerturned in the otherwise case.
data T3 = MkT3 Int Int
f3 :: T3 -> Int
f3 (MkT3 x y) | h x y = f3 (MkT3 x (y-1))
| otherwise = x
------- f4 -----------
-- Just like f2, but MkT4 can't unbox its strict
-- argument automatically, as f2 can
data family Foo a
newtype instance Foo Int = Foo Int
data T4 a = MkT4 !(Foo a) Int
f4 :: T4 Int -> Int
f4 (MkT4 x@(Foo v) y) | y>0 = f4 (MkT4 x (y-1))
| otherwise = v