Copyright | © 2019 Elias Castegren and Kiko Fernandez-Reyes |
---|---|
License | MIT |
Stability | experimental |
Portability | portable |
Safe Haskell | Safe |
This module includes everything you need to get started type checking a program. To build the Abstract Syntax Tree (AST), please import and build the AST from Warning.AST.
The main entry point to the type checker is the combinator tcProgram
, which
takes an AST and returns either an error, or the typed program and a list of warnings.
For example, for the following program (using a made up syntax):
class C val f: Foo
should be parsed to generate this AST:
testClass1 = ClassDef {cname = "C" ,fields = [FieldDef {fmod = Val, fname = "f", ftype = ClassType "Foo"}] ,methods = []}
To type check the AST, run the tcProgram
combinator as follows:
tcProgram testClass1
This is an increment on top of the Typechecker
module,
that includes the Writer
monad to add warnings to the compiler.
Synopsis
- type TypecheckM a = forall m. (MonadReader Env m, MonadError TCError m, MonadWriter [TCWarning] m) => m a
- data TCError = TCError Error Backtrace
- data Error
- data TCWarning = TCWarning Warning Backtrace
- data Warning
- tcError :: Error -> TypecheckM a
- tcWarning :: Warning -> TypecheckM ()
- data Env = Env {}
- setConstructor :: Name -> Env -> Env
- emptyEnv :: Env
- lookupClass :: Name -> Env -> Maybe ClassDef
- lookupVar :: Name -> Env -> Maybe Type
- findClass :: Type -> TypecheckM ClassDef
- findMethod :: Type -> Name -> TypecheckM MethodDef
- findField :: Type -> Name -> TypecheckM FieldDef
- findVar :: Name -> TypecheckM Type
- isBound :: Name -> TypecheckM Bool
- genEnv :: Program -> Env
- addVariable :: Name -> Type -> Env -> Env
- addParameters :: [Param] -> Env -> Env
- tcProgram :: Program -> Either TCError (Program, [TCWarning])
- class Typecheckable a where
- doTypecheck :: a -> TypecheckM a
- typecheck :: Backtraceable a => a -> TypecheckM a
- checkShadowing :: Name -> TypecheckM ()
- checkVariableUsage :: Expr -> Name -> TypecheckM ()
- hasType :: Expr -> Type -> TypecheckM Expr
- buildClass :: String -> ClassDef
- testClass1 :: ClassDef
- testClass2 :: ClassDef
- testClass3 :: [ClassDef]
- testProgram :: Program
- testProgramW :: Program
- testSuite :: IO ()
Type checking monad
type TypecheckM a = forall m. (MonadReader Env m, MonadError TCError m, MonadWriter [TCWarning] m) => m a Source #
The type checking monad. The type checking monad is the stacking
of the Reader
and Exception
monads.
Type checking errors
Declaration of type checking errors.
Data declaration of available errors. Value constructors are used to create statically known errors. For example:
UnknownClassError (Name c)
creates a UnknownClassError
. This error should be created whenever there
is a class whose declaration is unknown or inexistent.
UnknownClassError Name | Reference of a class that does not exists |
UnknownFieldError Name | Reference of a field that does not exists |
UnknownMethodError Name | Reference of a method that does not exists |
UnboundVariableError Name | Unbound variable |
TypeMismatchError Type Type | Type mismatch error, the first |
ImmutableFieldError Expr | Immutable field error, used when someone violates immutability |
NonLValError Expr | Error to indicate that a one cannot assign a value to expression |
PrimitiveNullError Type | Error indicating that the return type cannot be |
NonClassTypeError Type | Used to indicate that |
NonArrowTypeError Type | Expecting a function (arrow) type but got another type instead. |
ConstructorCallError Type | Tried to call a constructor outside of instantiation |
UninferrableError Expr | Cannot infer type of |
Declaration of type checking warnings.
Declaration of available warnings. Value constructors are used to create statically known warnings. For example:
ShadowedVarWarning (Name c)
creates a ShadowedVarWarning
. This error should be created whenever there
is a variable who shadows an existing variable in the environment.
ShadowedVarWarning Name | Shadowing existing variable warning |
UnusedVariableWarning Name | Unused variable warning |
tcError :: Error -> TypecheckM a Source #
Returns a type checking monad, containing the error err
.
A common example throughout the code is the following:
tcError $ UnknownClassError c
The code above throws an error because the class c
was not
found in the environment Env
.
tcWarning :: Warning -> TypecheckM () Source #
Returns a type checking monad, containing the warning wrn
.
A common example throughout the code is the following:
when shadow $ tcWarning $ ShadowedVarWarning x
The code above throws a warning because the variable x
was shadowing
an existing variable from the environment.
Type checking combinators
Environment. The Env
is used during type checking, and is updated as
the type checker runs. Most likely, one uses the Reader
monad to hide details
of how the environment is updated, via the common local
function.
setConstructor :: Name -> Env -> Env Source #
Conditionally update the environment to track if we are in a constructor method.
lookupClass :: Name -> Env -> Maybe ClassDef Source #
Helper function to lookup a class given a Name
and an Env
. Usually
it relies on the Reader
monad, so that passing the Env
can be omitted.
For example:
findClass :: Type -> TypecheckM ClassDef findClass ty@(ClassType c) = do cls <- asks $ lookupClass c case cls of Just cdef -> return cdef Nothing -> tcError $ UnknownClassError c findClass ty = tcError $ NonClassTypeError ty
In this function (findClass
), the Reader
function asks
injects
the Reader
monad as the last argument. More details in the paper.
findMethod :: Type -> Name -> TypecheckM MethodDef Source #
Find a method declaration by its Type
and method name m
findField :: Type -> Name -> TypecheckM FieldDef Source #
Find a field declaration by its Type
(ty
) and field name f
isBound :: Name -> TypecheckM Bool Source #
Checks whether a variable name is bound
addVariable :: Name -> Type -> Env -> Env Source #
Add a variable name and its type to the environment Env
.
tcProgram :: Program -> Either TCError (Program, [TCWarning]) Source #
Main entry point of the type checker. This function type checks an AST returning either an error or a well-typed program and warnigns. For instance, assuming the following made up language: > > class C > val f: Foo >
it should be parsed to generate the following AST:
testClass1 = ClassDef {cname = "C" ,fields = [FieldDef {fmod = Val, fname = "f", ftype = ClassType "Foo"}] ,methods = []}
To type check the AST, run the tcProgram
combinator as follows:
tcProgram testClass1
which either returns an error or the resulting typed AST and possible warnings.
class Typecheckable a where Source #
The type class defines how to type check an AST node.
doTypecheck :: a -> TypecheckM a Source #
Type check the well-formedness of an AST node.
typecheck :: Backtraceable a => a -> TypecheckM a Source #
Type check an AST node, updating the environment's backtrace.
Instances
Typecheckable Expr Source # | |
Defined in Warning.Typechecker doTypecheck :: Expr -> TypecheckM Expr Source # | |
Typecheckable MethodDef Source # | |
Defined in Warning.Typechecker | |
Typecheckable Param Source # | |
Defined in Warning.Typechecker doTypecheck :: Param -> TypecheckM Param Source # | |
Typecheckable FieldDef Source # | |
Defined in Warning.Typechecker doTypecheck :: FieldDef -> TypecheckM FieldDef Source # | |
Typecheckable ClassDef Source # | |
Defined in Warning.Typechecker doTypecheck :: ClassDef -> TypecheckM ClassDef Source # | |
Typecheckable Program Source # | |
Defined in Warning.Typechecker doTypecheck :: Program -> TypecheckM Program Source # | |
Typecheckable Type Source # | |
Defined in Warning.Typechecker doTypecheck :: Type -> TypecheckM Type Source # |
checkShadowing :: Name -> TypecheckM () Source #
Checks whether a variable name shadows an existing variable
checkVariableUsage :: Expr -> Name -> TypecheckM () Source #
Checks whether a variable x
is used in the expression e
.
hasType :: Expr -> Type -> TypecheckM Expr Source #
This combinator is used whenever a certain type is expected. This function is quite important. Here follows an example:
doTypecheck mdef@(MethodDef {mparams, mbody, mtype}) = do -- typecheck the well-formedness of types of method parameters mparams' <- mapM typecheck mparams mtype' <- typecheck mtype -- extend environment with method parameters and typecheck body mbody' <- local (addParameters mparams) $ hasType mbody mtype' ...
in the last line, because we are type checking a method declaration,
it is statically known what should be the return type of the function body. In these
cases, one should use the hasType
combinator.
buildClass :: String -> ClassDef Source #
Helper function for generating classes
testClass1 :: ClassDef Source #
Class definition for didactic purposes. This AST represents the following
class, which is named C
, contains an immutable field f
of type Foo
:
class C: val f: Foo
This class is ill-typed, as there is no declaration of Foo
anywhere.
To check how to type checker catches this error, run:
tcProgram (Program [testClass1])
testClass2 :: ClassDef Source #
Test program with a class, field, method, and variable access. The class Bar
does not exist in the environment. The variable access is unbound.
This program is the AST equivalent of the following syntax:
class D val g: Bar def m(): Int x
testClass3 :: [ClassDef] Source #
Test program with a two classes, field, method, and variable access. The class declaration are duplicated.
This program is the AST equivalent of the following syntax:
class D val g: Bar def m(): Int x class D val g: Bar def m(): Int x
Test suite that runs testProgram
.