Execution
This section gives a detailed description of the program initialization and startup. Two important concepts related to execution, calling objects and raising exceptions, are briefly described. Other topics related to program execution are described later in sections Statements and Expressions.
Initialization
At the start of the program execution, all modules (including the main module) will be initialized. During module initialization:
- All global variable and constant definitions with initializers, and any statements in the module are evaluated.
- Initializers and statements in a single file will be evaluated in the
order they appear
in the file, from the start of the file to the end.
If a module contains multiple files, these files will be initialized in an
arbitrary order.
Note: It may therefore be helpful to collect all initialized global variable and constant definitions in a module to a single source file to avoid problems with different evaluation orders.
- After the initialization is complete, the Main function of the module is called, if it is defined. The Main function is not generally given any arguments.
As an exception, the Main function of the main module may take a single argument that will receive the command line arguments given to the program as an array of strings. The Main function is always optional.
Module dependencies
A module x depends on module y if
- module x imports module y, or
- any module imported by module x depends on module y.
This definition is used in the next section.
Order of initialization
The order of module initialization is not fully specified, but it must follow these constraints:
- If module x depends on module y but module y does not depend on module x, module y will be initialized before module x.
- Initialization of multiple modules cannot be interleaved, i.e. a single module must first be fully initialized before another module can be initialized.
In practice, these rules mean that all imported modules will be initialized before the module that imports them, unless there are circular dependency chains. The main module will be initialized last (and thus the Main function of the main source file will be run last after all modules have been initialized).
Calling objects
Functions, methods and types may be called implicitly or explicitly. Explicit calls are performed using call expressions. Implicit calls include these:
- calling module Main functions during initialization
- calling methods when evaluating many operators in expressions
- calling a getter method when evaluating a member reference
- calling a setter method when assigning to a member
- calling the _set method when a subscript expression is used as an lvalue in an assignment statement
- calling the create method during object construction
- calling the hasNext and next methods in for loops
- calling type constructors when built-in exceptions such as std::TypeError are raised implicitly during evaluation.
Both implicit and explicit calls are evaluated in the same fashion. The rest of this section only deals with function calls, but the same rules apply to method and type calls.
A function call involves creating a new invocation of the function. The invocation contains instances of any local variables and other state needed during the evaluation of the function. Multiple invocations of the same function can be alive at the same time, and their contents are independent of each other.
Function invocations form a stack: as a function is called, the new invocation is pushed to the top of the stack and the previous function calls remain inactive in the stack below the new invocation and only become active when the new invocation returns or propagates an exception, causing that invocation to be destroyed. The implementation may limit the maximum depth of the call stack.
An invocation may return by finishing the evaluation of the function body or by evaluating a return statement within the body. In the latter case, the invocation may optionally return a value as the result of the function call. The caller may use or discard the return value.
The bottom of the call stack contains a special invocation that is not associated with any defined function. It is used to evaluate the initialization expressions of global variables and constants and to call the Main functions. After all the modules in a program have been initialized, this invocation causes the program execution to end.
Raising exceptions
As their name implies, exceptions are raised in response to exceptional conditions. They may be raised by many operations in the language. The programmer can also raise them explicitly with the raise statement.
Each exception is associated with an exception object whose type must be std::Exception or its subtype.
When an exception is raised, the evaluation of the current expression or statement is terminated. Any try blocks enclosing the current location in code are consulted, starting from the innermost one and proceeding outwards (see section Try statement for details).
If no try statement in the current function or method catches the exception, it is propagated to the next invocation in the call stack. The exception acts as if it was raised in the call statement that was being evaluated in the function. This process is continued until the exception is caught or until the call stack becomes empty, in which case the program is terminated due to an uncaught exception.