Silk

Modules, names, and visibility

Each Silk source file defines one module. A module's identity comes from its logical path relative to the compilation source root, and imports create explicit names for declarations in other modules. Imports are compile-time only: they do not run code or create values.

This page defines module identity, source lookup, import bindings, name collisions, and cross-module visibility. Declaration-specific contracts remain on their corresponding reference pages. Package acquisition and version selection are outside the language.

Terminology

  • A source root is the logical directory from which project module identities are derived.
  • A module identity is a case-sensitive, extensionless path such as model/User.
  • An import path is the dotted source spelling of a module identity, such as model.User.
  • A namespace binding is a local name that qualifies members of one imported module.
  • A selected binding is one imported declaration available directly under a local name.
  • An alias is an explicit local name replacing a namespace or selected binding's default name.
  • A qualified name begins with a namespace binding or nominal module-scope binding, as in User.make.
  • A nominal module scope is a struct, service, or interface name that also qualifies the public declarations of the module whose file basename it matches.
  • A module scope is the set of top-level declaration and import names visible throughout one module.
  • A module closure is the root module and every module reachable through its transitive imports.

Module identity and loading

MODULE-001 — One source file defines one path-identified module

Status: Confirmed

Each .silk source file is one module. Its canonical identity is its case-sensitive, extensionless path relative to the compilation source root, written with / between path segments.

<source root>/model/User.silk  ->  model/User
<source root>/app/Main.silk    ->  app/Main

Source code names the same identities with dotted import paths:

import model.User

Every segment is exact. model.User identifies model/User.silk; it does not find model/user.silk, model/User/index.silk, or another extension.

Boundary: The identity is logical rather than an absolute host path. Absolute paths, source extensions, empty segments, ., and .. are not valid module identities. A source supplied from memory still has one explicit logical identity even when it has no filesystem path.

Diagnostics: An import whose exact module does not exist reports MOD0001 at the complete import path. A compilation request whose root identity itself is noncanonical is a client error rather than a Silk source diagnostic. A project module attempting to occupy the reserved silk/ standard-library source space reports MOD0004.

Evidence: module closure, source resolution, module closure tests.

MODULE-002 — Source text does not declare or override module identity

Status: Confirmed

A Silk file contains declarations and imports but no module declaration. The source resolver assigns its identity before parsing the file.

// model/User.silk is already module model/User.
pub struct User { id: i32 }

Moving or renaming the file changes the module identity and therefore the canonical identities of its declarations. Imports must name the new path.

Boundary: Module documentation, a namespace alias, or a declaration named User does not change the containing module's identity. Two identical source texts supplied as model/User and archive/User define distinct modules and distinct declarations.

Diagnostics: Silk has no module-declaration syntax. A declaration-like spelling attempting to introduce one receives the ordinary lexer or parser diagnostic for unsupported syntax. An import that still names the old path reports MOD0001.

Evidence: canonical module identity, syntax files.

MODULE-003 — Import paths resolve from the source root, not from the importing file

Status: Confirmed

Every project import is absolute within the compilation source root. The same import path identifies the same module no matter which module contains it.

<source root>/app/features/Profile.silk
<source root>/model/User.silk
// app/features/Profile.silk
import model.User

This resolves <source root>/model/User.silk. It does not begin lookup below app/features/.

By default, the entry file's containing directory is the source root. A project or compiler client may select an explicit source root; a nested entry then keeps its path below that root as its module identity.

Boundary: Silk has no relative import spelling. An import cannot use . or .. to walk from the importing module, and resolution does not probe parent directories or alternate file layouts.

Diagnostics: A syntactically valid path with no exact source reports MOD0001. A file that exists but cannot be read produces an operational source-resolution failure rather than pretending that the module is absent.

Evidence: filesystem source lookup, source resolver tests.

MODULE-004 — Compilation loads only the transitively reachable module closure

Status: Confirmed

Compilation begins with one root module and follows its imports transitively. Each reachable module identity is resolved and parsed at most once, even when several modules import it.

app/Main -> feature/Left  -> shared/Value
         -> feature/Right -> shared/Value

shared/Value belongs to the closure once. A source available to the resolver but unreachable from app/Main is not part of that compilation merely because it exists below the source root.

Boundary: Loading a module makes its declarations available for static analysis; it does not mean that every function in that module is executable or included in the emitted program. Runtime reachability is a separate property beginning at the selected entry point.

