Objective‑C 3.0 Draft Specification (Working Draft) — Table of Contents

Last generated: 2025-12-28 Working draft: v0.10

This is a working draft of an Objective‑C 3.0 specification broken into numbered, implementable parts.
Each part is a separate Markdown file intended to be read independently, but the parts are cross-referenced where needed.

Front matter

Parts

How to read this draft

  1. Start with INTRODUCTION.md (goals + cross-cutting constraints).
  2. Read Part 1 next (PART_1_VERSIONING_COMPATIBILITY_CONFORMANCE.md) for conformance levels and migration strategy.
  3. Then choose a “slice” (types/errors/concurrency/system/perf) depending on what you’re implementing.

Status / scope note

This draft is ambitious but implementable:

Pass notes


Objective‑C 3.0 Draft Specification (Working Draft) — Introduction

Last generated: 2025-12-28 Working draft: v0.10

1. What Objective‑C 3.0 is trying to be

Objective‑C 3.0 is a modernization of Objective‑C focused on:

This draft is written to be implementable primarily in Clang/LLVM, with minimal required runtime changes. Where runtime support is needed, it is specified as small libraries or well-defined hooks.

2. Guiding design principles

2.1 Compatibility boundaries are explicit

Objective‑C 3.0 uses a language mode (and optional conformance “strictness” levels) so that:

2.2 New syntax must map to existing concepts

New features should lower to:

2.3 Safety is enforced where it can be proven; escape hatches are explicit

If the compiler can prove a property (nonnullness, non-escaping borrowed pointers, cleanup correctness), it shall enforce it in strict modes. If it cannot, the language must provide explicit annotations/constructs (e.g., unsafeEscapeBorrowed, @unsafeSendable, unowned) that make risk obvious in code review.

2.4 “System programming” is a first-class audience

The language shall accommodate:

2.5 Tooling is part of the spec

Objective‑C 3.0 is not just grammar: it includes required diagnostics, fix-its, and a conformance test suite model. A feature that cannot be audited and migrated is not “done”.

3. Non-goals (important)

Objective‑C 3.0 does not aim to:

4. The “big rocks” of ObjC 3.0 (high-level)

5. Document organization

Each part is a separate Markdown file. The system-programming chapter (Part 8) is intentionally detailed because it anchors “library-defined subset” accommodation, but it is only one part of a broader specification.

6. Open issues and expected iteration

This is a working draft. Each part includes “Open Issues” subsections for unresolved syntax bikeshedding and deeper ABI questions. The goal is to converge by:

v0.3 update

This bundle expands Part 3 (types/optionals), Part 6 (errors), and Part 7 (concurrency) with concrete grammar and enforceable rules, including optional message sends ([x? foo]), try/throw/do-catch, and executor/actor isolation scaffolding.

v0.4 update

This revision makes three “ship/no‑ship” decisions:

  1. Optional chaining (?., [x? foo]) is reference-only in v1 (no scalar/struct chaining).
  2. throws is untyped in v1 (always id<Error>); typed throws is deferred.
  3. Task spawning remains library-defined in v1; no task {} keyword syntax (compiler recognition uses standardized attributes).

v0.5 update

This pass resolves:

v0.8 pass focus

This pass tightens the specification specifically for separate compilation and implementability:

This is a deliberate step toward a spec that can be implemented in Clang/LLVM with predictable behavior across modules.

v0.9 pass focus

This pass makes the draft more “engineer-ready” by tightening the boundary between source semantics and cross-module implementation reality:


Objective‑C 3.0 — Design Decisions Log (v0.10)

Last updated: 2025-12-28

This log captures explicit “ship/no‑ship” decisions made to keep Objective‑C 3.0 ambitious but implementable (especially under separate compilation).


D-001: Optional chaining is reference-only in v1

Decision: Optional member access (?.) and optional message sends ([receiver? selector]) are supported only when the accessed member/method returns:

Optional chaining for scalar/struct returns is not supported in v1.

Rationale:

Spec impact: Part 3 §3.4.


D-002: throws is untyped in v1; typed throws deferred

Decision: The v1 throws effect is always untyped, with thrown values of type id<Error>.

Typed throws syntax (e.g., throws(E)) is reserved for future extension but is not part of v1 grammar/semantics.

Rationale:

Spec impact: Part 6.


D-003: Task spawning is library-defined in v1 (no task {} keyword)

Decision: Objective‑C 3.0 v1 does not introduce a task { ... } keyword expression/statement. Task creation and structured concurrency constructs are provided via the standard library.

The compiler recognizes task entry points via attributes (see D-007 and Part 7).

Rationale: Keeps parsing surface small; avoids freezing spawn semantics before runtime patterns stabilize.

Spec impact: Part 7 §7.5.


D-004: Executor annotations — canonical spelling and meaning (v1)

Decision: Executor affinity is expressed with the canonical spelling:

If a declaration is annotated objc_executor(X), then:

Rationale: __attribute__((...)) integrates into existing LLVM/Clang pipelines and is stable for module interface emission.

Spec impact: Part 7 and B/C.


D-005: Optional propagation (T? with postfix ?) follows carrier rules (v1)

Decision: Postfix propagation e? is allowed on an optional e : T? only when the enclosing function returns an optional type.

Rationale: Prevents accidental “nil becomes an error” magic; keeps control-flow explicit.

Spec impact: Part 3 and Part 6.


D-006: Autorelease pool boundaries at suspension points (v1)

Decision: On Objective‑C runtimes with autorelease semantics, each task execution slice (resume → next suspension or completion) runs inside an implicit autorelease pool that is drained:

Rationale: Predictable memory behavior for Foundation-heavy code; avoids autorelease buildup across long async chains.

Spec impact: Part 7, Part 4, and C.


D-007: Canonical spellings and interface emission are attribute/pragma-first (v1)

Decision: Features that must survive module interface emission and separate compilation have canonical spellings defined in:

Sugar spellings (macros, @-directives, alternate attribute syntaxes) may exist, but the canonical emitted form shall use the catalog spellings.

Rationale: Keeps generated interfaces unambiguous and independent of user macro environments.

Spec impact: B, Part 2, Part 12.


D-008: Generic methods are deferred in v1

Decision: v1 includes generic types (pragmatic, erased generics) but defers generic methods/functions.

Rationale:

Spec impact: Part 3 §3.5 (generic methods moved to future extensions).


D-009: throws uses a stable “error-out” calling convention (v1)

Decision: v1 requires a stable ABI for throws that supports separate compilation. The recommended (and default) convention is a trailing error-out parameter (outError) of type id<Error> _Nullable * _Nullable.

Rationale: Matches long-standing Cocoa patterns (NSError-out) and is easy to lower in LLVM without stack unwinding.

Spec impact: C and Part 6.


D-010: async lowers to coroutines scheduled by executors (v1)

Decision: v1 async semantics are implemented as coroutine state machines with suspension at await. Resumption is scheduled by the active executor, and the runtime/stdlib exposes enough primitives to:

Rationale: This is the most direct path to implement structured async/await in LLVM while preserving ARC and Objective‑C runtime behavior.

Spec impact: C and Part 7.


D-011: await is required for any potentially suspending operation (v1)

Decision: In v1, await is not restricted to “calling explicitly-async functions.”
It is required for any operation that may suspend, including:

await remains permitted only in async contexts.

Rationale: Executor and actor isolation are implemented by hops that may suspend even when the callee’s body is “logically synchronous.” Requiring await keeps suspension explicit at the call site while preserving ergonomic isolation.

Spec impact: Part 7, C, D.


D-012: Required module metadata set is normative (v1)

Decision: For ObjC 3.0 semantics to survive separate compilation, the required information enumerated in D is normative: a conforming toolchain shall preserve that information in module metadata and in emitted textual interfaces.

Rationale: Without an explicit checklist, toolchains drift into “works in a single TU” but fails at module boundaries. D makes the “separate compilation contract” testable.

Spec impact: C, D, Part 2, Part 12.


Objective‑C 3.0 — Attribute and Syntax Catalog

Working draft v0.10 — last updated 2025-12-28

B.0 Purpose

Objective‑C 3.0 introduces new language effects, annotations, and a small amount of surface syntax. To keep separate compilation reliable and to keep module interfaces stable, this document defines the canonical spellings that:

Implementations may accept additional “sugar” spellings (keywords, pragmas, macros), but those spellings are not required for conformance unless explicitly stated elsewhere.

B.1 Canonical vs. optional spellings

B.1.1 Canonical spellings

The canonical spellings use forms already widely supported by LLVM/Clang-family toolchains:

B.1.2 Optional sugar spellings

Implementations may additionally provide sugar spellings such as:

Such sugar spellings shall not change semantics.

B.2 Nullability defaults

B.2.1 Nonnull-by-default region pragmas (canonical)

Objective‑C 3.0 defines a nonnull-by-default region using:

#pragma objc assume_nonnull begin
// declarations
#pragma objc assume_nonnull end

Semantics are defined in Part 3 (§3.2.4).

B.2.2 Optional aliases (non-normative)

Implementations may treat the following as aliases with identical semantics:

B.3 Concurrency and executors

B.3.1 Executor affinity annotation (canonical)

Executor affinity is expressed with:

__attribute__((objc_executor(main)))
__attribute__((objc_executor(global)))
__attribute__((objc_executor(named("com.example.myexecutor"))))

Semantics are defined in Part 7 (§7.4).

B.3.2 Task-spawn recognition attributes (canonical)

The compiler recognizes standard-library task entry points via attributes:

__attribute__((objc_task_spawn))
__attribute__((objc_task_detached))
__attribute__((objc_task_group))

Semantics are defined in Part 7 (§7.5).

Note: These attributes are intended to attach to functions/methods that take an async block/callback and create tasks/groups, not to arbitrary user functions.

B.3.3 Unsafe Sendable escape hatch (canonical)

For strict concurrency checking, implementations shall recognize an explicit escape hatch that marks a type as “Sendable-like” by programmer promise, even if the compiler cannot prove it.

Canonical attribute:

__attribute__((objc_unsafe_sendable))

This attribute may be applied to:

Semantics are defined in Part 7 (§7.8).

Toolchains may also provide sugar spellings (e.g., @unsafeSendable), but emitted interfaces must use the canonical attribute.

B.3.4 Actor isolation modifier: nonisolated (canonical)

For actor types, implementations shall support a way to mark specific members as nonisolated.

Canonical attribute:

__attribute__((objc_nonisolated))

This attribute may be applied to:

Semantics are defined in Part 7 (§7.7.4).

Toolchains may additionally support a contextual keyword spelling (nonisolated) as sugar, but emitted interfaces should preserve semantics and may prefer the canonical attribute spelling.

