packages feed

clash-lib-1.10.2: tests/Clash/Tests/Core/Subst.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

module Clash.Tests.Core.Subst (tests) where

#if MIN_VERSION_ghc(9,0,0)
import           GHC.Types.SrcLoc        (noSrcSpan)
#else
import           SrcLoc                  (noSrcSpan)
#endif

import           Test.Tasty
import           Test.Tasty.HUnit

import           Clash.Core.Name         (Name(..), NameSort(..), OccName)
import           Clash.Core.Term         (Bind(..), Pat(..), Term(..))
import           Clash.Core.Type         (ConstTy(..), Type(ConstTy))
import           Clash.Core.Subst
import           Clash.Core.VarEnv
import           Clash.Core.Var          (Id, IdScope(..), Var(..))
import           Clash.Unique            (Unique)

fakeName :: Name a
fakeName =
  Name
    { nameSort=User
    , nameOcc="fake"
    , nameUniq=0
    , nameLoc=noSrcSpan
    }

unique :: Unique
unique = 20

mkTestId :: IdScope -> OccName -> Unique -> Id
mkTestId scope occ uniq = Id {
    varName = fakeName {nameUniq=uniq, nameOcc=occ}
  , varUniq = uniq
  , varType = ConstTy (TyCon fakeName)
  , idScope = scope
  }

termVar :: Var Term
termVar = mkTestId LocalId "term" unique

term1 :: Term
term1 = Var termVar

fakeType :: Type
fakeType = ConstTy (TyCon fakeName)

localX, localY, localZ, localW, globalG :: Id
localX = mkTestId LocalId "x" 21
localY = mkTestId LocalId "y" 22
localZ = mkTestId LocalId "z" 23
localW = mkTestId LocalId "w" 24
globalG = mkTestId GlobalId "g" 25

-- | The term substituted for 'localX' in the 'unsafeSubstTm' tests
payload :: Term
payload = Var localY

-- | Deshadowed w.r.t. an in-scope set holding 'localX' and 'localY', so it
-- satisfies 'unsafeSubstTm's precondition for substituting 'localX'
deshadowedTerm :: Term
deshadowedTerm =
  Lam localZ
    (Let (NonRec localW (Var localX))
      (Case (Var localW) fakeType
        [(DefaultPat, App (Var localX) (Var localZ))]))

tests :: TestTree
tests =
  testGroup
    "Clash.Tests.Core.Subst"
    [ testCase "deShadow type/term" $
        term1 @=? deShadowTerm (extendInScopeSet emptyInScopeSet termVar) term1

    , testCase "unsafeSubstTm substitutes a local variable" $
        App payload (Var localZ) @=?
          unsafeSubstTm emptyVarEnv (unitVarEnv localX payload)
            (App (Var localX) (Var localZ))

    , testCase "unsafeSubstTm leaves unmatched variables alone" $
        Var localZ @=?
          unsafeSubstTm emptyVarEnv (unitVarEnv localX payload) (Var localZ)

    , testCase "unsafeSubstTm looks globals up in the global substitution" $ do
        payload @=? unsafeSubstTm (unitVarEnv globalG payload) emptyVarEnv
                      (Var globalG)
        -- A global is never looked up in the local substitution, nor the other
        -- way around
        Var globalG @=? unsafeSubstTm emptyVarEnv (unitVarEnv globalG payload)
                          (Var globalG)
        Var localX @=? unsafeSubstTm (unitVarEnv localX payload) emptyVarEnv
                         (Var localX)

    , testCase "unsafeSubstTm agrees with substTm on a deshadowed term" $
        let
          is = extendInScopeSetList emptyInScopeSet [localX, localY]
          subst = extendIdSubst (mkSubst is) localX payload
        in
          substTm "unsafeSubstTm test" subst deshadowedTerm @=?
            unsafeSubstTm emptyVarEnv (unitVarEnv localX payload)
              deshadowedTerm
    ]