packages feed

fortran-vars-0.3.1: test/TypeCheckSpec.hs

module TypeCheckSpec where

import           Test.Hspec
import           Control.Monad                  ( zipWithM_ )
import           Data.Either                    ( isLeft )
import           Data.List                      ( find )
import qualified Data.Map                      as M
import           Data.Data                      ( Data )
import           Language.Fortran.Extras        ( allPUS
                                                , allPU
                                                )
import           Language.Fortran.Extras.Test   ( getTestProgramAnalysis )
import           Language.Fortran.AST
import           Language.Fortran.Analysis      ( stripAnalysis )
import           Language.Fortran.Vars          ( programFileModel )
import           Language.Fortran.Vars.Types    ( Type
                                                , SemType(..)
                                                , CharacterLen(..)
                                                )
import           Language.Fortran.Vars.TypeCheck
                                                ( typeOf
                                                , TypeOf
                                                , TypeError(..)
                                                )
import           Language.Fortran.Vars.SymbolTable
                                                ( collectSymbols )
import           Language.Fortran.Vars.StructureTable
                                                ( collectStructures )

-- | Given a varaible name, 'RHSFunc' search assignment statements within a program
-- unit and returns the RHS of first assignment statement whose LHS matches the
-- input variable name.
type RHSFunc a = Name -> Expression a

-- | Given a 'ProgramUnit', returns a 'RHSFunc' function that can be used to
-- search the 'ProgramFile' and return the RHS of an assignment.
getRhsFunc :: Data a => ProgramUnit a -> RHSFunc a
getRhsFunc pu lhs = head
  [ rhs
  | (StExpressionAssign _ _ (ExpValue _ _ (ValVariable name)) rhs) <- allPUS pu
  , name == lhs
  ]


helper :: String -> Name -> IO (TypeOf A0, RHSFunc A0)
helper path unit = do
  pf <- getTestProgramAnalysis path
  let pfm                = programFileModel pf
      Just (symTable, _) = M.lookup (Named unit) pfm
      strTable           = collectStructures symTable pu
      typeof             = typeOf strTable symTable
      Just pu            = find (\x -> getName x == Named unit) (allPU pf)
      rhs                = getRhsFunc (stripAnalysis pu)
  return (typeof, rhs)

unknownType :: Either TypeError Type -> Bool
unknownType (Left (UnknownType _)) = True
unknownType _                      = False

typeError :: Either TypeError Type -> Bool
typeError (Left TypeError{}) = True
typeError _                  = False

unboundVariable :: Either TypeError Type -> Bool
unboundVariable (Left (UnboundVariable _)) = True
unboundVariable _                          = False

