Compiler operation The compiler has a number of phases: * Parsing. Initially the compiler parses all the source files and keeps a parsed representation in memory. Very syntax error checking is performed at this point. The compiler stores the information in classes whose names represent the language construct, for example, the "if" construct is stored in an `If' class. A class is stored in a `Class'. * The TypeManager The TypeManager loads all the assemblies that were referenced by the programmer. The CLR type system is used as our repository for types defined as well. So the same interface that is used to query the types, properties and flags about system types is the same interface that we use for our types. As we work our way through the code generation and semantic analysis, new types are entered into the Type system through the use of System.Reflection.Emit. The TypeManager will lookup types on both the user defined types and on the system defined ones. So special care has to be used. The order in which we proceeed from here is important. * Base class resolution and type definition. Once the parsing has happened, the compiler resolves the inheritance tree for interfaces. This is done recursively and we catch recursive interface definitions here. After this is done, we continue on with classes. Classes have can have an optional "parent" inherit from or the implicit System.Object class (for normal builds, builds with /nostdlib will allow you to compile class System.Object with no parent). At this point we do some error checking and verify that the inherits/implements section of a class is correct (since we have previously built the interface inheritance). By the time we are done, all classes, structs and interfaces have been created using System.Reflection.Emit and registered with the Type Manager. This allows us to define fields and resolve argument names for methods, properties, indexers and events. * Field generation Fields are generated next, we go through all the type containers (classes and structs) and enter the fields into their types. * Method, Properties, Indexers and events definitions Now all the methods, constructors, properties, indexers and events are entered. They are only `defined' using System.Reflection.Emit. No code generation will happen until everything has been entered into System.Reflection.Emit. This is important because to actually generate code we need to know everything about the environment in which the code is being generated. * Code Generation At this point all the definitions have been entered into the type manager through System.Reflection.Emit. We can now use System.Reflection to query all the information about the types. Your normal semantic analysis and code generation phase lives here. * Statements Most of the statements are handled in the codegen.cs file. * Expressions * Error reporting Always use `Report::Error' or `Report::Warning' methods of Report class. The actual Report instance is available via local context. An expression error reporting has to be done during Resolve phase, except when it's Emit specific (very rare). Error reporting should try to use the same codes that the Microsoft compiler uses (if only so we can track which errors we handle and which ones we dont). If there is an error which is specific to MSC, use negative numbers, and register the number in mcs/errors/errors.txt Try to write a test case for any error that you run into the code of the compiler if there is none already. Put your test case in a file called csNNNN.cs in the mcs/errors directory, and have the first two lines be: // csNNNN.cs: This is the error message // Line: XXX // Compiler options: an optional compiler options Where `XXX' is the line where the error ocurrs. We will later use this as a regression test suite for catching errors in the compiler.