Diagnostics: Each absent reachable target reports its import diagnostic. A damaged import path retains its parser diagnostic without also requesting a fabricated module or emitting MOD0001. One operational resolution failure does not erase successfully loaded modules, although the compilation cannot commit its requested artifact.

Evidence: reachable module closure, module closure tests, executable closure.

MODULE-005 — Imports have no runtime behavior

Status: Confirmed

An import contributes names to its containing module's static scope. It does not execute the imported source, initialize runtime module state, construct an Effect, allocate storage, or change ownership.

import logging.Logger

pub fn main() -> i32 {
  return 42
}

The import alone performs no logging and creates no Logger value or service requirement.

Boundary: Calling an imported function, constructing an imported value, or mentioning an imported service in an Effect contract has the ordinary behavior of that declaration. The import itself contributes none of that behavior.

An import also does not implicitly re-export names or activate unlisted methods, operators, overloads, or runtime providers. Interface-conformance availability is defined with interface and specialization rules rather than by runtime import side effects.

Diagnostics: A valid but unused import is not a compiler error. Tooling may report a removable unused-import warning. Invalid bindings retain their import or name-resolution diagnostics even when no runtime expression uses them.

Evidence: behavior-neutral imports, module semantic surface.

MODULE-006 — Import cycles are valid and do not choose lookup order

Status: Confirmed

Distinct modules may import each other. The cycle itself is not an error because all reachable top-level declaration headers are known before function bodies resolve.

// a/A.silk
import b.B

pub fn main() -> i32 {
  return B.answer()
}
// b/B.silk
import a.A

pub fn answer() -> i32 {
  return 42
}

The modules form an import cycle, and A.main still resolves B.answer canonically.

Boundary: Valid import cycles do not make invalid type or value cycles safe. A recursively embedded nominal value with no indirection remains invalid under its value-type rules. Calling mutually recursive functions can also fail to terminate at runtime even though their names resolve.

Diagnostics: An import cycle alone emits no source diagnostic. Each declaration inside it still receives its ordinary signature, visibility, type, ownership, and control-flow diagnostics.

Evidence: module cycle facts, cycle-safe lookup, name-resolution cycle tests.

Import bindings

IMPORT-001 — A namespace import binds the target's final path segment

Status: Confirmed

A namespace import without an alias creates one local namespace binding. Its default name is the last segment of the imported module path.

import model.User

pub fn main() -> i32 {
  let user = User.make(42)
  return user.id
}

model.User identifies module model/User and binds local namespace User. User.make then looks up public member make in that module.

Boundary: The namespace is not a runtime value and does not import any member as an unqualified name. make(42) is unresolved unless make is local or separately selected into scope.

Diagnostics: An unknown target reports MOD0001 at the import path. A valid namespace with no public member named by a qualified lookup reports the unknown-member diagnostic for that member. A private member reports SEM0015 rather than appearing unknown.

Evidence: explicit import bindings, namespace lookup tests.

IMPORT-002 — A namespace alias replaces the default local name

Status: Confirmed

as assigns an explicit local name to the imported module namespace.

import compiler.Syntax as Tree

pub fn main() -> i32 {
  return Tree.parse()
}

The module identity remains compiler/Syntax; only this importing module's local binding changes from Syntax to Tree.

Boundary: The default name is not retained alongside the alias. Syntax.parse() is unresolved unless another visible binding independently names Syntax. An alias does not rename the imported module or any declaration it owns.

A redundant alias such as import compiler.Syntax as Syntax is valid under IMPORT-005. Tooling may offer to remove the unchanged alias.

Diagnostics: A missing alias name receives the parser's missing-token diagnostic without a fabricated binding. Binding collisions use the collision rule rather than silently replacing an existing name. An unchanged alias is compiler-valid; the LSP may report LSP0002 and offer to remove only the redundant alias clause.

Evidence: import aliases, alias tests.

IMPORT-003 — A selective import binds only its listed public members

Status: Confirmed

A selective import creates direct bindings for the declarations listed between braces. It does not also create the module's default namespace binding.

import compiler.Syntax { Node, node, parse as read }

pub fn main() -> i32 {
  let value = node(42)
  return read(move value)
}

This binds public type Node as Node, constructor function node as node, and public function parse as read. It does not bind namespace Syntax.

Boundary: Selection is explicit and non-recursive. Other public declarations in compiler/Syntax do not become visible, and a selected declaration does not bring names used by its implementation into the importing module.

Selecting a private declaration is distinct from selecting an unknown declaration: the former has a known but inaccessible candidate; the latter has no candidate with that name.

