Type checking or static checking is performed by the compiler (checking is done at the compiler time). Specific forms of programming faults will be recognized and reported as a result of this.
A compiler should ensure that the source program follows the source language's syntactic and semantic conversions. Static checking is the term for this type of checking. Static checking is a method of detecting and reporting code mistakes.
If an operator is applied to the incompatible operand, the compiler should give an error. Type checking is the term used to describe this procedure.
When code is generated, the type information collected by a type checker may be required. For instance, various operands may require different arithmetic operators at the machine level (real and integer).
The technique of verifying and enforcing type restrictions in values is type checking.
Many texts are filtered out by the compiler's Lexical Analysis and parsing phases. Still, these techniques cannot handle many programming languages with well-formed needs since they are frequently not context-free and cannot be checked by the membership of a context-free grammar.
The type-checking Phases of Compiler design is interleaved with the syntax analysis phase, so it is done before a program's execution or translation (static typing), and the information is gathered for use by subsequent phases, such as the translator, which will naturally combine type calculation with the actual translation.
A type checker ensures that a construct's type matches the type expected by its context.
For example, in Pascal, the arithmetic operator mod requires integer operands; hence a type checker ensures that the operands of the mod are of type integer.
When the code is generated, type information acquired by a type checker may be required.
The following are some examples of static/type checks:
Type Check
If an operator is applied to an incompatible operand, the compiler should notify an error.
The flow of Control Checks
Statements that cause the flow of control to exit a construct must have a destination for the flow of control to be transferred, for instance, branching to labels that do not exist. For example, there is no enclosing statement in a switch statement, such as break.
Checks for uniqueness
Objects should only be specified once. This holds true in a variety of languages.
Name-related Checks
It is not uncommon for the same name to appear twice or more. For example, in Eve, the block name 'e' must appear both at the block's beginning and end.
Types of Type Checking
There are two types of type checking:
Static Type Checking
Dynamic Type Checking
Static Type Checking
Static type checking in compiler design refers to the process of analyzing the source code during compilation to ensure that variables and expressions adhere to a consistent and predetermined set of data types. The compiler checks for type-related errors and violations before generating the executable code. This early detection at compile-time helps prevent many common type-related issues, improving program reliability and performance. Languages like Java and C++ employ static type checking.
Dynamic Type Checking
Dynamic type checking in compiler design involves checking the data types of variables and expressions during program execution. Instead of enforcing type constraints at compile-time, the compiler generates code that includes runtime checks for type compatibility. This approach provides more flexibility, allowing variables to change types dynamically during program execution. However, it may lead to runtime errors if unexpected type mismatches occur. Languages like Python and JavaScript use dynamic type checking.
Type System
The information about the syntactic constructions in the language, the notion of types, and the rules for assigning types to language constructs are used to develop a type checker for that language.
"If both operands of the arithmetic operators +, -, and * are of type integer, the result is of type integer".
The type system is a set of rules for assigning type expressions to various program parts. A type system is implemented with the help of a type checker. It is syntax-directed. Compilers and processors for the same language may utilize different type systems.
Type Expression
The type system is a set of rules for associating type expressions with varying parts of the program. A type checker implements a type system. Distinct compilers or processors of the system Language may employ different type systems.
Static type checking is performed by a compiler, whereas terminal dynamic type checking is performed when the target program is run.
Since a source type system allows us to know statically that type errors will not occur when the target program executes; it eliminates dynamic type checking requirements.
Type checking should have the property for error recovery.
A "type expression" will indicate the kind of a language construct. A type expression can be a simple type or one that is created by applying an operator called the type constructor to other type expressions. The basic types and constructors are determined by the language being checked.
The definitions of type expressions are as follows:
Type expressions are basic types such as boolean, char, integer, and real. During type checking, a basic type-type_error will signify an error; void, which denotes "the lack of a value," allows statements to be checked.
A type name is a type expression since type expressions can be named.
A type expression is a type constructor applied to the type expressions.
Variables whose values are type expressions can be found in type expressions.
Design Spaces in Type Checking
Statically typed languages: C, C++, and Java are examples of statically typed programming languages that do type checking at compile time.
Dynamically typed languages: These are those languages whereby type checking is performed during run-time or dynamically. They include Javascript, Python, PHP.
Strongly typed languages: These are implementations of languages in which the operation's arguments are of the type declared by the operation whenever an operation is done. If a language's compiler can guarantee that the programs it accepts will execute without type errors, it is said to be strongly typed. For example, a text type and a floating-point number cannot be concatenated.
Weakly typed languages: The types of objects and variables are not explicitly specified in this language. These are primarily used in systems programming, where you alter data without consideration to what it represents (moving, encrypting, compressing).
Some programming languages, such as C, include both static and dynamic typing, which means that some types are checked before execution and others are checked during execution.
The design space for static vs. dynamic type and weak vs. strong typing:
Attributes in Type Checking
The abstract syntax tree (AST) is used for type-checking, and it can perform numerous recursive runs on this tree to acquire information or use knowledge from prior phases.
Attributes are the terms used to describe this information.
Attributes can be synthesized (attributes passed up the AST) or inherited (attributes passed down the AST).
A symbol table synthesized by a declaration and inherited by the declaration's scope is an example of an attribute synthesized on one subtree that can be inherited in another subtree.
A syntactic category in grammar is a type in the AST's data structure or a group of related non-terminals.
Each syntactic category will have its own set of characteristics.
When a type checker is written as a set of mutually recursive functions, each syntactic category will have one or more of these functions.
Frequently Asked Questions
What is type checking in compiler design?
Type checking in compiler design is the process of verifying that variables and expressions in a program adhere to the specified data types, ensuring type consistency.
What is static and dynamic type checking?
Static type checking is done at compile-time, while dynamic type checking occurs at runtime, allowing for flexibility but with potential for runtime errors.
What are the types of type system in compiler design?
Type systems in compiler design include static typing (compile-time checks), dynamic typing (runtime checks), and hybrid systems combining aspects of both.
What is the difference between type checking and type conversion?
Type checking verifies variable types for consistency, ensuring adherence to rules. Type conversion involves changing a variable's type, potentially requiring explicit or implicit conversion operations to maintain compatibility.
Conclusion
In this blog, we learned the concepts of type checking of compiler design in detail. We discussed type systems, type expressions, design spaces and attributes in type checking.
We understood the differences between static, dynamic, strongly typed, and weakly typed languages and the characteristics and benefits of each.
Type checking is also done during lexing and parsing, although some languages have well-formed requirements that cannot be met by the techniques employed in these two phases. To learn more about type checking, refer here.