docs / specification / Specification

Chapters — 7. Names, Scopes & Packages
  1. 1. Introduction
  2. 2. Grammar & Lexical Structure
  3. 3. Types, Values & Variables
  4. 4. Allocation & Storage
  5. 5. Ownership & the Borrow Checker
  6. 6. Conversions & Contexts
  7. 7. Names, Scopes & Packages
  8. 8. Classes
  9. 9. Interfaces
  10. 10. Annotations & Aspects
  11. 11. Templates & Wildcards
  12. 12. Arrays, Views, Slices & Records
  13. 13. Statements & Patterns
  14. 14. Expressions
  15. 15. Errors & Stack Traces
  16. 16. Concurrency
  17. 17. Accelerated Compute (XPU)
  18. 18. Script Units
  19. 19. Notebook Kernel & Jupyter Compatibility
  20. 20. Execution & Program Lifecycle
  21. 21. Complete Grammar

7 — Names, Scopes & Packages

This chapter defines how names are introduced, resolved, and controlled: the scopes declarations govern, shadowing, simple and qualified names, packages and imports, access control, and the visibility a compilation unit’s declarations have to other code.

7.1 Declarations and Scopes

A local variable’s scope runs from its declaration to the closing brace of the declaring block. A block-nested declaration may shadow a name from an enclosing scope. Inside the block, the inner declaration governs, and the outer binding is untouched and resumes at the block’s end.

Example 7.1-1. Block shadowing.

public final class C {
    public static int32 run() {
        int32 x = 1;
        { int32 x = 2; System.stdout.println("inner " + x); }   // inner 2
        return x;                                                // 1
    }
}
System.stdout.println(C.run());

A block-local shadowing a script unit’s session binding is an ordinary local: it drops at block exit and leaves the session binding untouched (Script Units §18).

Class members are in scope throughout their class body regardless of declaration order. Fields are read and written through this (or another receiver) — this.field — and this in an instance method or constructor denotes the receiver.

7.2 Simple and Qualified Names

A simple name resolves against the enclosing scopes: locals and formals first, then class members, then types made visible by imports and the implicit root package (§7.3).

A qualified name spells its path. A fully qualified type name is usable in any type position without an import:

cajeta.collection.ArrayList<int32> xs = heap cajeta.collection.ArrayList<int32>();

Under multiple inheritance of behavior, the qualified selectors super<Base>.method() and this<Base>.field name a specific base’s member. Resolution among colliding base members is Classes §8.

7.3 Packages and Imports

A compilation unit may open with a package declaration naming the package its types belong to. import a.b.C; makes C usable by its simple name in the importing unit.

Every program implicitly has access to the cajeta.lang root types — Object, String, Encoding, Optional<T>, Pair<K, V> — and to the System and Cajeta namespaces, with no import.

Discussion. As of 0.27.0 an unqualified standard-library name outside cajeta.lang may also resolve without its import in some contexts. That leniency is not a guarantee. Portable code imports every type it names, or qualifies it fully.

7.4 Access Control

Members declare their accessibility with public, protected, or private. A private member is accessible only within its declaring class. Invoking an inaccessible method is a compile-time error.

Example 7.4-1. A rejected program: a private method invoked from outside its class.

public class Secret {
    private int32 hidden() { return 42; }
}

Secret s = heap Secret();
System.stdout.println(s.hidden());    // CAJETA_ERROR_METHOD_NOT_ACCESSIBLE

Discussion. Two enforcement gaps are open as of 0.27.0: a private field read from outside the class is not yet diagnosed, and the accessibility of a member declared with no modifier is not yet specified. Both are to be bound here when enforcement lands. Until then, treat private state as private regardless of what the compiler accepts, and write modifiers explicitly.

7.5 Compilation Units and Archives

A compilation unit is one source file: either a set of type declarations, or a script unit (Script Units §18). A library ships as a .cja archive. A consumer that resolves the library through its project manifest can import the library’s public types, exactly as it imports types from its own project (the archive format and resolution mechanics are the Runtime & ABI companion’s and the toolchain’s concern).

Source: docs/specification/07-names-scopes-packages.md · 2 min · 520 words