B.4 Errors

B.4.1 NSError bridging attribute (canonical)

For interop with NSError-out-parameter conventions, implementations shall recognize:

__attribute__((objc_nserror))

Semantics are defined in Part 6 (§6.9).

B.4.2 Status-code bridging attribute (canonical)

For return-code APIs, implementations shall recognize:

__attribute__((objc_status_code(/* parameters */)))

Semantics are defined in Part 6 (§6.10).

B.5 Performance and dynamism controls

B.5.1 Direct methods (canonical)

Direct method intent is expressed with:

__attribute__((objc_direct))

Applied to methods. (Class-wide defaults are defined in Part 9.)

B.5.2 Final and sealed (canonical)

Objective‑C 3.0 uses the following canonical spellings:

__attribute__((objc_final))                  // methods or classes
__attribute__((objc_sealed))                 // classes (module-sealed)

If a toolchain already provides an equivalent attribute (e.g., objc_subclassing_restricted), it may treat that attribute as an alias.

Semantics are defined in Part 9.

B.6 Metaprogramming

B.6.1 Derive / synthesize (canonical)

Derivation requests use:

__attribute__((objc_derive("TraitName")))

Semantics are defined in Part 10.

B.6.2 Macro expansion (canonical)

If AST macros are supported, macro entry points may be annotated with:

__attribute__((objc_macro))

The actual macro declaration syntax is implementation-defined (Part 10), but interface emission shall preserve the canonical attributes and any synthesized declarations.

B.7 Module interface emission requirements (normative)

If an implementation provides any facility that emits a textual interface for a module (e.g., generated headers, module interface stubs, API dumps), then:

  1. The emitted interface shall be semantics-preserving: importing it must reconstruct the same declarations, effects, and attributes.
  2. The emitter shall use the canonical spellings from this catalog (or semantically equivalent spellings defined as canonical elsewhere in the spec).
  3. The emitted interface shall not depend on user macros for semantics (macros may remain for documentation convenience, but the semantic attributes/pragmas must be explicit).

B.8 System programming extensions

This section catalogs canonical spellings for “system programming extensions” described in Part 8. These spellings are intended to survive header/module interface emission and mixed ObjC/ObjC++ builds.

B.8.1 Resource cleanup on scope exit (canonical)

Variable attribute form:

__attribute__((objc_resource(close=CloseFn, invalid=InvalidExpr)))

Semantics: See Part 8 §8.3.2. The compiler shall treat a variable annotated with objc_resource as a single-owner handle for purposes of move/use-after-move diagnostics (where enabled).

Optional sugar (non-normative):

B.8.2 Plain cleanup hook (canonical / existing)

Variable attribute form (Clang existing):

__attribute__((cleanup(CleanupFn)))

Optional sugar (non-normative):

B.8.3 Borrowed interior pointers (canonical)

Type qualifier form (canonical):

borrowed T *

The borrowed qualifier is a contextual keyword in type grammar (Part 8 §8.7). It is not reserved as a general identifier.

B.8.4 Function return is borrowed from an owner (canonical)

Function/return attribute form:

__attribute__((objc_returns_borrowed(owner_index=N)))

Semantics: See Part 8 §8.7.2. Toolchains shall preserve this attribute in module metadata (D Table A).

B.8.5 Block capture list contextual keywords (canonical)

The capture list feature in Part 8 uses contextual keywords:

These tokens are contextual within capture list grammar only and shall not be reserved globally.

B.9 Reserved tokens

Objective‑C 3.0 reserves (at minimum) the following tokens as keywords:

Additional reserved keywords may be added by other parts.


Objective‑C 3.0 — Lowering, ABI, and Runtime Contracts

Working draft v0.10 — last updated 2025-12-28

C.0 Scope and audience

This document is primarily normative for implementations (compiler + runtime + standard library). It does not change source-level semantics defined elsewhere; it constrains how a conforming implementation supports those semantics under separate compilation and across module boundaries.

Where this document provides “canonical lowering” patterns, those are:

C.1 Definitions

C.2 Separate compilation requirements (normative)

A conforming implementation shall ensure:

  1. Effect and isolation consistency is recorded in module metadata.
    If a declaration is imported from a module, its:
    • async/throws effects,
    • executor affinity (objc_executor(...)),
    • actor isolation (actor type + isolated/nonisolated members), and
    • any dispatch-affecting attributes (objc_direct, objc_sealed, etc.) shall be part of the imported type information.

    The minimum required set is enumerated normatively in MODULE_METADATA_AND_ABI_TABLES.md.

  2. Mismatched redeclarations are diagnosed.
    Redeclaring an imported function/method with a different effect set or incompatible lowering-affecting attributes is ill-formed.

  3. Interface emission is semantics-preserving.
    If a textual module interface is emitted, it shall preserve effects and attributes (see B.7).

C.2.1 Required module metadata (normative)

A conforming implementation shall preserve in module metadata and interface emission the items listed in D.3.1 Table A.

This requirement is intentionally testable: if importing a module can change whether a call requires try/await, or can change dispatch legality (objc_direct), the implementation is non-conforming.

C.3 Canonical lowering patterns

C.3.1 Optional message send [receiver? ...] (normative)

Optional message send semantics are defined in Part 3. Conforming implementations shall lower:

[receiver? sel:arg1 other:arg2]

as if by the following abstract steps:

  1. Evaluate receiver exactly once into a temporary.
  2. If the receiver temporary is nil, produce the “nil case” result:
    • object/block return: nil
    • void return: no effect
  3. Otherwise, evaluate argument expressions in source order and perform the message send.

Key requirement: argument expressions shall not be evaluated in the nil case.

a ?? b should lower to a single evaluation of a with a conditional branch. Implementations should avoid duplicating retains/releases when a is an object pointer.

The postfix propagation operator in Parts 3 and 6 should lower to a structured early-exit:

Implementations should preserve left-to-right evaluation and should not introduce hidden temporaries with observable lifetimes beyond what ARC already requires.

C.4 throws ABI and lowering

C.4.1 Canonical ABI shape for throwing functions (normative for implementations)

For a function declared:

R f(A1 a1, A2 a2) throws;

a conforming implementation shall provide a stable calling convention that allows the caller to receive either:

Canonical ABI (recommended and permitted as normative): the implementation uses an additional trailing error-out parameter:

R f(A1 a1, A2 a2, id<Error> _Nullable * _Nullable outError);

Rules:

Note: For void return, the return value is omitted and the outError store alone indicates failure.

An implementation may choose a different ABI shape, but only if it is:

For Objective‑C methods, the same “trailing error-out parameter” approach is recommended: the selector’s parameter list includes the implicit trailing outError at the ABI level. The language surface does not expose that parameter; it is introduced/consumed by the compiler as part of the throws effect.

C.4.4 Objective‑C selectors vs ABI signatures (normative intent)

For Objective‑C methods, the throws effect is not part of the selector spelling. The selector used for lookup remains the source-level selector.

However, the call ABI of the method’s implementation may include the canonical trailing error-out parameter described in C.4.1.

A conforming implementation shall ensure that:

Reflection note (non-normative): baseline Objective‑C runtime type-encoding strings do not represent throws. This draft does not require extending the runtime type encoding in v1. Toolchains may provide extended metadata for reflective invocation as an extension.

A try call should lower to:

try? and try! should be implemented as specified in Part 6, using the same underlying error value.

C.5 async ABI and lowering

C.5.1 Coroutine model (normative for implementations)

A conforming implementation shall implement async functions such that:

Recommended lowering: lower async functions to LLVM coroutine state machines (e.g., using LLVM’s coroutine intrinsics), with resumption scheduled by the active executor.

C.5.2 Task context (normative)

While executing an async function, there exists a current task context that provides at least:

The concrete representation is implementation-defined, but the values must be queryable by the standard library and usable by the runtime to enforce executor hops.

When entering a declaration annotated with objc_executor(X), implementations should:

Implementations should provide (directly or via the standard library) a primitive equivalent to:

so that the compiler can lower hops in a uniform way.

C.5.4 Call-site lowering for isolation boundaries (normative intent)

Part 7 defines await as the marker for potential suspension (Decision D-011), not merely “calling explicitly-async functions.”

A conforming implementation shall treat the following as potentially-suspending at call sites when the relevant metadata indicates an isolation boundary is being crossed:

Recommended lowering pattern (informative):

  1. If already on the required executor, call directly.
  2. Otherwise:
    • suspend the current task,
    • enqueue the continuation onto the required executor (or actor executor),
    • resume and perform the call,
    • then continue.

This may be implemented either as:

The observable requirement is: the call may suspend, and the work executes on the correct executor.

C.6 Actors: runtime representation and dispatch

C.6.1 Actor executor (normative)

Each actor instance shall be associated with a serial executor used to protect isolated mutable state. Cross-actor access requires enqueueing work onto that executor.

C.6.2 Actor method entry (recommended)

A non-reentrant actor method entry should:

Reentrancy behavior is defined by Part 7; this document only constrains that runtime scheduling is consistent with that behavior.

C.7 Autorelease pools at suspension points (normative for ObjC runtimes)

On Objective‑C runtimes with autorelease semantics, implementations shall ensure:

This rule exists to make async code’s memory behavior predictable for Foundation-heavy code.

C.9 Macro/derive expansion and ABI (normative for implementations)

If macro expansion or derives synthesize declarations that affect layout or vtables/dispatch surfaces (including generated ivars, properties, methods), then:

This section collects implementation guidance for Part 8 features that are primarily enforced by diagnostics and analysis rather than runtime hooks.

C.10.1 Resources and cleanup

objc_resource(...) (Part 8) should lower equivalently to a scope-exit cleanup action (similar to cleanup(...)), but implementations should additionally:

C.10.2 Borrowed pointers

borrowed T * and objc_returns_borrowed(owner_index=...) (Part 8) should be represented in the AST and module metadata (D Table A) so that:

No runtime support is required by these features in v1; the contract is primarily compile-time diagnostics and toolability.

C.11 Conformance tests (non-normative)

Implementations are encouraged to provide a conformance suite that includes:


Objective‑C 3.0 — Module Metadata and ABI Surface Tables

Working draft v0.10 — last updated 2025-12-28

D.0 Purpose

Objective‑C 3.0 adds source-level semantics that must survive module boundaries: effects (async, throws), nullability defaults, executor/actor isolation, and dispatch controls (objc_direct, objc_sealed, etc.).

This document is a normative checklist for what a conforming implementation must preserve in:

It also summarizes which features are ABI-affecting and what ABI stability constraints apply.

This document does not mandate a specific on-disk format. It defines required information.

D.1 Terminology

D.2 Module metadata requirements (normative)

