-- | Inline the definition of a type constructor applied to variables,
-- adding the result to the environment if it succeeds.
inlineBaseTcS :: (Ord c, Ord tc, Ord tv)
  => TcEnv v c tc tv -> Set tc -> tc
  -> BaseInliner c tc tv (TypeRank, [(Weight, DataPat c tc tv)])
inlineBaseTcS env stack tc = do
  s <- get
  remember s $ do
    let stack' = Set.insert tc stack
        (vs, cs) = tyConEnv env Map.! tc
    mixedAlts <- forM cs $ \c -> do
      let Forall _ ty = conEnv env Map.! c
          (cargs, _) = unFun ty
      case cargs of
        -- Failed attempts are left unrecorded, as they may later succeed
        -- when the corresponding type constructor is at the root instead.
        [] -> return (Simple, always (DPCon c []))
        _ -> do
          cargs' <- forM cargs $ inlineBaseS env stack'
          return $ priorityCon c cargs'
    return $ filterSimplest mixedAlts
  where
    -- If it has already been inlined previously, return the memoized value.
    remember s m =
      case Map.lookup tc (_inlined s) of
        Just alts -> return alts
        Nothing -> do
          alts <- m
          case fst alts of
            Cyclic -> return ()
            _ -> inlined %= Map.insert tc alts
          return alts

-- | Combine all alternatives of constructor arguments.
inlineBaseTcS :: (Ord c, Ord tc, Ord tv)
  => TcEnv v c tc tv -> Set tc -> tc
  -> BaseInliner c tc tv (TypeRank, [(Weight, DataPat c tc tv)])
inlineBaseTcS env stack tc = do
  s <- get
  remember s $ do
    let stack' = Set.insert tc stack
        (vs, cs) = tyConEnv env Map.! tc
    mixedAlts <- forM cs $ \c -> do
      let Forall _ ty = conEnv env Map.! c
          (cargs, _) = unFun ty
      case cargs of
        -- Failed attempts are left unrecorded, as they may later succeed
        -- when the corresponding type constructor is at the root instead.
        [] -> return (Simple, always (DPCon c []))
        _ -> do
          cargs' <- forM cargs $ inlineBaseS env stack'
          return $ priorityCon c cargs'
    return $ filterSimplest mixedAlts
  where
    -- If it has already been inlined previously, return the memoized value.
    remember s m =
      case Map.lookup tc (_inlined s) of
        Just alts -> return alts
        Nothing -> do
          alts <- m
          case fst alts of
            Cyclic -> return ()
            _ -> inlined %= Map.insert tc alts
          return alts

-- | Combine all alternatives of constructor arguments.
inlineBaseTcS env stack tc = do
  s <- get
  remember s $ do
    let stack' = Set.insert tc stack
        (vs, cs) = tyConEnv env Map.! tc
    mixedAlts <- forM cs $ \c -> do
      let Forall _ ty = conEnv env Map.! c
          (cargs, _) = unFun ty
      case cargs of
        -- Failed attempts are left unrecorded, as they may later succeed
        -- when the corresponding type constructor is at the root instead.
        [] -> return (Simple, always (DPCon c []))
        _ -> do
          cargs' <- forM cargs $ inlineBaseS env stack'
          return $ priorityCon c cargs'
    return $ filterSimplest mixedAlts
  where
    -- If it has already been inlined previously, return the memoized value.
    remember s m =
      case Map.lookup tc (_inlined s) of
        Just alts -> return alts
        Nothing -> do
          alts <- m
          case fst alts of
            Cyclic -> return ()
            _ -> inlined %= Map.insert tc alts
          return alts

-- | Combine all alternatives of constructor arguments.