Diagnostics: An unknown selected member reports SEM0014 at that member. A private selected member reports SEM0015. A local spelling claimed by another valid binding reports SEM0016 without choosing a winner.

Evidence: selected import bindings, selected import tests.

IMPORT-004 — A hybrid import may bind one namespace and selected members together

Status: Confirmed

One import may combine a namespace binding with selected-member bindings from the same target.

import compiler.Syntax as Tree { Node, node, parse }

pub fn main() -> i32 {
  return parse(node(20)) + Tree.width(Tree.node(22))
}

This binds namespace Tree and direct members Node, node, and parse. All bindings still identify declarations owned by canonical module compiler/Syntax.

Boundary: The namespace alias affects only the namespace binding. It does not prefix or rename the selected members. Each selected member may use its own explicit alias when a different local name is needed.

The hybrid form is a compact way to request both namespace and selected bindings. IMPORT-006 also permits separate declarations naming the same target when that organization is clearer.

Diagnostics: Each binding is checked independently for unknown members, inaccessibility, and local collisions. Repeating the same canonical target is not itself a diagnostic.

Evidence: hybrid import bindings, hybrid syntax provenance.

Names and collisions

NAME-001 — Top-level declarations are visible throughout their defining module

Status: Confirmed

A top-level declaration may be named anywhere in its defining module, including from a declaration written earlier in the file. Source order does not control name availability.

pub fn main() -> i32 {
  return answer()
}

fn answer() -> i32 {
  return 42
}

main resolves the later answer declaration. Every top-level declaration header is collected before function bodies resolve.

Boundary: This rule applies to top-level declarations, not local values. A local let binding, parameter, or pattern binding exists only in its lexical scope and cannot be used before its declaration. Runtime initialization order is not inferred from top-level source order because imports and declarations do not execute module initialization.

Diagnostics: A top-level reference with no matching local or imported declaration receives the ordinary unknown-name diagnostic for its position. It is not reported merely because the matching declaration occurs later in the file.

Evidence: declaration indexing, declaration index tests.

NAME-002 — One flat module namespace contains top-level declarations and imports

Status: Confirmed

Functions, nominal types, constants, services, interfaces, module namespace bindings, and selected imports claim names in one flat module scope. Two different bindings cannot use the same local spelling merely because they have different declaration kinds.

struct Token {}
fn Token() -> i32 { return 42 }

The second Token is invalid. The language does not choose between a type meaning and a function meaning based on the expression's expected kind.

Boundary: A namespace and one of its members may legitimately have the same spelling because qualification keeps them at different lookup positions. With import model.User, User.User may name public declaration User inside module namespace User. The two User components are not competing bindings in one scope.

Generic parameters, function parameters, local bindings, fields, and pattern bindings have their own lexical or declaration-local scopes. Their collision rules are defined with those constructs.

Diagnostics: A repeated top-level declaration reports SEM0003 at the later declaration and points to the first. Distinct import or declaration bindings claiming one module-scope spelling report SEM0016 without selecting a winner.

Evidence: flat module namespace, canonical declaration index.

NAME-003 — A binding collision has no source-order winner

Status: Confirmed

When different valid declarations or imports claim the same local spelling, the name is unavailable until the source gives the bindings distinct names. Import order, declaration order, and declaration kind never select a winner.

import text.Parser { parse }
import binary.Parser { parse }

pub fn main() -> i32 {
  return parse()
}

Both imports claim parse, so the call resolves to neither function.

The same rule applies when an imported name collides with a declaration in the importing module:

import text.Parser { parse }

fn parse() -> i32 { return 42 }

Boundary: PRELUDE-001 defines no lower-priority standard-library namespace tier. Language bindings retain their own reserved or collision rules; they do not let the module's explicit bindings shadow one another. Distinct lexical scopes may reuse a spelling only where the corresponding local binding rules allow it.

Diagnostics: Each colliding module-scope binding reports SEM0016 with the complete candidate set available to diagnostics and tooling. A use through that spelling remains unavailable instead of producing a second misleading lookup result.

Evidence: binding conflict diagnostics.

NAME-004 — Explicit aliases resolve imported-name collisions

Status: Confirmed

Give colliding namespaces or selected members distinct local aliases.

import text.Parser
import binary.Parser as BinaryParser

pub fn main() -> i32 {
  return Parser.parse() + BinaryParser.parse()
}