A conforming implementation shall preserve, for all exported declarations:

  1. Full type information including:
    • parameter/return types,
    • nullability qualifiers and defaults (Part 3),
    • ownership qualifiers and ARC-related attributes (Part 4),
    • pragmatic generics information (Part 3) sufficient for type checking (even if erased at ABI).
  2. Effect information:
    • whether the callable is throws,
    • whether the callable is async,
    • and whether the callable is potentially suspending at call sites due to isolation (Part 7).
  3. Isolation and scheduling metadata:
    • executor affinity (objc_executor(...)) (Part 7),
    • actor isolation (actor type, isolated vs nonisolated members) (Part 7),
    • Sendable-like constraints for cross-task/actor boundaries (Part 7).
  4. Dispatch and dynamism controls:
    • objc_direct, objc_final, objc_sealed (Part 9),
    • any attributes that change call legality or override/subclass legality.
  5. Interop bridging markers:
    • objc_nserror and objc_status_code(...) (Part 6),
    • any language-defined overlays/bridges that affect call lowering.
  6. Provenance:
    • the owning module of each exported declaration (Part 2),
    • API partition membership (public/SPI/private) if the toolchain supports it (Part 2).

D.3 Required metadata tables

D.3.1 Table A — “must preserve across module boundaries”

Feature Applies to Must be recorded in module metadata Must be emitted in textual interface Notes
Nullability qualifiers (_Nullable, _Nonnull) types, params, returns, properties Includes inferred defaults where part of the public contract.
Nonnull-by-default regions headers / interface units Canonical pragmas from B.2.
Pragmatic generics parameters class/protocol types, collections ABI may erase; type checking must not.
T?, T! (optional spellings) type surface Emitted form may choose canonical spellings but semantics must match.
throws effect functions/methods/blocks ABI-affecting (see Table B).
async effect functions/methods/blocks ABI-affecting (see Table B).
Executor affinity objc_executor(...) funcs/methods/types May imply call-site await when crossing executors (Part 7).
Actor type / actor isolation actor classes + members Includes nonisolated markings.
Sendable-like constraints types, captures, params/returns Importers must be able to enforce strict checks.
Borrowed pointer qualifier (borrowed T *) types (params/returns) Part 8; enables escape diagnostics in strict-system. Importers must preserve the qualifier.
Borrowed-return marker (objc_returns_borrowed(owner_index=N)) functions/methods Part 8; identifies the owner parameter for lifetime checking of interior pointers.
Task-spawn recognition (objc_task_spawn etc.) stdlib entry points Needed for compiler enforcement at call sites.
Direct method objc_direct methods Changes call legality and category interactions.
Final objc_final classes/methods Optimization + legality constraints.
Sealed objc_sealed classes Cross-module subclass legality.
Derive/macro synthesized declarations types/members Synthesized members must be visible to importers before layout/ABI decisions.
Error bridging markers (objc_nserror, objc_status_code) funcs/methods Affects lowering of try and wrappers.
Availability / platform gates all exported decls Not a new ObjC 3.0 feature, but required for correctness.

D.3.2 Table B — ABI boundary summary (v1)

Feature ABI impact Required stability property Canonical/recommended ABI shape
throws Yes Caller and callee must agree on calling convention across modules Recommended: trailing id<Error> * _Nullable outError (C.4).
async Yes Importers must call using the same coroutine/continuation ABI Recommended: LLVM coroutine lowering (C.5), with a task/executor context.
Executor/actor isolation Usually no (call-site scheduling) Metadata must exist so importer inserts hops and requires await No ABI change required for sync bodies; hop is an awaited scheduling operation.
Optional chaining/sends No Semantics preserved in caller IR Lower to conditional receiver check; args not evaluated on nil (C.3.1).
Direct methods Sometimes (dispatch surface) Importers must know legality + dispatch mode Recommended: direct symbol call + omit dynamic lookup where permitted (C.8).
Generics (pragmatic) No (erased) Importers must type-check consistently Erased in ABI; preserved in metadata/interface.
Key paths Library ABI Standard library must define stable representation ABI governed by stdlib; metadata must preserve KeyPath<Root,Value> types.

D.3.3 Table C — Runtime hooks (minimum expectations)

This table names conceptual hooks. Implementations may use different symbol names.

Area Minimum required capability Notes
Executors Enqueue continuation onto an executor; query “current executor” Needed for objc_executor(...) and for await resumption.
Tasks Create child/detached tasks; join; cancellation state Standard library provides API; compiler enforces via attributes.
Actors Each actor instance maps to a serial executor; enqueue isolated work Representation is implementation-defined (inline field or side table).
Autorelease pools Push/pop implicit pools around execution slices Normative on ObjC runtimes (C.7).
Diagnostics metadata Preserve enough source mapping for async/macro debugging Part 12 requires debuggability.

D.4 Conformance tests (minimum)

A conforming implementation’s test suite should include:


Objective‑C 3.0 — Conformance Profile Checklist

Working draft v0.10 — last updated 2025-12-28

This document defines conformance profiles for Objective‑C 3.0 v1 and provides a checklist for compiler/toolchain implementers (and, where relevant, SDK authors) to make conformance claims concrete and testable.

It is intentionally redundant with the rest of the specification: the goal is to let an implementer answer “Do we actually implement ObjC 3.0?” without reading every part end-to-end.

E.1 How to use this checklist

E.1.1 Conformance is a claim about a toolchain, not just a compiler

A conformance claim applies to the toolchain bundle:

E.1.2 Tags

Checklist items are annotated with tags indicating which conformance profile requires them.

Optional feature sets:

E.1.3 What it means to “pass”

A toolchain passes a profile when it satisfies all checklist items tagged for that profile, and when it can demonstrate (via tests) that required module metadata and runtime contracts behave correctly under separate compilation.

E.2 Profile definitions (normative)

E.2.1 ObjC 3.0 v1 Core

A toolchain claiming ObjC 3.0 v1 Core shall:

“Core” does not require the optional feature sets (metaprogramming macros, Swift overlays) unless separately claimed.

E.2.2 ObjC 3.0 v1 Strict

A toolchain claiming ObjC 3.0 v1 Strict shall:

E.2.3 ObjC 3.0 v1 Strict Concurrency

A toolchain claiming ObjC 3.0 v1 Strict Concurrency shall:

E.2.4 ObjC 3.0 v1 Strict System

A toolchain claiming ObjC 3.0 v1 Strict System shall:

“Strict System” is intended for SDKs and codebases like IOKit, DriverKit, and other “handle heavy” or ownership-sensitive domains.

E.3 Checklist

E.3.1 Language mode selection, feature tests, and versioning

E.3.2 Modules, namespacing, and interface emission

E.3.3 Type system: nullability defaults, optionals, generics, key paths

E.3.4 Memory management and lifetime

E.3.5 Control flow and safety constructs

E.3.6 Errors and throws

E.3.7 Concurrency: async/await, executors, cancellation, actors

E.3.8 System programming extensions (Part 8)

E.3.9 Performance and dynamism controls

E.3.10 Metaprogramming (optional feature set)

E.3.11 Interoperability (optional feature sets)

E.3.12 Diagnostics, tooling, and tests

A serious conformance claim should ship with:


Part 0 — Baseline and Normative References

Working draft v0.10 — last updated 2025-12-28

0.1 Purpose

This part establishes:

  1. The baseline language and de‑facto dialect that Objective‑C 3.0 builds upon.
  2. How this specification relates to existing compiler specifications and platform ABIs.
  3. Common terms and definitions used throughout the specification.
  4. The interpretation of normative language (“shall”, “should”, etc.).

Objective‑C 3.0 is defined as a language mode that extends a stable baseline. The baseline is the Objective‑C language as implemented by a conforming compiler, including widely deployed features (ARC, blocks, modules, nullability annotations, direct methods, etc.).

0.2 Normative reference model

A specification may reference external documents in two ways:

This draft treats certain de‑facto specifications as normative because they are widely implemented and relied upon for ABI correctness:

Where this draft extends or constrains baseline behavior, it does so only in ObjC 3.0 mode (Part 1).

Note: This draft intentionally avoids rewriting existing ARC/Blocks specs verbatim; it specifies extensions and constraints needed by ObjC 3.0 features.

0.3 Normative terms

The terms shall, must, shall not, must not, should, and may are to be interpreted as in RFC-style standards documents.

0.4 Definitions and shared terminology

0.4.1 Translation unit

A translation unit is a single source file after preprocessing and module import processing, compiled as one unit.

0.4.2 Objective‑C 3.0 mode

Objective‑C 3.0 mode is the compilation mode in which the grammar, defaults, and diagnostics of Objective‑C 3.0 are enabled (Part 1).

0.4.3 Conformance level

A conformance level is a set of additional requirements and diagnostics applied on top of ObjC 3.0 mode (Part 1).

0.4.4 Retainable pointer type

A retainable pointer type is a type for which ARC may insert retain/release operations. This includes:

0.4.5 Object pointer type

An object pointer type is a pointer to an Objective‑C object, including id and Class. In this draft, “object pointer” excludes CoreFoundation “toll-free bridged” pointers unless explicitly annotated as retainable families (Part 8).

0.4.6 Block pointer type

A block pointer type is a pointer to a block literal/closure as defined by the Blocks ABI.

0.4.7 Nullability

Nullability is a type property (for object and block pointer types) indicating whether nil is a valid value. Nullability kinds are defined in Part 3.

0.4.8 Optional type

An optional type is written T? (sugar) in ObjC 3.0 mode. Optional types are a type-system feature for object and block pointer types (v1), distinct from Objective‑C’s “message to nil returns zero” runtime behavior (Part 3).

0.4.9 IUO type

An implicitly unwrapped optional type is written T! (sugar). In v1, IUO exists primarily as a migration aid and is discouraged in strict modes (Part 3).

0.4.10 Carrier type

A carrier type is a type that represents either a success value or an alternative early-exit/absence value and participates in propagation (?). In v1, carriers include:

0.4.11 Effects

Effects are part of a function’s type and govern call-site obligations. In v1, effects include throws (Part 6) and async (Part 7).

0.4.12 Executor

An executor is an abstract scheduler for task continuations (Part 7). Executors may represent:

0.4.13 Task

A task is a unit of asynchronous work managed by the concurrency runtime/library. Tasks may be child tasks or detached tasks and may be cancellable (Part 7).

0.4.14 Actor

An actor is a concurrency-isolated reference type whose mutable state is protected by isolation rules (Part 7). Cross-actor interactions are mediated by await and Sendable-like constraints.

0.5 Relationship to C and C++

Objective‑C 3.0 remains a superset of C (and compatible with Objective‑C++) as implemented by the toolchain. Where syntax is extended, it is designed to avoid changing meaning in non‑ObjC3 translation units.