spec :: Spec
spec = do

  describe "Constants" $ do

    let path   = "test/type_check/constants.f"
    let puName = "constants"
    it "Integer Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "i1") `shouldBe` Right (TInteger 4)
      typeof (rhs "i2") `shouldBe` Right (TInteger 4)
      typeof (rhs "i3") `shouldBe` Right (TInteger 4)
      typeof (rhs "i4") `shouldBe` Right (TInteger 4)


    it "Logical Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "l1") `shouldBe` Right (TLogical 4)
      typeof (rhs "l2") `shouldBe` Right (TLogical 4)

    it "Real Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "r1") `shouldBe` Right (TReal 4)
      typeof (rhs "r2") `shouldBe` Right (TReal 4)
      typeof (rhs "r3") `shouldBe` Right (TReal 4)
      typeof (rhs "r4") `shouldBe` Right (TReal 4)
      typeof (rhs "r5") `shouldBe` Right (TReal 4)

      typeof (rhs "r6") `shouldBe` Right (TReal 8)
      typeof (rhs "r7") `shouldBe` Right (TReal 8)

    it "Complex Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "c1") `shouldBe` Right (TComplex 8)
      typeof (rhs "c2") `shouldBe` Right (TComplex 8)
      typeof (rhs "c3") `shouldBe` Right (TComplex 8)
      typeof (rhs "c4") `shouldBe` Right (TComplex 8)

      typeof (rhs "c5") `shouldBe` Right (TComplex 16)
      typeof (rhs "c6") `shouldBe` Right (TComplex 16)
      typeof (rhs "c7") `shouldBe` Right (TComplex 16)

    it "Character Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "s1") `shouldBe` Right (TCharacter (CharLenInt 8) 1)

    it "BOZ Constant" $ do
      (typeof, rhs) <- helper path puName
      mapM_ (\v -> typeof (rhs v) `shouldBe` Right (TByte 4))
            ["b1", "b2", "b3", "b4"]

    it "Hollerith Constant" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "h1") `shouldBe` Right (TByte 2)
      typeof (rhs "h2") `shouldBe` Right (TByte 8)

  describe "Variables" $ do
    let path   = "test/type_check/variables.f"
        puName = "variables"

    it "Byte" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b") `shouldBe` Right (TByte 1)

    it "Character" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "c") `shouldBe` Right (TCharacter (CharLenInt 1) 1)
      typeof (rhs "c1") `shouldBe` Right (TCharacter (CharLenInt 7) 1)
      typeof (rhs "c2") `shouldBe` Right (TCharacter (CharLenInt 7) 1)
      typeof (rhs "c3") `shouldBe` Right (TCharacter (CharLenInt 4) 1)
      typeof (rhs "c4") `shouldBe` Right (TCharacter (CharLenInt 8) 1)

    it "Integer" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "i") `shouldBe` Right (TInteger 4)
      typeof (rhs "i2") `shouldBe` Right (TInteger 2)
      typeof (rhs "i4") `shouldBe` Right (TInteger 4)
      typeof (rhs "i8") `shouldBe` Right (TInteger 8)
      typeof (rhs "i2ns") `shouldBe` Right (TInteger 2)
      typeof (rhs "i4ns") `shouldBe` Right (TInteger 4)
      typeof (rhs "i8ns") `shouldBe` Right (TInteger 8)

    it "Real" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "r") `shouldBe` Right (TReal 4)
      typeof (rhs "r4") `shouldBe` Right (TReal 4)
      typeof (rhs "r8") `shouldBe` Right (TReal 8)
      typeof (rhs "r4ns") `shouldBe` Right (TReal 4)
      typeof (rhs "r8ns") `shouldBe` Right (TReal 8)
      typeof (rhs "dp") `shouldBe` Right (TReal 8)

  describe "Array" $ do
    let path   = "test/type_check/array_and_substring.f"
        puName = "array"

    it "Array" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "arr1") `shouldBe` Right (TInteger 4)
      typeof (rhs "arr2") `shouldBe` Right (TInteger 4)

    it "Adjustable Array" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "arr3") `shouldBe` Right (TInteger 4)

    it "Assumed-size Array" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "arr4") `shouldBe` Right (TInteger 4)

    it "Array partial access" $ do
      -- implicit lower bound arrays
      (typeof, rhs) <- helper path puName
      typeof (rhs "arr5") `shouldBe` Right (TInteger 4)

    it "Index ranges" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "i1") `shouldBe` Right (TArray (TInteger 4) (Just [(1, 10)]))
      typeof (rhs "i2") `shouldBe` Right (TArray (TInteger 4) (Just [(1, 10)]))
      typeof (rhs "i3") `shouldBe` Right (TArray (TInteger 4) Nothing)

    it "Erroneous expressions" $ do
      -- These expressions aren't valid but any subscript can be assumed to
      -- access a scalar element from it
      (typeof, rhs) <- helper path puName
      typeof (rhs "err1") `shouldBe` Right (TInteger 4)
      typeof (rhs "err2") `shouldBe` Right (TInteger 4)
      typeof (rhs "err3") `shouldBe` Right (TInteger 4)
      -- Can't deal with array sections
      isLeft (typeof (rhs "err4")) `shouldBe` True

  describe "String" $ do
    let path   = "test/type_check/array_and_substring.f"
        puName = "string"

    it "Assumed-size string" $ do
      -- TODO all Nothings, unsure though what assumed-size means
      (typeof, rhs) <- helper path puName
      typeof (rhs "s1") `shouldBe` Right (TCharacter CharLenStar 1)
      typeof (rhs "s2") `shouldBe` Right (TCharacter CharLenStar 1)
      typeof (rhs "s3") `shouldBe` Right (TCharacter CharLenStar 1)

    it "substring" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "s4") `shouldBe` Right (TCharacter (CharLenInt 5) 1)
      typeof (rhs "s5") `shouldBe` Right (TCharacter (CharLenInt 6) 1)
      typeof (rhs "s6") `shouldBe` Right (TCharacter (CharLenInt 10) 1)
      typeof (rhs "s7") `shouldBe` Right (TCharacter (CharLenInt 3) 1)
      -- Dynamic arrays
      typeof (rhs "s8") `shouldBe` Right (TCharacter CharLenStar 1)
      typeof (rhs "s9") `shouldBe` Right (TCharacter CharLenStar 1)
      typeof (rhs "s10") `shouldBe` Right (TCharacter CharLenStar 1)

  describe "Expression" $ do
    let path   = "test/type_check/expression.f"
        puName = "expression"

    it "Unary Expression" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "u1") `shouldBe` Right (TInteger 4)
      typeof (rhs "u2") `shouldBe` Right (TInteger 4)
      typeof (rhs "u3") `shouldBe` Right (TReal 4)
      typeof (rhs "u4") `shouldBe` Right (TReal 4)
      typeof (rhs "u5") `shouldBe` Right (TLogical 4)
      typeof (rhs "u6") `shouldBe` Right (TLogical 4)

    it "Arithmetic Expression with Type Promotion" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "a1") `shouldBe` Right (TInteger 4)
      typeof (rhs "a2") `shouldBe` Right (TInteger 4)
      typeof (rhs "a3") `shouldBe` Right (TInteger 8)
      typeof (rhs "a4") `shouldBe` Right (TInteger 8)
      typeof (rhs "a5") `shouldBe` Right (TInteger 8)

      typeof (rhs "a6") `shouldBe` Right (TInteger 4)
      typeof (rhs "a7") `shouldBe` Right (TInteger 4)
      typeof (rhs "a8") `shouldBe` Right (TInteger 4)
      typeof (rhs "a9") `shouldBe` Right (TInteger 8)

      typeof (rhs "a10") `shouldBe` Right (TReal 4)
      typeof (rhs "a11") `shouldBe` Right (TReal 8)
      typeof (rhs "a12") `shouldBe` Right (TReal 8)
      typeof (rhs "a13") `shouldBe` Right (TReal 8)
      typeof (rhs "a14") `shouldBe` Right (TReal 8)
      typeof (rhs "a15") `shouldBe` Right (TReal 8)
      typeof (rhs "a16") `shouldBe` Right (TReal 8)
      typeof (rhs "a17") `shouldBe` Right (TReal 4)

      typeof (rhs "a18") `shouldBe` Right (TComplex 8)
      typeof (rhs "a19") `shouldBe` Right (TComplex 8)
      typeof (rhs "a20") `shouldBe` Right (TComplex 16)
      typeof (rhs "a21") `shouldBe` Right (TComplex 16)

    it "Character Concatenation" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "s1") `shouldBe` Right (TCharacter (CharLenInt 8) 1)
      typeof (rhs "s1") `shouldBe` Right (TCharacter (CharLenInt 8) 1)

    it "Relational Expression" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "re1") `shouldBe` Right (TLogical 4)
      typeof (rhs "re2") `shouldBe` Right (TLogical 4)
      typeof (rhs "re3") `shouldBe` Right (TLogical 4)
      typeof (rhs "re4") `shouldBe` Right (TLogical 4)
      typeof (rhs "re5") `shouldBe` Right (TLogical 4)
      typeof (rhs "re6") `shouldBe` Right (TLogical 4)

    it "Logical Expression" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "le1") `shouldBe` Right (TLogical 4)
      typeof (rhs "le2") `shouldBe` Right (TInteger 8)
      typeof (rhs "le3") `shouldBe` Right (TLogical 2)
      typeof (rhs "le4") `shouldBe` Right (TLogical 2)
      typeof (rhs "le5") `shouldBe` Right (TLogical 4)
      typeof (rhs "le6") `shouldSatisfy` typeError

    it "More expressions" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "e1") `shouldBe` Right (TInteger 8)
      typeof (rhs "e2") `shouldBe` Right (TInteger 4)
      typeof (rhs "e3") `shouldBe` Right (TComplex 8)
      typeof (rhs "e4") `shouldBe` Right (TLogical 4)
      typeof (rhs "e5") `shouldBe` Right (TInteger 4)

  describe "Intrinsics" $ do
    let path   = "test/type_check/intrinsic.f"
        puName = "intrinsictest"

    it "Integer intrinsic" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b1") `shouldBe` Right (TInteger 4)
      typeof (rhs "b2") `shouldBe` Right (TInteger 4)

    it "Real intrinsic" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b3") `shouldBe` Right (TReal 4)
      typeof (rhs "b4") `shouldBe` Right (TReal 4)

    it "Character intrinsic" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b5") `shouldBe` Right (TCharacter (CharLenInt 1) 1)

    it "Generic intrinsic" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b6") `shouldBe` Right (TInteger 4)
      typeof (rhs "b7") `shouldBe` Right (TReal 4)
      typeof (rhs "b8") `shouldBe` Right (TInteger 8)
      typeof (rhs "b9") `shouldBe` Right (TReal 8)

    it "INT(e), INT(e, k), and INT2" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b10") `shouldBe` Right (TInteger 4)
      typeof (rhs "b11") `shouldBe` Right (TInteger 2)
      typeof (rhs "b12") `shouldBe` Right (TInteger 4)
      typeof (rhs "b13") `shouldBe` Right (TInteger 8)
      typeof (rhs "b14") `shouldBe` Right (TInteger 8)
      typeof (rhs "b15") `shouldBe` Right (TInteger 2)
      typeof (rhs "b16") `shouldBe` Right (TInteger 2)
      typeof (rhs "b17") `shouldBe` Right (TInteger 2)

    it "iand, ior, ishft" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b18") `shouldBe` Right (TInteger 4)
      typeof (rhs "b19") `shouldBe` Right (TInteger 4)
      typeof (rhs "b20") `shouldBe` Right (TInteger 8)
      typeof (rhs "b21") `shouldBe` Right (TInteger 4)
      typeof (rhs "b22") `shouldBe` Right (TInteger 4)
      typeof (rhs "b23") `shouldBe` Right (TInteger 8)
      typeof (rhs "b24") `shouldBe` Right (TInteger 4)
      typeof (rhs "b25") `shouldBe` Right (TInteger 8)
      typeof (rhs "b26") `shouldBe` Right (TInteger 2)

    it "Misc intrinsics" $ do
      (typeof, rhs) <- helper path puName
      typeof (rhs "b27") `shouldBe` Right (TReal 8)
      typeof (rhs "b28") `shouldBe` Right (TInteger 4)
      typeof (rhs "b29") `shouldBe` Right (TInteger 4)
      typeof (rhs "b30") `shouldBe` Right (TInteger 4)
      typeof (rhs "b31") `shouldBe` Right (TInteger 4)
      typeof (rhs "b32") `shouldBe` Right (TInteger 4)
      typeof (rhs "b33") `shouldBe` Right (TInteger 4)
      typeof (rhs "b34") `shouldBe` Right (TInteger 8)
      typeof (rhs "b35") `shouldBe` Right (TInteger 4)
      typeof (rhs "b36") `shouldBe` Right (TInteger 4)
      typeof (rhs "b37") `shouldBe` Right (TInteger 8)
      typeof (rhs "b38") `shouldBe` Right (TLogical 4)
      typeof (rhs "b39") `shouldBe` Right (TReal 8)
      typeof (rhs "b40") `shouldBe` Right (TInteger 8)

  describe "Implied Do" $ it "data statements" $ do
    ProgramFile _ (pu : _) <- getTestProgramAnalysis
      "test/type_check/implied_do.f"
    let
      symt = collectSymbols pu
      strt = collectStructures symt pu
      dgs  = [ aStrip dgs' | StData _ _ dgs' <- allPUS pu ]
      test (DataGroup _ _ es _ : _) dims = typeOf strt symt (head $ aStrip es)
        `shouldBe` Right (TArray (TInteger 2) (Just dims))
      test _ _ = error "Shouldn't reach this"
    let res =
          [ [(1, 5)]
          , [(1, 3)]
          , [(1, 3)]
          , [(1, 9)]
          , [(1, 2)]
          , [(1, 6)]
          , [(1, 3)]
          , [(1, 2)]
          , [(1, 2)]
          ]
    length dgs `shouldBe` length res
    zipWithM_ test dgs res