Import declaration

Grammar§

ImportDeclaration ::= Attribute* "import" (ImportSelection | ImportModuleList) ";" ImportModuleList ::= (TypeIdentifier ",")* TypeIdentifier? ImportSelection ::= TypeIdentifier ("," TypeIdentifier)* "in" TypeIdentifier

Semantics§

ImportModuleList§

An import gives access to declarations located in foreign units.

Each foreign unit is specified using a TypeIdentifier that matches to the UnitDeclaration and the physical path of the foreign unit.

unit a;
unit b;
import a;

Matching units are resolved this way

  1. if the UnitDeclaration of one of the source file that was passed to the compiler matches then it is selected
  2. otherwise the compiler tries to locate the unit in an import path

An import creates a regular scoped symbol that can be optionally used to fully qualify the foreign symbols.

unit a;
alias B = u8;
unit b;
import a;
var B b1;
var a.B b2; // if "unit b" declares a "B", distinction is possible

In the same way, imports declared in an import can be fully qualified

unit a;
alias B = u8;
unit b;
import a;
unit c;
import b;
var b.a.B b1;

but only if they are public.

unit a;
alias B = u8;
unit b;
protection (private) import a;
unit c;
import b;
var b.a.B b1; // error, "a" not visible

Because imports create regular symbols, other declarations can conflict with them

unit b;
import a;
var s32 a; // error, "a" already declared
unit b;
var s32 a;
import a; // error, "a" already declared

These symbols take for name the first identifier of the chain.

unit a;
import b.c; // adds a symbol "b" to the unit scope.

The symbols matching to the root of an import dont conflict between themselves

unit b;
import a.b, a.c; // "a" can be redeclared, it is always the same root unit
var s32 a;       // error, "a" already declared

They dont create conflict even if located directly in the matching root

unit a;
import a.b;

The relationship between root and child units only exists in the symbols created for the imports, not directly in the units.

This is for example why this

unit a;
unit a.b;
alias B = u8;
unit unrelated;
import a;       // does not leak "a.b" declarations.
alias C = B;    // error, cannot solve type "B"

or that

unit a;
import b; // error, because the link to "b" does not exist in "a" scope.
unit a.b;

does not compile.

ImportSelection§

An import can be used to restrict the set of symbols that can be accessed without qualifier.

In this case the TypeIdentifiers located in the list located before in must be composed of single Identifiers.

They represent the selection for which unqualified searches in the import work.

unit a;
function f(){}
function g(){}
unit b;
function f(){}
function g(){}
unit c;
import g in a;
import f in b;

alias fb = f;   // select `f` from `b` as `f` from `a` is not allowed in an unqualified search.

The selection has to represent valid symbols.

unit a;
function space(){}
unit c;
import spice in a; // error, identifier `spice` not found in target unit

The selection does not prevent to pick fully qualified symbols

unit a;
function left(){}
function right(){}
unit c;
import left in a;

function test()
{
    left(); // ok, unqualified and specified in "a" selections
    right(); // not ok, it is not specified in "a" selections
    a.right(); // ok, not in "a" selection but fully qualified,
               // hence, no conflict possible
}

Cycles§

Cycles between two units are allowed if at least one of the involved import declaration is @private, or specifies a selection, or is nested in a sub scope (function body, aggregate body, etc.).

Otherwise the cycle is considered as an error

§u/a.sx
unit u.a;
import u.c;

§u/b.sx
unit u.b;
import u.a;  // `@private import u.a;` to fix the cycle

§u/c.sx
unit u.c;
import u.b;  // error, circular import between `unit u.a` and `unit u.c`