Both canonical modules retain their original identities. Only their local names differ.

Selected members may be aliased independently:

import text.Parser { parse as parseText }
import binary.Parser { parse as parseBinary }

Boundary: Aliasing changes no declaration identity, type identity, visibility, ownership, or runtime behavior. It also does not create an overload set: two functions still need distinct local names when both would otherwise claim the same spelling.

Diagnostics: An alias that still collides with another binding reports SEM0016. A missing alias name retains the parser diagnostic. An alias identical to the default spelling is the harmless redundancy defined by IMPORT-005.

Evidence: explicit import aliases, namespace alias tests.

NAME-005 — A file-named struct or contract also scopes that module's public members

Status: Confirmed

A top-level struct, service, or interface doubles as its defining module's qualifier only when its declaration name matches the module's final path segment after removing underscores and ignoring case. For example, declaration UserProfile in module model/user_profile is that module's nominal scope.

// model/user_profile.silk
pub struct UserProfile {}

pub fn answer() -> i32 {
  return 42
}
// app/main.silk
import model.user_profile { UserProfile }

pub fn main() -> i32 {
  return UserProfile.answer()
}

UserProfile.answer resolves the public function in model/user_profile. The same rule applies to qualified type paths and follows the canonical declaration through a selected-import alias: an alias changes the local qualifier spelling, not whether the declaration names its module.

Boundary: A different nominal declaration in the same file does not expose the module's functions or types. For example, Provider.answer() is unavailable when Provider is declared in model/user_profile; only UserProfile matches the basename. The comparison ignores only case and underscores, not arbitrary punctuation or path segments.

Scalar enums are deliberately different: their qualifier is reserved for declared members and the generated value operation, so even a file-named enum does not expose other top-level module members. Service and interface operations remain operations of their declaring contract regardless of the file name. The filename rule governs their access to other top-level module members. It never turns a private member or an imported binding into a public re-export.

Diagnostics: An unknown type path through a valid nominal module scope reports SEM0014. An unknown call through a struct, service, or interface qualifier—including a declaration that does not name its module—reports the actor-operation diagnostic SEM0010 instead of searching that declaration's defining module. A private module member remains inaccessible as SEM0015.

Evidence: nominal scope selection, call resolution, expression resolution, completion coverage.

Visibility

VIS-001 — Declarations are private by default and pub exposes them

Status: Confirmed

A top-level declaration without pub is private to its defining module. pub makes the declaration eligible for access from another module through an explicit namespace or selected binding.

pub fn answer() -> i32 {
  return hidden()
}

fn hidden() -> i32 {
  return 42
}

Both functions are available inside their defining module. Another module may import or qualify answer but not hidden.

The same default applies to nominal types, scalar enums, constants, services, interfaces, and other top-level declarations that support cross-module access. Struct fields have their own pub marker under the same private-by-default principle. A scalar enum's pub marker exposes its type and complete member set; individual enum members have no separate visibility marker.

Boundary: pub grants name accessibility; it does not import the declaration anywhere, re-export it from an importing module, execute it, or weaken its type, Effect, ownership, or target contract.

Diagnostics: No diagnostic applies merely because a declaration is private or unused. An external attempt to select or qualify it reports SEM0015 at that use.

Evidence: visibility lookup, struct visibility, name-resolution tests.

VIS-002 — Private declarations remain fully visible inside their defining module

Status: Confirmed

Privacy is a module boundary, not a declaration-order or same-file restriction. Any declaration body in the defining module may name a unique private declaration.

fn normalize(value: i32) -> i32 {
  return value
}

pub fn parse(value: i32) -> i32 {
  return normalize(value)
}

parse may call normalize even though callers from another module cannot.

Boundary: A nested module is not created by directory structure within one source file. Every source file has its own module boundary, so a neighboring file in the same directory does not gain private access.

Diagnostics: A valid same-module private reference receives no visibility diagnostic. A private declaration of the wrong kind still receives the ordinary kind or type diagnostic for its use.

Evidence: defining-module visibility, declaration index.

VIS-003 — Unknown and private imported members are different errors

Status: Confirmed

Lookup preserves whether an imported member is absent or exists but is private.

import model.User

User.missing() // no declaration named missing
User.secret()  // declaration exists, but is private

The first lookup is unknown. The second retains the private declaration as an inaccessible candidate without making it callable.

This distinction applies equally to namespace qualification and selective imports, and to values, types, constants, services, and interfaces.

