{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
#if __GLASGOW_HASKELL__ < 806
{-# LANGUAGE TypeInType #-}
#endif
{-# OPTIONS_GHC -Wno-missing-methods #-}
module Clash.Class.HasDomain.HasSpecificDomain where
import Clash.Class.HasDomain.CodeGen (mkHasDomainTuples)
import Clash.Class.HasDomain.Common
import Clash.Sized.Vector (Vec)
import Clash.Signal.Internal
(Signal, Domain, Clock, Reset, Enable)
import Clash.Signal.Delayed.Internal (DSignal)
import Data.Proxy (Proxy)
import Data.Kind (Type)
import Type.Errors
(IfStuck, DelayError, Pure, ErrorMessage(ShowType))
type Outro =
""
:$$$: "------"
:$$$: ""
:$$$: "You tried to apply an explicitly routed clock, reset, or enable line"
:$$$: "to a construct with, possibly, an implicitly routed one. Clash failed to"
:$$$: "unambigously link the given domain (by passing in a 'Clock', 'Reset', or"
:$$$: "'Enable') to the component passed in."
:$$$: ""
type NotFoundError (dom :: Domain) (t :: Type) =
"Could not find domain '" :<<>>: 'ShowType dom :<<>>: "' in the following type:"
:$$$: ""
:$$$: " " :<<>>: t
:$$$: ""
:$$$: "If that type contains that domain anyway, you might need to provide an"
:$$$: "additional type instance of HasDomain. Example implementations:"
:$$$: ""
:$$$: " * type instance HasDomain dom (MyVector n a) = HasDomain dom a"
:$$$: " * type instance HasDomain dom1 (MyCircuit dom2 a) = DomEq dom1 dom2"
:$$$: " * type instance HasDomain dom1 (MyTuple a b) = Merge dom a b"
:$$$: ""
:$$$: Outro
type WithSpecificDomain dom r =
(HasSpecificDomain dom r, dom ~ GetDomain dom r)
data HasDomainWrapperResult
= NotFound
| Found
type family MergeWorker (n :: HasDomainWrapperResult) (m :: HasDomainWrapperResult) :: HasDomainWrapperResult where
MergeWorker 'Found b = 'Found
MergeWorker a 'Found = 'Found
MergeWorker 'NotFound 'NotFound = 'NotFound
type Merge (dom :: Domain) (n :: Type) (m :: Type) =
MergeWorker (HasDomainWrapper dom n) (HasDomainWrapper dom m)
type family DomEqWorker (n :: Domain) (m :: Domain) :: HasDomainWrapperResult where
DomEqWorker n n = 'Found
DomEqWorker n m = 'NotFound
type DomEq (n :: Domain) (m :: Domain) =
IfStuck (DomEqWorker n m) ('NotFound) (Pure (DomEqWorker n m))
type family HasDomain (dom :: Domain) (n :: Type) :: HasDomainWrapperResult
type instance HasDomain dom1 (Proxy dom2) = DomEq dom1 dom2
type instance HasDomain dom1 (Signal dom2 a) = DomEq dom1 dom2
type instance HasDomain dom1 (DSignal dom2 delay a) = DomEq dom1 dom2
type instance HasDomain dom1 (Clock dom2) = DomEq dom1 dom2
type instance HasDomain dom1 (Reset dom2) = DomEq dom1 dom2
type instance HasDomain dom1 (Enable dom2) = DomEq dom1 dom2
type instance HasDomain dom (Vec n a) = HasDomain dom a
type instance HasDomain dom (a, b) = Merge dom a b
type instance HasDomain dom (a -> b) = Merge dom a b
type family ErrOnNotFound (dom :: Domain) (n :: HasDomainWrapperResult) (t :: Type) :: Domain where
ErrOnNotFound dom 'NotFound t = DelayError (NotFoundError dom t)
ErrOnNotFound dom 'Found t = dom
type family HasDomainWrapper (dom :: Domain) (n :: Type) :: HasDomainWrapperResult where
HasDomainWrapper dom n =
IfStuck
(HasDomain dom n)
('NotFound)
(Pure (HasDomain dom n))
type family ResolveOrErr (dom :: Domain) (t :: Type) :: Domain where
ResolveOrErr dom t =
IfStuck
(HasDomainWrapper dom t)
(ErrOnNotFound dom 'NotFound t)
(Pure (ErrOnNotFound dom (HasDomainWrapper dom t) t))
class HasSpecificDomain (dom :: Domain) (r :: Type) where
type GetDomain dom r :: Domain
type GetDomain dom r = ResolveOrErr dom r
instance HasSpecificDomain dom a
mkHasDomainTuples ''HasDomain ''Merge