extern and extern library
Entities may have up to three declarations:
class MyClass;.class MyClass { ... }.extern library "<owning_library>" declaration.
extern library "OtherLibrary" class MyClass;.extern declaration, and
must also contain a declaration.extern modifier
(without library).
extern class MyClass;.For example, a library can have a forward declaration of an entity in the API file, and use the implementation file for the entity's definition. Putting the definition in an implementation file this way can reduce the dependencies for API file evaluation, improving compile time. This is commonly done with functions. For example:
library "MyLibrary";
fn DoSomething();
impl library "MyLibrary";
fn DoSomething() {
...
}
In order to determine whether two redeclarations refer to the same entity, we apply the rules:
extern library declaration,
declarations differ if they don't match semantically.The program is invalid if it contains two declarations of the same entity that differ.
class A {
// This function will be redeclared in order to provide a definition.
fn F(n: i32);
}
// ✅ Valid: The declaration matches syntactically.
fn A.F(n: i32) {}
// ❌ Invalid: The parameter name differs.
fn A.F(m: i32) {}
// ❌ Invalid: The parameter type differs syntactically.
fn A.F(n: (i32)) {}
TODO: Figure out what details to pull from #3762 and #3763.
extern and extern libraryThere are two forms of the extern modifier:
extern limits access to the definition.
extern library declaration is optional.extern library allows references to an entity
without depending on the owning library.
For example, a use of both might look like:
library "owner";
// This `import` is required due to the `extern library`, but we also make use
// of `MyClassFactory` below. This is a circular use of `MyClass` that we
// couldn't split between libraries without `extern`.
import library "factory";
extern class MyClass {
fn Make() -> MyClass* {
return MyClassFactory();
}
var val: i32 = 0;
}
library "factory";
// Declares `MyClass` so that `MyClassFactory` can return it.
extern library "owner" class MyClass;
fn MyClassFactory(val: i32) -> MyClass*;
impl library "factory";
// Imports the definition of `MyClass`.
import library "owner";
extern fn MyClassFactory(val: i32) -> MyClass* {
var c: MyClass* = new MyClass();
c->val = val;
return c;
}
externThe extern modifier is only valid on namespace-scoped entities, including in
the file scope. In other words, class C { extern fn F(); } is invalid.
Indirect imports won't see the definition of an extern entity. We expect this
to primarily affect return types of functions. If an incomplete type is
encountered this way, it can be resolved by directly importing the definition.
For example:
library "type";
// Because this is `extern`, the definition must be directly imported.
extern class MyType { var x: i32 }
library "make_type";
import library "type";
// Here we have a function which returns the type.
fn MakeMyType() -> MyType*;
library "invalid_use";
import library "make_type";
fn InvalidUse() -> i32 {
// ❌ Invalid: `MyType` is incomplete because it's `extern` and not directly
// imported. `x` cannot be accessed.
return MakeMyType()->x;
}
library "valid_use";
import library "make_type";
// ✅ Valid: By directly importing the definition, we can now access `x`.
import library "type";
fn ValidUse() -> i32 {
return MakeMyType()->x;
}
extern keywordextern namingextern to privateextern declarationsextern declarationsextern on the first owning declarationextern syntaxesextern members re-export themextern library declarationsextern declarations