Boundary: Privacy does not deliberately hide the fact that a declaration exists from compiler diagnostics or navigation metadata. It prevents semantic access. User-facing diagnostics should identify the inaccessible declaration without exposing private implementation details such as hidden field sets.

Diagnostics: An unknown selected or qualified member reports SEM0014. A known private member reports SEM0015. The compiler does not fall back to another module or prelude declaration after either failed lookup.

Evidence: visibility outcomes, private import tests.

VIS-004 — A public contract cannot expose a private nominal type

Status: Confirmed

Every nominal type appearing in a public declaration's externally visible contract must itself be public. This includes function parameters and results, public struct fields, and other published type positions.

struct Hidden {}

pub fn reveal(value: Hidden) -> Hidden {
  return move value
}

reveal is invalid because an importing module could name the function but could not name its parameter or result type.

A public type may use private representation types behind private fields:

struct Storage { value: i32 }

pub struct Counter {
  storage: Storage
}

The private field does not enter Counter's externally accessible construction or projection contract.

Boundary: A private declaration may freely mention other private declarations in its own contract. A public function body may also use private types internally as long as they do not escape through its published signature.

Diagnostics: Each private nominal type exposed by a public contract reports SEM0019 at the type use and retains the referenced declaration identity. The compiler does not pretend the type is unknown or silently make it public.

Evidence: nominal visibility, struct visibility specification, private exposure tests.

Redundancy, prelude, and publication

IMPORT-005 — Redundant aliases are semantically harmless

Status: Confirmed

An alias equal to the binding's default name does not change the program and should remain valid.

import model.User as User

pub fn main() -> i32 {
  return User.answer()
}

This has the same binding and runtime meaning as import model.User.

Boundary: An alias that claims another binding's spelling is not merely redundant; it is a real collision under NAME-003. A missing alias name remains malformed syntax.

Diagnostics: The compiler does not reject an unchanged alias. Language tooling may report a non-blocking LSP0002 simplification warning and offer to remove as User.

Evidence: current alias policy, redundant alias tests.

IMPORT-006 — The same module may be imported more than once

Status: Confirmed

Import declarations are judged by the bindings they create, not by whether another declaration names the same target module. Distinct declarations may request separate noncolliding views of one module.

import model.User as UserApi
import model.User { User }

fn reset(user: User) -> User {
  return UserApi.withId(move user, 0)
}

Both imports identify canonical module model/User. The first binds namespace UserApi; the second binds public type User directly.

Boundary: Repeated imports do not permit conflicting local names. Two declarations importing different members as the same spelling still report SEM0016. Importing an identical binding twice is an idempotent redundancy rather than a second declaration identity.

Diagnostics: Repeated targets alone do not produce a compiler error. Tooling may suggest combining compatible imports into one hybrid declaration or removing an exact duplicate. The LSP uses LSP0001 for an exact duplicate and LSP0003 when declarations can be consolidated.

Evidence: current repeated-target restriction, duplicate import tests.

IMPORT-007 — Reserved words may appear in import paths but cannot become implicit bindings

Status: Confirmed

When the parser expects an import-path segment, it accepts identifier and reserved-word tokens without changing their lexical classification. A reserved final segment must use an explicit legal namespace alias or a selected-member list so the import never creates a binding whose spelling is otherwise reserved.

import silk.effect { Effect, suspend }

Reserved segments in any nonfinal position are ordinary parts of the canonical module identity. Every module-closure, name-resolution, formatting, and tooling consumer observes the same ordered path segments.

Boundary: Contextual acceptance is limited to import paths. A reserved word remains invalid as a declaration name or explicit alias. import silk.effect is invalid because its implicit namespace binding would be named effect; selected-member imports such as import silk.effect { Effect } are valid because they state legal bindings explicitly. A legal explicit namespace alias remains valid under IMPORT-002 when a caller specifically needs the module namespace.

Diagnostics: A reserved final segment without an alias or selected-member list reports PAR0004 at that segment and explains that the import needs an explicit binding form. The parser retains the complete path for recovery, but name resolution does not synthesize a reserved binding.

Evidence: import syntax requirements, name-resolution requirements, contextual path parser tests, canonical path consumer tests, explicit completion behavior.

PRELUDE-001 — Only language bindings are implicit

Status: Confirmed

Foundational type spellings, language syntax, and the sealed Intrinsic namespace are available without imports. Ordinary standard-library actor namespaces are not. A module imports every standard-library API it names.

import silk.option { Option }