0.6 Open issues


Part 1 — Versioning, Compatibility, and Conformance

Working draft v0.10 — last updated 2025-12-28

1.1 Purpose

This part defines:

1.2 Language mode selection

1.2.1 Compiler option

A conforming implementation shall provide a command-line mechanism equivalent to:

Selecting ObjC 3.0 mode shall:

1.2.2 Translation-unit granularity

ObjC 3.0 mode applies to a translation unit. Mixing ObjC 3.0 and non‑ObjC3 within a single translation unit is not required and is discouraged.

A conforming implementation may support per-file directives (e.g., for build systems), but the normative model is “one mode per translation unit.”

1.2.3 Source directive (optional)

A conforming implementation may provide a source directive such as:

If supported, the directive shall apply to the translation unit.

Open issue: allow per-region switching only if it can be made unsurprising and compatible with module/import semantics. This draft assumes per‑translation‑unit switching.

1.3 Reserved keywords and conflict handling

1.3.1 Reserved keywords

1.3.1 Contextual keywords (non-normative guidance)

Some tokens are treated as contextual keywords only within specific grammar positions, to reduce breakage in existing codebases.

Examples include:

Conforming implementations should avoid reserving these tokens globally. In ObjC 3.0 mode, at minimum the following tokens are reserved as keywords:

1.3.2 Raw identifiers

A conforming implementation shall provide a compatibility escape hatch for identifiers that collide with reserved keywords.

Two acceptable designs:

The toolchain shall also provide fix-its to mechanically rewrite collisions.

1.3.3 Backward compatibility note

This specification does not require keyword reservation to apply outside ObjC 3.0 mode.

1.4 Feature test macros

1.4.1 Required macros

A conforming implementation shall provide predefined macros to allow conditional compilation:

1.4.2 Per-feature macros

A conforming implementation shall provide per-feature macros:

Examples:

Feature macros shall be defined in ObjC 3.0 mode even if a feature is disabled by a profile, to allow feature probing (0 meaning “recognized but disabled”).

1.4.3 __has_feature integration (optional)

A conforming implementation may expose ObjC 3.0 features through a __has_feature-style mechanism. If present, it should align with the per-feature macros.

1.5 Conformance levels (strictness)

1.5.1 Levels

A translation unit in ObjC 3.0 mode may additionally declare a conformance level.

1.5.2 Selecting a level

A conforming implementation shall provide an option equivalent to:

1.5.3 Diagnostic escalation rule

If a construct is ill‑formed in strict mode, it may still be accepted in permissive mode with a warning only if:

1.6 Orthogonal checking modes (submodes)

1.6.1 Strict concurrency checking

Concurrency checking is orthogonal: a translation unit may enable additional checking beyond the strictness level.

A conforming implementation shall provide an option equivalent to:

Strict concurrency checking enables additional diagnostics defined in Part 7/12 (Sendable, actor isolation misuse, executor affinity misuse, etc.).

1.6.2 Strict performance checking (optional)

Implementations may provide a “strict performance” mode enabling additional diagnostics and/or runtime assertions for Part 9 features (static regions, direct methods). If provided, the option shall be orthogonal to strictness levels.

Note: This draft treats strict performance checking as an optional extension because runtime enforcement strategies differ by platform.

1.7 Source compatibility principles

1.7.1 No silent semantic changes in non‑ObjC3 code

Compiling code not in ObjC 3.0 mode shall not change meaning due to this specification.

1.7.2 Contained default changes

Default changes (e.g., nonnull-by-default) apply only inside ObjC 3.0 translation units and/or explicitly marked module boundaries (Part 2/3).

1.7.3 Header compatibility

Headers intended to be consumed by non‑ObjC3 translation units shall:

1.8 Migration tooling requirements

A conforming implementation shall provide:

Minimum migrator capabilities:

  1. Insert nullability annotations based on static inference and usage (Part 3).
  2. Convert “weak‑strong dance” patterns to capture lists (Part 8).
  3. Convert common manual cleanup patterns to defer or @resource (Part 8).
  4. Suggest withLifetime/keepAlive when borrowed-pointer diagnostics trigger (Part 8).
  5. Provide stubs for throws and Result bridging from NSError patterns (Part 6).
  6. Where possible, annotate completion-handler C/ObjC APIs for async bridging overlays (Part 11).

1.9 Profiles (hook point)

Objective‑C 3.0 defines optional profiles: named bundles of additional restrictions, defaults, and required library/runtime surfaces aimed at specific domains (e.g., “system”, “app”, “freestanding”).

Profiles are selected by toolchain configuration, not by source-level directives. A conforming implementation shall provide a mechanism equivalent to:

Profiles may:

This part defines the hook point and selection mechanism; profile contents are specified in CONFORMANCE_PROFILE_CHECKLIST.md.


Part 2 — Modules, Namespacing, and API Surfaces

Working draft v0.10 — last updated 2025-12-28

2.1 Purpose

This part defines:

This part is closely related to:

2.2 Modules

2.2.1 @import

Objective‑C 3.0 standardizes @import as the preferred import mechanism.

A module import shall:

2.2.2 #import and mixed-mode code (non-normative)

#import remains supported for compatibility with existing headers. However, toolchains are encouraged to treat @import as the semantic source of truth for:

2.2.3 Module maps and header ownership (implementation-defined)

The mechanism that maps headers to modules (module maps, build system metadata, etc.) is implementation-defined. A conforming implementation shall behave as if each public declaration belongs to exactly one owning module for the purpose of:

2.3 Module-qualified names

2.3.1 Motivation

Objective‑C historically uses global prefixes (e.g., NS, UI, CG) to avoid collisions. As module ecosystems grow, prefixes are increasingly insufficient and noisy.

Objective‑C 3.0 introduces a lightweight module-qualified name form that:

2.3.2 Syntax (normative)

A module-qualified name is written:

@ModuleName.Identifier
@TopLevel.Submodule.Identifier

Grammar (informative):

2.3.3 Where module-qualified names may appear (normative)

Module-qualified names may appear in:

They shall not appear in:

2.3.4 Name resolution (normative)

A module-qualified name resolves by:

  1. Resolving the module path to a module (or submodule) that is visible in the current translation unit.
  2. Looking up the identifier in that module’s exported declarations.

If no matching declaration exists, the program is ill-formed.

2.3.5 Interface emission (normative)

If a declaration is referenced using a module-qualified name in an emitted textual interface, the emitter shall preserve the module-qualified form or emit an equivalent unqualified name together with imports that make it unambiguous.

2.4 API surface contracts

2.4.0 Cross-module semantic preservation (normative)

A module’s public API contract includes not only names and types, but also ObjC 3.0 semantics that affect call legality and call lowering. The minimum required set is enumerated in D.3.1 Table A.

2.4.1 Public vs SPI vs private (normative)

A module’s API surface is partitioned into (at minimum):

The mechanism used to mark partitions is implementation-defined (attributes, build system flags, header directory layout, etc.), but a conforming toolchain shall preserve partition metadata in module files and interface emission.

2.4.2 Effects and attributes are part of the API contract (normative)

For exported functions/methods, the following are API-significant and must be preserved across module boundaries:

2.5 Extracted interfaces and semantic preservation

2.5.1 Requirement (normative)

If an implementation provides any facility that emits an interface description (textual or AST-based), then importing that interface must reconstruct:

Canonical spellings are defined in B.

2.5.2 Format (implementation-defined)

This draft does not require a particular distribution format (text vs binary). However, the format must be sufficient to support:

2.6 Required diagnostics (minimum)


Part 3 — Types: Nullability, Optionals, Pragmatic Generics, and Typed Key Paths

Working draft v0.10 — last updated 2025-12-28

3.0 Overview

v0.8 resolved decisions

Objective‑C 3.0 improves type safety without abandoning Objective‑C’s model:

This part defines syntax, static semantics, dynamic semantics, and required diagnostics for these features.


3.1 Lexical and grammar additions

3.1.1 New keywords

In Objective‑C 3.0 mode, the following keywords are reserved by this part:

(Additional reserved keywords are specified in Part 1.)

3.1.2 New punctuators

This part introduces new punctuators in specific grammatical contexts:

The tokens shall be recognized only where specified by the grammar; in all other contexts, existing C/Objective‑C meaning applies.


3.2 Nullability

3.2.1 Nullability kinds

Objective‑C 3.0 recognizes the following nullability kinds for Objective‑C object pointer types and block pointer types:

The unspecified kind exists only for compatibility and shall be treated as “potentially nil” for safety diagnostics.

3.2.2 Where nullability applies

Nullability applies to:

Nullability does not apply directly to:

(Those may be covered by separate annotations; out of scope for this part.)

3.2.3 Nullability spelling forms

Objective‑C 3.0 supports three equivalent spelling forms for object/block pointer nullability:

  1. Qualifier form (baseline spelling):
    • _Nonnull, _Nullable, _Null_unspecified (or implementation-provided equivalents)
  2. Objective‑C contextual keywords (when supported by implementation):
    • nonnull, nullable
  3. Optional sugar (ObjC 3.0):
    • T? (nullable)
    • T! (implicitly unwrapped optional; see §3.3.3)

3.2.3.1 Canonicalization rule

For type-checking purposes, implementations shall canonicalize:

If a type already includes an explicit nullability qualifier, the sugar form shall be rejected as redundant.

3.2.4 Nonnull-by-default regions

3.2.4.1 Region introduction

A translation unit in ObjC 3.0 mode may establish a nonnull-by-default region using one of:

The canonical spelling is the pragma form (B):

#pragma objc assume_nonnull begin
// declarations
#pragma objc assume_nonnull end

Implementations may additionally support aliases such as #pragma clang assume_nonnull begin/end or NS_ASSUME_NONNULL_BEGIN/END, but emitted interfaces shall use the canonical spelling.

3.2.4.2 Region semantics

Inside a nonnull-by-default region:

Outside such a region:

Rationale: this allows headers to become “safe by default” while preserving compatibility with legacy code.

3.2.5 Nullability completeness

3.2.5.1 Public interface completeness

A module’s exported interface shall be considered nullability-complete if every exported object/block pointer type has an effective nullability (explicit or default-region-derived) that is not “unspecified”.

In strict mode (Part 1):

In permissive mode:

3.2.5.2 Local completeness

Within a translation unit, incomplete nullability is permitted but should be diagnosed when it impacts type safety (e.g., assigning “unspecified” to “nonnull”).


3.3 Optionals

3.3.1 Optional type sugar (T?)

A type written with the suffix ? denotes a nullable object/block pointer type.

Examples:

NSString*? maybeName;
id<NSCopying>? maybeKey;
void (^? callback)(int);

