{-# language RankNTypes, FlexibleContexts, FlexibleInstances, ImpredicativeTypes, MultiParamTypeClasses, DataKinds, ConstraintKinds #-}

-- | The implementation details in this module are subject to change
-- and breaking without a corresponding PVP version bump. Import at your
-- own risk.
module RequireCallStack.Internal where

import Unsafe.Coerce
import GHC.Stack

-- | If you're running into this class, then you need to add
-- 'RequireCallStack' to your function's signature, or discharge the
-- constraint using 'provideCallStack'.
--
-- I'd like to provide a 'TypeError' instance here with a good message, but
-- unfortunately, I won't be able to do that because it fails early. I need
-- @Unsatisfiable@ in GHC 9.8 for that. So, until I get that, you'll  have
-- to see an error message that smuggles the suggestion in:
--
-- > No instance for 'Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack'
--
-- Which, hey, it is better than nothing!
--
-- @since 0.1.0.0
class Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack

-- | An alias to make referring to
-- 'Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack' easier,
-- since it is a bit of a mouthful.
--
-- If you see this, you probably need to either add 'RequireCallStack' to
-- the function constraints, or you need to call 'provideCallStack' to
-- discharge it.
--
-- @since 0.1.0.0
type RequireCallStackImpl = Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack

-- | An internal detail. This is a specialization of the trick used in the
-- @reflection@ library to reify constraints. It's based on some GHC
-- trickery - notably, that dictionaries become runtime parameters, and
-- a no method dictionary has the same runtime rep as @()@.
--
-- @since 0.1.0.0
newtype MagicCallStack r = MagicCallStack (RequireCallStackImpl => r)

-- | Satisfy a 'RequireCallStack' constraint for the given block. Can be
-- used instead of propagating a 'RequireCallStack' up the call graph.
--
-- Usage:
--
-- @
-- main :: IO ()
-- main = do
--   provideCallStack $ do
--       errorRequireCallStack "hello"
-- @
--
-- Note how 'main' does not have a 'HasCallStack' or 'RequireCallStack'
-- constraint. This function eliminates them, so that
-- 'errorRequireCallStack' can be called without compilation error.
--
-- @since 0.1.0.0
provideCallStack :: HasCallStack => (RequireCallStackImpl => r) -> r
provideCallStack :: forall r. HasCallStack => (RequireCallStackImpl => r) -> r
provideCallStack RequireCallStackImpl => r
r = (forall a b. a -> b
unsafeCoerce (forall r. (RequireCallStackImpl => r) -> MagicCallStack r
MagicCallStack RequireCallStackImpl => r
r) :: () -> r) ()