content: {-# 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
  