const std = @import("std"); pub const Span = struct { start: u32, end: u32, }; pub const Node = struct { span: Span, data: Data, source_file: ?[]const u8 = null, pub const Data = union(enum) { root: Root, fn_decl: FnDecl, block: Block, int_literal: IntLiteral, float_literal: FloatLiteral, bool_literal: BoolLiteral, string_literal: StringLiteral, identifier: Identifier, enum_literal: EnumLiteral, binary_op: BinaryOp, chained_comparison: ChainedComparison, unary_op: UnaryOp, call: Call, field_access: FieldAccess, if_expr: IfExpr, match_expr: MatchExpr, match_arm: MatchArm, const_decl: ConstDecl, var_decl: VarDecl, assignment: Assignment, multi_assign: MultiAssign, destructure_decl: DestructureDecl, enum_decl: EnumDecl, struct_decl: StructDecl, struct_literal: StructLiteral, union_decl: UnionDecl, error_set_decl: ErrorSetDecl, lambda: Lambda, type_expr: TypeExpr, param: Param, defer_stmt: DeferStmt, push_stmt: PushStmt, comptime_expr: ComptimeExpr, insert_expr: InsertExpr, return_stmt: ReturnStmt, import_decl: ImportDecl, namespace_decl: NamespaceDecl, array_type_expr: ArrayTypeExpr, slice_type_expr: SliceTypeExpr, array_literal: ArrayLiteral, parameterized_type_expr: ParameterizedTypeExpr, index_expr: IndexExpr, slice_expr: SliceExpr, pointer_type_expr: PointerTypeExpr, many_pointer_type_expr: ManyPointerTypeExpr, optional_type_expr: OptionalTypeExpr, error_type_expr: ErrorTypeExpr, raise_stmt: RaiseStmt, try_expr: TryExpr, catch_expr: CatchExpr, onfail_stmt: OnFailStmt, /// `#caller_location` — a marker that, as a parameter default, resolves /// to a `Source_Location` of the call site (ERR E4.1b). The node's /// `span`/`source_file` carry the location (rewritten to the call site /// during default expansion). No payload. caller_location: void, pack_index_type_expr: PackIndexTypeExpr, comptime_pack_ref: ComptimePackRef, force_unwrap: ForceUnwrap, null_coalesce: NullCoalesce, deref_expr: DerefExpr, null_literal: void, while_expr: WhileExpr, for_expr: ForExpr, spread_expr: SpreadExpr, break_expr: void, continue_expr: void, undef_literal: void, inferred_type: void, builtin_expr: void, compiler_expr: void, foreign_expr: ForeignExpr, library_decl: LibraryDecl, framework_decl: FrameworkDecl, function_type_expr: FunctionTypeExpr, closure_type_expr: ClosureTypeExpr, tuple_type_expr: TupleTypeExpr, tuple_literal: TupleLiteral, ufcs_alias: UfcsAlias, c_import_decl: CImportDecl, protocol_decl: ProtocolDecl, impl_block: ImplBlock, ffi_intrinsic_call: FfiIntrinsicCall, foreign_class_decl: ForeignClassDecl, jni_env_block: JniEnvBlock, pub fn declName(self: Data) ?[]const u8 { return switch (self) { .fn_decl => |d| d.name, .const_decl => |d| d.name, .var_decl => |d| d.name, .enum_decl => |d| d.name, .struct_decl => |d| d.name, .union_decl => |d| d.name, .error_set_decl => |d| d.name, .namespace_decl => |d| d.name, .ufcs_alias => |d| d.name, .c_import_decl => |d| d.name, .protocol_decl => |d| d.name, .foreign_class_decl => |d| d.name, else => null, }; } }; }; pub const Root = struct { decls: []const *Node, }; pub const CallingConvention = enum { default, c }; /// Linkage modifier written in the postfix slot after `callconv(...)`: /// `name :: (sig) -> Ret [callconv(.x)] [extern | export] [;|{…}];` /// `extern` = import (external linkage, C ABI, no sx ctx — `#foreign`'s role); /// `export` = define + expose (body + external linkage + C ABI + no ctx). /// Both imply `callconv(.c)`. Variants carry a trailing `_` to dodge the Zig /// keywords. `.none` = no linkage modifier (the ordinary sx-internal decl). pub const ExternExportModifier = enum { none, extern_, export_ }; pub const FnDecl = struct { name: []const u8, params: []const Param, return_type: ?*Node, body: *Node, type_params: []const StructTypeParam = &.{}, is_arrow: bool = false, call_conv: CallingConvention = .default, /// Postfix linkage modifier (`extern`/`export`) written after the /// `callconv(...)` slot. `.none` for an ordinary sx-internal function. /// Parsed in Phase 0.1; not consumed by the fn-decl path until Phase 1. extern_export: ExternExportModifier = .none, /// Optional library reference + symbol-name override for an `extern`/`export` /// function, mirroring `#foreign LIB "csym"` (foreign_lib/foreign_name). Both /// optional: `extern` alone resolves the sx name against the default-linked /// libs; `extern LIB` names the source library; `extern "csym"` renames the /// symbol. Required for `extern` to be a behavior-equivalent superset of /// `#foreign` (Gate A→B) — the migration of 466 `#foreign` uses across 6 libs /// must preserve each symbol's library. Parsed/consumed in Phase 1.2. extern_lib: ?[]const u8 = null, extern_name: ?[]const u8 = null, /// Span of the function's name token, for the reserved-type-name decl /// diagnostic. Synthesized decls (e.g. `#import c` foreign /// functions, lowering-time objc/protocol method synthesis) leave it zero. name_span: Span = .{ .start = 0, .end = 0 }, /// True when the function NAME was written as a backtick raw identifier /// (`` `i2 :: … ``) or synthesized by a `#import c` foreign decl. A raw /// name is exempt from the reserved-type-name binding check. /// Every PARSER fn_decl is built through `parseFnDecl`, whose `name_is_raw` /// is a REQUIRED parameter, so a parser site cannot drop it; the default /// here serves only post-check synthesized decls (which are never raw). is_raw: bool = false, /// `name :: ufcs (params) { body }` — the fn opted into dot-call /// dispatch (`recv.name(args)`). Dot-calls on free functions are /// OPT-IN: only `is_ufcs` fns and `ufcs` aliases dispatch; a plain /// fn is callable directly or via `|>` only. is_ufcs: bool = false, }; pub const Param = struct { name: []const u8, name_span: Span, type_expr: *Node, is_variadic: bool = false, is_comptime: bool = false, /// Heterogeneous protocol-constrained variadic pack: `..xs: Protocol` /// (no `[]`, no `$`). The annotation is a bare protocol the trailing args /// each conform to with their own type-arg — distinct from a slice variadic /// (`..xs: []T`, `is_pack == false`) and from the comptime type-pack /// (`..$xs`, `is_comptime == true`). Always implies `is_variadic`. is_pack: bool = false, /// Optional default value expression. When the caller omits this /// parameter, lowering substitutes this expression in its place. default_expr: ?*Node = null, /// True when the param name was written as a backtick raw identifier /// (`` `i2 ``) or synthesized by a `#import c` foreign decl. A raw name is /// exempt from the reserved-type-name binding check. is_raw: bool = false, }; pub const Block = struct { stmts: []const *Node, /// True when the block's last statement is its value — i.e. a trailing /// expression with NO `;`. A trailing `;` (or a non-expression last /// statement) discards the value and leaves the block void. Match-arm and /// else-arm bodies are built with this forced true (the arm `;` is an arm /// terminator, not a value-discard). produces_value: bool = false, /// When `produces_value` is false *because* the last statement was an /// expression terminated by `;` (as opposed to a decl/return/empty block), /// the span of that discarding `;`. Lets a value-position diagnostic point /// precisely at the semicolon to drop. Null otherwise. discarded_semi: ?Span = null, }; pub const IntLiteral = struct { value: i64, }; pub const FloatLiteral = struct { value: f64, }; pub const BoolLiteral = struct { value: bool, }; pub const StringLiteral = struct { raw: []const u8, is_raw: bool = false, }; pub const Identifier = struct { name: []const u8, /// True when written as a backtick raw identifier (`` `i2 ``). Carried so a /// destructure target (`` `i2, b := … ``) can be recognised as raw and /// exempted from the reserved-type-name binding check. is_raw: bool = false, }; pub const EnumLiteral = struct { name: []const u8, // without the leading dot }; pub const BinaryOp = struct { op: Op, lhs: *Node, rhs: *Node, pub const Op = enum { add, sub, mul, div, mod, eq, neq, lt, lte, gt, gte, and_op, or_op, bit_and, bit_or, bit_xor, shl, shr, in_op, }; }; pub const ChainedComparison = struct { operands: []const *Node, ops: []const BinaryOp.Op, }; pub const UnaryOp = struct { op: Op, operand: *Node, pub const Op = enum { negate, not, bit_not, xx, address_of, }; }; pub const Call = struct { callee: *Node, args: []const *Node, }; /// `#objc_call(T)(recv, "sel:", args...)`, /// `#jni_call(T)(env, target, "name", "(Sig)R", args...)`, /// `#jni_static_call(T)(class, "name", "(Sig)R", args...)`. /// The return-type T sits in the first parens; the actual call args /// follow in the second parens. Codegen branches on `kind` to pick /// the lowering (objc_msgSend / CallXxxMethod / CallStaticXxxMethod). pub const FfiIntrinsicKind = enum { objc_call, jni_call, jni_static_call, }; pub const FfiIntrinsicCall = struct { kind: FfiIntrinsicKind, return_type: *Node, args: []const *Node, }; pub const FieldAccess = struct { object: *Node, field: []const u8, is_optional: bool = false, }; pub const IfExpr = struct { condition: *Node, then_branch: *Node, else_branch: ?*Node, is_inline: bool, // true for `if cond then a else b` is_comptime: bool = false, // true for `inline if` — compile-time branch elimination binding_name: ?[]const u8 = null, // for `if val := expr { ... }` optional binding binding_span: ?Span = null, // span of `binding_name` (set iff `binding_name` is) /// True when the optional binding was a backtick raw identifier /// (`` if `i2 := … ``) — exempt from the reserved-type-name check. binding_is_raw: bool = false, }; pub const MatchExpr = struct { subject: *Node, arms: []const MatchArm, is_comptime: bool = false, }; pub const MatchArm = struct { pattern: ?*Node, // null = else (default) arm body: *Node, is_break: bool, capture: ?[]const u8 = null, // payload binding name: case .variant: (name) { ... } capture_span: ?Span = null, // span of `capture` (set iff `capture` is) /// True when the capture was a backtick raw identifier /// (`` case .v: (`i2) ``) — exempt from the reserved-type-name check. capture_is_raw: bool = false, }; pub const ConstDecl = struct { name: []const u8, type_annotation: ?*Node, value: *Node, /// Span of the constant's name token, for the reserved-type-name decl /// diagnostic. NO default: every construction site must set /// it explicitly, so a struct-body const can't silently fall back to a /// 1:1 caret (the finding-1 bug). name_span: Span, /// True when the constant NAME was written as a backtick raw identifier /// (`` `i2 :: … ``). NO default: required at every site so the reserved- /// name exemption can't be dropped — mirrors `checkBindingName`'s required /// `is_raw` argument so the parser and the check can't desync. is_raw: bool, }; pub const VarDecl = struct { name: []const u8, name_span: Span, type_annotation: ?*Node, value: ?*Node, is_foreign: bool = false, foreign_lib: ?[]const u8 = null, foreign_name: ?[]const u8 = null, /// `extern`-global form `g : T extern [LIB] ["csym"];` — a reference to a /// global defined elsewhere (external linkage, resolved at link time). The /// new extern-named surface; distinct from the legacy `#foreign` path above. /// `extern_lib` is the optional source-library reference and `extern_name` /// the optional symbol-name override (mirroring foreign_lib/foreign_name, so /// `extern` fully supersedes `#foreign`). Parsed in Phase 0.1; not consumed /// by the var-decl path until Phase 1.2. is_extern: bool = false, extern_lib: ?[]const u8 = null, extern_name: ?[]const u8 = null, /// True when the binding name was written as a backtick raw identifier /// (`` `i2 := … ``). A raw name is exempt from the reserved-type-name /// binding check. is_raw: bool = false, }; pub const Assignment = struct { target: *Node, op: Op, value: *Node, pub const Op = enum { assign, add_assign, sub_assign, mul_assign, div_assign, mod_assign, and_assign, or_assign, xor_assign, shl_assign, shr_assign, }; }; pub const MultiAssign = struct { targets: []const *Node, values: []const *Node, }; pub const DestructureDecl = struct { names: []const []const u8, name_spans: []const Span, // one per entry in `names`, same order /// One per entry in `names`, same order: true when that target was a /// backtick raw identifier (`` `i2, b := … ``) — exempt from the /// reserved-type-name binding check. name_is_raw: []const bool, value: *Node, }; pub const EnumDecl = struct { name: []const u8, variant_names: []const []const u8, variant_types: []const ?*Node = &.{}, // null entries = no payload; empty = payload-less enum is_flags: bool = false, variant_values: []const ?*Node = &.{}, // explicit value per variant (null = auto), empty = all auto backing_type: ?*Node = null, // optional backing type: enum u8 { ... } /// True when the declared NAME was a backtick raw identifier /// (`` `i2 :: enum { … } ``) — exempt from the reserved-type-name decl /// check. A bare reserved-name decl still errors. is_raw: bool = false, }; pub const UnionDecl = struct { name: []const u8, field_names: []const []const u8, field_types: []const *Node, /// True when the declared NAME was a backtick raw identifier — exempt from /// the reserved-type-name decl check. is_raw: bool = false, }; /// `Foo :: error { TagA, TagB }` — a named error set. Tags are bare /// identifiers (no payload, no explicit value), unlike enum variants. pub const ErrorSetDecl = struct { name: []const u8, tag_names: []const []const u8, /// True when the declared NAME was a backtick raw identifier — exempt from /// the reserved-type-name decl check. is_raw: bool = false, }; pub const StructTypeParam = struct { name: []const u8, // e.g. "N" or "T" (without $) constraint: *Node, // type_expr: "u32" for value param, "Type" for type param protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable /// `..$Ts: []Type` — a pack type-param binding the remaining type args as a /// sequence (must be last). Field types reference it via `(..$Ts)` etc. is_variadic: bool = false, }; pub const UsingEntry = struct { insert_index: u32, // position in field_names where used fields are spliced type_name: []const u8, // struct type to inline }; pub const StructDecl = struct { name: []const u8, field_names: []const []const u8, field_types: []const *Node, // type_expr nodes field_defaults: []const ?*Node, // default value per field, null if none type_params: []const StructTypeParam = &.{}, using_entries: []const UsingEntry = &.{}, methods: []const *Node = &.{}, // fn_decl nodes for struct methods constants: []const *Node = &.{}, // const_decl nodes for struct-level constants /// True when the declared NAME was a backtick raw identifier /// (`` `i2 :: struct { … } ``) — exempt from the reserved-type-name decl /// check. A bare reserved-name decl still errors. is_raw: bool = false, }; pub const StructFieldInit = struct { name: ?[]const u8, // null for positional, non-null for named/shorthand value: *Node, }; pub const StructLiteral = struct { struct_name: ?[]const u8, // null for anonymous `.{ ... }` type_expr: ?*Node = null, // for GenericType(args).{ ... } field_inits: []const StructFieldInit, init_block: ?*Node = null, // optional `{ stmts }` block after struct literal }; pub const Lambda = struct { params: []const Param, return_type: ?*Node, body: *Node, type_params: []const StructTypeParam = &.{}, call_conv: CallingConvention = .default, }; pub const TypeExpr = struct { name: []const u8, is_generic: bool = false, protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable /// True when written as a backtick raw identifier in type position /// (`` `i2 ``). Such a reference is the LITERAL name `i2` used as a type — /// resolution skips the builtin/reserved classifier and looks up a /// `` `i2 ``-declared type (struct/enum/union/alias), else "unknown /// type". A bare `i2` keeps `is_raw = false` and is the int type. is_raw: bool = false, }; /// `$[]` in type position. Resolves to the i-th /// element type of the active pack binding. Step 3 of the variadic /// heterogeneous type packs feature — used in trampoline bodies, /// generic conversions, struct fields parameterised over the pack. pub const PackIndexTypeExpr = struct { pack_name: []const u8, index: u32, }; /// `$` (no indexing) in expression position. Evaluates /// to a comptime `[]Type` slice — the WHOLE pack as data. Step 4 /// final slice: lets builder fns walk the pack types and emit /// per-position code (the shape step 5's generic Into(Block) needs /// for its trampoline body). pub const ComptimePackRef = struct { pack_name: []const u8, }; pub const DeferStmt = struct { expr: *Node, }; // ── Error handling (ERR stream) ────────────────────────────────────────── /// `raise EXPR;` — terminates control flow like `return`, populating the /// error channel. `tag` is a tag-typed expression: `error.X` (a field /// access on the `error` keyword) or a tag-bound variable (`raise e`). pub const RaiseStmt = struct { tag: *Node, }; /// `try X` — a failable attempt. Unary prefix, binds tighter than any /// binary operator. Sema (E1.4) rejects a non-failable operand. pub const TryExpr = struct { operand: *Node, }; /// `X catch [e] BODY` — inline failure handler (postfix). The binding is a /// bare name (no parens) and optional. Body is a block, a bare expression, /// or — when `is_match_body` — a `match_expr` from the `== { case ... }` /// sugar (whose subject is the binding). pub const CatchExpr = struct { operand: *Node, binding: ?[]const u8 = null, binding_span: ?Span = null, // span of `binding` (set iff `binding` is) /// True when the binding was a backtick raw identifier /// (`` x catch `i2 { … } ``) — exempt from the reserved-type-name check. binding_is_raw: bool = false, body: *Node, is_match_body: bool = false, }; /// `onfail [e] BODY` — cleanup run on error-exit of the enclosing block. /// Binding optional (bare name). Body is a block (`onfail [e] { ... }`) or /// a bare expression (`onfail EXPR;`). pub const OnFailStmt = struct { binding: ?[]const u8 = null, binding_span: ?Span = null, // span of `binding` (set iff `binding` is) /// True when the binding was a backtick raw identifier /// (`` onfail `i2 { … } ``) — exempt from the reserved-type-name check. binding_is_raw: bool = false, body: *Node, }; pub const PushStmt = struct { context_expr: *Node, body: *Node, }; pub const ComptimeExpr = struct { expr: *Node, }; pub const InsertExpr = struct { expr: *Node, }; pub const ReturnStmt = struct { value: ?*Node, }; pub const ImportDecl = struct { path: []const u8, name: ?[]const u8, /// True when the namespace NAME was a backtick raw identifier /// (`` `i2 :: #import "…" ``) — exempt from the reserved-type-name decl /// check. A flat `#import` (name == null) binds nothing. is_raw: bool = false, }; pub const ArrayTypeExpr = struct { length: *Node, // int_literal for the size element_type: *Node, // type_expr for the element type }; pub const SliceTypeExpr = struct { element_type: *Node, // type_expr for the element type }; pub const ArrayLiteral = struct { elements: []const *Node, type_expr: ?*Node = null, }; pub const ParameterizedTypeExpr = struct { name: []const u8, // e.g. "Vector", or later generic struct names args: []const *Node, // e.g. [int_literal(3), type_expr("f32")] /// True when the base name was a backtick raw identifier in type position /// (`` `i2(i64) ``). Such a reference is the LITERAL name `i2` used as a /// parameterized type — resolution skips the builtin parameterized /// classifier (e.g. the `Vector` intrinsic) and instantiates a /// `` `i2 ``-declared generic template. is_raw: bool = false, }; pub const IndexExpr = struct { object: *Node, index: *Node, }; pub const SliceExpr = struct { object: *Node, start: ?*Node = null, // null = 0 end: ?*Node = null, // null = len /// `<..` family — slice begins one past `start`. start_exclusive: bool = false, /// `..=` family — slice includes `end`. end_inclusive: bool = false, }; pub const PointerTypeExpr = struct { pointee_type: *Node, }; pub const ManyPointerTypeExpr = struct { element_type: *Node, }; pub const OptionalTypeExpr = struct { inner_type: *Node, }; /// The error channel of a multi-return result list: bare `!` (inferred /// set) or `!Named` (a declared `error { ... }` set). Appears only as /// the trailing result element; the parser enforces the position and /// sema (E1) restricts it to return positions. pub const ErrorTypeExpr = struct { /// `null` = inferred set (bare `!`); non-null = named set (`!Named`). name: ?[]const u8 = null, }; pub const ForceUnwrap = struct { operand: *Node, }; pub const NullCoalesce = struct { lhs: *Node, rhs: *Node, }; pub const DerefExpr = struct { operand: *Node, }; pub const WhileExpr = struct { condition: *Node, body: *Node, binding_name: ?[]const u8 = null, // for `while val := expr { ... }` optional binding binding_span: ?Span = null, // span of `binding_name` (set iff `binding_name` is) /// True when the optional binding was a backtick raw identifier /// (`` while `i2 := … ``) — exempt from the reserved-type-name check. binding_is_raw: bool = false, }; /// One position of a (possibly multi-iterable) `for` header. pub const ForIterable = struct { /// Collection expression, or the range START for the range forms. expr: *Node, /// Range end. Null for a plain collection AND for the open-ended range /// `a..` (distinguished by `is_range`). range_end: ?*Node = null, /// True for any range form. Each side of `..` takes an optional bound /// marker — `=` inclusive, `<` exclusive — with defaults start-inclusive, /// end-exclusive: `a..b` ≡ `a=.. expr;` arrow body. pub const ForExpr = struct { iterables: []ForIterable, captures: []ForCapture, body: *Node, /// `inline for` — comptime-unrolled (single bounded range, comptime bounds). is_inline: bool = false, }; pub const SpreadExpr = struct { operand: *Node, }; pub const NamespaceDecl = struct { name: []const u8, decls: []const *Node, /// Decls AUTHORED in the namespaced module itself (its `own_decls`), a /// subset of `decls` (which also carries the module's transitive flat /// imports). Lowering registers these under their module-qualified name /// (`ns.fn`) so `pkg.fn(...)` resolves to a unique FuncId distinct from a /// same-named function in another module. own_decls: []const *Node = &.{}, /// The resolved path of the module this alias targets — the importing file's /// own path for a `#import c` namespace (its members are synthesized there). /// Captured at import-resolution time (the `resolved_path` that is otherwise /// not retained on the node) so `buildImportFacts` can record the namespace /// edge without re-walking the import graph. target_module_path: []const u8, /// True when the namespace NAME was a backtick raw identifier — exempt /// from the reserved-type-name decl check. is_raw: bool = false, }; pub const ForeignExpr = struct { library_ref: ?[]const u8 = null, // identifier name of library constant c_name: ?[]const u8 = null, // C symbol name override }; pub const LibraryDecl = struct { lib_name: []const u8, name: []const u8, // sx-side constant name /// True when the constant NAME was a backtick raw identifier — exempt from /// the reserved-type-name decl check. is_raw: bool = false, }; pub const FrameworkDecl = struct { name: []const u8, // framework name, e.g. "Foundation" }; pub const FunctionTypeExpr = struct { param_types: []const *Node, param_names: ?[]const ?[]const u8 = null, // optional documentation names return_type: ?*Node, // null = void return call_conv: CallingConvention = .default, }; pub const ClosureTypeExpr = struct { param_types: []const *Node, param_names: ?[]const ?[]const u8 = null, // optional documentation names return_type: ?*Node, // null = void return /// Variadic heterogeneous type pack trailing the param list. /// `Closure(..$args) -> R` ⇒ pack_name = "args", param_types = []. /// `Closure(Prefix, ..$args)` ⇒ pack_name = "args", param_types = [Prefix]. pack_name: ?[]const u8 = null, /// Projection on the pack: `Closure(..sources.T) -> R` ⇒ pack_name = /// "sources", pack_projection = "T". Null for a bare `..pack`. pack_projection: ?[]const u8 = null, }; pub const TupleTypeExpr = struct { field_types: []const *Node, field_names: ?[]const []const u8, // null for positional }; pub const TupleLiteral = struct { elements: []const TupleElement, }; pub const TupleElement = struct { name: ?[]const u8, // null for positional value: *Node, }; pub const UfcsAlias = struct { name: []const u8, target: []const u8, /// True when the alias NAME was a backtick raw identifier — exempt from /// the reserved-type-name decl check. is_raw: bool = false, }; pub const CImportDecl = struct { includes: []const []const u8, sources: []const []const u8, defines: []const []const u8, flags: []const []const u8, name: ?[]const u8 = null, bitcode_paths: []const []const u8 = &.{}, // populated during import resolution /// True when the namespace NAME was a backtick raw identifier — exempt /// from the reserved-type-name decl check. is_raw: bool = false, }; pub const ProtocolMethodDecl = struct { name: []const u8, params: []const *Node, // type_expr nodes for parameter types (excluding implicit self) param_names: []const []const u8, // parameter names (excluding implicit self) param_name_spans: []const Span = &.{}, // one per `param_names` entry; empty for synthesized methods /// One per `param_names` entry: true when written as a backtick raw /// identifier — exempt from the reserved-type-name check. /// Empty for synthesized methods (treated as all-false). param_name_is_raw: []const bool = &.{}, return_type: ?*Node, // null = void return default_body: ?*Node, // null = required method, non-null = default implementation }; pub const ProtocolDecl = struct { name: []const u8, methods: []const ProtocolMethodDecl, is_inline: bool = false, // #inline — embedded fn ptrs instead of vtable pointer type_params: []const StructTypeParam = &.{}, // for `protocol(Target: Type) { ... }` /// True when the declared NAME was a backtick raw identifier — exempt from /// the reserved-type-name decl check. is_raw: bool = false, /// Defining module path (stamped by `resolveImports`), so a parameterized /// protocol instantiated cross-module resolves its method signature types in /// the module that declares it (E4 — the protocol analog of /// `StructTemplate.source_file`). Null for a synthesized/sourceless decl. source_file: ?[]const u8 = null, }; pub const ForeignRuntime = enum { jni_class, jni_interface, objc_class, objc_protocol, swift_class, swift_struct, swift_protocol, }; pub const ForeignMethodDecl = struct { name: []const u8, params: []const *Node, // type_expr nodes — first is `*Self` for instance methods param_names: []const []const u8, param_name_spans: []const Span = &.{}, // one per `param_names` entry; empty for synthesized methods /// One per `param_names` entry: true when written as a backtick raw /// identifier — exempt from the reserved-type-name check. /// Empty for synthesized methods (treated as all-false). param_name_is_raw: []const bool = &.{}, return_type: ?*Node, // null = void is_static: bool = false, // true for `static name :: ...` jni_descriptor_override: ?[]const u8 = null, // `#jni_method_descriptor("(Sig)Ret")` — JNI runtime only selector_override: ?[]const u8 = null, // `#selector("explicit:string")` — Obj-C runtime only (Phase 3.2) body: ?*Node = null, // sx-side implementation (defined-class only). null = `;`-terminated decl referencing inherited / external method. }; pub const ForeignFieldDecl = struct { name: []const u8, field_type: *Node, // type_expr node /// True iff the declaration carries a `#property[(...)]` directive /// (M2.2). For foreign classes, that means synthesize getter/setter /// dispatch through `objc_msgSend`; for sx-defined classes it adds /// runtime-introspectable property metadata + ARC-aware setter /// emission (Month 4 wires the latter). is_property: bool = false, /// Comma-separated modifier names from `#property(strong, weak, ...)`. /// Stored verbatim; semantic interpretation lands in M4.2. property_modifiers: []const []const u8 = &.{}, }; pub const ForeignClassMember = union(enum) { method: ForeignMethodDecl, field: ForeignFieldDecl, // JNI runtime only (sema-checked in later step) extends: []const u8, // sx-side alias name (right of `#extends`) implements: []const u8, // sx-side alias name (right of `#implements`) }; pub const ForeignClassDecl = struct { name: []const u8, // sx-side alias (left of `::`) foreign_path: []const u8, // directive arg: "java/path/Foo" / "NSString" / "Foundation.URL" runtime: ForeignRuntime, members: []const ForeignClassMember = &.{}, is_foreign: bool = false, // `#foreign #...` prefix — class is provided by the foreign runtime; we only reference it is_main: bool = false, // `#jni_main` / `#objc_main` — class is the launchable entry (Activity / UIApplicationDelegate / ...) /// True when the sx-side alias NAME was a backtick raw identifier — exempt /// from the reserved-type-name decl check. is_raw: bool = false, /// Defining module path (stamped by `resolveImports`), so the IMP trampolines /// emitted for an sx-defined class resolve their method-signature types in the /// module that declares the class — not the (cross-module) lowering site that /// happens to trigger emission (E4). Null for a synthesized/sourceless decl. source_file: ?[]const u8 = null, }; pub const JniEnvBlock = struct { env: *Node, // expression yielding the *JNIEnv for this scope body: *Node, // block (or expression) — runs with `env` scoped via TL push/pop }; pub const ImplBlock = struct { protocol_name: []const u8, target_type: []const u8, target_type_params: []const StructTypeParam = &.{}, // for `impl P for List($T)` methods: []const *Node, // fn_decl nodes protocol_type_args: []const *Node = &.{}, // for `impl Into(Block) for Source` — type args on the protocol side target_type_expr: ?*Node = null, // populated for parameterised-protocol impls; carries non-identifier source spellings (e.g. `Closure() -> void`) };