pub fn main() -> i32 {
  let value = Option.some<i32>(42)
  drop value
  return 42
}

The type spelling i32 needs no import because it is part of the language. Option.some needs the explicit Option actor binding because Option is an ordinary standard-library actor. Because the actor matches the module filename, that selected declaration also qualifies the module's public operations under NAME-005. The same rule applies to Effect, Vector, Result, filesystem services, target providers, and primitive actor operations:

import silk.effect { Effect }
import silk.i32

Effect<A ! E ? R> remains language type syntax. The selected actor binding Effect names the matching nominal declaration and qualifies ordinary standard-library functions such as Effect.provide.

Boundary: The toolchain may resolve the reserved silk/ source origin differently from project files, but that packaging privilege does not inject its declarations into every module scope. Tooling may add missing imports automatically; auto-import is a source edit, not invisible lookup.

A foundational type spelling does not create an ordinary nominal declaration in the module scope. Therefore import silk.i32 may bind actor namespace i32 while type positions continue to use the closed language spelling i32; selecting the matching Effect actor behaves similarly beside Effect type syntax. This does not create general separate type and value namespaces for user declarations.

Diagnostics: Naming an unimported standard-library namespace uses the ordinary unknown-name diagnostic. Catalog-backed completion may add a visible, collision-aware import edit.

Evidence: current prelude tier, standard-library namespace tests, foundational type names.

EXPORT-001 — Imports do not re-export declarations

Status: Confirmed

An import creates bindings only in the importing module. Those bindings are not members that a third module can import through it.

// api/Public.silk
import model.User

pub fn makeUser(id: i32) -> User.User {
  return User.make(id)
}

Another module may access Public.makeUser, but it cannot access Public.User merely because api/Public imported model/User.

Silk currently has no explicit re-export declaration. In particular, pub import is unsupported:

pub import model.User

Boundary: A module may expose ordinary public wrapper functions whose contracts use public types from another module. Those types retain their original canonical identities. Wrapping does not create a type alias or re-exported namespace.

Explicit re-export syntax is deferred until Silk's native library and package model is designed. At that point it must state exactly which declarations become members, preserve their canonical identities, define collision behavior, and avoid turning every ordinary import into a public API commitment.

Diagnostics: Attempting pub import receives a parser diagnostic because no such declaration exists. Looking up an importing module's private import binding from another module reports an unknown member rather than following the import transitively.

Evidence: non-re-exporting imports, module semantic surfaces.

Implementation evidence

The compiler closes modules only through parsed imports, keeps catalog declarations out of source scope, accepts harmless import redundancy, and preserves collision diagnostics for genuinely different bindings. The LSP indexes the catalog independently, inserts explicit collision-aware imports, and owns optional redundancy warnings and consolidation actions. Repository examples, fixtures, tests, and generated documentation use the same explicit-import model.

Re-exports remain deferred: pub import is unsupported and ordinary imports are not exported.

On this page

TerminologyModule identity and loadingMODULE-001 — One source file defines one path-identified moduleMODULE-002 — Source text does not declare or override module identityMODULE-003 — Import paths resolve from the source root, not from the importing fileMODULE-004 — Compilation loads only the transitively reachable module closureMODULE-005 — Imports have no runtime behaviorMODULE-006 — Import cycles are valid and do not choose lookup orderImport bindingsIMPORT-001 — A namespace import binds the target's final path segmentIMPORT-002 — A namespace alias replaces the default local nameIMPORT-003 — A selective import binds only its listed public membersIMPORT-004 — A hybrid import may bind one namespace and selected members togetherNames and collisionsNAME-001 — Top-level declarations are visible throughout their defining moduleNAME-002 — One flat module namespace contains top-level declarations and importsNAME-003 — A binding collision has no source-order winnerNAME-004 — Explicit aliases resolve imported-name collisionsNAME-005 — A file-named struct or contract also scopes that module's public membersVisibilityVIS-001 — Declarations are private by default and pub exposes themVIS-002 — Private declarations remain fully visible inside their defining moduleVIS-003 — Unknown and private imported members are different errorsVIS-004 — A public contract cannot expose a private nominal typeRedundancy, prelude, and publicationIMPORT-005 — Redundant aliases are semantically harmlessIMPORT-006 — The same module may be imported more than onceIMPORT-007 — Reserved words may appear in import paths but cannot become implicit bindingsPRELUDE-001 — Only language bindings are implicitEXPORT-001 — Imports do not re-export declarationsImplementation evidence