3.3.2 Optional binding: if let and guard let

3.3.2.1 Grammar

if-let-statement:
    'if' let-binding-list compound-statement ('else' compound-statement)?

guard-let-statement:
    'guard' let-binding-list 'else' compound-statement

let-binding-list:
    'let' let-binding (',' let-binding)*
  | 'var' let-binding (',' let-binding)*

let-binding:
    identifier '=' expression

3.3.2.2 Semantics

For if let:

For guard let:

3.3.2.3 let vs var

Implementations shall diagnose mutation of let bindings as an error.

3.3.3 Implicitly unwrapped optionals (T!)

T! denotes a nullable value that is implicitly unwrapped at use sites in certain contexts.

3.3.3.1 Intended use

T! exists primarily for interop boundaries where the API is “logically nonnull” but not yet fully annotated, or where legacy behavior is relied upon.

3.3.3.2 Static semantics

In ObjC 3.0 strict mode:

3.3.3.3 Dynamic behavior (optional)

An implementation may provide a runtime trap when an implicitly unwrapped optional is observed to be nil at a use site, but this is not required by this draft.

Recommendation: treat IUO primarily as a static-typing aid and migration tool, not as a runtime “bomb”.

3.3.4 Nil-coalescing operator (??)

3.3.4.1 Grammar

nil-coalescing-expression:
    logical-or-expression
    nil-coalescing-expression '??' nil-coalescing-expression

?? is right-associative.

3.3.4.2 Semantics

a ?? b evaluates a:

3.3.4.3 Typing

If a has type T? and b has type T (nonnull), then a ?? b has type T (nonnull).

More generally:

In strict mode, b must be type-compatible with the unwrapped type of a; otherwise error.


3.4 Nullable receivers and optional access

Objective‑C historically allows messaging nil and returns zero/nil. ObjC 3.0 preserves this behavior for compatibility but provides explicit, typed optional access and enables strict diagnostics.

3.4.1 Optional member access: ?.

3.4.1.1 Grammar

postfix-expression:
    ...
    postfix-expression '?.' identifier

3.4.1.2 Semantics

For x?.p where p resolves to a property access:

3.4.1.3 Typing restrictions

?. is permitted only when the property type is an object/block pointer type.

If the property type is scalar/struct:

3.4.2 Optional message send: [receiver? selector]

3.4.2.1 Grammar

message-expression:
    '[' receiver-expression optional-send-indicator? message-selector ']'

optional-send-indicator:
    '?'

The optional-send indicator applies to the receiver position only.

Example:

id? x = ...;
NSString*? s = [x? description];

3.4.2.2 Semantics

3.4.2.3 Typing restrictions

3.4.2.4 Argument evaluation (normative)

For an optional message send of the form:

[receiver? method:arg1 other:arg2]

the implementation shall:

  1. Evaluate receiver exactly once.
  2. If receiver is nil, the expression yields the “nil case” result (§3.4.2.2) and the argument expressions arg1, arg2, … shall not be evaluated.
  3. If receiver is non-nil, evaluate argument expressions in the same order as ordinary message sends, then perform the message send.

This differs intentionally from ordinary Objective‑C message sends, where arguments are evaluated even if the receiver is nil.

Optional message send is permitted only if the method return type is:

If the method returns scalar/struct, optional send is ill-formed in v1 (all conformance levels).

Rationale: nil-messaging returning 0 for scalars is a major source of silent logic bugs; ObjC 3 strictness is allowed to reject it.

3.4.3 Ordinary sends on nullable receivers

In permissive mode:

In strict mode:

This rule is the cornerstone of “nonnull actually means something” in ObjC 3.

3.4.4 Flow-sensitive receiver refinement

If control flow proves a receiver nonnull, ordinary sends are permitted within that scope:

if (x != nil) {
  [x foo]; // OK: x refined to nonnull
}

This refinement also applies to if let / guard let.


3.4.5 v1 restriction: no scalar/struct optional chaining (normative)

In Objective‑C 3.0 v1, optional chaining constructs in §3.4.1 and §3.4.2 are defined only for:

If the selected property or method returns any scalar, vector, enum, or struct/union type, use of ?. or [receiver? ...] is ill‑formed.

Note: Ordinary Objective‑C message sends to nil receivers that yield scalar zero values remain part of the baseline language behavior. However, in ObjC 3.0 strict mode, ordinary sends on nullable receivers are rejected (see §3.4.3), pushing scalar-return calls into explicitly nonnull contexts and eliminating silent fallbacks.

3.5 Pragmatic generics

3.5.1 Goals and constraints

Objective‑C 3.0 generics shall:

3.5.2 Generic type declarations

3.5.2.1 Grammar (provisional)

generic-parameter-clause:
    '<' generic-parameter (',' generic-parameter)* '>'

generic-parameter:
    variance? identifier generic-constraints?

variance:
    '__covariant'
  | '__contravariant'

generic-constraints:
    ':' type-constraint (',' type-constraint)*

Example:

@interface Box<__covariant T> : NSObject
@end

3.5.2.2 Constraints

A type-constraint may be:

The constraint grammar is intentionally limited for implementability.

3.5.3 Generic method/function declarations (future extension)

Objective‑C 3.0 v1 defers generic methods/functions. Toolchains may reserve the syntax, but in v1 it is ill‑formed.

Rationale: integrating generic method syntax with Objective‑C selector grammar and redeclaration rules introduces significant complexity for separate compilation and interface emission.

3.5.4 Type argument application

Type arguments may be applied to generic types using angle brackets:

Box<NSString*>* b;

3.5.5 Erasure and runtime behavior

Unless a declaration is explicitly marked @reify_generics (future extension), generic arguments are erased at runtime:

3.5.6 Variance

If a generic parameter is declared covariant:

If invariant (default):

Variance is enforced purely statically.

3.5.7 Diagnostics

In strict mode, the compiler shall diagnose:


3.6 Typed key paths

3.6.1 Key path literal

3.6.1.1 Syntax

Objective‑C 3.0 introduces a key path literal:

@keypath(RootType, member.chain)

Examples:

auto kp1 = @keypath(Person, name);
auto kp2 = @keypath(Person, address.street);

Within instance methods, self may be used as root:

auto kp = @keypath(self, title);

3.6.1.2 Grammar

keypath-literal:
    '@keypath' '(' keypath-root ',' keypath-components ')'

keypath-root:
    type-name
  | 'self'

keypath-components:
    identifier ('.' identifier)*

3.6.2 KeyPath types

The standard library shall define:

A key path literal has type KeyPath<Root, Value> where:

If any component is writable (settable) and the entire chain is writable, the literal may be typed as WritableKeyPath.

3.6.3 Static checking

The compiler shall validate at compile time:

If validation fails, the program is ill-formed.

3.6.4 Application and bridging

The standard library shall provide:

The spec permits (but does not require) syntactic sugar such as:

Key paths may be explicitly converted to string keys for KVC where safe:

Such conversions shall be explicit and diagnosed in strict mode if they may lose safety.

3.6.5 KVO/Observation integration

Where an observation API accepts key paths, it shall prefer typed key paths over strings. (Actual observation APIs are library-defined; this spec defines the language value representation and checking.)


3.7 Required diagnostics and fix-its

3.7.1 Nullability

Required diagnostics (at least warnings; errors in strict):

3.7.2 Optional misuse

Required diagnostics:

3.7.3 Generics

Required diagnostics:

3.7.4 Key paths

Required diagnostics:


3.8 Examples

3.8.1 Optional binding and optional send

NSString*? maybeName = [user? name];   // optional send returns nullable
NSString* name = maybeName ?? @"(none)";

guard let u = user else { return; }
NSString* n = [u name];               // safe: u is nonnull

3.8.2 Key paths

auto kp = @keypath(Person, address.street);
NSString* s = KeyPathGet(kp, person);

3.9 Open issues

  1. Whether to standardize id/Class optional sugar and how it maps to nullability qualifiers.
  2. Whether to introduce a first-class value optional ABI (Optional<T>) in a future revision.
  3. Whether and how to add generic methods/functions in a future revision without breaking selector grammar.

Part 4 — Memory Management and Ownership

Working draft v0.10 — last updated 2025-12-28

4.1 Purpose

This part defines how ObjC 3.0:

4.2 ARC as normative baseline

In ObjC 3.0 mode with ARC enabled:

ObjC 3.0 does not change ARC’s fundamental model: it remains reference counting without cycle collection.

4.3 Ownership qualifiers (standardized)

4.3.1 __strong

Strong references keep objects alive.

4.3.2 __weak

Weak references are zeroing and do not keep objects alive.

4.3.3 __unsafe_unretained

Non-owning and non-zeroing; use-after-free is possible.

4.3.4 __autoreleasing

Used primarily for NSError-style out parameters and bridging to autorelease pools.

4.4 Precise lifetime and lifetime shortening

Optimizing compilers may shorten object lifetimes relative to source-level scope when it is safe under ARC rules.

Objective‑C 3.0 standardizes a precise lifetime mechanism:

This is foundational for borrowed interior pointers (Part 8).

4.5 Transfer and consumption annotations

Objective‑C 3.0 introduces standardized attributes for ownership transfer in APIs.

4.5.1 @consumes parameter

A @consumes parameter indicates that:

4.5.2 @returns_owned / @returns_unowned

For return values:

4.5.3 Diagnostics

In strict mode:

4.6 Move and “one-time” values

Objective‑C 3.0 introduces the concept of move for:

Move semantics are explicit:

4.7 Autorelease pools and bridging

ObjC 3.0 retains autorelease pool semantics and requires that new language constructs (defer, async) preserve correctness:

Per-task autorelease pool behavior at suspension points is defined normatively in Part 7 and C (Decision D-006).

4.7 Async suspension and autorelease pools

4.7.1 Normative rule (Objective‑C runtimes)

On Objective‑C runtimes with autorelease semantics, a conforming implementation shall ensure:

This rule is required to avoid unbounded autorelease growth across long async chains and to make memory behavior predictable for Foundation-heavy code.

See Part 7 §7.9 and C.7.

4.8 Interactions with retainable C families

Retainable families (CoreFoundation-like, dispatch/xpc integration, custom refcounted C objects) are specified in Part 8. Part 4 defines their participation in the ownership model:

4.9 Open issues


Part 5 — Control Flow and Safety Constructs

Working draft v0.10 — last updated 2025-12-28

5.1 Purpose

This part defines:

This part specifies syntax and high-level semantics. Precise cleanup ordering (especially for system resources) is specified in Part 8 and is normative.

5.2 defer

5.2.1 Syntax

defer compound-statement

Example:

FILE *f = fopen(path, "r");
defer { if (f) fclose(f); }

