Skip to content

Imports

Accepted

Accepted for V1 import semantics.

import has one job: import all public declarations from a namespace into the current scope.

import module("json")

const std = module("std")
import std.math

The operand must be a comptime-known namespace. It may be produced directly with module("...") or reached through a bound namespace such as std.math.

Imports are declaration-scope only in V1. They are not allowed inside function bodies or local blocks. Imports change name and impl visibility, so keeping them at declaration scope preserves readable, tooling-friendly semantic environments.

Imports include all public declarations in the namespace, including public namespace declarations. Namespaces are declarations too. If importing a namespace declaration would conflict with an existing name, the normal import conflict rule applies.

Public contract implementation/conformance declarations are imported too. This preserves the scoped conformance model: importing an adapter namespace can make its public impls visible, while private impls remain private.

Imported impls are visible in the importing namespace for local checking, but importing does not re-export those impls as part of the importing namespace's public API. If an impl should be visible through a module's main namespace, declare the pub impl in that namespace. Imports must not become hidden impl re-export mechanisms.

Imported named declarations are also not re-exported by import. Imports affect local scope, not public API. A namespace's public surface consists of declarations explicitly declared or re-exported in that namespace:

import std.math
pub const min = std.math.min

This prevents broad imports from accidentally leaking APIs.

import does not resolve bare module names. Its operand follows ordinary name resolution. Therefore import std.math is valid only if std is already bound to a namespace; otherwise std is an undefined name and compilation fails. Use import module("std") to resolve a root module directly, or bind the root namespace and import a public subnamespace:

import module("json")

const std = module("std")
import std.math

Explicit imports from ordinary namespaces should be used sparingly because they increase unqualified-name conflicts. Prefer namespace-qualified access or ordinary destructuring for selected declarations.

An import that would introduce a name already declared in the current scope is a compile error. Imports that introduce the same name for the same declaration identity are idempotent. Imports that introduce the same name for different declaration identities are hard conflicts. Use namespace-qualified access or ordinary destructuring with a renamed binding instead:

const { local_min = min } = std.math

Imports should appear at the top of the file or module before ordinary declarations. This is lint policy rather than a grammar restriction, so generated code and unusual module shapes can still be represented.

Top-level declarations are order-independent for name resolution, including namespace bindings:

fn f() void {
  std.testing.assert(true)
}

const std = module("std")

This can resolve, but should lint because namespace wiring and imports should appear at the top for readability and tooling.

Imports follow the same order-independent declaration model:

import std.math
const std = module("std")

This can resolve after whole-file declaration collection, but should lint because imports and namespace bindings should be grouped at the top in dependency order.

V1 has no separate selective import syntax. Use destructuring instead:

const std = module("std")
const { min, max } = std.math

Tooling should be able to resolve every name deterministically.

pub import and priv import are invalid in V1. Imports affect local checking scope; they are not declarations in the namespace public surface. Re-export named declarations or impls explicitly with pub const and pub impl.

Import conflict diagnostics use the active namespace value for the current build context. If an imported namespace is a conditional namespace alias, conflicts are diagnosed against the active selected namespace; cross-configuration conflict analysis is tooling/lint territory.

Related module resolution, prelude initial-scope, and manifest rules are documented in Namespaces and Source Loading, Prelude Boundary, and Module Roots and Manifests.