c-expr-dsl-0.1.0.0: test/Test/CExpr/Typecheck/Classify.hs
module Test.CExpr.Typecheck.Classify (
tests
) where
import Data.Map qualified as Map
import Data.Maybe
import Data.Vec.Lazy (Vec (..))
import DeBruijn (Idx (..), pattern I1)
import Test.Tasty
import Test.Tasty.HUnit
import C.Expr.Syntax
import C.Expr.Typecheck
import Test.CExpr.Typecheck.Infra
tests :: TestTree
tests = testGroup "classify" [
tests_keywordTypes
, tests_typeApp
, tests_intLiterals
, tests_arithmetic
, tests_functionLike
, tests_typeEnvChain
, tests_errors
]
{-------------------------------------------------------------------------------
Group 1: keyword type bodies
-------------------------------------------------------------------------------}
tests_keywordTypes :: TestTree
tests_keywordTypes = testGroup "keyword type bodies" [
testCase "int" $ assertTypeMacro $ classifyOne "M" VNil (tyLit (TypeInt Nothing (Just SizeInt)))
, testCase "unsigned" $ assertTypeMacro $ classifyOne "M" VNil (tyLit (TypeInt (Just Unsigned) Nothing))
, testCase "float" $ assertTypeMacro $ classifyOne "M" VNil (tyLit (TypeFloat SizeFloat))
, testCase "double" $ assertTypeMacro $ classifyOne "M" VNil (tyLit (TypeFloat SizeDouble))
, testCase "_Bool" $ assertTypeMacro $ classifyOne "M" VNil (tyLit TypeBool)
, testCase "char" $ assertTypeMacro $ classifyOne "M" VNil (tyLit (TypeChar Nothing))
, testCase "struct Foo" $ assertTypeMacro $ classifyOne "M" VNil (mtagged "Foo" TagStruct)
, testCase "union Bar" $ assertTypeMacro $ classifyOne "M" VNil (mtagged "Bar" TagUnion)
, testCase "enum Baz" $ assertTypeMacro $ classifyOne "M" VNil (mtagged "Baz" TagEnum)
]
{-------------------------------------------------------------------------------
Group 2: type application bodies
-------------------------------------------------------------------------------}
tests_typeApp :: TestTree
tests_typeApp = testGroup "type application bodies" [
testCase "int *" $ assertTypeMacro $ classifyOne "M" VNil (ptrOf (tyLit intTy))
, testCase "const int" $ assertTypeMacro $ classifyOne "M" VNil (constOf (tyLit intTy))
, testCase "const int *" $ assertTypeMacro $ classifyOne "M" VNil (ptrOf (constOf (tyLit intTy)))
, testCase "int * const" $ assertTypeMacro $ classifyOne "M" VNil (constOf (ptrOf (tyLit intTy)))
, testCase "void *" $ assertTypeMacro $ classifyOne "M" VNil (ptrOf (tyLit TypeVoid))
, testCase "struct Foo *" $ assertTypeMacro $ classifyOne "M" VNil (ptrOf (mtagged "Foo" TagStruct))
]
{-------------------------------------------------------------------------------
Group 3: integer literal bodies
-------------------------------------------------------------------------------}
tests_intLiterals :: TestTree
tests_intLiterals = testGroup "integer literal bodies" [
testCase "0" $ assertValueMacro $ classifyOne "M" VNil (intLit 0)
, testCase "1" $ assertValueMacro $ classifyOne "M" VNil (intLit 1)
, testCase "42" $ assertValueMacro $ classifyOne "M" VNil (intLit 42)
, testCase "-1" $ assertValueMacro $ classifyOne "M" VNil (intLit (-1))
]
{-------------------------------------------------------------------------------
Group 4: arithmetic expression bodies
-------------------------------------------------------------------------------}
tests_arithmetic :: TestTree
tests_arithmetic = testGroup "arithmetic expression bodies" [
testCase "1 + 2" $ assertValueMacro $ classifyOne "M" VNil (add (intLit 1) (intLit 2))
, testCase "1 << 4" $ assertValueMacro $ classifyOne "M" VNil (shiftLeft (intLit 1) (intLit 4))
]
{-------------------------------------------------------------------------------
Group 5: function-like macro bodies (with formal parameters)
-------------------------------------------------------------------------------}
tests_functionLike :: TestTree
tests_functionLike = testGroup "function-like macro bodies" [
testCase "identity: \\x -> x" $
assertValueMacro $
classifyOne "IDENTITY" ("x" ::: VNil) (mlocal IZ)
, testCase "add: \\a b -> a + b" $
assertValueMacro $
classifyOne "ADD" ("a" ::: "b" ::: VNil) (add (mlocal I1) (mlocal IZ))
]
{-------------------------------------------------------------------------------
Group 6: TypeEnv chain — value macro references
-------------------------------------------------------------------------------}
tests_typeEnvChain :: TestTree
tests_typeEnvChain = testGroup "TypeEnv chain (value macro references)" [
testCase "B references A (both value macros)" $ do
let results = runTcSeq
[ ("A", intLit 1)
, ("B", mvar "A")
]
mapM_ assertValueMacro results
, testCase "C references B which references A" $ do
let results = runTcSeq
[ ("A", intLit 42)
, ("B", mvar "A")
, ("C", add (mvar "B") (intLit 1))
]
assertValueMacro $ fromJust $ Map.lookup "C" results
]
{-------------------------------------------------------------------------------
Group 7: error cases
-------------------------------------------------------------------------------}
tests_errors :: TestTree
tests_errors = testGroup "error cases" [
testCase "unbound variable" $
-- A bare identifier not in TypeEnv and not a macro argument is an
-- unbound variable. 'tcMacros' reports it as 'MacroTcError'.
assertCheckError $ classifyOne "M" VNil (mvar "unknown")
, testCase "value macro referencing unknown name" $
-- Even inside arithmetic, an unbound reference fails.
assertCheckError $
classifyOne "M" VNil (add (intLit 1) (mvar "UNDEFINED"))
, testCase "type macro with unused parameter" $
assertCheckError $
classifyOne "M" ("X" ::: VNil) ((tyLit (TypeInt Nothing Nothing)))
, testCase "bare void type macro is rejected" $
assertCheckError $ classifyOne "M" VNil (tyLit TypeVoid)
, testCase "const void type macro is rejected" $
assertCheckError $ classifyOne "M" VNil (constOf (tyLit TypeVoid))
]
where
assertCheckError :: (Show a) => MacroTcResult a -> Assertion
assertCheckError = \case
MacroTcError _ ->
pure ()
r ->
assertFailure $ "expected MacroTcError; got: " ++ show r
{-------------------------------------------------------------------------------
Helpers
-------------------------------------------------------------------------------}
-- | Signed int literal, used in multiple test groups.
intTy :: TypeLit
intTy = TypeInt (Just Signed) (Just SizeInt)