packages feed

fir-0.1.0.0: src/FIR/Validation/CFG.hs

{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: FIR.Validation.CFG

Validation module providing checks for control-flow based operations.

-}

module FIR.Validation.CFG
  ( InLoop
  , ValidBreak, ValidContinue
  )
  where

-- base
import Data.Kind
  ( Constraint )
import GHC.TypeLits
  ( TypeError
  , ErrorMessage(..)
  )
import GHC.TypeNats
  ( Nat, CmpNat )

-- fir
import Data.Type.Nat
  ( Succ )
import FIR.ProgramState
  ( ProgramState(ProgramState), CFGState(..) )

-------------------------------------------------

-- | The program state within the body of a loop (see 'FIR.Syntax.Program.while'
-- and 'FIR.Syntax.Program.loop'): the loop nesting level is one higher.
--
-- A combinator which runs a user-supplied computation within a loop, e.g. once for
-- each element of a collection, takes a computation of type @Program (InLoop i) (InLoop i) a@.
type family InLoop ( i :: ProgramState ) = ( i' :: ProgramState ) | i' -> i where
  InLoop ( 'ProgramState bds ctx cfg funs eps iface rayQueries bk ) =
    'ProgramState bds ctx ( CFGStateAddLoop cfg ) funs eps iface rayQueries bk

type family CFGStateAddLoop ( cfg :: CFGState ) = ( cfg' :: CFGState ) | cfg' -> cfg where
  CFGStateAddLoop ( 'CFGState currLoopLevel ) = 'CFGState ( Succ currLoopLevel )


-- | Check whether a break statement is valid.
type family ValidBreak ( n :: Nat ) ( i :: ProgramState ) :: Constraint where
  ValidBreak 0 _ = ()
  ValidBreak n ( 'ProgramState _ _ ('CFGState currLoopDepth) _ _ _ _ _ ) =
    EnsureValidBreakNb n currLoopDepth ( n `CmpNat` currLoopDepth )

type family EnsureValidBreakNb ( n :: Nat ) ( currLoopDepth :: Nat ) ( cmp :: Ordering ) :: Constraint where
  EnsureValidBreakNb n currLoopDepth GT =
    TypeError
      (    Text "Cannot break out of " :<>: ShowType n :<>: Text " loops."
      :$$: Text "Current loop nesting level is only " :<>: ShowType currLoopDepth :<>: Text "."
      )
  EnsureValidBreakNb _ _ _ = ()

-- | Check whether a continue statement is valid.
type family ValidContinue ( n :: Nat ) ( i :: ProgramState ) :: Constraint where
  ValidContinue n ( 'ProgramState _ _ ('CFGState currLoopDepth) _ _ _ _ _ ) =
    EnsureValidBreakContinue n currLoopDepth ( n `CmpNat` currLoopDepth )

type family EnsureValidBreakContinue ( n :: Nat ) ( currLoopDepth :: Nat ) ( cmp :: Ordering ) :: Constraint where
  EnsureValidBreakContinue _ _             LT = ()
  EnsureValidBreakContinue 0 currLoopDepth _ =
    TypeError
      ( Text "Unexpected 'continue' statement: not inside a loop" )
  EnsureValidBreakContinue n currLoopDepth _ =
    TypeError
      (    Text "'breakContinue': cannot continue after breaking out of " :<>: ShowType n :<>: Text " loops."
      :$$: Text "Current loop nesting level is only " :<>: ShowType currLoopDepth :<>: Text "."
      )