This document describes the intended Solcore module and namespace system.
- The entry file is the path passed to
-f/--file. - The main library root is configured with
--root. - The default main library root is the current working directory.
- The std library root is configured with
--include. With the default options this isstd. - External libraries are registered explicitly with repeated
--lib NAME=DIRflags.
- Module identity is logical, not canonical-path-based.
- A module id is
(library, logical module path). - Libraries are:
- the main library
- the std library
- named external libraries
foo.barmaps tofoo/bar.solc.- Directories are not modules.
fooandfoo.bartherefore refer to different files.- The source file path is stored separately from module identity.
- If two logical module paths reach the same physical file, they are still treated as different modules when their module ids differ.
Supported forms:
import M;
import M as A;
import M.{X, Y};
import M.{*};
import M.{*} hiding {X};
import lib.foo.bar;
import @ext.foo.bar;Import path kinds:
import foo.bar;- relative to the importing module's logical directory
import lib.foo.bar;- from the current library root
import @ext.foo.bar;- from the external library registered as
ext
- from the external library registered as
Current std-specific behavior:
import std;resolves to the std library root from any library.import std.dispatch;resolves todispatch.solcunder the std root.- Bare imports do not fall back to the std root.
- Imports do not have constructor-specific selector syntax.
Exported constructors are accessed through qualified constructor paths such as
T.C,M.T.C, oralias.T.C.
import M;does not open names into unqualified scope.import M as A;binds onlyA.import M.{X, Y};imports selected exported names into unqualified scope.import M.{*};imports all exported item names into unqualified scope.import M.{...} hiding {X, Y};removes names from the selector result after expansion.- Items inside
{...}may mix simple item names and*. Dotted item paths are not supported there.
Default module bindings:
import foo.bar;bindsbar.import foo.bar as B;bindsBand does not bindbar.- Non-alias module imports also support full-path qualification, so
import foo.bar;allows bothbar.xandfoo.bar.x. - If two imports would bind the same final segment, it is an error.
For example,
import foo.bar; import baz.bar;is rejected.
Validation rules:
- Duplicate qualifier names are rejected.
- Duplicate explicit names inside one selective import are rejected.
- Duplicate names inside one
hiding {...}list are rejected. - Unknown selected names are rejected.
- Unknown hidden names are rejected.
- Ambiguous names introduced by selective or glob imports are rejected.
Supported forms:
export {X, Y};
export {*};
export {T(C1, C2)};
export {T(*)};
export {M.*, *};
export M;
export M as N;
export M.{X, Y};
export M.{T(C1, C2)};
export M.{T(*)};
export M.*;Current behavior:
- A module may contain multiple export declarations.
- The public interface is the union of all export declarations after expansion.
- If a module has no export declarations, its public interface is empty.
exportdeclarations do not add names to the current module's local scope.- Only exported names and re-exported modules are visible to importers.
Expansion rules:
export {X}exports a local item.export {T(C1, C2)}exports the local data typeTand the named constructors ofT.export {T(*)}exports the local data typeTand all constructors ofT.export {*}exports all local importable top-level items, but not subordinate constructors.export {M.*}re-exports all exported item names fromM.export M;re-exports the module under its final segment.export M as N;re-exports the module underN.export M.{X, Y};re-exports selected item names fromM.export M.{T(C1, C2)};re-exports the imported data typeTand the named constructors ofTthat are visible throughM.export M.{T(*)};re-exports the imported data typeTand all constructors ofTthat are visible throughM.export M.*;re-exports all exported item names fromM, preserving constructor visibility on exported data types, but not the module binding itself.
Constructor-export rules:
- Constructor names are not exported as standalone top-level items.
They must be exported through a data type item such as
T(C1, C2)orT(*). T(C1, C2)andT(*)always imply export of the typeTitself.*never implies export of constructors.
Validation rules:
- Unknown local exports are rejected.
- Unknown re-exported names are rejected.
- Naming a constructor that does not belong to the selected data type is rejected.
- Re-exporting a constructor that is not visible through the source module is rejected.
- Re-exporting two different underlying items under the same public name is rejected.
- Re-exporting two different module targets under the same public module name is rejected.
- Repeated exports of the same underlying item are normalized, so forms such as
export {main, *};are accepted.
Instance behavior:
- Instances are import-visible whenever their defining module is imported.
- Instances are not named individually in export lists.
Current duplicate checking is enforced separately for:
- the type namespace
- contracts
- data types
- type synonyms
- classes
- the term namespace
- functions
- constructors
- values
Unqualified lookup order:
- Local lexical scope
- Current module top-level declarations and names introduced by
import M.{...}/import M.{*}are treated at the same priority - If that combined non-local set contains more than one candidate in the same namespace, validation fails with a hard error
- Otherwise unresolved
Current behavior:
- Local parameters and local variables still shadow non-local names.
- Current-module top-level names no longer silently shadow selected or glob-imported names.
- Selected or glob-imported names no longer silently shadow current-module top-level names.
- Re-importing the same underlying declaration is normalized, so importing
std.{*}and thenstd.{uint256}does not fail by itself.
Constructors remain term-level names, but they are canonicalized after resolution to Type.Constructor form.
Constructor visibility:
export {T}exports the type only; external modules cannot name any constructors ofT.export {T(C1, C2)}exports only the named constructors ofT.export {T(*)}exports all constructors ofT.- Imports do not add constructors as unqualified names.
Instead, exported constructors are accessed through qualified paths such as
T.C,mod.T.C, andalias.T.C.
Examples of accepted source forms:
Option.Somemod.Option.Somealias.Option.Some.Somewhen expected type context is available
Current behavior:
- Bare constructor names are not resolved by default in the qualified-constructor model.
data Foo = Fooremains valid because type and term namespaces are separate.- A hidden constructor cannot be named from another module in either expressions or patterns.
Dot shorthand:
.Kis resolved during contextual typing, not during the initial name-resolution pass.- Pattern shorthand uses the expected scrutinee type.
- Expression shorthand uses the surrounding expected type.
- Nested shorthand works when outer context determines the constructor family.
.Konly resolves ifKis visible from the current module on the expected data type.- Missing expected type, non-data expected type, or missing constructor name is an error.
Pattern matching and exhaustiveness:
- Inside the defining module, pattern matching sees the full constructor set of a data type.
- Outside the defining module, only visible constructors may appear in patterns.
- If some constructors of a data type are hidden from the current module, matches on that type are treated as non-exhaustive unless they contain a wildcard or catch-all arm.
- If all constructors of a data type are visible, ordinary exhaustiveness checking applies.
- Pragmas are module-local.
- Pragmas do not propagate through imports.
- Imported instances are trusted during checking without importing the defining module's pragma environment.
- Import cycles are allowed.
- The loader computes strongly connected components of the import graph.
- A recursive component is compiled as a recursive module group.
- Public interfaces inside a recursive group are computed by fixed-point iteration.
- Compile surfaces for recursive groups are also computed by fixed-point iteration.
- Self-imports and mutual re-export cycles are therefore supported.
- If interface or compile-surface expansion does not stabilize, compilation fails for the group.
- External libraries are configured with
--lib NAME=DIR. - Source code imports them with
@NAME.module.path. - The external library name is part of module identity.
- Relative imports inside an external library stay within that external library.
lib.*inside an external library resolves from that external library's root.- Version selection and dependency lockfiles are not part of the language-level implementation yet.