{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE UndecidableInstances      #-}
{-# LANGUAGE GADTs                     #-}
{-# LANGUAGE PolyKinds                 #-}
{-# LANGUAGE TypeApplications          #-}

---------------  The original bug report --------------
--
-- See T12734a for a smaller version

module T12734 where

import Prelude
import Data.Kind
import Control.Applicative
import Control.Monad.Fix
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Class
import Control.Monad.IO.Class


data A
data B
data Net
data Ty

data Layer4 t l
data TermStore

-- Helpers: Stack

data Stack layers (t :: Type -> Type) where
    SLayer :: t l -> Stack ls t -> Stack (l ': ls) t
    SNull  :: Stack '[] t

instance ( Constructor m (t l)
         , Constructor m (Stack ls t)) => Constructor m (Stack (l ': ls) t)
instance Monad m                       => Constructor m (Stack '[]       t)


-- Helpers: Expr

newtype Expr  t layers    = Expr (TermStack t layers)
type TermStack t layers = Stack layers (Layer4 (Expr t layers))


-- Helpers: Funny typeclass

class Monad m => Constructor m t

instance ( Monad m, expr ~ Expr t layers, Constructor m (TermStack t layers)
         ) => Constructor m (Layer4 expr Ty)


-- HERE IS A FUNNY BEHAVIOR: the commented line raises context reduction stack
-- overflow
test_gr ::
    ( Constructor m (TermStack t layers), Inferable A layers m, Inferable B t m
      , bind ~ Expr t layers
--  ) => m (Expr t layers)
    ) => m bind
test_gr = undefined


-- Explicit information about a type which could be inferred

class Monad m => Inferable (cls :: Type) (t :: k) m | cls m -> t

newtype KnownTyx (cls :: Type) (t :: k) (m :: Type -> Type) (a :: Type) =
    KnownTyx (IdentityT m a)
  deriving (Show, Functor, Monad, MonadIO, MonadFix, MonadTrans,
            Applicative, Alternative)

instance {-# OVERLAPPABLE #-} (t ~ t', Monad m) =>
  Inferable cls t (KnownTyx cls t' m)
instance {-# OVERLAPPABLE #-} (Inferable cls t n, MonadTrans m, Monad (m n)) =>
  Inferable cls t (m n)


runInferenceTx :: forall cls t m a. KnownTyx cls t m a -> m a
runInferenceTx = undefined



-- running it

test_ghc_err :: (MonadIO m, MonadFix m)
        => m (Expr Net '[Ty])
test_ghc_err = runInferenceTx @B  @Net
             $ runInferenceTx @A @'[Ty]
             $ (test_gr)
