18. Globals

Valid global scope statements include:

  • Variable Declarations

  • Struct Declarations

  • Function and Procedure Declarations

  • Function and Procedure Prototypes

  • Typealias

All global statements are considered declarations. Global statements must be written in dependency order, and this is a hard requirement: a global may reference only symbols already defined earlier in the file, so globals are initialized in the order they are written. A global whose initializer references a global not yet defined at that point is ill-formed and the compiler must emit a SymbolError (see Errors).

const B = A;   // A is not yet defined here -- ill-formed
const A = 10;

procedure main() returns integer { return 0; }

Errors

This program is ill-formed; the compiler must reject it (SymbolError).

Only global variable initializers are required to be written in dependency order. References between functions and procedures are not bound by textual order. Consult prototype for forward declaration rules in functions

A statement other than a declaration at global scope must emit a GlobalError (see Errors). An assignment that targets a global from within a routine body instead an AssignError (see Errors), as every global is const (see below).

5 -> std_output;   // a statement, not a declaration, at global scope

procedure main() returns integer { return 0; }

Errors

This program is ill-formed; the compiler must reject it (GlobalError).

18.1. Variable Declarations

In Gazprea values can be assigned to a global identifier. All globals must be immutable (const). If a global identifier is declared with the var specifier, then the compiler must emit a GlobalError (see Errors). This restriction is in place since mutable global variables would make functional purity undecidable in general.

var integer g = 0;   // globals may not be declared 'var'

procedure main() returns integer { return 0; }

Errors

This program is ill-formed; the compiler must reject it (GlobalError).

Globals must always be initialized with a valid constant expression. Unlike a local variable, a global is never implicitly zero-initialized: a global declared without an initializer is ill-formed, and the compiler must emit a GlobalError (see Errors). If a zero-value is intended it must be written explicitly (for example const integer i = 0; or const integer[3] a = 0;). A global initializer may reference other globals and use arithmetic and constexpr aggregates, but it must be fully evaluable by the compiler before the program runs. As a consequence:

  • Functions, procedures, and I/O operations may not appear in a global’s initializer.

  • A global vector or string is permitted only when it is const with a constexpr initializer, its length is fixed at compile time, so a const vector is equivalent to an array the size of its initializer. Consequently const string s = "hi"; and const vector<integer> v = [1, 2, 3]; are legal globals. An inferred-size array such as const integer[*] X = [1, 2, 3] is likewise permitted if the initializer is a constexpr (see Sizing).

Legal globals in dependency order – a later global may read an earlier one:

const A = 10;
const B = A * 2;                  // may reference an earlier global
const integer[*] X = [1, 2, 3];

procedure main() returns integer {
  B -> std_output; '\n' -> std_output;
  X -> std_output;
  return 0;
}

Output

20
[1 2 3]

The compiler must emit a GlobalError (see Errors) for any violation of the above rules. For instance, a global with no initializer:

const integer i;   // a global is never implicitly zero-initialized

procedure main() returns integer { return 0; }

Errors

This program is ill-formed; the compiler must reject it (GlobalError).

  • lemma: All globals are constexpr.