// ... use f ...

5.2.2 Semantics (normative)

Executing a defer statement registers its body as a scope-exit action in the innermost enclosing lexical scope.

5.2.3 Restrictions (normative)

A deferred body shall not perform a non-local exit from the enclosing scope. The following are ill‑formed inside a defer body:

Rationale: defers must be reliably executed as cleanups; permitting non-local exits leads to un-auditable control flow.

5.2.4 Interaction with async

A defer body in an async function executes when the scope exits, even if the function suspends/resumes multiple times (Part 7 §7.9.2).

5.3 guard

5.3.1 Syntax

guard-statement:
    'guard' guard-condition-list 'else' compound-statement

A guard-condition-list is a comma-separated list of:

Example:

guard let user = maybeUser, user.isActive else {
  return nil;
}

5.3.2 Semantics (normative)

5.3.3 Mandatory exit (normative)

The else block of a guard statement shall perform a control-flow exit from the current scope. Conforming exits include:

If the compiler cannot prove that all control-flow paths in the else block exit, the program is ill‑formed.

5.3.4 Refinement

Names proven by guard conditions are refined after the guard:

5.4 match

5.4.1 Motivation

Objective‑C has switch, but it is limited:

match is designed to be:

5.4.2 Syntax (normative)

match-statement:
    'match' '(' expression ')' ' match-case+ match-default? '

match-case:
    'case' pattern ':' compound-statement

match-default:
    'default' ':' compound-statement

Notes:

5.4.3 Evaluation order (normative)

5.4.4 Scoping (normative)

Bindings introduced by a case pattern are scoped to that case body only.

5.4.5 Exhaustiveness (normative)

In strict mode, match must be exhaustive when the compiler can prove the matched type is a closed set.

The following are treated as closed sets in v1:

For closed sets:

5.4.6 Lowering (normative)

match shall lower to:

5.5 Patterns (v1)

5.5.1 Wildcard

_ matches anything and binds nothing.

5.5.2 Literal constants

Literal constants match values of compatible type:

Note: Rich structural matching for arbitrary objects is intentionally not part of v1.

5.5.3 Binding pattern

A binding pattern introduces a name:

In v1:

5.5.4 Result case patterns

Result<T, E> participates in pattern matching (Part 6).

The following case patterns are defined:

Example:

match (r) {
  case .Ok(let v): { use(v); }
  case .Err(let e): { log(e); }
}

5.5.5 Type-test patterns (optional in v1)

Implementations may provide a type-test pattern form:

If provided:

This feature is optional in v1 because it intersects with Objective‑C runtime reflection and optimization.

5.6 Required diagnostics

Minimum diagnostics include:

5.7 Open issues


Part 6 — Errors: Result, throws, try, and Propagation

Working draft v0.10 — last updated 2025-12-28

6.0 Overview

v0.9 resolved decisions

v0.8 resolved decisions

v0.5 resolved decisions

v0.4 resolved decisions

Objective‑C 3.0 standardizes a modern, explicit error model that can be used in new code while interoperating with existing Cocoa and system APIs.

This part defines:

This error system is designed to be implementable without relying on Objective‑C exceptions (@throw/@try/@catch), which remain separate.


6.1 Lexical and grammar additions

6.1.1 New keywords

In ObjC 3.0 mode, the following are reserved by this part:

6.1.2 Grammar summary (high level)

This part introduces:


6.2 The Error protocol and error values

6.2.1 Definition

Objective‑C 3.0 defines a standard protocol:

@protocol Error
@end

An error value is any Objective‑C object value that conforms to Error.

6.2.2 Bridging to NSError

For platforms with Foundation:

Implementations may provide:

Note: The language does not require Foundation, but defines the protocol so multiple ecosystems can conform.


6.3 throws effect

6.3.1 Grammar

A function or method declaration may include a throws specifier.

Provisional grammar (illustrative):

function-declaration:
    declaration-specifiers declarator throws-specifier? function-body

throws-specifier:
    'throws'

6.3.2 Semantics

A throws function may either:

The thrown value type is id<Error>.

6.3.3 Call-site requirements

A call to a throwing function is ill-formed unless it appears:

6.3.4 Function and block types

A throwing function’s type is distinct from a non-throwing function’s type.

Examples (illustrative):

Conversion rules (normative intent):

async and throws compose: async throws is a distinct combined effect set.

6.3.5 ABI and lowering (normative for implementations)

Source-level semantics for throws are defined in this part. In addition, conforming implementations shall ensure throws is stable under separate compilation (C.2).

A conforming implementation shall:

The recommended calling convention is the trailing error-out parameter described in C.4.

Note: This draft intentionally chooses an error-out convention

Reflection note (non-normative): v1 does not require encoding throws in Objective‑C runtime type encodings. Toolchains may provide extended metadata as an extension. rather than stack unwinding, to keep interoperability with NSError/return-code APIs straightforward and to align with existing Objective‑C runtime practices.

Throwing is part of a function’s type.

A throwing function value cannot be assigned to a non-throwing function type without an explicit adapter.


6.4 throw statement

6.4.1 Grammar

throw-statement:
    'throw' expression ';'

6.4.2 Static semantics

A throw statement is permitted only within a throws function or within a catch block.

The thrown expression must be convertible to:

6.4.3 Dynamic semantics

Executing throw e;:

Note: This is not Objective‑C exception throwing. It is a structured error return path.


6.5 try expressions

6.5.1 Grammar

try-expression:
    'try' expression
  | 'try' '?' expression
  | 'try' '!' expression

6.5.2 try e

try e evaluates e in a context that may throw.

6.5.3 try? e

try? e converts a throwing evaluation into an optional result:

In strict mode, discarding errors should produce a warning unless explicitly marked as intentional.

6.5.4 try! e

try! e forces a throwing evaluation:

6.5.5 Typing


6.6 Postfix propagation operator ? (Result / Optional)

6.6.1 Purpose

The postfix propagation operator provides Rust-like ergonomics for carrier types without relying on exceptions:

This operator is intentionally limited to avoid conflict with C’s conditional operator ?:.

6.6.2 Grammar and disambiguation

The propagation operator is a postfix ? applied to a postfix-expression, with a syntactic restriction:

Grammar:

propagate-expression:
    postfix-expression '?'

Note: This means foo()? + 1 must be written as (foo()?) + 1.

6.6.3 Semantics for Result

If e has type Result<T, E> then e? behaves as:

Mapping:

6.6.4 Semantics for optionals

If e has type T? then e? yields T if nonnull, else early-exits.

Carrier rule (normative):

If the enclosing function is throws or returns Result<…>, use of e? is ill‑formed in v1. Convert explicitly using guard let … else { throw … } or an explicit helper such as ok_or(...).

6.6.5 Diagnostics


6.7 do / catch

6.7.1 Grammar

do-statement:
    'do' compound-statement catch-clauses

catch-clauses:
    catch-clause+
catch-clause:
    'catch' catch-pattern? compound-statement

catch-pattern:
    '(' type-name identifier? ')'

6.7.2 Semantics

6.7.3 Catch matching

A catch pattern (T e) matches if the thrown value:

A bare catch { ... } matches any error.


6.8 Result<T, E> standard type

6.8.1 Definition

The standard library shall provide a closed two-case Result type:

Result<T, E> = Ok(T) | Err(E)

6.8.2 Requirements


6.9 Interoperability: NSError-out-parameter conventions

6.9.1 Recognizing an NSError-throwing signature

Eligible if:

6.9.2 Standard attribute

Canonical attribute: __attribute__((objc_nserror)) (see B.4.1).

6.9.3 try lowering for NSError-bridged calls

Compiler shall:


6.10 Interoperability: return-code APIs

6.10.1 Status attribute

Canonical attribute: __attribute__((objc_status_code(success: constant, error_type: Type, mapping: Function))) (see B.4.2).

6.10.2 Bridging


6.11 Required diagnostics

Minimum diagnostics:


6.12 Open issues

6.13 Future extensions (non-normative)

6.13.1 Typed throws

A future revision may introduce typed throws syntax (e.g., throws(E)) to restrict the set of throwable error types. This is explicitly not part of Objective‑C 3.0 v1 to keep the core error model simple and interoperable with NSError and return-code APIs.


Part 7 — Concurrency: async/await, Executors, Cancellation, and Actors

Working draft v0.10 — last updated 2025-12-28

7.0 Overview

v0.9 resolved decisions

v0.8 resolved decisions

v0.5 resolved decisions

v0.4 resolved decisions

Objective‑C 3.0 introduces structured concurrency that is composable, auditable, and implementable.

This part defines:


7.1 Lexical and grammar additions

7.1.1 New keywords

In ObjC 3.0 mode, the following are reserved by this part:

7.1.2 Effects and isolation

async and throws are effects that become part of a function’s type.

Executor affinity (objc_executor(...)) and actor isolation are isolation annotations that may cause a call to suspend at the call site even when the callee is not explicitly async (Decision D-011).


7.2 Async declarations

7.2.1 Grammar (illustrative)

function-effect-specifier:
    'async'
  | 'throws'
  | 'async' 'throws'
  | 'throws' 'async'

7.2.2 Static semantics

7.2.3 Block types

Async is part of a block’s type:


7.3 await expressions

7.3.1 Grammar

await-expression:
    'await' expression

7.3.2 Static semantics (normative)

A translation unit is ill-formed if it contains a potentially suspending operation that is not within the operand of an await expression.

The following are potentially suspending operations:

  1. A call to a declaration whose type includes the async effect.
  2. A cross-executor entry into a declaration annotated objc_executor(X) when the compiler cannot prove the current executor is X.
  3. A cross-actor entry to an actor-isolated member when the compiler cannot prove it is already executing on that actor’s executor.
  4. A task-join / task-group operation that is specified by the standard library contract to be async.

await applied to an expression that is not potentially suspending is permitted, but a conforming implementation should warn in strict modes (“unnecessary await”).

7.3.3 Dynamic semantics

await e may suspend:

In particular, await may represent:


7.4 Executors

7.4.1 Concept

An executor schedules task continuations.

7.4.2 Default executors

Runtime shall provide:

7.4.3 Executor annotations (normative)

Objective‑C 3.0 defines executor affinity using a standardized attribute spelling.

7.4.3.1 Canonical spelling

Executor affinity shall be expressed using a Clang-style attribute:

The attribute may be applied to:

7.4.3.2 Static semantics (normative)

If a declaration is annotated objc_executor(X), then entering that declaration from a context not proven to already be on executor X is a potentially suspending operation (§7.3.2) and therefore requires await.

In strict concurrency checking mode, the compiler shall reject a call to an executor-annotated declaration unless either:

7.4.3.3 Dynamic semantics (model)

