From 3e342a621c147c2b641f0d402d574332807435c1 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sun, 16 Sep 2007 17:20:57 +0000 Subject: [PATCH] Added ShowOccam and ShowRain type-classes, and changed existing code to use these new show methods in error messages The function showCode shows code as either occam or Rain depending on the frontend. This is then used by a formatCode function that acts similar to printf, which makes it easy to format error messages that use showCode. --- backends/GenerateC.hs | 11 ++- backends/GenerateCPPCSP.hs | 3 +- common/AST.hs | 29 +------ common/CompState.hs | 12 ++- common/EvalConstants.hs | 3 +- common/ShowCode.hs | 173 +++++++++++++++++++++++++++++++++++++ common/TestUtil.hs | 3 +- common/Types.hs | 9 +- frontends/ParseOccam.hs | 9 +- frontends/RainTypes.hs | 26 +++--- 10 files changed, 221 insertions(+), 57 deletions(-) create mode 100644 common/ShowCode.hs diff --git a/backends/GenerateC.hs b/backends/GenerateC.hs index 5845ec2..0617f98 100644 --- a/backends/GenerateC.hs +++ b/backends/GenerateC.hs @@ -37,6 +37,7 @@ import EvalLiterals import Metadata import Pass import Errors +import ShowCode import TLP import Types import Utils @@ -117,6 +118,7 @@ data GenOps = GenOps { genLiteral :: GenOps -> A.LiteralRepr -> CGen (), genLiteralRepr :: GenOps -> A.LiteralRepr -> CGen (), genMissing :: GenOps -> String -> CGen (), + genMissingC :: GenOps -> CGen String -> CGen (), genMonadic :: GenOps -> Meta -> A.MonadicOp -> A.Expression -> CGen (), genOutput :: GenOps -> A.Variable -> [A.OutputItem] -> CGen (), genOutputCase :: GenOps -> A.Variable -> A.Name -> [A.OutputItem] -> CGen (), @@ -204,6 +206,7 @@ cgenOps = GenOps { genLiteral = cgenLiteral, genLiteralRepr = cgenLiteralRepr, genMissing = cgenMissing, + genMissingC = (\ops x -> x >>= cgenMissing ops), genMonadic = cgenMonadic, genOutput = cgenOutput, genOutputCase = cgenOutputCase, @@ -376,7 +379,7 @@ cgenType _ (A.Chan _ _ t) = tell ["Channel *"] cgenType ops t = case call getScalarType ops t of Just s -> tell [s] - Nothing -> call genMissing ops $ "genType " ++ show t + Nothing -> call genMissingC ops $ formatCode "genType %" t -- | Generate the number of bytes in a type that must have a fixed size. cgenBytesIn :: GenOps -> A.Type -> Maybe A.Variable -> CGen () @@ -423,7 +426,7 @@ cgenBytesIn' _ (A.Chan {}) _ cgenBytesIn' ops t _ = case call getScalarType ops t of Just s -> tell ["sizeof (", s, ")"] >> return Nothing - Nothing -> die $ "genBytesIn' " ++ show t + Nothing -> dieC $ formatCode "genBytesIn' %" t --}}} --{{{ declarations @@ -817,7 +820,7 @@ cgenTypeSymbol :: GenOps -> String -> A.Type -> CGen () cgenTypeSymbol ops s t = case call getScalarType ops t of Just ct -> tell ["occam_", s, "_", ct] - Nothing -> call genMissing ops $ "genTypeSymbol " ++ show t + Nothing -> call genMissingC ops $ formatCode "genTypeSymbol %" t cgenIntrinsicFunction :: GenOps -> Meta -> String -> [A.Expression] -> CGen () cgenIntrinsicFunction ops m s es @@ -1503,7 +1506,7 @@ cgenAssign ops m [v] el tell [" = "] call genExpression ops e tell [";\n"] - Nothing -> call genMissing ops $ "assignment of type " ++ show t + Nothing -> call genMissingC ops $ formatCode "assignment of type %" t --}}} --{{{ input cgenInput :: GenOps -> A.Variable -> A.InputMode -> CGen () diff --git a/backends/GenerateCPPCSP.hs b/backends/GenerateCPPCSP.hs index d9d7d34..d719e0e 100644 --- a/backends/GenerateCPPCSP.hs +++ b/backends/GenerateCPPCSP.hs @@ -87,6 +87,7 @@ import EvalLiterals import Metadata import Pass import Errors +import ShowCode import TLP import Types import Utils @@ -1079,7 +1080,7 @@ cppgenType _ (A.Any) cppgenType ops t = case call getScalarType ops t of Just s -> tell [s] - Nothing -> call genMissing ops $ "genType " ++ show t + Nothing -> call genMissingC ops $ formatCode "genType %" t -- | Helper function for prefixing an underscore to a name. diff --git a/common/AST.hs b/common/AST.hs index 372fc4f..80641c1 100644 --- a/common/AST.hs +++ b/common/AST.hs @@ -127,34 +127,7 @@ data Type = | Any | Timer | Port Type - deriving (Eq, Typeable, Data) - -instance Show Type where - show Bool = "BOOL" - show Byte = "BYTE" - show UInt16 = "UINT16" - show UInt32 = "UINT32" - show UInt64 = "UINT64" - show Int = "INT" - show Int8 = "INT8" - show Int16 = "INT16" - show Int32 = "INT32" - show Int64 = "INT64" - show Real32 = "REAL32" - show Real64 = "REAL64" - show (Array ds t) - = concat [case d of - Dimension n -> "[" ++ show n ++ "]" - UnknownDimension -> "[]" - | d <- ds] ++ show t - show (UserDataType n) = nameName n ++ "{data type}" - show (Record n) = nameName n ++ "{record}" - show (UserProtocol n) = nameName n ++ "{protocol}" - show (Chan _ _ t) = "CHAN OF " ++ show t - show (Counted ct et) = show ct ++ "::" ++ show et - show Any = "ANY" - show Timer = "TIMER" - show (Port t) = "PORT OF " ++ show t + deriving (Show, Eq, Typeable, Data) -- | An array dimension. -- Depending on the context, an array type may have empty dimensions, which is diff --git a/common/CompState.hs b/common/CompState.hs index 0572d72..a3cc8ee 100644 --- a/common/CompState.hs +++ b/common/CompState.hs @@ -24,6 +24,7 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) import qualified Data.Set as Set +import Control.Monad.Error import Control.Monad.State import qualified AST as A @@ -77,7 +78,7 @@ data CompState = CompState { csAdditionalArgs :: Map String [A.Actual], csParProcs :: Set A.Name } - deriving (Show, Data, Typeable) + deriving (Data, Typeable) instance Show (A.Structured -> A.Structured) where show p = "(function on Structured)" @@ -247,3 +248,12 @@ makeNonceVariable :: CSM m => String -> Meta -> A.Type -> A.NameType -> A.Abbrev makeNonceVariable s m t nt am = defineNonce m s (A.Declaration m t) nt am --}}} + +diePC :: (CSM m, Die m) => Meta -> m String -> m a +diePC m str = str >>= (dieP m) + +dieC :: (CSM m, Die m) => m String -> m a +dieC str = str >>= die + +throwErrorC :: (CSM m,MonadError String m) => m String -> m a +throwErrorC str = str >>= throwError diff --git a/common/EvalConstants.hs b/common/EvalConstants.hs index 1095180..21035b7 100644 --- a/common/EvalConstants.hs +++ b/common/EvalConstants.hs @@ -37,6 +37,7 @@ import Errors import EvalLiterals import Metadata import Pass +import ShowCode import Types -- | Simplify an expression by constant folding, and also return whether it's a @@ -167,7 +168,7 @@ evalExpression (A.BytesInType _ t) = do b <- underlyingType t >>= bytesInType case b of BIJust n -> return $ OccInt (fromIntegral $ n) - _ -> throwError $ "BYTESIN non-constant-size type " ++ show t ++ " used" + _ -> throwErrorC $ formatCode "BYTESIN non-constant-size type % used" t evalExpression e = throwError "bad expression" evalMonadicOp :: (forall t. (Num t, Integral t, Bits t) => t -> t) -> OccValue -> EvalM OccValue diff --git a/common/ShowCode.hs b/common/ShowCode.hs new file mode 100644 index 0000000..efbc183 --- /dev/null +++ b/common/ShowCode.hs @@ -0,0 +1,173 @@ +{-# OPTIONS_GHC -fallow-incoherent-instances #-} +{- +Tock: a compiler for parallel languages +Copyright (C) 2007 University of Kent + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +-} + +-- | A module with type-classes and functions for displaying code, dependent on the context. +-- Primarily, this means showing code as occam in error messages for the occam frontend, and Rain code for the Rain frontend. +module ShowCode where + +import Control.Monad.State +import Text.Regex +import qualified AST as A +import CompState + +-- | A type-class that indicates that the data (AST item) is displayable as occam code. +class ShowOccam a where + showOccam :: a -> String + +-- | A type-class that indicates that the data (AST item) is displayable as Rain code. +class ShowRain a where + showRain :: a -> String + +-- | Shows the given code (AST item) as either occam or Rain code, depending on which frontend was selected +showCode :: (CSM m, ShowOccam a, ShowRain a) => a -> m String +showCode o + = do st <- get + return $ case csFrontend st of + FrontendOccam -> showOccam o + FrontendRain -> showRain o + +-- | Some type hackery to allow formatCode to take a variable number of functions. +class CSM m => ShowCodeFormat a m | a -> m where + chain :: [String] -> [m String] -> a + +instance CSM m => ShowCodeFormat (m String) m where + chain xs ys = (liftM concat) (sequence $ interleave (map return xs) (ys)) + where + --Given [a,b,c] [1,2], produces [a,1,b,2,c] etc + interleave :: [a] -> [a] -> [a] + interleave xs [] = xs + interleave [] ys = ys + interleave (x:xs) (y:ys) = (x:y: (interleave xs ys)) + + +instance (ShowOccam a, ShowRain a, ShowCodeFormat r m, CSM m) => ShowCodeFormat (a -> r) m where + chain a x = (\y -> chain a (x ++ [showCode y])) + + +-- | Formats the given code as either occam or Rain code, depending on the frontend (using showCode). +-- Use like this: +-- dieC $ formatCode "Types do not match: % and %" ta tb +formatCode :: (CSM m,ShowCodeFormat r m) => String -> r +formatCode fmt = chain (splitRegex (mkRegex "%") fmt) [] + + +--Type-class instances follow for ShowOccam and ShowRain: + +instance ShowOccam A.Type where + showOccam A.Bool = "BOOL" + showOccam A.Byte = "BYTE" + showOccam A.UInt16 = "UINT16" + showOccam A.UInt32 = "UINT32" + showOccam A.UInt64 = "UINT64" + showOccam A.Int = "INT" + showOccam A.Int8 = "INT8" + showOccam A.Int16 = "INT16" + showOccam A.Int32 = "INT32" + showOccam A.Int64 = "INT64" + showOccam A.Real32 = "REAL32" + showOccam A.Real64 = "REAL64" + showOccam (A.Array ds t) + = concat [case d of + A.Dimension n -> "[" ++ show n ++ "]" + A.UnknownDimension -> "[]" + | d <- ds] ++ showOccam t + showOccam (A.UserDataType n) = A.nameName n ++ "{data type}" + showOccam (A.Record n) = A.nameName n ++ "{record}" + showOccam (A.UserProtocol n) = A.nameName n ++ "{protocol}" + showOccam (A.Chan _ _ t) = "CHAN OF " ++ showOccam t + showOccam (A.Counted ct et) = showOccam ct ++ "::" ++ showOccam et + showOccam A.Any = "ANY" + showOccam A.Timer = "TIMER" + showOccam (A.Port t) = "PORT OF " ++ showOccam t + + +instance ShowRain A.Type where + showRain A.Bool = "bool" + showRain A.Byte = "uint8" + showRain A.UInt16 = "uint16" + showRain A.UInt32 = "uint32" + showRain A.UInt64 = "uint64" + showRain A.Int8 = "sint8" + showRain A.Int16 = "sint16" + showRain A.Int32 = "sint32" + showRain A.Int64 = "int" + showRain A.Int = "int" + showRain (A.Chan dir attr t) + = case dir of + A.DirUnknown -> "channel " ++ ao (A.caWritingShared attr) ++ "2" ++ ao (A.caReadingShared attr) ++ " " ++ showRain t + A.DirInput -> (if A.caReadingShared attr then "shared" else "") ++ " ?" ++ showRain t + A.DirOutput -> (if A.caWritingShared attr then "shared" else "") ++ " !" ++ showRain t + where + ao :: Bool -> String + ao b = if b then "any" else "one" + showRain x = "" + +instance ShowOccam A.DyadicOp where + showOccam A.Add = "+" + showOccam A.Subtr = "-" + showOccam A.Mul = "*" + showOccam A.Div = "/" + showOccam A.Rem = "REM" + showOccam A.Plus = "PLUS" + showOccam A.Minus = "MINUS" + showOccam A.Times = "TIMES" + showOccam A.BitAnd = "/\\" + showOccam A.BitOr = "\\/" + showOccam A.BitXor = "><" + showOccam A.LeftShift = "<<" + showOccam A.RightShift = ">>" + showOccam A.And = "AND" + showOccam A.Or = "OR" + showOccam A.Eq = "=" + showOccam A.NotEq = "<>" + showOccam A.Less = "<" + showOccam A.More = ">" + showOccam A.LessEq = "<=" + showOccam A.MoreEq = ">=" + showOccam A.After = "AFTER" + + +instance ShowRain A.DyadicOp where + showRain A.Div = "/" + showRain A.Rem = "%" + showRain A.Plus = "+" + showRain A.Minus = "-" + showRain A.Times = "*" + showRain A.And = "and" + showRain A.Or = "or" + showRain A.Eq = "==" + showRain A.NotEq = "<>" + showRain A.Less = "<" + showRain A.More = ">" + showRain A.LessEq = "<=" + showRain A.MoreEq = ">=" + showRain x = "" + +instance ShowOccam A.Variable where + showOccam (A.Variable _ n) = show n + showOccam (A.SubscriptedVariable _ s v) = showOccam v ++ "[" ++ show s ++ "]" + showOccam (A.DirectedVariable _ A.DirUnknown v) = showOccam v + showOccam (A.DirectedVariable _ A.DirInput v) = showOccam v ++ "?" + showOccam (A.DirectedVariable _ A.DirOutput v) = showOccam v ++ "!" + +instance ShowRain A.Variable where + showRain (A.Variable _ n) = show n + showRain (A.DirectedVariable _ A.DirInput v) = "?" ++ showRain v + showRain (A.DirectedVariable _ A.DirOutput v) = "!" ++ showRain v + showRain x = "" diff --git a/common/TestUtil.hs b/common/TestUtil.hs index e044ba7..1a146ab 100644 --- a/common/TestUtil.hs +++ b/common/TestUtil.hs @@ -49,6 +49,7 @@ import Control.Monad.State import Control.Monad.Error import Pass import CompState +import PrettyShow import Utils import qualified Data.Map as Map @@ -332,7 +333,7 @@ testPassShouldFail testName actualPass startStateTrans = do ret <- runPass actualPass (execState startStateTrans emptyState) case ret of (_,Left err) -> return () - _ -> assertFailure $ testName ++ " pass succeeded when expected to fail, data: " ++ (show ret) + _ -> assertFailure $ testName ++ " pass succeeded when expected to fail, data: " ++ (pshow ret) -- | Asserts that a particular variable is defined in the given 'CompState'. assertVarDef :: diff --git a/common/Types.hs b/common/Types.hs index 1763d8b..bc37c9c 100644 --- a/common/Types.hs +++ b/common/Types.hs @@ -48,6 +48,7 @@ import Errors import EvalLiterals import Intrinsics import Metadata +import ShowCode import Utils -- | Gets the 'A.SpecType' for a given 'A.Name' from the recorded types in the 'CompState'. Dies with an error if the name is unknown. @@ -124,7 +125,7 @@ plainSubscriptType m sub (A.Array (d:ds) t) ok = case ds of [] -> t _ -> A.Array ds t -plainSubscriptType m _ t = dieP m $ "subscript of non-array type " ++ show t +plainSubscriptType m _ t = diePC m $ formatCode "subscript of non-array type: %" t -- | Apply a subscript to a type, and return what the type is after it's been -- subscripted. @@ -145,7 +146,7 @@ subscriptType (A.SubscriptFor m count) t = sliceType m (makeConstant emptyMeta 0) count t subscriptType (A.SubscriptField m tag) t = typeOfRecordField m t tag subscriptType (A.Subscript m sub) t = plainSubscriptType m sub t -subscriptType _ t = die $ "unsubscriptable type: " ++ show t +subscriptType sub t = diePC (findMeta sub) $ formatCode "Unsubscriptable type: %" t -- | The inverse of 'subscriptType': given a type that we know is the result of -- a subscript, return what the type being subscripted is. @@ -165,10 +166,10 @@ unsubscriptType (A.Subscript _ sub) t -- subscriptType with constant 0 as a subscript, but without the checking. -- This is used for the couple of cases where we know it's safe and don't want -- the usage check. -trivialSubscriptType :: (Die m) => A.Type -> m A.Type +trivialSubscriptType :: (CSM m, Die m) => A.Type -> m A.Type trivialSubscriptType (A.Array [d] t) = return t trivialSubscriptType (A.Array (d:ds) t) = return $ A.Array ds t -trivialSubscriptType t = die $ "not plain array type: " ++ show t +trivialSubscriptType t = dieC $ formatCode "not plain array type: %" t -- | Gets the 'A.Type' of a 'A.Variable' by looking at the types recorded in the 'CompState'. typeOfVariable :: (CSM m, Die m) => A.Variable -> m A.Type diff --git a/frontends/ParseOccam.hs b/frontends/ParseOccam.hs index 6f45d9d..3f51884 100644 --- a/frontends/ParseOccam.hs +++ b/frontends/ParseOccam.hs @@ -38,6 +38,7 @@ import Intrinsics import LexOccam import Metadata import Pass +import ShowCode import Types import Utils @@ -294,7 +295,7 @@ maybeSliced inner subscripter typer t <- typer v >>= underlyingType case t of (A.Array _ _) -> return () - _ -> fail $ "slice of non-array type " ++ show t + _ -> fail $ "slice of non-array type " ++ showOccam t e <- intExpr sub <- case ff1 of @@ -392,7 +393,7 @@ matchType et rt else bad _ -> if rt == et then return () else bad where - bad = fail $ "type mismatch (got " ++ show rt ++ "; expected " ++ show et ++ ")" + bad = fail $ "type mismatch (got " ++ showOccam rt ++ "; expected " ++ showOccam et ++ ")" -- | Check that two lists of types match (for example, for parallel assignment). matchTypes :: [A.Type] -> [A.Type] -> OccParser () @@ -672,7 +673,7 @@ makeLiteral x@(A.Literal m t lr) wantT typesOK <- isValidLiteralType m t wantT when (not typesOK) $ - dieP m $ "default type of literal (" ++ show t ++ ") cannot be coerced to desired type (" ++ show wantT ++ ")" + dieP m $ "default type of literal (" ++ showOccam t ++ ") cannot be coerced to desired type (" ++ showOccam wantT ++ ")" case (underT, lr) of -- An array literal. @@ -1936,7 +1937,7 @@ actual (A.Formal am t n) A.Timer -> var timer A.Port _ -> var (portOfType t) _ -> var (variableOfType t) - "actual of type " ++ show t ++ " for " ++ show n + "actual of type " ++ showOccam t ++ " for " ++ show n where var inner = liftM (A.ActualVariable am t) inner --}}} diff --git a/frontends/RainTypes.hs b/frontends/RainTypes.hs index 4be2f02..d90f3b2 100644 --- a/frontends/RainTypes.hs +++ b/frontends/RainTypes.hs @@ -27,6 +27,7 @@ import Types import Control.Monad.State import CompState import Metadata +import ShowCode -- | A pass that records inferred types. Currently the only place where types are inferred is in seqeach\/pareach loops. @@ -148,8 +149,7 @@ coerceType :: String -> A.Type -> A.Type -> A.Expression -> PassM A.Expression coerceType customMsg to from item = if isImplicitConversionRain from to then return $ A.Conversion (findMeta item) A.DefaultConversion to item - else dieP (findMeta item) $ "Could not perform implicit cast from supplied type: " ++ (show from) ++ - " to expected type: " ++ (show to) ++ customMsg + else diePC (findMeta item) $ (liftM concat) $ sequence [formatCode "Could not perform implicit cast from supplied type: % to expected type: %" from to, return customMsg] -- | Checks the types in expressions @@ -161,14 +161,14 @@ checkExpressionTypes = everywhereASTM checkExpression = do tlhs <- typeOfExpression lhs trhs <- typeOfExpression rhs if (tlhs == trhs) - then (if validOp op tlhs then return e else dieP m $ "Operator: \"" ++ show op ++ "\" is not valid on type: \"" ++ show tlhs) + then (if validOp op tlhs then return e else diePC m $ formatCode "Operator: \"%\" is not valid on type: \"%\"" op tlhs) else if (isIntegerType tlhs && isIntegerType trhs) then case (leastGeneralSharedTypeRain [tlhs,trhs]) of - Nothing -> dieP m $ "Cannot find a suitable type to convert expression to, types are: " ++ show tlhs ++ " and " ++ show trhs - Just t -> if validOp op t then return $ A.Dyadic m op (convert t tlhs lhs) (convert t trhs rhs) else dieP m $ - "Operator: \"" ++ show op ++ "\" is not valid on type: \"" ++ show tlhs + Nothing -> diePC m $ formatCode "Cannot find a suitable type to convert expression to, types are: % and %" tlhs trhs + Just t -> if validOp op t then return $ A.Dyadic m op (convert t tlhs lhs) (convert t trhs rhs) else diePC m $ + formatCode "Operator: \"%\" is not valid on type: \"%\"" op tlhs else --The operators are not equal, and are not integers. Therefore this must be an error: - dieP m $ "Mis-matched types; no operator applies to types: " ++ show tlhs ++ " and " ++ show trhs + diePC m $ formatCode "Mis-matched types; no operator applies to types: % and %" tlhs trhs checkExpression e@(A.Monadic m op rhs) = do trhs <- typeOfExpression rhs if (op == A.MonadicMinus) @@ -176,13 +176,13 @@ checkExpressionTypes = everywhereASTM checkExpression A.Byte -> return $ A.Monadic m op $ convert A.Int16 trhs rhs A.UInt16 -> return $ A.Monadic m op $ convert A.Int32 trhs rhs A.UInt32 -> return $ A.Monadic m op $ convert A.Int64 trhs rhs - A.UInt64 -> dieP m $ "Cannot apply unary minus to type: " ++ show trhs ++ " because there is no type large enough to safely contain the result" - _ -> if (isIntegerType trhs) then return e else dieP m $ "Trying to apply unary minus to non-integer type: " ++ show trhs + A.UInt64 -> diePC m $ formatCode "Cannot apply unary minus to type: % because there is no type large enough to safely contain the result" trhs + _ -> if (isIntegerType trhs) then return e else diePC m $ formatCode "Trying to apply unary minus to non-integer type: %" trhs else if (op == A.MonadicNot) then case trhs of A.Bool -> return e - _ -> dieP m $ "Cannot apply unary not to non-boolean type: " ++ show trhs + _ -> diePC m $ formatCode "Cannot apply unary not to non-boolean type: %" trhs else dieP m $ "Invalid Rain operator: \"" ++ show op ++ "\"" checkExpression e@(A.Conversion m cm dest rhs) = do src <- typeOfExpression rhs @@ -190,7 +190,7 @@ checkExpressionTypes = everywhereASTM checkExpression then return e else if isImplicitConversionRain src dest then return e - else dieP m $ "Invalid cast from: " ++ show dest ++ " to: " ++ show src + else diePC m $ formatCode "Invalid cast from: % to: %" dest src checkExpression e = return e convert :: A.Type -> A.Type -> A.Expression -> A.Expression @@ -266,8 +266,8 @@ checkCommTypes = everywhereASTM checkInputOutput else if (innerType == destType) then return p - else dieP m $ "Mis-matching types; channel: " ++ show chanVar ++ " has inner-type: " ++ show innerType ++ - " but destination variable: " ++ show destVar ++ " has type: " ++ show destType + else diePC m $ formatCode "Mis-matching types; channel: \"%\" has inner-type: % but destination variable: \"%\" has type: %" + chanVar innerType destVar destType _ -> dieP m $ "Tried to input from a variable that is not of type channel: " ++ show chanVar checkInputOutput p@(A.Output m chanVar [A.OutExpression m' srcExp]) = do chanType <- typeOfVariable chanVar