An implementation shall ensure that execution of an executor-annotated declaration occurs on the specified executor.

When a call is type-checked as crossing an executor boundary, the implementation may insert an implicit hop provided the call site is explicitly marked with await. The observable effect is that executor-affine code never runs on an incorrect executor, and the call is treated as potentially suspending.

7.4.3.4 Notes


7.5 Tasks and structured concurrency (standard library contract)

7.5.1 Design decision (v1)

Objective‑C 3.0 v1 does not introduce a task { ... } keyword expression/statement. Task creation and structured concurrency constructs are provided by the standard library, but the compiler shall recognize task-related APIs via attributes so that:

7.5.2 Standard attributes (normative)

7.5.2.1 __attribute__((objc_task_spawn))

Applied to a function/method that creates a child task.

Requirements:

7.5.2.2 __attribute__((objc_task_detached))

Applied to a function/method that creates a detached task.

Requirements:

7.5.2.3 __attribute__((objc_task_group))

Applied to a function/method that establishes a structured task group scope.

Requirements:

7.5.3 Required standard library surface (abstract)

A conforming implementation shall provide, in a standard concurrency module, APIs equivalent in expressive power to:

The exact naming and Objective‑C surface syntax are implementation-defined, but the compiler shall be able to identify the spawn/group APIs via the attributes above.

7.5.4 Compiler checking hooks

In strict concurrency checking mode:

Note: A future revision may add task { ... } as sugar (possibly via macros) once patterns stabilize.

7.6 Cancellation


7.7 Actors

7.7.1 Actor types

actor class Name : NSObject
@end

7.7.2 Isolation

Actors provide data-race safety by associating each actor instance with a serial executor and treating the actor’s isolated mutable state as accessible only while running on that executor.

Informally: outside code must hop onto the actor to touch its state.

7.7.2.1 Actor-isolated members (normative)

Unless otherwise specified, instance methods and instance properties of an actor class are actor-isolated.

A reference to an actor-isolated member from outside the actor’s isolation domain is a potentially suspending operation (§7.3.2) and therefore requires await.

Example (illustrative):

actor class Counter : NSObject
@property (atomic) NSInteger value;
- (void)increment;
@end

async void f(Counter *c) {
  await [c increment];         // may hop to c's executor
  NSInteger v = await c.value; // may hop; read is isolated
}

Within actor-isolated code already executing on the actor’s executor, await is not required for isolated member access unless the member is explicitly async.

7.7.3 Reentrancy

An await inside actor-isolated code may suspend the current task while still preserving the actor’s serial executor. While suspended, other enqueued work on the same actor may run (i.e., reentrancy is possible).

Guidance (normative intent):

7.7.4 Nonisolated members (normative)

A member may be declared nonisolated to indicate it is safe to call without actor isolation.

Canonical spelling (attribute):

actor class Logger : NSObject
- (void)log:(NSString *)message __attribute__((objc_nonisolated));
@end

Rules:

Note: Toolchains may additionally accept a contextual keyword spelling (nonisolated) as sugar, but emitted interfaces should use the canonical attribute spelling (B.3.4).


7.8 Sendable-like checking

7.8.1 Marker protocol

@protocol Sendable
@end

7.8.2 Enforcement sites

Provide __attribute__((objc_unsafe_sendable)) escape hatch (B.3.3).


7.9 Interactions

7.9.1 try await composition

When calling an async throws function, both effects must be handled:

A conforming implementation shall accept try await f(...) as the canonical order.

7.9.2 defer and scope cleanup across suspension

Defers registered in an async function scope execute when that scope exits, even if the function has suspended and resumed multiple times.

7.9.3 ARC lifetimes across suspension

Objects referenced across an await must remain alive until no longer needed. Implementations shall retain values captured into an async frame and release them when the frame is destroyed (normal completion, throw, or cancellation unwind).

7.9.4 Autorelease pools at suspension points (normative on ObjC runtimes)

On platforms that support Objective‑C autorelease pools:

If an await completes synchronously without suspending, draining is permitted but not required.

Implementations may drain at additional safe points, but shall not permit unbounded autorelease pool growth across long-lived tasks that repeatedly suspend.


7.12 Lowering, ABI, and runtime hooks (normative for implementations)

7.12.1 Separate compilation requirements

Conforming implementations shall satisfy the separate compilation requirements in C.2 and the required metadata set in D for all concurrency-relevant declarations, including:

7.12.2 Coroutine lowering model (informative summary)

The recommended lowering model is:

7.12.3 Executor hop primitive

Implementations should provide (directly or via the standard library) a primitive that represents:

so that the compiler can uniformly lower:

7.12.4 Actor runtime representation (minimum)

Each actor instance shall be associated with a serial executor that:

The concrete queue representation is implementation-defined, but the association must be observable by the runtime/stdlib to implement awaited cross-actor calls.

7.12.5 Autorelease pools at suspension points

Autorelease pool boundaries at suspension points are required by Decision D-006 and defined normatively in C.7 and §7.9.4.

7.10 Diagnostics

Minimum diagnostics:

7.11 Open issues


Part 8 — System Programming Extensions

Working draft v0.10 — last updated 2025-12-28

8.0 Scope and goals

This part defines language features and attributes designed to accommodate “library-defined subsets” and system APIs, with improved safety, ergonomics, and performance predictability:

This part intentionally provides detailed normative semantics because system-library correctness depends on precise ordering.

8.0.1 Canonical attribute spellings (header interoperability)

Normative note: The canonical spellings for all attributes and pragmas used in this part are cataloged in ATTRIBUTE_AND_SYNTAX_CATALOG.md. Inline spellings here are illustrative; emitted interfaces and module metadata shall use the catalog spellings. Many system-facing features in this part are intended to be expressed in headers and preserved by module interfaces. Per Decision D-007, a conforming implementation shall support canonical __attribute__((...)) spellings for these features, so that:

This part uses ergonomic @resource(...)/@cleanup(...)/borrowed spellings in examples; implementations may accept these as ObjC 3.0 sugar. The canonical header spellings are described inline where relevant.


8.1 Unified scope-exit action model

Within a lexical scope, the abstract machine maintains a stack of scope-exit actions. Actions are registered when:

On scope exit, actions execute in reverse order of registration (LIFO).

Scope exit includes:

Non-local unwinding mechanisms that do not run cleanups (e.g., longjmp) are outside the guarantees of this part; crossing a cleanup scope using such mechanisms is undefined unless the platform ABI states otherwise.


8.2 defer (normative)

8.2.1 Syntax

defer compound-statement

8.2.2 Semantics

Executing a defer registers its body as a scope-exit action in the innermost enclosing lexical scope.

8.2.3 Ordering with ARC releases

In a scope:

Rationale: ensures cleanup code can safely reference locals without surprising early releases.

8.2.4 Restrictions

A deferred body shall not contain a statement that performs a non-local exit from the enclosing scope. Such code is ill-formed.


8.3 Resource values and cleanup

8.3.1 @cleanup(F)

A local variable may be annotated with a cleanup function.

Canonical header spelling (illustrative):

A local variable may be annotated with a cleanup function:

@cleanup(F) Type name = initializer;

Semantics:

8.3.2 @resource(F, invalid: X)

A @resource variable has a cleanup function and an invalid sentinel.

Canonical header spelling (illustrative):

A @resource variable has a cleanup function and an invalid sentinel:

@resource(CloseFn, invalid: 0) io_connect_t conn = 0;

At scope exit:

8.3.3 Cleanup signature matching

Cleanup function F is applicable if it can be called as:

8.3.4 Move support for resources

8.3.4.1 take(x)

take(x) yields the current value of resource variable x and sets x to its invalid sentinel.

Using x after take yields the invalid value; in strict-system mode, using it as if valid without reinitialization is diagnosed.

8.3.4.2 reset(x, v)

reset(x, v):

  1. cleans up the current value of x if valid,
  2. assigns v to x.

8.3.4.3 discard(x)

discard(x) cleans up and invalidates x immediately.

8.3.5 Type-directed cleanup diagnostics

Objective‑C 3.0 introduces type annotations usable on typedefs:

In strict-system mode, the compiler shall diagnose:

Note: This is explicitly intended to prevent “wrong close vs release” bugs in handle-heavy APIs.


8.4 Retainable C families and ObjC-integrated objects

8.4.1 Motivation

Some system libraries declare their objects as Objective‑C object types when compiling as Objective‑C so they can participate in ARC, blocks, and analyzer tooling. This behavior exists today in dispatch and XPC ecosystems.

Objective‑C 3.0 defines a language-level model so this is not “macro folklore.”

8.4.2 @retainable_family

A library may declare a retainable family type by specifying:

If ObjC-integrated:

8.4.3 Analyzer integration

A conforming implementation shall expose retainable family semantics to:


8.5 CoreFoundation ownership attributes (normative)

Objective‑C 3.0 standardizes ownership transfer attributes commonly used with ARC and analyzer:

The compiler shall use these to:


8.6 Explicit lifetime control

8.6.1 withLifetime(expr) stmt

Syntax:

withLifetime(expr) { ... }

Semantics:

8.6.2 keepAlive(expr);

Extends the lifetime of expr to at least the end of the current lexical scope via an implicit precise-lifetime binding.

8.6.3 objc_precise_lifetime

The implementation shall support a precise lifetime annotation for locals and define its effect: prevent lifetime shortening below lexical scope.


8.7 Borrowed interior pointers

8.7.1 borrowed T *

A borrowed pointer type indicates “valid only while some owner stays alive.”

Rules in strict-system mode:

8.7.2 @returns_borrowed(owner: N)

Functions may declare borrowed return relationships, enabling the compiler to enforce owner lifetime.

Canonical header spelling (illustrative):

8.7.3 Escape analysis (normative in strict-system)

In strict-system mode, a borrowed pointer value shall be treated as non-escaping unless an explicit unsafe escape is used.

The following are considered escaping uses and are ill-formed in strict-system mode:

The compiler shall provide diagnostics that suggest:

A conforming implementation may rely on a combination of front-end rules and static analysis to enforce this.


8.8 Block capture lists

8.8.1 Syntax

Blocks may include an explicit capture list:

^[weak self, move handle] { ... }

A capture list is a comma-separated list of capture items:

capture-list:
    '[' capture-item (',' capture-item)* ']'

capture-item:
    capture-modifier? identifier

Capture modifiers (v1):

8.8.2 Semantics (normative)

Capture list items are evaluated at block creation time, not at block invocation time.

For a capture item m x:

Modifier semantics:

8.8.3 Capture evaluation order (normative)

Capture list entries are evaluated left-to-right. This ordering matters when later captures depend on earlier ones (or when move semantics are involved).

8.8.4 Move capture rules

If x is captured with move:

For ordinary Objective‑C object pointers, “moved-from” is a safe state (typically nil) and use-after-move diagnostics are optional. For resource/handle types annotated as requiring cleanup (Part 8.3/8.3.5), strict-system mode shall diagnose:

8.8.5 Interaction with retain cycles

Capture lists provide a standard spelling for the “weak‑strong dance” pattern.

Example:

__weak typeof(self) weakSelf = self;
[obj setHandler:^{
  typeof(self) self = weakSelf;
  if (!self) return;
  [self doThing];
}];

can be expressed as:

[obj setHandler:^[weak self] {
  guard let self = self else { return; }
  [self doThing];
}];

8.8.6 Diagnostics

In strict-system mode, toolchains should:


8.9 Conformance and migration


8.10 Open issues


Part 9 — Performance and Dynamism Controls

Working draft v0.10 — last updated 2025-12-28

9.1 Purpose

Objective‑C’s dynamic dispatch is a strength, but “open world” dynamism can inhibit optimization and complicate reasoning about safety.

Objective‑C 3.0 provides opt-in controls that:

This part is designed to accommodate both Cocoa-style dynamic patterns and system/library subsets where predictability and performance matter.

9.2 Direct methods

9.2.1 Concept

A direct method is a method that:

9.2.2 Canonical spelling

A direct method is declared with:

__attribute__((objc_direct))

applied to an Objective‑C method declaration.

Toolchains may additionally support class-level defaults (e.g., “all members direct”) as an extension, but such defaults must be representable in emitted interfaces (B.7).

9.2.3 Semantics (normative)

  1. A direct method shall not be overridden in any subclass.
  2. A direct method shall not be introduced or replaced by a category with the same selector.
  3. A direct method shall not participate in message forwarding semantics (forwardInvocation:) as part of selector lookup.

Calling model:

Programmer model: “If you want dynamic, do not mark it direct.”

9.2.4 Separate compilation notes (normative)

Because direct methods change call legality and dispatch surfaces, the objc_direct attribute shall be:

Effect/attribute mismatches across modules are ill-formed (C.2).

Recommended lowering is described in C.8. At a high level:

9.3 Final methods and classes

9.3.1 Concept

A final declaration prohibits overriding/subclassing but does not necessarily remove the declaration from dynamic dispatch.

Final is primarily a reasoning and optimization tool:

9.3.2 Canonical spelling

Final declarations use:

__attribute__((objc_final))

applied to:

9.3.3 Semantics (normative)

Violations are ill-formed.

9.4 Sealed classes

9.4.1 Concept

A sealed class prohibits subclassing outside the defining module, but may allow subclassing inside the module.

This enables:

9.4.2 Canonical spelling

Sealed classes use:

__attribute__((objc_sealed))

Toolchains may treat existing equivalent attributes (e.g., “subclassing restricted”) as aliases.

9.4.3 Semantics (normative)

At minimum, sealed shall be strong enough to support the “no external subclassing” contract.

9.5 Interaction with categories, swizzling, and reflection

9.5.1 Categories

9.5.2 Swizzling and IMP replacement (non-normative guidance)

Swizzling is a common dynamic technique. Objective‑C 3.0 does not outlaw it, but it makes the tradeoff explicit:

9.6 ABI and visibility rules (normative for implementations)

Implementations shall ensure that direct/final/sealed remain safe under:

Minimum requirements:

  1. Attributes that affect dispatch legality (objc_direct) must be recorded in module metadata.
  2. Interface emission must preserve the attributes using canonical spellings (B.7).
  3. Calling a method with incompatible assumptions about directness/finality across module boundaries must be diagnosed when possible (Part 12).

9.7 Required diagnostics (minimum)

9.8 Open issues


Part 10 — Metaprogramming, Derives, Macros, and Property Behaviors

Working draft v0.10 — last updated 2025-12-28

10.1 Purpose

Objective‑C has historically relied on:

Objective‑C 3.0 introduces structured mechanisms that reduce boilerplate while remaining auditable and toolable:

This part is constrained by:

10.2 Derives

10.2.1 Canonical spelling

A derive request is attached using:

__attribute__((objc_derive("TraitName")))

Multiple derives may be listed by repeating the attribute.

10.2.2 Semantics (normative)

A derive request causes the compiler (or derive implementation) to synthesize declarations as if the user had written them.

Rules:

  1. Derives are deterministic: the same source must produce the same expanded declarations.
  2. Derives are pure with respect to the compilation: they shall not depend on network, wall-clock time, or other nondeterministic inputs.
  3. If a derive cannot be satisfied (missing requirements, ambiguous synthesis), compilation is ill-formed and shall produce diagnostics identifying the missing inputs.

10.2.3 ABI and interface emission (normative)

If a derive synthesizes declarations that affect:

then those synthesized declarations are part of the module’s API surface and:

10.2.4 Standard derives (non-normative)

A standard library may provide derives analogous to:

This draft does not require a particular set in v1; it requires the mechanism and the interface emission guarantees.

10.3 AST macros

10.3.1 Concept

AST macros are compile-time programs that transform syntax/AST into expanded declarations or expressions.

10.3.2 Canonical marker attribute

Macro entry points may be annotated with:

__attribute__((objc_macro))

The macro definition and packaging format are implementation-defined.

10.3.3 Safety constraints (normative)

A conforming implementation shall provide enforcement such that macros used in trusted builds are:

10.3.4 Expansion phases (normative)

Macro expansion (and derive expansion) that introduces declarations affecting layout/ABI shall occur before:

This ensures ABI is computed from the expanded program (C.9).

10.3.5 Interface emission (normative)

If macro expansion introduces exported declarations, the emitted interface shall include the expanded declarations (or an equivalent representation that reconstructs them), not merely the macro invocation.

10.4 Property behaviors / wrappers

10.4.1 Concept

A property behavior is a reusable specification of:

10.4.2 Exported ABI restrictions (normative)

Behaviors that affect layout or emitted ivars of exported types shall be constrained such that:

In v1, implementations may restrict behaviors on exported ABI surfaces to a “safe subset” (e.g., behaviors that desugar into explicit ivars/accessors).

10.4.3 Composition (normative)

If multiple behaviors are applied to a property, the composition order and conflict rules must be deterministic and diagnosable. Ambiguous compositions are ill-formed.

10.4.4 Performance contracts

Behaviors must declare whether:

Compilers should surface these contracts in diagnostics to help developers choose appropriate behaviors.

10.5 Required diagnostics (minimum)

10.6 Open issues


Part 11 — Interoperability: C, C++, and Swift

Working draft v0.10 — last updated 2025-12-28

11.1 Purpose

Objective‑C’s success depends on interoperability:

This part defines interoperability expectations so ObjC 3.0 features can be adopted without breaking the ecosystem.

This part is intentionally conservative about ABI: where a feature affects ABI or lowering, it is cross-referenced to C and summarized in D.

11.2 C interoperability

11.2.1 Baseline ABI compatibility

Objective‑C 3.0 preserves baseline C ABI calling conventions for declarations that do not use new effects.

For declarations that use ObjC 3.0 effects (throws, async), a conforming toolchain shall provide a stable, documented calling convention (C.4, C.5).

11.2.2 Exporting throws to C (normative intent)

When a throwing function is made visible to C, it shall be representable using a C-callable surface.

Recommended representation:

This aligns with Cocoa NSError patterns and enables C callers to handle errors without language support for try.

11.2.3 Exporting async to C (normative intent)

When an async function is made visible to C, it shall be representable using a C-callable surface.

This draft does not require a single canonical signature, but a conforming implementation shall provide at least one of:

The chosen representation must be stable under separate compilation and recordable in module metadata (C.2).

11.3 C++ / ObjC++ interoperability

11.3.1 Parsing and canonical spellings

Canonical spellings in B are chosen to be compatible with ObjC++ translation units:

Implementations may additionally support C++11 attribute spellings ([[...]]) in ObjC++ as sugar, but interface emission shall use canonical spellings (B.7).

11.3.2 Exceptions

Objective‑C exceptions (@throw/@try/@catch) remain distinct from ObjC 3.0 throws. C++ exceptions remain distinct as well.

Interoperability guidance:

11.4 Swift interoperability

11.4.1 Nullability and optionals

ObjC 3.0 nullability annotations and T? sugar import naturally as Swift optionals where applicable, consistent with existing Swift/ObjC interop rules.

11.4.2 Errors

11.4.3 Concurrency

11.4.4 Executors and isolation

Executor affinity (objc_executor(main)) is intended to map cleanly to Swift main-thread isolation concepts. The exact imported attribute spelling is implementation-defined, but semantics must be preserved across the boundary.

Actor isolation metadata should be preserved such that Swift clients do not accidentally violate ObjC actor invariants (and vice versa).

11.4.5 Performance/dynamism controls

Direct/final/sealed intent should map, where possible, to Swift’s:

The mapping is implementation-defined but should preserve the core constraints.

11.5 Required diagnostics (minimum)

11.6 Open issues


Part 12 — Diagnostics, Tooling, and Test Suites

Working draft v0.10 — last updated 2025-12-28

12.1 Purpose

Objective‑C 3.0 treats tooling requirements as part of conformance:

This part is cross-cutting and references:

12.2 Diagnostic principles (normative)

A conforming implementation shall provide diagnostics that:

  1. Identify the precise source range of the problem.
  2. Explain the rule being violated in terms of Objective‑C 3.0 concepts (nonnull, optional send, executor hop, etc.).
  3. Provide at least one actionable suggestion.
  4. Provide a fix-it when the transformation is mechanical and semantics-preserving.

12.3 Required diagnostics (minimum set)

12.3.1 Nullability and optionals

12.3.2 Errors and throws

12.3.3 Concurrency (async/await, executors, actors)

12.3.4 Modules and interface emission

12.3.5 Performance/dynamism controls

12.3.6 System programming extensions

A conforming implementation shall provide diagnostics (at least warnings, and errors in strict-system profiles) for:

12.4 Required tooling capabilities (minimum)

12.4.1 Migrator

A conforming toolchain shall provide (or ship) a migrator that can:

12.4.2 Interface emission / verification

If the toolchain supports emitting an interface description for distribution, it shall also support a verification mode that checks:

12.4.3 Static analysis hooks

Implementations shall expose enough information for analyzers to:

12.5 Conformance test suite (minimum expectations)

A conforming implementation shall ship or publish a test suite that covers at least:

12.5.1 Parsing/grammar

12.5.2 Type system and diagnostics

12.5.3 Dynamic semantics

12.5.4 Runtime contracts

12.6 Debuggability requirements (minimum)

For features like macros and async:

12.7 Open issues