refactor(ffi-linkage): Phase 9.3-src — purge 'foreign' from src/ comments + a user-facing diagnostic
Reword every 'foreign' comment to the extern/runtime-class vocabulary matching the renamed identifiers (foreign call→extern call, foreign class→runtime class, foreign path→runtime path, the #foreign-literal comment mentions → extern, etc.). Also fixes two USER-FACING issues: the 'expected … #foreign … after type annotation' parse error no longer advertises the removed keyword, and the Android 'no #jni_main' help diagnostic now shows '#jni_class(…) extern' instead of the rejected '#foreign #jni_class'. Removed the now-dead prefix-#foreign-vs-postfix conflict branch in parseRuntimeClassDecl (the caller rejects #foreign before it runs). src/ now contains 'foreign' ONLY in the hash_foreign token machinery + its 4 rejection messages — the deprecation mechanism (kept per the 9.0 recommendation; the message MUST name #foreign to guide migration). Snapshot-neutral; suite green (646 corpus / 444 unit, 0 failed).
This commit is contained in:
14
src/ast.zig
14
src/ast.zig
@@ -124,7 +124,7 @@ pub const CallingConvention = enum { default, c };
|
|||||||
|
|
||||||
/// Linkage modifier written in the postfix slot after `callconv(...)`:
|
/// Linkage modifier written in the postfix slot after `callconv(...)`:
|
||||||
/// `name :: (sig) -> Ret [callconv(.x)] [extern | export] [;|{…}];`
|
/// `name :: (sig) -> Ret [callconv(.x)] [extern | export] [;|{…}];`
|
||||||
/// `extern` = import (external linkage, C ABI, no sx ctx — `#foreign`'s role);
|
/// `extern` = import (external linkage, C ABI, no sx ctx — `extern`'s role);
|
||||||
/// `export` = define + expose (body + external linkage + C ABI + no ctx).
|
/// `export` = define + expose (body + external linkage + C ABI + no ctx).
|
||||||
/// Both imply `callconv(.c)`. Variants carry a trailing `_` to dodge the Zig
|
/// Both imply `callconv(.c)`. Variants carry a trailing `_` to dodge the Zig
|
||||||
/// keywords. `.none` = no linkage modifier (the ordinary sx-internal decl).
|
/// keywords. `.none` = no linkage modifier (the ordinary sx-internal decl).
|
||||||
@@ -143,20 +143,20 @@ pub const FnDecl = struct {
|
|||||||
/// Parsed in Phase 0.1; not consumed by the fn-decl path until Phase 1.
|
/// Parsed in Phase 0.1; not consumed by the fn-decl path until Phase 1.
|
||||||
extern_export: ExternExportModifier = .none,
|
extern_export: ExternExportModifier = .none,
|
||||||
/// Optional library reference + symbol-name override for an `extern`/`export`
|
/// Optional library reference + symbol-name override for an `extern`/`export`
|
||||||
/// function, mirroring `#foreign LIB "csym"` (foreign_lib/foreign_name). Both
|
/// function, the optional library + symbol-name override. Both
|
||||||
/// optional: `extern` alone resolves the sx name against the default-linked
|
/// optional: `extern` alone resolves the sx name against the default-linked
|
||||||
/// libs; `extern LIB` names the source library; `extern "csym"` renames the
|
/// libs; `extern LIB` names the source library; `extern "csym"` renames the
|
||||||
/// symbol. Required for `extern` to be a behavior-equivalent superset of
|
/// symbol. Required for `extern` to be a behavior-equivalent superset of
|
||||||
/// `#foreign` (Gate A→B) — the migration of 466 `#foreign` uses across 6 libs
|
/// `extern` (Gate A→B) — the migration of 466 `extern` uses across 6 libs
|
||||||
/// must preserve each symbol's library. Parsed/consumed in Phase 1.2.
|
/// must preserve each symbol's library. Parsed/consumed in Phase 1.2.
|
||||||
extern_lib: ?[]const u8 = null,
|
extern_lib: ?[]const u8 = null,
|
||||||
extern_name: ?[]const u8 = null,
|
extern_name: ?[]const u8 = null,
|
||||||
/// Span of the function's name token, for the reserved-type-name decl
|
/// Span of the function's name token, for the reserved-type-name decl
|
||||||
/// diagnostic. Synthesized decls (e.g. `#import c` foreign
|
/// diagnostic. Synthesized decls (e.g. `#import c` extern
|
||||||
/// functions, lowering-time objc/protocol method synthesis) leave it zero.
|
/// functions, lowering-time objc/protocol method synthesis) leave it zero.
|
||||||
name_span: Span = .{ .start = 0, .end = 0 },
|
name_span: Span = .{ .start = 0, .end = 0 },
|
||||||
/// True when the function NAME was written as a backtick raw identifier
|
/// True when the function NAME was written as a backtick raw identifier
|
||||||
/// (`` `i2 :: … ``) or synthesized by a `#import c` foreign decl. A raw
|
/// (`` `i2 :: … ``) or synthesized by a `#import c` extern decl. A raw
|
||||||
/// name is exempt from the reserved-type-name binding check.
|
/// name is exempt from the reserved-type-name binding check.
|
||||||
/// Every PARSER fn_decl is built through `parseFnDecl`, whose `name_is_raw`
|
/// 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
|
/// is a REQUIRED parameter, so a parser site cannot drop it; the default
|
||||||
@@ -185,7 +185,7 @@ pub const Param = struct {
|
|||||||
/// parameter, lowering substitutes this expression in its place.
|
/// parameter, lowering substitutes this expression in its place.
|
||||||
default_expr: ?*Node = null,
|
default_expr: ?*Node = null,
|
||||||
/// True when the param name was written as a backtick raw identifier
|
/// 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
|
/// (`` `i2 ``) or synthesized by a `#import c` extern decl. A raw name is
|
||||||
/// exempt from the reserved-type-name binding check.
|
/// exempt from the reserved-type-name binding check.
|
||||||
is_raw: bool = false,
|
is_raw: bool = false,
|
||||||
};
|
};
|
||||||
@@ -866,7 +866,7 @@ pub const RuntimeFieldDecl = struct {
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
field_type: *Node, // type_expr node
|
field_type: *Node, // type_expr node
|
||||||
/// True iff the declaration carries a `#property[(...)]` directive
|
/// True iff the declaration carries a `#property[(...)]` directive
|
||||||
/// (M2.2). For foreign classes, that means synthesize getter/setter
|
/// (M2.2). For runtime classes, that means synthesize getter/setter
|
||||||
/// dispatch through `objc_msgSend`; for sx-defined classes it adds
|
/// dispatch through `objc_msgSend`; for sx-defined classes it adds
|
||||||
/// runtime-introspectable property metadata + ARC-aware setter
|
/// runtime-introspectable property metadata + ARC-aware setter
|
||||||
/// emission (Month 4 wires the latter).
|
/// emission (Month 4 wires the latter).
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ pub const Ops = struct {
|
|||||||
pub fn emitObjcMsgSend(self: Ops, instruction: *const Inst, msg: ObjcMsgSend) void {
|
pub fn emitObjcMsgSend(self: Ops, instruction: *const Inst, msg: ObjcMsgSend) void {
|
||||||
const msg_send = self.e.getObjcMsgSendValue();
|
const msg_send = self.e.getObjcMsgSendValue();
|
||||||
// Detect the sret case: >16 B non-HFA struct return.
|
// Detect the sret case: >16 B non-HFA struct return.
|
||||||
// Same predicate as the plain-foreign-call path so the
|
// Same predicate as the plain-extern-call path so the
|
||||||
// two arms stay in lockstep.
|
// two arms stay in lockstep.
|
||||||
const raw_ret_ty = self.e.toLLVMType(instruction.ty);
|
const raw_ret_ty = self.e.toLLVMType(instruction.ty);
|
||||||
const uses_sret = self.e.needsByval(instruction.ty, raw_ret_ty);
|
const uses_sret = self.e.needsByval(instruction.ty, raw_ret_ty);
|
||||||
@@ -516,7 +516,7 @@ pub const Ops = struct {
|
|||||||
call_args[sret_off + 1] = self.e.coerceArg(self.e.resolveRef(msg.sel), self.e.cached_ptr);
|
call_args[sret_off + 1] = self.e.coerceArg(self.e.resolveRef(msg.sel), self.e.cached_ptr);
|
||||||
// additional args take their IR types, with ABI
|
// additional args take their IR types, with ABI
|
||||||
// coercion applied so structs / strings decay the
|
// coercion applied so structs / strings decay the
|
||||||
// same way they do for any C foreign call.
|
// same way they do for any C extern call.
|
||||||
for (msg.args, 0..) |arg_ref, i| {
|
for (msg.args, 0..) |arg_ref, i| {
|
||||||
const raw_ty = self.e.argIRTypeOrFail(arg_ref);
|
const raw_ty = self.e.argIRTypeOrFail(arg_ref);
|
||||||
const raw_llvm = self.e.toLLVMType(raw_ty);
|
const raw_llvm = self.e.toLLVMType(raw_ty);
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ pub fn processCImport(
|
|||||||
// Native C compilation (compile to .o, not LLVM module)
|
// Native C compilation (compile to .o, not LLVM module)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// ── #foreign ref validation ───────────────────────────────────────────
|
// ── extern ref validation ───────────────────────────────────────────
|
||||||
|
|
||||||
fn collectExternRefTargets(valid: *std.StringHashMap(void), decls: []const *Node) !void {
|
fn collectExternRefTargets(valid: *std.StringHashMap(void), decls: []const *Node) !void {
|
||||||
for (decls) |d| {
|
for (decls) |d| {
|
||||||
@@ -349,7 +349,7 @@ fn checkExternRefs(valid: *const std.StringHashMap(void), decls: []const *Node,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate every `#foreign <ref>` in the merged program: the ref must
|
/// Validate every `extern <ref>` in the merged program: the ref must
|
||||||
/// name a `#library` constant or a NAMED `#import c` unit. Refs are
|
/// name a `#library` constant or a NAMED `#import c` unit. Refs are
|
||||||
/// author-side module-local names, but modules merge flat or
|
/// author-side module-local names, but modules merge flat or
|
||||||
/// namespaced into one tree, so existence is checked program-wide.
|
/// namespaced into one tree, so existence is checked program-wide.
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ pub const Compilation = struct {
|
|||||||
pub fn lowerToIR(self: *Compilation) !ir.Module {
|
pub fn lowerToIR(self: *Compilation) !ir.Module {
|
||||||
const root = self.resolved_root orelse self.root orelse return ir.Module.init(self.allocator);
|
const root = self.resolved_root orelse self.root orelse return ir.Module.init(self.allocator);
|
||||||
|
|
||||||
// Every `#foreign <ref>` must name a #library constant or a named
|
// Every `extern <ref>` must name a #library constant or a named
|
||||||
// `#import c` unit — a typo'd ref otherwise resolves silently
|
// `#import c` unit — a typo'd ref otherwise resolves silently
|
||||||
// through whatever image happens to carry the symbol.
|
// through whatever image happens to carry the symbol.
|
||||||
try c_import.validateExternRefs(self.allocator, root, &self.diagnostics);
|
try c_import.validateExternRefs(self.allocator, root, &self.diagnostics);
|
||||||
|
|||||||
@@ -366,8 +366,8 @@ pub const ResolvedModule = struct {
|
|||||||
/// types/aliases and supports same-name value consts (step E5). Everything
|
/// types/aliases and supports same-name value consts (step E5). Everything
|
||||||
/// else keeps the first-wins name-merge: FUNCTIONS (the shadowed
|
/// else keeps the first-wins name-merge: FUNCTIONS (the shadowed
|
||||||
/// author stays reachable via its qualified name / SelectedFunc), and crucially
|
/// author stays reachable via its qualified name / SelectedFunc), and crucially
|
||||||
/// `var_decl`s, including a `#foreign` extern global declared in two files
|
/// `var_decl`s, including a `extern` extern global declared in two files
|
||||||
/// (e.g. `__stdinp : *void #foreign;`) that MUST resolve to the ONE libSystem
|
/// (e.g. `__stdinp : *void extern;`) that MUST resolve to the ONE libSystem
|
||||||
/// symbol, not split into a duplicate `__stdinp.1`.
|
/// symbol, not split into a duplicate `__stdinp.1`.
|
||||||
fn isPerSourceDecl(decl: *const Node) bool {
|
fn isPerSourceDecl(decl: *const Node) bool {
|
||||||
return switch (decl.data) {
|
return switch (decl.data) {
|
||||||
@@ -852,7 +852,7 @@ fn stampStructMethodSources(sd: ast.StructDecl, file_path: []const u8) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stamp the defining module path onto every bodied method of an sx-defined
|
/// Stamp the defining module path onto every bodied method of an sx-defined
|
||||||
/// foreign class, so the method's sx body lowers in the class's own module.
|
/// runtime class, so the method's sx body lowers in the class's own module.
|
||||||
fn stampRuntimeClassMethodSources(fcd: ast.RuntimeClassDecl, file_path: []const u8) void {
|
fn stampRuntimeClassMethodSources(fcd: ast.RuntimeClassDecl, file_path: []const u8) void {
|
||||||
for (fcd.members) |m| {
|
for (fcd.members) |m| {
|
||||||
if (m == .method) {
|
if (m == .method) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// 2. The `CallPlan` object built by `CallResolver.plan` — its selected
|
// 2. The `CallPlan` object built by `CallResolver.plan` — its selected
|
||||||
// kind / target / variant and the receiver / `__sx_ctx` / default-arg
|
// kind / target / variant and the receiver / `__sx_ctx` / default-arg
|
||||||
// properties, across every call form pinned by A3.2 sub-step 1
|
// properties, across every call form pinned by A3.2 sub-step 1
|
||||||
// (direct / UFCS / protocol / closure / fn-pointer / foreign / enum /
|
// (direct / UFCS / protocol / closure / fn-pointer / extern / enum /
|
||||||
// namespace). `resultType` is just `plan(c).return_type`, so these also
|
// namespace). `resultType` is just `plan(c).return_type`, so these also
|
||||||
// lock the typing the regression suite relies on.
|
// lock the typing the regression suite relies on.
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ test "plan: struct (UFCS) method via #compiler dispatch + prepends receiver" {
|
|||||||
try std.testing.expect(p.prepends_receiver);
|
try std.testing.expect(p.prepends_receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "plan: foreign-class instance vs static dispatch" {
|
test "plan: runtime-class instance vs static dispatch" {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
const alloc = arena.allocator();
|
||||||
|
|||||||
@@ -92,13 +92,13 @@ pub const CallPlan = struct {
|
|||||||
/// `Lowering.inferExprType`'s call arm. Discovers the IR type a call
|
/// `Lowering.inferExprType`'s call arm. Discovers the IR type a call
|
||||||
/// expression evaluates to — across builtins / reflection builtins, generic
|
/// expression evaluates to — across builtins / reflection builtins, generic
|
||||||
/// and plain free functions (lowered or lazy via `fn_ast_map`), closure /
|
/// and plain free functions (lowered or lazy via `fn_ast_map`), closure /
|
||||||
/// function-typed locals, protocol dispatch, foreign-class instance/static
|
/// function-typed locals, protocol dispatch, runtime-class instance/static
|
||||||
/// methods, struct (UFCS) methods, qualified namespace calls, and
|
/// methods, struct (UFCS) methods, qualified namespace calls, and
|
||||||
/// enum/tagged-union construction.
|
/// enum/tagged-union construction.
|
||||||
///
|
///
|
||||||
/// A `*Lowering` facade (Principle 5, like `ExprTyper` / `PackResolver`): call
|
/// A `*Lowering` facade (Principle 5, like `ExprTyper` / `PackResolver`): call
|
||||||
/// typing reads live lexical-scope / target-type state and the function /
|
/// typing reads live lexical-scope / target-type state and the function /
|
||||||
/// foreign-class / protocol resolver helpers, so it borrows `*Lowering` rather
|
/// runtime-class / protocol resolver helpers, so it borrows `*Lowering` rather
|
||||||
/// than re-threading every field.
|
/// than re-threading every field.
|
||||||
pub const CallResolver = struct {
|
pub const CallResolver = struct {
|
||||||
l: *Lowering,
|
l: *Lowering,
|
||||||
@@ -159,7 +159,7 @@ pub const CallResolver = struct {
|
|||||||
// Plain bare same-name flat collision (R5 §C): route through the ONE
|
// Plain bare same-name flat collision (R5 §C): route through the ONE
|
||||||
// author producer `selectedFreeAuthor` so `plan` types the call as the
|
// author producer `selectedFreeAuthor` so `plan` types the call as the
|
||||||
// SAME author the lowering call-path binds — they can no longer
|
// SAME author the lowering call-path binds — they can no longer
|
||||||
// disagree. A generic / foreign / builtin author is not
|
// disagree. A generic / extern / builtin author is not
|
||||||
// plain-free so the producer returns `.none`; `.ambiguous` / `.none`
|
// plain-free so the producer returns `.none`; `.ambiguous` / `.none`
|
||||||
// fall through to the first-wins path below, byte-for-byte.
|
// fall through to the first-wins path below, byte-for-byte.
|
||||||
switch (self.selectedFreeAuthor(c)) {
|
switch (self.selectedFreeAuthor(c)) {
|
||||||
@@ -386,7 +386,7 @@ pub const CallResolver = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Type.variant(args) — qualified construction; foreign static; or a
|
// Type.variant(args) — qualified construction; runtime static; or a
|
||||||
// qualified namespace function. Reached for namespace / type
|
// qualified namespace function. Reached for namespace / type
|
||||||
// prefixes (and inert for value receivers handled above).
|
// prefixes (and inert for value receivers handled above).
|
||||||
const type_name = switch (cfa.object.data) {
|
const type_name = switch (cfa.object.data) {
|
||||||
@@ -506,7 +506,7 @@ pub const CallResolver = struct {
|
|||||||
/// name is never a same-name free-fn collision → `.none`.
|
/// name is never a same-name free-fn collision → `.none`.
|
||||||
/// - field-access callee with a VALUE receiver: a free-function UFCS
|
/// - field-access callee with a VALUE receiver: a free-function UFCS
|
||||||
/// (`recv.fn(args)`). A namespace / type prefix receiver → `.none`. The
|
/// (`recv.fn(args)`). A namespace / type prefix receiver → `.none`. The
|
||||||
/// verdict over-selects a struct-method / protocol / foreign call whose
|
/// verdict over-selects a struct-method / protocol / extern call whose
|
||||||
/// field happens to name a free fn, but those dispatch BEFORE the free-fn
|
/// field happens to name a free fn, but those dispatch BEFORE the free-fn
|
||||||
/// UFCS path in both `plan` and `lowerCall`, so the verdict is consumed only
|
/// UFCS path in both `plan` and `lowerCall`, so the verdict is consumed only
|
||||||
/// when the call truly is a free-fn UFCS.
|
/// when the call truly is a free-fn UFCS.
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ test "emit: type conversion toLLVMType" {
|
|||||||
|
|
||||||
// ── A7.1 scaffolding: ABI param coercion ────────────────────────────
|
// ── A7.1 scaffolding: ABI param coercion ────────────────────────────
|
||||||
// Lock the C-ABI struct-coercion buckets (abiCoerceParamType / needsByval),
|
// Lock the C-ABI struct-coercion buckets (abiCoerceParamType / needsByval),
|
||||||
// which feed callconv(.c) / #foreign signatures, before they move to
|
// which feed callconv(.c) / #extern signatures, before they move to
|
||||||
// src/backend/llvm/abi.zig in A7.1 sub-step 2.
|
// src/backend/llvm/abi.zig in A7.1 sub-step 2.
|
||||||
|
|
||||||
const llvm = @import("../llvm_api.zig");
|
const llvm = @import("../llvm_api.zig");
|
||||||
@@ -1103,7 +1103,7 @@ test "emit: ERR E3.0 — no DWARF without a debug context (unit-test default)" {
|
|||||||
// JNI Call<Type>Method / non-virtual / constructor). A ref it cannot resolve is
|
// JNI Call<Type>Method / non-virtual / constructor). A ref it cannot resolve is
|
||||||
// a codegen invariant violation; it must surface the dedicated `.unresolved`
|
// a codegen invariant violation; it must surface the dedicated `.unresolved`
|
||||||
// tripwire sentinel (which `toLLVMType` hard-panics on) rather than the old
|
// tripwire sentinel (which `toLLVMType` hard-panics on) rather than the old
|
||||||
// silent `.void` default that would emit a void-typed foreign-call argument.
|
// silent `.void` default that would emit a void-typed extern-call argument.
|
||||||
test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref (issue 0074)" {
|
test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref (issue 0074)" {
|
||||||
const alloc = std.testing.allocator;
|
const alloc = std.testing.allocator;
|
||||||
var module = Module.init(alloc);
|
var module = Module.init(alloc);
|
||||||
@@ -1136,7 +1136,7 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
|
|||||||
|
|
||||||
// Fail-before: the old `getRefIRType(arg) orelse .void` would silently
|
// Fail-before: the old `getRefIRType(arg) orelse .void` would silently
|
||||||
// yield `.void` here — a real, load-bearing type that downstream ABI
|
// yield `.void` here — a real, load-bearing type that downstream ABI
|
||||||
// coercion treats as a legitimate (void-typed) foreign argument.
|
// coercion treats as a legitimate (void-typed) extern argument.
|
||||||
try std.testing.expectEqual(TypeId.void, emitter.getRefIRType(bogus) orelse TypeId.void);
|
try std.testing.expectEqual(TypeId.void, emitter.getRefIRType(bogus) orelse TypeId.void);
|
||||||
|
|
||||||
// Pass-after: the helper returns the dedicated `.unresolved` sentinel,
|
// Pass-after: the helper returns the dedicated `.unresolved` sentinel,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const Value = interp_mod.Value;
|
|||||||
|
|
||||||
// The vendored error-trace ring buffer (library/vendors/sx_trace_runtime/sx_trace.c)
|
// The vendored error-trace ring buffer (library/vendors/sx_trace_runtime/sx_trace.c)
|
||||||
// is linked into the compiler. Comptime `#run` evaluation pushes frames to it via
|
// is linked into the compiler. Comptime `#run` evaluation pushes frames to it via
|
||||||
// foreign `sx_trace_push` calls; after a `#run` we read it here to render the
|
// extern `sx_trace_push` calls; after a `#run` we read it here to render the
|
||||||
// return trace for an escaping comptime error (E5.2).
|
// return trace for an escaping comptime error (E5.2).
|
||||||
extern fn sx_trace_len() u32;
|
extern fn sx_trace_len() u32;
|
||||||
extern fn sx_trace_frame_at(i: u32) u64;
|
extern fn sx_trace_frame_at(i: u32) u64;
|
||||||
@@ -371,7 +371,7 @@ pub const LLVMEmitter = struct {
|
|||||||
self.ffiCtors().emitObjcSelectorInit();
|
self.ffiCtors().emitObjcSelectorInit();
|
||||||
|
|
||||||
// Pass 2.5b: Emit Obj-C class-pair registration constructor for
|
// Pass 2.5b: Emit Obj-C class-pair registration constructor for
|
||||||
// sx-defined classes (M1.2 A.4+). Runs BEFORE the foreign
|
// sx-defined classes (M1.2 A.4+). Runs BEFORE the runtime
|
||||||
// class-cache populator (2.5c) so a sx-defined class is already
|
// class-cache populator (2.5c) so a sx-defined class is already
|
||||||
// registered with the Obj-C runtime by the time
|
// registered with the Obj-C runtime by the time
|
||||||
// `objc_getClass(\"SxFoo\")` runs to populate the Phase 3.1
|
// `objc_getClass(\"SxFoo\")` runs to populate the Phase 3.1
|
||||||
@@ -888,7 +888,7 @@ pub const LLVMEmitter = struct {
|
|||||||
|
|
||||||
const llvm_global = c.LLVMAddGlobal(self.llvm_module, llvm_ty, name_z.ptr);
|
const llvm_global = c.LLVMAddGlobal(self.llvm_module, llvm_ty, name_z.ptr);
|
||||||
|
|
||||||
// Extern globals (`<name> : <type> #foreign;`) resolve at link time
|
// Extern globals (`<name> : <type> extern;`) resolve at link time
|
||||||
// to a libSystem / framework symbol — no initializer, default linkage.
|
// to a libSystem / framework symbol — no initializer, default linkage.
|
||||||
if (global.is_extern) {
|
if (global.is_extern) {
|
||||||
c.LLVMSetLinkage(llvm_global, c.LLVMExternalLinkage);
|
c.LLVMSetLinkage(llvm_global, c.LLVMExternalLinkage);
|
||||||
@@ -1242,7 +1242,7 @@ pub const LLVMEmitter = struct {
|
|||||||
// main always returns i32 at the LLVM level (JIT expects it)
|
// main always returns i32 at the LLVM level (JIT expects it)
|
||||||
const raw_ret_ty = self.toLLVMType(func.ret);
|
const raw_ret_ty = self.toLLVMType(func.ret);
|
||||||
const needs_c_abi = func.is_extern or func.call_conv == .c;
|
const needs_c_abi = func.is_extern or func.call_conv == .c;
|
||||||
// A foreign `-> string` / `-> ?string` receives ONE `char *` from C;
|
// An extern `-> string` / `-> ?string` receives ONE `char *` from C;
|
||||||
// the fat sx value is synthesized at the call site (emitCall's
|
// the fat sx value is synthesized at the call site (emitCall's
|
||||||
// cstrReturnToSx). Never sret — the C callee knows nothing about an
|
// cstrReturnToSx). Never sret — the C callee knows nothing about an
|
||||||
// out-pointer.
|
// out-pointer.
|
||||||
@@ -1259,7 +1259,7 @@ pub const LLVMEmitter = struct {
|
|||||||
else if (needs_c_abi) self.abiCoerceParamTypeEx(func.ret, raw_ret_ty, func.is_extern)
|
else if (needs_c_abi) self.abiCoerceParamTypeEx(func.ret, raw_ret_ty, func.is_extern)
|
||||||
else raw_ret_ty;
|
else raw_ret_ty;
|
||||||
|
|
||||||
// Build parameter types — apply C ABI coercion for foreign/callconv(.c) functions.
|
// Build parameter types — apply C ABI coercion for extern/callconv(.c) functions.
|
||||||
// When uses_sret, prepend the sret pointer at index 0.
|
// When uses_sret, prepend the sret pointer at index 0.
|
||||||
const sret_offset: usize = if (uses_sret) 1 else 0;
|
const sret_offset: usize = if (uses_sret) 1 else 0;
|
||||||
const param_count: c_uint = @intCast(func.params.len + sret_offset);
|
const param_count: c_uint = @intCast(func.params.len + sret_offset);
|
||||||
@@ -1322,7 +1322,7 @@ pub const LLVMEmitter = struct {
|
|||||||
// Apple ARM64 ABI for >16B non-HFA composites: pass by reference
|
// Apple ARM64 ABI for >16B non-HFA composites: pass by reference
|
||||||
// via a pointer in the next int register (NOT via LLVM's `byval`
|
// via a pointer in the next int register (NOT via LLVM's `byval`
|
||||||
// attribute, which lowers the struct on the stack — incompatible
|
// attribute, which lowers the struct on the stack — incompatible
|
||||||
// with what `clang` emits and what foreign C callees expect).
|
// with what `clang` emits and what extern C callees expect).
|
||||||
// abiCoerceParamType returned `ptr` for these slots, so the formal
|
// abiCoerceParamType returned `ptr` for these slots, so the formal
|
||||||
// param IS a plain pointer; the prologue loads the struct back.
|
// param IS a plain pointer; the prologue loads the struct back.
|
||||||
|
|
||||||
@@ -2267,7 +2267,7 @@ pub const LLVMEmitter = struct {
|
|||||||
return .none;
|
return .none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the sx-level value for a foreign call that returned a `char *`:
|
/// Build the sx-level value for a extern call that returned a `char *`:
|
||||||
/// `{ptr, strlen(ptr)}` for `string` (NULL → `{null, 0}`), wrapped in
|
/// `{ptr, strlen(ptr)}` for `string` (NULL → `{null, 0}`), wrapped in
|
||||||
/// `{string, i1}` with `has = ptr != null` for `?string`. The strlen call
|
/// `{string, i1}` with `has = ptr != null` for `?string`. The strlen call
|
||||||
/// is branch-guarded — `select` would evaluate `strlen(NULL)`.
|
/// is branch-guarded — `select` would evaluate `strlen(NULL)`.
|
||||||
@@ -2452,12 +2452,12 @@ pub const LLVMEmitter = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the IR type of a foreign-call argument ref. Every FFI arg ref is
|
/// Resolve the IR type of a extern-call argument ref. Every FFI arg ref is
|
||||||
/// a real function param or block instruction result, so a `null` here is a
|
/// a real function param or block instruction result, so a `null` here is a
|
||||||
/// codegen invariant violation, not a recoverable case: return the dedicated
|
/// codegen invariant violation, not a recoverable case: return the dedicated
|
||||||
/// `.unresolved` sentinel — never `.void`/`.i64` — so the failure cannot be
|
/// `.unresolved` sentinel — never `.void`/`.i64` — so the failure cannot be
|
||||||
/// mistaken for a real type and trips `toLLVMType`'s hard tripwire at the call
|
/// mistaken for a real type and trips `toLLVMType`'s hard tripwire at the call
|
||||||
/// site instead of silently emitting a void-typed foreign argument.
|
/// site instead of silently emitting a void-typed extern argument.
|
||||||
pub fn argIRTypeOrFail(self: *LLVMEmitter, arg_ref: Ref) TypeId {
|
pub fn argIRTypeOrFail(self: *LLVMEmitter, arg_ref: Ref) TypeId {
|
||||||
return self.getRefIRType(arg_ref) orelse .unresolved;
|
return self.getRefIRType(arg_ref) orelse .unresolved;
|
||||||
}
|
}
|
||||||
@@ -2519,7 +2519,7 @@ pub const LLVMEmitter = struct {
|
|||||||
return self.typeLowering().toLLVMType(ty);
|
return self.typeLowering().toLLVMType(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── C ABI coercion for foreign functions ──────────────────────────
|
// ── C ABI coercion for extern functions ──────────────────────────
|
||||||
// The coercion logic lives in `backend/llvm/abi.zig` (`AbiLowering`);
|
// The coercion logic lives in `backend/llvm/abi.zig` (`AbiLowering`);
|
||||||
// these stay the facade entry points (callers in signature/call emission +
|
// these stay the facade entry points (callers in signature/call emission +
|
||||||
// the block-trampoline path use abiCoerceParamTypeEx directly).
|
// the block-trampoline path use abiCoerceParamTypeEx directly).
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ pub const ObjcLowering = struct {
|
|||||||
.isize => try out.append(self.l.alloc, 'q'),
|
.isize => try out.append(self.l.alloc, 'q'),
|
||||||
.usize => try out.append(self.l.alloc, 'Q'),
|
.usize => try out.append(self.l.alloc, 'Q'),
|
||||||
.pointer => |p| {
|
.pointer => |p| {
|
||||||
// Pointer to a foreign Obj-C class (or sx-defined #objc_class)
|
// Pointer to a runtime Obj-C class (or sx-defined #objc_class)
|
||||||
// encodes as `@`. Anything else falls to `^v` — generic
|
// encodes as `@`. Anything else falls to `^v` — generic
|
||||||
// pointer; the runtime treats it as opaque.
|
// pointer; the runtime treats it as opaque.
|
||||||
const pointee_info = self.l.module.types.get(p.pointee);
|
const pointee_info = self.l.module.types.get(p.pointee);
|
||||||
@@ -378,7 +378,7 @@ pub const ObjcLowering = struct {
|
|||||||
const pointee = self.l.module.types.get(field_ty).pointer.pointee;
|
const pointee = self.l.module.types.get(field_ty).pointer.pointee;
|
||||||
// `*void` is NOT considered an object pointer — ambiguous.
|
// `*void` is NOT considered an object pointer — ambiguous.
|
||||||
if (pointee == .void) break :blk false;
|
if (pointee == .void) break :blk false;
|
||||||
// `*T` where T is a foreign-class struct (Obj-C class).
|
// `*T` where T is a runtime-class struct (Obj-C class).
|
||||||
if (pointee.isBuiltin()) break :blk false;
|
if (pointee.isBuiltin()) break :blk false;
|
||||||
const pointee_info = self.l.module.types.get(pointee);
|
const pointee_info = self.l.module.types.get(pointee);
|
||||||
if (pointee_info != .@"struct") break :blk false;
|
if (pointee_info != .@"struct") break :blk false;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ pub fn callVoidRet(symbol: *anyopaque, args: []const usize) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Variadic cdecl dispatch ─────────────────────────────────────────
|
// ── Variadic cdecl dispatch ─────────────────────────────────────────
|
||||||
// For foreign functions declared with `args: ..T` (C-variadic, e.g.
|
// For extern functions declared with `args: ..T` (C-variadic, e.g.
|
||||||
// libc `open(path, flags, ...)`). The trailing args must be passed
|
// libc `open(path, flags, ...)`). The trailing args must be passed
|
||||||
// through the C-variadic ABI — arm64 places them on the stack rather
|
// through the C-variadic ABI — arm64 places them on the stack rather
|
||||||
// than in argument registers. Calling a variadic function as if it
|
// than in argument registers. Calling a variadic function as if it
|
||||||
|
|||||||
@@ -498,7 +498,7 @@ pub const Function = struct {
|
|||||||
linkage: Linkage = .internal,
|
linkage: Linkage = .internal,
|
||||||
call_conv: CallingConvention = .default,
|
call_conv: CallingConvention = .default,
|
||||||
source_file: ?[]const u8 = null,
|
source_file: ?[]const u8 = null,
|
||||||
/// Variadic tail at the IR signature level. Only `#foreign` decls reach
|
/// Variadic tail at the IR signature level. Only `extern` decls reach
|
||||||
/// IR with this set — sx-side `..T` params are slice-packed before
|
/// IR with this set — sx-side `..T` params are slice-packed before
|
||||||
/// lowering, so anything that survives is the C calling convention's
|
/// lowering, so anything that survives is the C calling convention's
|
||||||
/// `...`. emit_llvm passes `is_var_arg=1` to `LLVMFunctionType`; call
|
/// `...`. emit_llvm passes `is_var_arg=1` to `LLVMFunctionType`; call
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ pub const Interpreter = struct {
|
|||||||
max_call_depth: u32 = 256,
|
max_call_depth: u32 = 256,
|
||||||
/// Active sx call-frame chain (oldest→newest), maintained across `call` for
|
/// Active sx call-frame chain (oldest→newest), maintained across `call` for
|
||||||
/// `trace.print_interpreter_frames()` (ERR E4.1). Only sx-bodied frames are
|
/// `trace.print_interpreter_frames()` (ERR E4.1). Only sx-bodied frames are
|
||||||
/// tracked — foreign calls return before the frame is pushed.
|
/// tracked — extern calls return before the frame is pushed.
|
||||||
call_chain: std.ArrayList(FuncId) = .empty,
|
call_chain: std.ArrayList(FuncId) = .empty,
|
||||||
|
|
||||||
/// File → source text (the diagnostics' import_sources). Set by the host
|
/// File → source text (the diagnostics' import_sources). Set by the host
|
||||||
@@ -218,7 +218,7 @@ pub const Interpreter = struct {
|
|||||||
return error.CannotEvalComptime;
|
return error.CannotEvalComptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `bailDetail` but returns a `TypeError` — for foreign-arg
|
/// Like `bailDetail` but returns a `TypeError` — for extern-arg
|
||||||
/// marshalling sites that previously erased the reason.
|
/// marshalling sites that previously erased the reason.
|
||||||
fn typeErrorDetail(comptime msg: []const u8) InterpError {
|
fn typeErrorDetail(comptime msg: []const u8) InterpError {
|
||||||
if (last_bail_detail == null) last_bail_detail = msg;
|
if (last_bail_detail == null) last_bail_detail = msg;
|
||||||
@@ -258,7 +258,7 @@ pub const Interpreter = struct {
|
|||||||
|
|
||||||
/// Write `val` to the raw host address `addr` using exactly the
|
/// Write `val` to the raw host address `addr` using exactly the
|
||||||
/// number of bytes declared by `val_ty`. Used when the
|
/// number of bytes declared by `val_ty`. Used when the
|
||||||
/// protocol-dispatch chain bottoms out at a foreign-libc-malloc
|
/// protocol-dispatch chain bottoms out at a extern-libc-malloc
|
||||||
/// pointer and sx code stores through it. Comptime safety is the
|
/// pointer and sx code stores through it. Comptime safety is the
|
||||||
/// caller's responsibility — wild writes will fault.
|
/// caller's responsibility — wild writes will fault.
|
||||||
fn storeAtRawPtr(self: *Interpreter, addr: i64, val: Value, val_ty: @import("types.zig").TypeId) InterpError!void {
|
fn storeAtRawPtr(self: *Interpreter, addr: i64, val: Value, val_ty: @import("types.zig").TypeId) InterpError!void {
|
||||||
@@ -414,7 +414,7 @@ pub const Interpreter = struct {
|
|||||||
.heap_ptr => |hp| blk: {
|
.heap_ptr => |hp| blk: {
|
||||||
// `heapSlice` returns the slice already advanced by `hp.offset`,
|
// `heapSlice` returns the slice already advanced by `hp.offset`,
|
||||||
// so its `.ptr` IS the offset address. Adding `hp.offset` again
|
// so its `.ptr` IS the offset address. Adding `hp.offset` again
|
||||||
// double-counts and lands the foreign call past the buffer end.
|
// double-counts and lands the extern call past the buffer end.
|
||||||
_ = self.heapSlice(hp) orelse return error.TypeError;
|
_ = self.heapSlice(hp) orelse return error.TypeError;
|
||||||
break :blk @intFromPtr(self.heap.items[hp.id].ptr) + hp.offset;
|
break :blk @intFromPtr(self.heap.items[hp.id].ptr) + hp.offset;
|
||||||
},
|
},
|
||||||
@@ -451,7 +451,7 @@ pub const Interpreter = struct {
|
|||||||
},
|
},
|
||||||
// Raw host pointer (from libc_malloc-backed
|
// Raw host pointer (from libc_malloc-backed
|
||||||
// cstring). Read bytes from real memory and copy
|
// cstring). Read bytes from real memory and copy
|
||||||
// into a null-terminated buffer the foreign call
|
// into a null-terminated buffer the extern call
|
||||||
// can consume.
|
// can consume.
|
||||||
.int => |addr| {
|
.int => |addr| {
|
||||||
const src: [*]const u8 = @ptrFromInt(@as(usize, @bitCast(addr)));
|
const src: [*]const u8 = @ptrFromInt(@as(usize, @bitCast(addr)));
|
||||||
@@ -461,12 +461,12 @@ pub const Interpreter = struct {
|
|||||||
tmp.append(self.alloc, buf) catch return error.TypeError;
|
tmp.append(self.alloc, buf) catch return error.TypeError;
|
||||||
break :blk @intFromPtr(buf.ptr);
|
break :blk @intFromPtr(buf.ptr);
|
||||||
},
|
},
|
||||||
else => return typeErrorDetail("comptime foreign call: unsupported aggregate data-field kind (expected heap_ptr/string/int)"),
|
else => return typeErrorDetail("comptime extern call: unsupported aggregate data-field kind (expected heap_ptr/string/int)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return typeErrorDetail("comptime foreign call: aggregate arg must be a {ptr, len} fat-pointer pair");
|
return typeErrorDetail("comptime extern call: aggregate arg must be a {ptr, len} fat-pointer pair");
|
||||||
},
|
},
|
||||||
else => return typeErrorDetail("comptime foreign call: unsupported arg Value kind"),
|
else => return typeErrorDetail("comptime extern call: unsupported arg Value kind"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +491,7 @@ pub const Interpreter = struct {
|
|||||||
fn callExtern(self: *Interpreter, func: *const inst_mod.Function, args: []const Value) InterpError!Value {
|
fn callExtern(self: *Interpreter, func: *const inst_mod.Function, args: []const Value) InterpError!Value {
|
||||||
const name = self.module.types.getString(func.name);
|
const name = self.module.types.getString(func.name);
|
||||||
|
|
||||||
// A foreign call may not return (e.g. `process.exit` → `_exit`), which
|
// A extern call may not return (e.g. `process.exit` → `_exit`), which
|
||||||
// would discard the interpreter's buffered `print` output (otherwise
|
// would discard the interpreter's buffered `print` output (otherwise
|
||||||
// flushed only after `#run` completes). Flush it first so comptime
|
// flushed only after `#run` completes). Flush it first so comptime
|
||||||
// diagnostics emitted just before a terminating call survive.
|
// diagnostics emitted just before a terminating call survive.
|
||||||
@@ -499,13 +499,13 @@ pub const Interpreter = struct {
|
|||||||
_ = std.c.write(1, self.output.items.ptr, self.output.items.len);
|
_ = std.c.write(1, self.output.items.ptr, self.output.items.len);
|
||||||
self.output.clearRetainingCapacity();
|
self.output.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
const symbol = (host_ffi.lookupSymbol(self.alloc, name) catch return bailDetail("comptime foreign call: dlsym error looking up symbol")) orelse {
|
const symbol = (host_ffi.lookupSymbol(self.alloc, name) catch return bailDetail("comptime extern call: dlsym error looking up symbol")) orelse {
|
||||||
if (last_bail_detail == null) last_bail_detail = "comptime foreign call: symbol not found via dlsym (target-specific binding called at compile time?)";
|
if (last_bail_detail == null) last_bail_detail = "comptime extern call: symbol not found via dlsym (target-specific binding called at compile time?)";
|
||||||
return error.CannotEvalComptime;
|
return error.CannotEvalComptime;
|
||||||
};
|
};
|
||||||
|
|
||||||
var packed_args: [8]usize = undefined;
|
var packed_args: [8]usize = undefined;
|
||||||
if (args.len > packed_args.len) return bailDetail("comptime foreign call: more than 8 args (host_ffi trampolines max out at 8)");
|
if (args.len > packed_args.len) return bailDetail("comptime extern call: more than 8 args (host_ffi trampolines max out at 8)");
|
||||||
|
|
||||||
var tmp = std.ArrayList([]u8).empty;
|
var tmp = std.ArrayList([]u8).empty;
|
||||||
defer {
|
defer {
|
||||||
@@ -517,7 +517,7 @@ pub const Interpreter = struct {
|
|||||||
}
|
}
|
||||||
const argv = packed_args[0..args.len];
|
const argv = packed_args[0..args.len];
|
||||||
|
|
||||||
// Variadic foreign functions (declared `args: ..T`) must be
|
// Variadic extern functions (declared `args: ..T`) must be
|
||||||
// dispatched through C-variadic trampolines so the trailing
|
// dispatched through C-variadic trampolines so the trailing
|
||||||
// args land in the right place per the target's variadic
|
// args land in the right place per the target's variadic
|
||||||
// ABI. The fixed-arity trampolines would put them in arg
|
// ABI. The fixed-arity trampolines would put them in arg
|
||||||
@@ -567,7 +567,7 @@ pub const Interpreter = struct {
|
|||||||
const func = self.module.getFunction(func_id);
|
const func = self.module.getFunction(func_id);
|
||||||
if (func.is_extern or func.blocks.items.len == 0) {
|
if (func.is_extern or func.blocks.items.len == 0) {
|
||||||
// Dispatch to host libc via dlsym. Lets `#run` (and the
|
// Dispatch to host libc via dlsym. Lets `#run` (and the
|
||||||
// post-link bundler) call ordinary foreign symbols like
|
// post-link bundler) call ordinary extern symbols like
|
||||||
// `puts`, `getenv`, `posix_spawn`, etc.
|
// `puts`, `getenv`, `posix_spawn`, etc.
|
||||||
return self.callExtern(func, args);
|
return self.callExtern(func, args);
|
||||||
}
|
}
|
||||||
@@ -863,7 +863,7 @@ pub const Interpreter = struct {
|
|||||||
const byte: u8 = @intCast(@as(u64, @bitCast(val.asInt() orelse return error.TypeError)) & 0xFF);
|
const byte: u8 = @intCast(@as(u64, @bitCast(val.asInt() orelse return error.TypeError)) & 0xFF);
|
||||||
self.heapStoreByte(hp, byte);
|
self.heapStoreByte(hp, byte);
|
||||||
},
|
},
|
||||||
// Raw host pointer (from foreign call, e.g. libc_malloc).
|
// Raw host pointer (from extern call, e.g. libc_malloc).
|
||||||
// `val_ty` carries the declared destination width so we
|
// `val_ty` carries the declared destination width so we
|
||||||
// write exactly that many bytes — no neighbor clobber.
|
// write exactly that many bytes — no neighbor clobber.
|
||||||
.int => |p| {
|
.int => |p| {
|
||||||
@@ -1452,7 +1452,7 @@ pub const Interpreter = struct {
|
|||||||
.offset = hp.offset + @as(u32, @intCast(offset)),
|
.offset = hp.offset + @as(u32, @intCast(offset)),
|
||||||
} } };
|
} } };
|
||||||
},
|
},
|
||||||
// Raw host pointer (from foreign call return,
|
// Raw host pointer (from extern call return,
|
||||||
// e.g. libc_malloc). Byte-addressed offset
|
// e.g. libc_malloc). Byte-addressed offset
|
||||||
// matches the heap_ptr branch above — both
|
// matches the heap_ptr branch above — both
|
||||||
// are u8-granular for sx's string/slice ops.
|
// are u8-granular for sx's string/slice ops.
|
||||||
@@ -1534,7 +1534,7 @@ pub const Interpreter = struct {
|
|||||||
const result = try self.call(fid, args);
|
const result = try self.call(fid, args);
|
||||||
return .{ .value = result };
|
return .{ .value = result };
|
||||||
},
|
},
|
||||||
else => return bailDetail("comptime call_indirect: callee is not a func_ref Value (raw fn-pointers from foreign calls aren't dispatchable in interp)"),
|
else => return bailDetail("comptime call_indirect: callee is not a func_ref Value (raw fn-pointers from extern calls aren't dispatchable in interp)"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
// f64 → D (jdouble)
|
// f64 → D (jdouble)
|
||||||
// []T → [<elem>
|
// []T → [<elem>
|
||||||
// [*]T → [<elem> (sx many-pointer treated as array for now)
|
// [*]T → [<elem> (sx many-pointer treated as array for now)
|
||||||
// *Self → L<enclosing-foreign-path>;
|
// *Self → L<enclosing-runtime-path>;
|
||||||
// *Foo → L<Foo's foreign path>; (cross-class — step 2.9)
|
// *Foo → L<Foo's runtime path>; (cross-class — step 2.9)
|
||||||
//
|
//
|
||||||
// `#jni_method_descriptor("...")` (step 2.6) overrides this whole walk
|
// `#jni_method_descriptor("...")` (step 2.6) overrides this whole walk
|
||||||
// when set; sema/lowering use the override verbatim.
|
// when set; sema/lowering use the override verbatim.
|
||||||
@@ -36,7 +36,7 @@ pub const DeriveError = error{
|
|||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Map from sx-side alias → foreign path of declared `#jni_class` /
|
/// Map from sx-side alias → runtime path of declared `#jni_class` /
|
||||||
/// `#jni_interface` decls. Used to resolve `*Foo` into `L<path>;` in
|
/// `#jni_interface` decls. Used to resolve `*Foo` into `L<path>;` in
|
||||||
/// the descriptor. Built during lowering's scan pass.
|
/// the descriptor. Built during lowering's scan pass.
|
||||||
pub const ClassRegistry = std.StringHashMap([]const u8);
|
pub const ClassRegistry = std.StringHashMap([]const u8);
|
||||||
@@ -81,7 +81,7 @@ pub fn writeType(
|
|||||||
try writeType(allocator, buf, ctx, arr.element_type);
|
try writeType(allocator, buf, ctx, arr.element_type);
|
||||||
},
|
},
|
||||||
.pointer_type_expr => |ptr| {
|
.pointer_type_expr => |ptr| {
|
||||||
// *Self → L<enclosing>;, *Foo → L<Foo's foreign path>;,
|
// *Self → L<enclosing>;, *Foo → L<Foo's runtime path>;,
|
||||||
// *void → Ljava/lang/Object; (opaque jobject — common when
|
// *void → Ljava/lang/Object; (opaque jobject — common when
|
||||||
// users don't have a precise Java type for the value).
|
// users don't have a precise Java type for the value).
|
||||||
const inner = ptr.pointee_type;
|
const inner = ptr.pointee_type;
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ test "default-package class (no slash in runtime_path)" {
|
|||||||
const out = try emit.emitJavaSource(a, &fcd, .{});
|
const out = try emit.emitJavaSource(a, &fcd, .{});
|
||||||
defer a.free(out);
|
defer a.free(out);
|
||||||
|
|
||||||
// No `package ...;` line when the foreign path has no slashes.
|
// No `package ...;` line when the runtime path has no slashes.
|
||||||
try std.testing.expect(std.mem.indexOf(u8, out, "package ") == null);
|
try std.testing.expect(std.mem.indexOf(u8, out, "package ") == null);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, out, "public class SxNoPackage") != null);
|
try std.testing.expect(std.mem.indexOf(u8, out, "public class SxNoPackage") != null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
// f32/f64)
|
// f32/f64)
|
||||||
// - `(self: *Self)` plus primitive params
|
// - `(self: *Self)` plus primitive params
|
||||||
// - cross-class refs (`*Foo` where Foo is another declared
|
// - cross-class refs (`*Foo` where Foo is another declared
|
||||||
// `#foreign #jni_class`) lower to Foo's foreign path → Java
|
// `#jni_class(…) extern`) lower to Foo's runtime path → Java
|
||||||
// fully-qualified type
|
// fully-qualified type
|
||||||
// - `*void` → `Object` (opaque jobject)
|
// - `*void` → `Object` (opaque jobject)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ pub const EmitError = error{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
/// Map from sx alias → foreign path of declared `#jni_class` decls.
|
/// Map from sx alias → runtime path of declared `#jni_class` decls.
|
||||||
/// Used to resolve `*Foo` cross-class refs in method signatures.
|
/// Used to resolve `*Foo` cross-class refs in method signatures.
|
||||||
classes: ?*const std.StringHashMap([]const u8) = null,
|
classes: ?*const std.StringHashMap([]const u8) = null,
|
||||||
/// Default superclass when the user doesn't write `#extends ...;`.
|
/// Default superclass when the user doesn't write `#extends ...;`.
|
||||||
@@ -79,7 +79,7 @@ pub fn injectLoadLibrary(allocator: Allocator, java_source: []const u8, lib_name
|
|||||||
return try buf.toOwnedSlice(allocator);
|
return try buf.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit a `.java` source for the given foreign-class decl. Result is
|
/// Emit a `.java` source for the given runtime-class decl. Result is
|
||||||
/// heap-allocated through `allocator`; caller owns it.
|
/// heap-allocated through `allocator`; caller owns it.
|
||||||
pub fn emitJavaSource(
|
pub fn emitJavaSource(
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ test "lower: objcTypeEncodingFromSignature emits @ for Obj-C class pointers" {
|
|||||||
defer module.deinit();
|
defer module.deinit();
|
||||||
var lowering = Lowering.init(&module);
|
var lowering = Lowering.init(&module);
|
||||||
|
|
||||||
// Synthesize a foreign Obj-C class entry so the encoder recognises
|
// Synthesize a runtime Obj-C class entry so the encoder recognises
|
||||||
// `*NSString` as an object pointer.
|
// `*NSString` as an object pointer.
|
||||||
const ns_name = module.types.internString("NSString");
|
const ns_name = module.types.internString("NSString");
|
||||||
const ns_struct = module.types.intern(.{ .@"struct" = .{ .name = ns_name, .fields = &.{} } });
|
const ns_struct = module.types.intern(.{ .@"struct" = .{ .name = ns_name, .fields = &.{} } });
|
||||||
@@ -666,7 +666,7 @@ test "lower: deriveObjcSelector — niladic / keyword / multi-keyword / override
|
|||||||
try std.testing.expectEqual(true, overridden.is_override);
|
try std.testing.expectEqual(true, overridden.is_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "lower: isObjcClassPointer recognises pointer-to-foreign-Obj-C-class" {
|
test "lower: isObjcClassPointer recognises pointer-to-runtime-Obj-C-class" {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
const alloc = arena.allocator();
|
||||||
@@ -705,7 +705,7 @@ test "lower: isObjcClassPointer recognises pointer-to-foreign-Obj-C-class" {
|
|||||||
try lowering.program_index.runtime_class_map.put("NSCopying", &proto_fcd);
|
try lowering.program_index.runtime_class_map.put("NSCopying", &proto_fcd);
|
||||||
try std.testing.expect(lowering.objc().isObjcClassPointer(proto_ptr));
|
try std.testing.expect(lowering.objc().isObjcClassPointer(proto_ptr));
|
||||||
|
|
||||||
// *Plain where Plain is a non-foreign struct → false.
|
// *Plain where Plain is a non-extern struct → false.
|
||||||
const plain_name = module.types.internString("Plain");
|
const plain_name = module.types.internString("Plain");
|
||||||
const plain_struct = module.types.intern(.{ .@"struct" = .{ .name = plain_name, .fields = &.{} } });
|
const plain_struct = module.types.intern(.{ .@"struct" = .{ .name = plain_name, .fields = &.{} } });
|
||||||
try std.testing.expect(!lowering.objc().isObjcClassPointer(module.types.ptrTo(plain_struct)));
|
try std.testing.expect(!lowering.objc().isObjcClassPointer(module.types.ptrTo(plain_struct)));
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ pub const Lowering = struct {
|
|||||||
/// Null / absent for the comptime `..$args` pack (no constraint).
|
/// Null / absent for the comptime `..$args` pack (no constraint).
|
||||||
pack_constraint: ?std.StringHashMap([]const u8) = null,
|
pack_constraint: ?std.StringHashMap([]const u8) = null,
|
||||||
struct_const_map: std.StringHashMap(StructConstInfo), // "Struct.CONST" → value info
|
struct_const_map: std.StringHashMap(StructConstInfo), // "Struct.CONST" → value info
|
||||||
extern_name_map: std.StringHashMap([]const u8), // sx name → C name for #foreign renames
|
extern_name_map: std.StringHashMap([]const u8), // sx name → C name for #extern renames
|
||||||
target_config: ?@import("../target.zig").TargetConfig = null, // compilation target (for inline if)
|
target_config: ?@import("../target.zig").TargetConfig = null, // compilation target (for inline if)
|
||||||
comptime_constants: std.StringHashMap(ComptimeValue), // compile-time known constants (e.g. OS, ARCH)
|
comptime_constants: std.StringHashMap(ComptimeValue), // compile-time known constants (e.g. OS, ARCH)
|
||||||
diagnostics: ?*errors.DiagnosticList = null, // error reporting with source locations
|
diagnostics: ?*errors.DiagnosticList = null, // error reporting with source locations
|
||||||
@@ -836,8 +836,8 @@ pub const Lowering = struct {
|
|||||||
}
|
}
|
||||||
return .unresolved;
|
return .unresolved;
|
||||||
}
|
}
|
||||||
// `*Self` substitution inside foreign-class member declarations
|
// `*Self` substitution inside runtime-class member declarations
|
||||||
// — both foreign and sx-defined — resolves to the class's own
|
// — both runtime and sx-defined — resolves to the class's own
|
||||||
// 0-field stub struct (i.e. the opaque Obj-C pointer type).
|
// 0-field stub struct (i.e. the opaque Obj-C pointer type).
|
||||||
// This matches the Obj-C idiom where `self` IS the object.
|
// This matches the Obj-C idiom where `self` IS the object.
|
||||||
// `self.field` access on sx-defined classes is rewritten by
|
// `self.field` access on sx-defined classes is rewritten by
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
|||||||
var c = c_in;
|
var c = c_in;
|
||||||
// A bare reserved-type-name spelling in call position parses as a
|
// A bare reserved-type-name spelling in call position parses as a
|
||||||
// `.type_expr` (e.g. `i2(4)`), but if a function of that name is in
|
// `.type_expr` (e.g. `i2(4)`), but if a function of that name is in
|
||||||
// scope — a backtick-declared sx fn or a `#import c` foreign fn whose C
|
// scope — a backtick-declared sx fn or a `#import c` extern fn whose C
|
||||||
// name collides with a reserved type spelling — it is a CALL to that
|
// name collides with a reserved type spelling — it is a CALL to that
|
||||||
// function. `TypeName(val)` is not a cast (casts are `cast(T, val)`), so
|
// function. `TypeName(val)` is not a cast (casts are `cast(T, val)`), so
|
||||||
// there is no ambiguity. Rewrite the callee to an identifier so the
|
// there is no ambiguity. Rewrite the callee to an identifier so the
|
||||||
@@ -38,7 +38,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
|||||||
// reference that already resolves via scope/globals.
|
// reference that already resolves via scope/globals.
|
||||||
//
|
//
|
||||||
// Scoped to RAW provenance: only a backtick (`is_raw`) or `#import c`
|
// Scoped to RAW provenance: only a backtick (`is_raw`) or `#import c`
|
||||||
// foreign fn declaration may legally carry a reserved-name spelling
|
// extern fn declaration may legally carry a reserved-name spelling
|
||||||
// (the decl check rejects every bare reserved-name sx fn). Refusing the
|
// (the decl check rejects every bare reserved-name sx fn). Refusing the
|
||||||
// rewrite for a non-raw match keeps a genuine reserved type spelling a
|
// rewrite for a non-raw match keeps a genuine reserved type spelling a
|
||||||
// type — belt-and-suspenders should any future path ever reintroduce a
|
// type — belt-and-suspenders should any future path ever reintroduce a
|
||||||
@@ -603,7 +603,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
|||||||
return self.lowerSuperCall(fa.field, args.items, c.callee.span);
|
return self.lowerSuperCall(fa.field, args.items, c.callee.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `Alias.method(args)` where Alias is a foreign-class
|
// `Alias.method(args)` where Alias is a runtime-class
|
||||||
// identifier and `method` is a `static` member — JNI
|
// identifier and `method` is a `static` member — JNI
|
||||||
// dispatch via FindClass + GetStaticMethodID + CallStatic*,
|
// dispatch via FindClass + GetStaticMethodID + CallStatic*,
|
||||||
// OR (for `new`) via FindClass + GetMethodID("<init>") +
|
// OR (for `new`) via FindClass + GetMethodID("<init>") +
|
||||||
@@ -1251,7 +1251,7 @@ pub fn allocViaContext(self: *Lowering, size_ref: Ref, void_ptr_ty: TypeId) Ref
|
|||||||
} }, void_ptr_ty);
|
} }, void_ptr_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit a call to a foreign-declared function looked up by name.
|
/// Emit a call to a extern-declared function looked up by name.
|
||||||
/// Used for the compiler-internal byte-copy in the protocol-erasure
|
/// Used for the compiler-internal byte-copy in the protocol-erasure
|
||||||
/// heap path and the closure env-copy path, both of which need
|
/// heap path and the closure env-copy path, both of which need
|
||||||
/// libc `memcpy` after the `#builtin` form was dropped.
|
/// libc `memcpy` after the `#builtin` form was dropped.
|
||||||
@@ -1285,7 +1285,7 @@ fn protocolHasMethod(proto_info: anytype, name: []const u8) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveFuncByName(self: *Lowering, name: []const u8) ?FuncId {
|
pub fn resolveFuncByName(self: *Lowering, name: []const u8) ?FuncId {
|
||||||
// Check foreign name map first (e.g., "c_abs" → "abs")
|
// Check extern name map first (e.g., "c_abs" → "abs")
|
||||||
const effective_name = self.extern_name_map.get(name) orelse name;
|
const effective_name = self.extern_name_map.get(name) orelse name;
|
||||||
const name_id = self.module.types.internString(effective_name);
|
const name_id = self.module.types.internString(effective_name);
|
||||||
for (self.module.functions.items, 0..) |func, i| {
|
for (self.module.functions.items, 0..) |func, i| {
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
|||||||
// be passed directly. For a capture-free closure whose return type matches
|
// be passed directly. For a capture-free closure whose return type matches
|
||||||
// the slot, emit an adapter with the bare ABI. Reject the cases the bare
|
// the slot, emit an adapter with the bare ABI. Reject the cases the bare
|
||||||
// ABI can't represent: a capturing closure (env has nowhere to live), and
|
// ABI can't represent: a capturing closure (env has nowhere to live), and
|
||||||
// a failable closure into a non-failable slot (foreign code can't observe
|
// a failable closure into a non-failable slot (extern code can't observe
|
||||||
// the error channel — ERR E5.1 FFI-boundary rule).
|
// the error channel — ERR E5.1 FFI-boundary rule).
|
||||||
if (self.target_type) |tt| {
|
if (self.target_type) |tt| {
|
||||||
if (!tt.isBuiltin() and self.module.types.get(tt) == .function) {
|
if (!tt.isBuiltin() and self.module.types.get(tt) == .function) {
|
||||||
@@ -319,7 +319,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
|||||||
const adapter = self.createClosureToBareFnAdapter(func_id, self.module.types.get(tt).function, ret_ty, lam.body.span);
|
const adapter = self.createClosureToBareFnAdapter(func_id, self.module.types.get(tt).function, ret_ty, lam.body.span);
|
||||||
return self.builder.emit(.{ .func_ref = adapter }, tt);
|
return self.builder.emit(.{ .func_ref = adapter }, tt);
|
||||||
} else if (self.errorChannelOf(ret_ty) != null and self.errorChannelOf(slot_ret) == null) {
|
} else if (self.errorChannelOf(ret_ty) != null and self.errorChannelOf(slot_ret) == null) {
|
||||||
if (self.diagnostics) |d| d.addFmt(.err, lam.body.span, "failable closure cannot be assigned to a non-failable function-type slot; foreign code can't observe the error channel — handle the error in a wrapper closure that absorbs it", .{});
|
if (self.diagnostics) |d| d.addFmt(.err, lam.body.span, "failable closure cannot be assigned to a non-failable function-type slot; extern code can't observe the error channel — handle the error in a wrapper closure that absorbs it", .{});
|
||||||
} else if (self.diagnostics) |d| {
|
} else if (self.diagnostics) |d| {
|
||||||
d.addFmt(.err, lam.body.span, "closure return type does not match the function-type slot", .{});
|
d.addFmt(.err, lam.body.span, "closure return type does not match the function-type slot", .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ pub fn checkRequiredEntryPoints(self: *Lowering) void {
|
|||||||
diags.addFmt(.err, null, "target is Android but no `#jni_main` Activity declared. " ++
|
diags.addFmt(.err, null, "target is Android but no `#jni_main` Activity declared. " ++
|
||||||
"The OS launches a Java-side Activity that delegates lifecycle " ++
|
"The OS launches a Java-side Activity that delegates lifecycle " ++
|
||||||
"callbacks into sx — declare one like:\n\n" ++
|
"callbacks into sx — declare one like:\n\n" ++
|
||||||
" Bundle :: #foreign #jni_class(\"android/os/Bundle\") {{ }}\n\n" ++
|
" Bundle :: #jni_class(\"android/os/Bundle\") extern {{ }}\n\n" ++
|
||||||
" MyApp :: #jni_main #jni_class(\"co/example/MyApp\") {{\n" ++
|
" MyApp :: #jni_main #jni_class(\"co/example/MyApp\") {{\n" ++
|
||||||
" onCreate :: (self: *Self, b: *Bundle) {{ /* ... */ }}\n" ++
|
" onCreate :: (self: *Self, b: *Bundle) {{ /* ... */ }}\n" ++
|
||||||
" }}", .{});
|
" }}", .{});
|
||||||
@@ -375,7 +375,7 @@ pub fn detectContextDecl(decls: []const *const Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a sx function declaration should receive the
|
/// Returns true if a sx function declaration should receive the
|
||||||
/// implicit `__sx_ctx` parameter. False for foreign-libc bindings,
|
/// implicit `__sx_ctx` parameter. False for extern-libc bindings,
|
||||||
/// #builtin / #compiler bodies, and C-conv functions (which keep
|
/// #builtin / #compiler bodies, and C-conv functions (which keep
|
||||||
/// their literal C ABI). Also false for OS-called entry points
|
/// their literal C ABI). Also false for OS-called entry points
|
||||||
/// (`isExportedEntryName`): main and JNI hooks are invoked by the
|
/// (`isExportedEntryName`): main and JNI hooks are invoked by the
|
||||||
@@ -1101,7 +1101,7 @@ pub fn registerTopLevelGlobal(self: *Lowering, vd: *const ast.VarDecl) void {
|
|||||||
// Use self.resolveType so type aliases like `Handle :: u32;` resolve
|
// Use self.resolveType so type aliases like `Handle :: u32;` resolve
|
||||||
// to their target type (not a synthetic empty struct). When the
|
// to their target type (not a synthetic empty struct). When the
|
||||||
// user omitted the annotation, infer from the initializer
|
// user omitted the annotation, infer from the initializer
|
||||||
// expression; foreign globals with no annotation are diagnosed
|
// expression; extern globals with no annotation are diagnosed
|
||||||
// because their type can't be inferred without an initializer.
|
// because their type can't be inferred without an initializer.
|
||||||
const var_ty: TypeId = if (vd.type_annotation) |ta|
|
const var_ty: TypeId = if (vd.type_annotation) |ta|
|
||||||
self.resolveType(ta)
|
self.resolveType(ta)
|
||||||
@@ -1113,7 +1113,7 @@ pub fn registerTopLevelGlobal(self: *Lowering, vd: *const ast.VarDecl) void {
|
|||||||
break :blk .void;
|
break :blk .void;
|
||||||
};
|
};
|
||||||
// Foreign / extern globals reference a symbol defined in libSystem etc.
|
// Foreign / extern globals reference a symbol defined in libSystem etc.
|
||||||
// (`_NSConcreteStackBlock : *void #foreign;` or `… : *void extern;`). The C
|
// (`_NSConcreteStackBlock : *void extern;` or `… : *void extern;`). The C
|
||||||
// symbol name is the optional override (`extern_name`) or the sx name itself.
|
// symbol name is the optional override (`extern_name`) or the sx name itself.
|
||||||
const sym_name = vd.extern_name orelse vd.name;
|
const sym_name = vd.extern_name orelse vd.name;
|
||||||
const name_id = self.module.types.internString(sym_name);
|
const name_id = self.module.types.internString(sym_name);
|
||||||
@@ -1397,7 +1397,7 @@ pub fn lowerRetainedSameNameAuthors(self: *Lowering) void {
|
|||||||
if (winner == fd) continue;
|
if (winner == fd) continue;
|
||||||
|
|
||||||
// Only plain free functions get an out-of-line slot; generic /
|
// Only plain free functions get an out-of-line slot; generic /
|
||||||
// foreign / builtin / #compiler authors keep their existing
|
// extern / builtin / #compiler authors keep their existing
|
||||||
// dispatch (mirrors lazyLowerFunction / declareFunction guards).
|
// dispatch (mirrors lazyLowerFunction / declareFunction guards).
|
||||||
if (!isPlainFreeFn(fd)) continue;
|
if (!isPlainFreeFn(fd)) continue;
|
||||||
|
|
||||||
@@ -1452,7 +1452,7 @@ pub const TypeHeadResolution = union(enum) {
|
|||||||
pending,
|
pending,
|
||||||
/// A flat-visible author DOES declare `name` as a type, but its TypeId
|
/// A flat-visible author DOES declare `name` as a type, but its TypeId
|
||||||
/// slot is not registered yet — a forward / self / mutual reference
|
/// slot is not registered yet — a forward / self / mutual reference
|
||||||
/// resolved mid-registration (`next: *ArenaChunk`), or a foreign /
|
/// resolved mid-registration (`next: *ArenaChunk`), or an extern /
|
||||||
/// lazily-registered author with no `findByName` slot. `resolveNominalLeaf`
|
/// lazily-registered author with no `findByName` slot. `resolveNominalLeaf`
|
||||||
/// keeps the empty-struct stub, which `internNamedTypeDecl` ADOPTS (key-
|
/// keeps the empty-struct stub, which `internNamedTypeDecl` ADOPTS (key-
|
||||||
/// stable `updatePreservingKey`) when the type registers — so the forward
|
/// stable `updatePreservingKey`) when the type registers — so the forward
|
||||||
@@ -1513,7 +1513,7 @@ pub const TypeHeadResolution = union(enum) {
|
|||||||
/// same module is one author): `≥2 distinct` → `.ambiguous`; exactly one
|
/// same module is one author): `≥2 distinct` → `.ambiguous`; exactly one
|
||||||
/// that DIFFERS from the winner → select it; otherwise `.none`.
|
/// that DIFFERS from the winner → select it; otherwise `.none`.
|
||||||
///
|
///
|
||||||
/// Generic / comptime / foreign / builtin authors are never rerouted — the
|
/// Generic / comptime / extern / builtin authors are never rerouted — the
|
||||||
/// existing dispatch owns those shapes; `isPlainFreeFn` filters them out
|
/// existing dispatch owns those shapes; `isPlainFreeFn` filters them out
|
||||||
/// BEFORE the count gate (so a same-name collision of non-plain authors is
|
/// BEFORE the count gate (so a same-name collision of non-plain authors is
|
||||||
/// NOT ambiguous), and the selector returns `.none`. No eager
|
/// NOT ambiguous), and the selector returns `.none`. No eager
|
||||||
@@ -1540,7 +1540,7 @@ pub fn selectPlainCallableAuthor(self: *Lowering, name: []const u8, caller_file:
|
|||||||
|
|
||||||
// Caller does not author `name` as a fn → its flat-reachable authors.
|
// Caller does not author `name` as a fn → its flat-reachable authors.
|
||||||
// Filter to plain free functions BEFORE counting: a same-name collision
|
// Filter to plain free functions BEFORE counting: a same-name collision
|
||||||
// of non-plain authors (e.g. two flat-imported modules each `#foreign`ing
|
// of non-plain authors (e.g. two flat-imported modules each `extern`ing
|
||||||
// the same symbol) is NOT counted as ambiguous — it falls through to
|
// the same symbol) is NOT counted as ambiguous — it falls through to
|
||||||
// `.none` and the existing first-wins path.
|
// `.none` and the existing first-wins path.
|
||||||
var the_one: ?*const ast.FnDecl = null;
|
var the_one: ?*const ast.FnDecl = null;
|
||||||
@@ -1597,7 +1597,7 @@ pub fn selectNominalLeaf(self: *Lowering, name: []const u8, from: []const u8, ra
|
|||||||
}
|
}
|
||||||
// Bare nominal name. A bare TYPE name is visible iff a flat-import-
|
// Bare nominal name. A bare TYPE name is visible iff a flat-import-
|
||||||
// reachable module authors it AS A TYPE — and a TYPE author is EITHER a
|
// reachable module authors it AS A TYPE — and a TYPE author is EITHER a
|
||||||
// named type (struct/enum/union/error-set/protocol/foreign class) OR a
|
// named type (struct/enum/union/error-set/protocol/runtime class) OR a
|
||||||
// type ALIAS (`Name :: <type>`, a `const_decl` whose value resolved to a
|
// type ALIAS (`Name :: <type>`, a `const_decl` whose value resolved to a
|
||||||
// type, recorded in E0's `type_aliases_by_source`). Both kinds are gated
|
// type, recorded in E0's `type_aliases_by_source`). Both kinds are gated
|
||||||
// identically: `moduleTypeAuthor` is the SINGLE source of truth, so a
|
// identically: `moduleTypeAuthor` is the SINGLE source of truth, so a
|
||||||
@@ -1741,7 +1741,7 @@ pub fn selectNominalLeaf(self: *Lowering, name: []const u8, from: []const u8, ra
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TRUE iff `raw` declares a NAMED TYPE — struct / enum / union / error-set /
|
/// TRUE iff `raw` declares a NAMED TYPE — struct / enum / union / error-set /
|
||||||
/// protocol / foreign class. A `fn_decl`, a value-or-alias `const_decl`, and a
|
/// protocol / runtime class. A `fn_decl`, a value-or-alias `const_decl`, and a
|
||||||
/// `namespace_decl` are NOT named types. A type ALIAS is a `const_decl`;
|
/// `namespace_decl` are NOT named types. A type ALIAS is a `const_decl`;
|
||||||
/// it is recognised via `type_aliases_by_source` separately from named types.
|
/// it is recognised via `type_aliases_by_source` separately from named types.
|
||||||
pub fn isNamedTypeKind(raw: resolver_mod.RawDeclRef) bool {
|
pub fn isNamedTypeKind(raw: resolver_mod.RawDeclRef) bool {
|
||||||
@@ -1766,7 +1766,7 @@ pub fn isNamedTypeKind(raw: resolver_mod.RawDeclRef) bool {
|
|||||||
/// through `internNamedTypeDecl` (`registerEnumDecl` / `registerUnionDecl`),
|
/// through `internNamedTypeDecl` (`registerEnumDecl` / `registerUnionDecl`),
|
||||||
/// keyed by the raw-facts decl pointer, with the `findByName` fallback for a
|
/// keyed by the raw-facts decl pointer, with the `findByName` fallback for a
|
||||||
/// single author registered before its slot lands. error-set / protocol /
|
/// single author registered before its slot lands. error-set / protocol /
|
||||||
/// foreign-class keep the legacy `findByName` resolution (their same-name
|
/// runtime-class keep the legacy `findByName` resolution (their same-name
|
||||||
/// shadows are later E6 sub-steps — E6b/E6c/E6d).
|
/// shadows are later E6 sub-steps — E6b/E6c/E6d).
|
||||||
pub fn namedRefTid(self: *Lowering, ref: resolver_mod.RawDeclRef, name: []const u8) ?TypeId {
|
pub fn namedRefTid(self: *Lowering, ref: resolver_mod.RawDeclRef, name: []const u8) ?TypeId {
|
||||||
const table = &self.module.types;
|
const table = &self.module.types;
|
||||||
@@ -1831,7 +1831,7 @@ pub fn localTypeInAnySource(self: *Lowering, name: []const u8) bool {
|
|||||||
/// Resolve the bare TYPE leaf to a `TypeId` for `resolveTypeWithBindings`.
|
/// Resolve the bare TYPE leaf to a `TypeId` for `resolveTypeWithBindings`.
|
||||||
/// Routes through the source-aware `selectNominalLeaf`. `.pending` (forward
|
/// Routes through the source-aware `selectNominalLeaf`. `.pending` (forward
|
||||||
/// alias) and `.forward` (a real author not interned yet — self / forward /
|
/// alias) and `.forward` (a real author not interned yet — self / forward /
|
||||||
/// foreign reference) keep the empty-struct stub, which the type ADOPTS on
|
/// extern reference) keep the empty-struct stub, which the type ADOPTS on
|
||||||
/// registration (`internNamedTypeDecl`). `.undeclared` (NO author anywhere)
|
/// registration (`internNamedTypeDecl`). `.undeclared` (NO author anywhere)
|
||||||
/// is genuinely-undeclared: in a NON-main module — which the
|
/// is genuinely-undeclared: in a NON-main module — which the
|
||||||
/// `UnknownTypeChecker` trusts and never walks — the leaf is the only guard,
|
/// `UnknownTypeChecker` trusts and never walks — the leaf is the only guard,
|
||||||
@@ -2298,14 +2298,14 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
|||||||
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
||||||
self.current_runtime_class = fcd;
|
self.current_runtime_class = fcd;
|
||||||
}
|
}
|
||||||
// No AST? (builtins, foreign functions, or imported functions not in this file)
|
// No AST? (builtins, extern functions, or imported functions not in this file)
|
||||||
const fd = self.program_index.fn_ast_map.get(name) orelse return;
|
const fd = self.program_index.fn_ast_map.get(name) orelse return;
|
||||||
// Foreign declarations stay as extern stubs but need to be REGISTERED
|
// Foreign declarations stay as extern stubs but need to be REGISTERED
|
||||||
// in the current module so callers get a real FuncId. Without this,
|
// in the current module so callers get a real FuncId. Without this,
|
||||||
// a comptime-lowered function (e.g. `concat` from std.sx pulled into
|
// a comptime-lowered function (e.g. `concat` from std.sx pulled into
|
||||||
// a fresh ct_module via `evalComptimeString`) emits `.call` against a
|
// a fresh ct_module via `evalComptimeString`) emits `.call` against a
|
||||||
// FuncId that doesn't exist locally; the interp can't find the
|
// FuncId that doesn't exist locally; the interp can't find the
|
||||||
// foreign target and silently no-ops instead of dispatching to libc.
|
// extern target and silently no-ops instead of dispatching to libc.
|
||||||
if (fd.extern_export == .extern_) {
|
if (fd.extern_export == .extern_) {
|
||||||
if (self.resolveFuncByName(name) == null) {
|
if (self.resolveFuncByName(name) == null) {
|
||||||
self.declareFunction(fd, name);
|
self.declareFunction(fd, name);
|
||||||
@@ -2511,10 +2511,10 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i
|
|||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the function body is a builtin or foreign declaration (no body
|
// Check if the function body is a builtin or extern declaration (no body
|
||||||
// needed). `extern` imports are declare-only too (empty placeholder body).
|
// needed). `extern` imports are declare-only too (empty placeholder body).
|
||||||
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr or fd.extern_export == .extern_) {
|
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr or fd.extern_export == .extern_) {
|
||||||
// Already declared by scanDecls/declareFunction (which handles #foreign renames)
|
// Already declared by scanDecls/declareFunction (which handles #extern renames)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
|||||||
}
|
}
|
||||||
|
|
||||||
// M2.2 — `obj.field` where `field` is declared with `#property`
|
// M2.2 — `obj.field` where `field` is declared with `#property`
|
||||||
// on a foreign Obj-C class lowers as `[obj field]` (the synthesized
|
// on a runtime Obj-C class lowers as `[obj field]` (the synthesized
|
||||||
// getter). Receiver stays opaque — no auto-deref.
|
// getter). Receiver stays opaque — no auto-deref.
|
||||||
if (self.lookupObjcPropertyOnPointer(fa.object, fa.field)) |prop| {
|
if (self.lookupObjcPropertyOnPointer(fa.object, fa.field)) |prop| {
|
||||||
return self.lowerObjcPropertyGetter(fa.object, prop, fa.field, span);
|
return self.lowerObjcPropertyGetter(fa.object, prop, fa.field, span);
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ pub fn lowerJniCall(self: *Lowering, fic: *const ast.FfiIntrinsicCall) Ref {
|
|||||||
} }, ret_ty);
|
} }, ret_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lower an `inst.method(args)` call where `inst`'s type is a foreign-class
|
/// Lower an `inst.method(args)` call where `inst`'s type is a runtime-class
|
||||||
/// alias declared by `#jni_class("...") { ... }` (or its parallel forms).
|
/// alias declared by `#jni_class("...") { ... }` (or its parallel forms).
|
||||||
/// JNI runtimes lower directly to `jni_msg_send` with a descriptor derived
|
/// JNI runtimes lower directly to `jni_msg_send` with a descriptor derived
|
||||||
/// from the method's sx signature; Obj-C / Swift runtimes are deferred to
|
/// from the method's sx signature; Obj-C / Swift runtimes are deferred to
|
||||||
@@ -252,7 +252,7 @@ pub fn lowerRuntimeMethodCall(
|
|||||||
// child receiver, not the parent.
|
// child receiver, not the parent.
|
||||||
const found = self.findRuntimeMethodInChain(fcd, method_name) orelse {
|
const found = self.findRuntimeMethodInChain(fcd, method_name) orelse {
|
||||||
if (self.diagnostics) |d| {
|
if (self.diagnostics) |d| {
|
||||||
d.addFmt(.err, span, "no method '{s}' on foreign class '{s}' (or any `#extends` ancestor)", .{ method_name, fcd.name });
|
d.addFmt(.err, span, "no method '{s}' on runtime class '{s}' (or any `#extends` ancestor)", .{ method_name, fcd.name });
|
||||||
}
|
}
|
||||||
return Ref.none;
|
return Ref.none;
|
||||||
};
|
};
|
||||||
@@ -263,7 +263,7 @@ pub fn lowerRuntimeMethodCall(
|
|||||||
// receiver derives a selector from the sx method name (default
|
// receiver derives a selector from the sx method name (default
|
||||||
// mangling: split on `_`, each piece becomes a keyword with a
|
// mangling: split on `_`, each piece becomes a keyword with a
|
||||||
// trailing `:`; niladic stays verbatim) and lowers to
|
// trailing `:`; niladic stays verbatim) and lowers to
|
||||||
// `objc_msg_send`. Both foreign and sx-defined classes flow
|
// `objc_msg_send`. Both runtime and sx-defined classes flow
|
||||||
// through the same path — sx-defined classes have their IMPs
|
// through the same path — sx-defined classes have their IMPs
|
||||||
// registered at module-init (M1.2 A.4b.iii) so `objc_msgSend`
|
// registered at module-init (M1.2 A.4b.iii) so `objc_msgSend`
|
||||||
// finds them. The Swift runtimes still bail — Phase 4.
|
// finds them. The Swift runtimes still bail — Phase 4.
|
||||||
@@ -292,7 +292,7 @@ pub fn lowerRuntimeMethodCall(
|
|||||||
const env_ref = self.jni_env_stack.items[self.jni_env_stack.items.len - 1];
|
const env_ref = self.jni_env_stack.items[self.jni_env_stack.items.len - 1];
|
||||||
|
|
||||||
// Build a ClassRegistry snapshot so descriptor derivation can
|
// Build a ClassRegistry snapshot so descriptor derivation can
|
||||||
// resolve `*Foo` cross-class refs to their foreign paths.
|
// resolve `*Foo` cross-class refs to their runtime paths.
|
||||||
var registry = jni_descriptor.ClassRegistry.init(self.alloc);
|
var registry = jni_descriptor.ClassRegistry.init(self.alloc);
|
||||||
defer registry.deinit();
|
defer registry.deinit();
|
||||||
var it = self.program_index.runtime_class_map.iterator();
|
var it = self.program_index.runtime_class_map.iterator();
|
||||||
@@ -353,8 +353,8 @@ pub fn lowerRuntimeMethodCall(
|
|||||||
// `self.objc()`. Emission-heavy IMP builders live in lower/objc_class.zig;
|
// `self.objc()`. Emission-heavy IMP builders live in lower/objc_class.zig;
|
||||||
// the `lowerObjc*Call` lowering paths are below.
|
// the `lowerObjc*Call` lowering paths are below.
|
||||||
|
|
||||||
/// Resolve a foreign-class member type, substituting `Self` (and `*Self`)
|
/// Resolve a runtime-class member type, substituting `Self` (and `*Self`)
|
||||||
/// with the foreign class's own struct type. Without this substitution
|
/// with the runtime class's own struct type. Without this substitution
|
||||||
/// chained calls like `Cls.alloc().init()` see the inner result as a
|
/// chained calls like `Cls.alloc().init()` see the inner result as a
|
||||||
/// fictitious `Self` struct and the next dispatch lookup fails.
|
/// fictitious `Self` struct and the next dispatch lookup fails.
|
||||||
pub fn resolveRuntimeClassMemberType(
|
pub fn resolveRuntimeClassMemberType(
|
||||||
@@ -559,7 +559,7 @@ pub fn lowerObjcStaticCall(
|
|||||||
} }, ret_ty);
|
} }, ret_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lower `Alias.new(args)` where `Alias` is a foreign-class identifier
|
/// Lower `Alias.new(args)` where `Alias` is a runtime-class identifier
|
||||||
/// with `static new :: (...) -> *Self;` — JNI constructor dispatch:
|
/// with `static new :: (...) -> *Self;` — JNI constructor dispatch:
|
||||||
/// `FindClass + GetMethodID("<init>", "(args)V") + NewObject(env,
|
/// `FindClass + GetMethodID("<init>", "(args)V") + NewObject(env,
|
||||||
/// clazz, mid, args...)`. Returns the new jobject.
|
/// clazz, mid, args...)`. Returns the new jobject.
|
||||||
@@ -587,7 +587,7 @@ pub fn lowerRuntimeStaticCall(
|
|||||||
return Ref.none;
|
return Ref.none;
|
||||||
}
|
}
|
||||||
if (!std.mem.eql(u8, method.name, "new")) {
|
if (!std.mem.eql(u8, method.name, "new")) {
|
||||||
if (self.diagnostics) |d| d.addFmt(.err, span, "static foreign-class call '{s}.{s}' not yet supported via `Alias.method()` syntax \u{2014} only `new` is wired today; use `#jni_static_call` directly for other static methods", .{ fcd.name, method.name });
|
if (self.diagnostics) |d| d.addFmt(.err, span, "static runtime-class call '{s}.{s}' not yet supported via `Alias.method()` syntax \u{2014} only `new` is wired today; use `#jni_static_call` directly for other static methods", .{ fcd.name, method.name });
|
||||||
return Ref.none;
|
return Ref.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +628,7 @@ pub fn lowerRuntimeStaticCall(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// sx-side return type is `*Self` — resolve to a pointer to the
|
// sx-side return type is `*Self` — resolve to a pointer to the
|
||||||
// foreign-class struct type so method dispatch on the new
|
// runtime-class struct type so method dispatch on the new
|
||||||
// jobject works (`view := SurfaceView.new(ctx); view.getHolder()`).
|
// jobject works (`view := SurfaceView.new(ctx); view.getHolder()`).
|
||||||
// At LLVM level still ptr; the sx type table is what method
|
// At LLVM level still ptr; the sx type table is what method
|
||||||
// resolution consults.
|
// resolution consults.
|
||||||
@@ -671,7 +671,7 @@ pub fn lowerRuntimeStaticCall(
|
|||||||
/// method's name (the common case — `super.onCreate(b)` from inside
|
/// method's name (the common case — `super.onCreate(b)` from inside
|
||||||
/// `onCreate :: (self, b)` override), the enclosing method's
|
/// `onCreate :: (self, b)` override), the enclosing method's
|
||||||
/// signature is reused. Other method names require the parent class
|
/// signature is reused. Other method names require the parent class
|
||||||
/// to be declared via `#foreign #jni_class` so the signature can be
|
/// to be declared via `#jni_class(…) extern` so the signature can be
|
||||||
/// looked up.
|
/// looked up.
|
||||||
pub fn lowerSuperCall(
|
pub fn lowerSuperCall(
|
||||||
self: *Lowering,
|
self: *Lowering,
|
||||||
@@ -701,7 +701,7 @@ pub fn lowerSuperCall(
|
|||||||
|
|
||||||
// Resolve method signature. Same-name fast path reuses the
|
// Resolve method signature. Same-name fast path reuses the
|
||||||
// enclosing method's descriptor; cross-method super calls require
|
// enclosing method's descriptor; cross-method super calls require
|
||||||
// the parent class to be declared via `#foreign #jni_class`.
|
// the parent class to be declared via `#jni_class(…) extern`.
|
||||||
var descriptor: []const u8 = "";
|
var descriptor: []const u8 = "";
|
||||||
var resolved_method: ?ast.RuntimeMethodDecl = null;
|
var resolved_method: ?ast.RuntimeMethodDecl = null;
|
||||||
if (self.current_runtime_method) |em| {
|
if (self.current_runtime_method) |em| {
|
||||||
@@ -725,7 +725,7 @@ pub fn lowerSuperCall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const method = resolved_method orelse {
|
const method = resolved_method orelse {
|
||||||
if (self.diagnostics) |d| d.addFmt(.err, span, "no method '{s}' found for `super.{s}(...)` — declare the parent class via `#foreign #jni_class` to make cross-method super calls available", .{ method_name, method_name });
|
if (self.diagnostics) |d| d.addFmt(.err, span, "no method '{s}' found for `super.{s}(...)` — declare the parent class via `#jni_class(…) extern` to make cross-method super calls available", .{ method_name, method_name });
|
||||||
return Ref.none;
|
return Ref.none;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -780,13 +780,13 @@ pub fn lowerSuperCall(
|
|||||||
|
|
||||||
// ── Foreign-class registration ──────────────────────────────────
|
// ── Foreign-class registration ──────────────────────────────────
|
||||||
|
|
||||||
/// Register a foreign-class declaration. The alias goes into
|
/// Register a runtime-class declaration. The alias goes into
|
||||||
/// `runtime_class_map` for method-dispatch lookup. The underlying
|
/// `runtime_class_map` for method-dispatch lookup. The underlying
|
||||||
/// type (e.g. `*Activity`) is resolved via the existing struct
|
/// type (e.g. `*Activity`) is resolved via the existing struct
|
||||||
/// fallback in `type_bridge.resolveTypeName` (which interns unknown
|
/// fallback in `type_bridge.resolveTypeName` (which interns unknown
|
||||||
/// named types as 0-field structs).
|
/// named types as 0-field structs).
|
||||||
///
|
///
|
||||||
/// sx-defined Obj-C classes (no `#foreign`, runtime == .objc_class)
|
/// sx-defined Obj-C classes (no `extern`, runtime == .objc_class)
|
||||||
/// also land in `module.objc_defined_class_cache` in declaration
|
/// also land in `module.objc_defined_class_cache` in declaration
|
||||||
/// order AND have their bodied methods registered into `fn_ast_map`
|
/// order AND have their bodied methods registered into `fn_ast_map`
|
||||||
/// under qualified names `<ClassName>.<methodName>`. Lazy lowering
|
/// under qualified names `<ClassName>.<methodName>`. Lazy lowering
|
||||||
@@ -821,7 +821,7 @@ pub fn registerRuntimeClassDecl(self: *Lowering, fcd: *const ast.RuntimeClassDec
|
|||||||
/// Resolve the `#extends ParentAlias` declaration on a sx-defined
|
/// Resolve the `#extends ParentAlias` declaration on a sx-defined
|
||||||
/// `#objc_class` to the actual Obj-C runtime class name. Falls
|
/// `#objc_class` to the actual Obj-C runtime class name. Falls
|
||||||
/// back to "NSObject" when no `#extends` is declared.
|
/// back to "NSObject" when no `#extends` is declared.
|
||||||
/// Aliases that resolve to foreign Obj-C classes use the
|
/// Aliases that resolve to runtime Obj-C classes use the
|
||||||
/// runtime_path; aliases for OTHER sx-defined classes use the
|
/// runtime_path; aliases for OTHER sx-defined classes use the
|
||||||
/// alias name directly (which equals the Obj-C class name for
|
/// alias name directly (which equals the Obj-C class name for
|
||||||
/// sx-defined classes).
|
/// sx-defined classes).
|
||||||
@@ -1007,7 +1007,7 @@ pub fn getJniEnvTlFids(self: *Lowering) struct { get: FuncId, set: FuncId } {
|
|||||||
return .{ .get = self.jni_env_tl_get_fid.?, .set = self.jni_env_tl_set_fid.? };
|
return .{ .get = self.jni_env_tl_get_fid.?, .set = self.jni_env_tl_set_fid.? };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When a namespaced import (`Ns :: #import "..."`) contains foreign-class
|
/// When a namespaced import (`Ns :: #import "..."`) contains runtime-class
|
||||||
/// declarations, ALSO register them under their qualified name `Ns.Class`
|
/// declarations, ALSO register them under their qualified name `Ns.Class`
|
||||||
/// so receiver types like `*Ns.Class` can find the fcd. The recursive
|
/// so receiver types like `*Ns.Class` can find the fcd. The recursive
|
||||||
/// scan/lower already handles bare-name registration; this only adds the
|
/// scan/lower already handles bare-name registration; this only adds the
|
||||||
@@ -1169,7 +1169,7 @@ pub fn synthesizeJniMainStub(self: *Lowering, fcd: *const ast.RuntimeClassDecl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// JNI param/return type resolution: user-declared types pass through
|
/// JNI param/return type resolution: user-declared types pass through
|
||||||
/// `resolveType` so the method body can dispatch on richer foreign-class
|
/// `resolveType` so the method body can dispatch on richer runtime-class
|
||||||
/// types (`holder.getSurface()` etc.). At LLVM level both `*SurfaceHolder`
|
/// types (`holder.getSurface()` etc.). At LLVM level both `*SurfaceHolder`
|
||||||
/// and `*void` lower to the same `ptr`, so the C ABI shape Java sees is
|
/// and `*void` lower to the same `ptr`, so the C ABI shape Java sees is
|
||||||
/// unchanged — only sx-side method resolution benefits.
|
/// unchanged — only sx-side method resolution benefits.
|
||||||
|
|||||||
@@ -808,14 +808,14 @@ pub fn hasComptimeParams(fd: *const ast.FnDecl) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A plain free function: no type params (not generic) and an ordinary sx
|
/// A plain free function: no type params (not generic) and an ordinary sx
|
||||||
/// body (not `#foreign` / `#builtin` / `#compiler` / `extern`). Only these get
|
/// body (not `extern` / `#builtin` / `#compiler` / `extern`). Only these get
|
||||||
/// an out-of-line identity-addressable slot — the bare-call disambiguation
|
/// an out-of-line identity-addressable slot — the bare-call disambiguation
|
||||||
/// and the shadow-author lowering pass leave every other shape
|
/// and the shadow-author lowering pass leave every other shape
|
||||||
/// to the existing name-keyed dispatch.
|
/// to the existing name-keyed dispatch.
|
||||||
pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
||||||
if (fd.type_params.len > 0) return false;
|
if (fd.type_params.len > 0) return false;
|
||||||
// An `extern` import is an external C symbol with no sx-lowerable body —
|
// An `extern` import is an external C symbol with no sx-lowerable body —
|
||||||
// name-keyed first-wins dispatch like a `#foreign` body, never a plain free
|
// name-keyed first-wins dispatch like a `extern` body, never a plain free
|
||||||
// fn. `export` DEFINES a real body, so it stays plain-free.
|
// fn. `export` DEFINES a real body, so it stays plain-free.
|
||||||
if (fd.extern_export == .extern_) return false;
|
if (fd.extern_export == .extern_) return false;
|
||||||
return switch (fd.body.data) {
|
return switch (fd.body.data) {
|
||||||
@@ -1032,7 +1032,7 @@ pub fn headNameOfCallee(callee: *const Node) ?HeadName {
|
|||||||
/// (`.proceed`) when import facts are unwired, the source context is absent,
|
/// (`.proceed`) when import facts are unwired, the source context is absent,
|
||||||
/// the default-Context emitter is running (built-in infrastructure resolves
|
/// the default-Context emitter is running (built-in infrastructure resolves
|
||||||
/// independent of the user's import style, F1), the querying source is the OWN
|
/// independent of the user's import style, F1), the querying source is the OWN
|
||||||
/// author, a single flat author is not registered yet (a forward / foreign /
|
/// author, a single flat author is not registered yet (a forward / extern /
|
||||||
/// generic template — the caller instantiates it), or `name` is a block-local
|
/// generic template — the caller instantiates it), or `name` is a block-local
|
||||||
/// of this source / no type author at all. Library-internal heads stay visible
|
/// of this source / no type author at all. Library-internal heads stay visible
|
||||||
/// because every instantiation kind is source-pinned to the template's defining
|
/// because every instantiation kind is source-pinned to the template's defining
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ pub fn shadowNominalId(self: *Lowering, name_id: types.StringId) u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TRUE iff `name` is authored AS A NAMED TYPE (struct / enum / union /
|
/// TRUE iff `name` is authored AS A NAMED TYPE (struct / enum / union /
|
||||||
/// error-set / protocol / foreign class) by ≥2 DISTINCT modules in the import
|
/// error-set / protocol / runtime class) by ≥2 DISTINCT modules in the import
|
||||||
/// raw facts — the authoritative same-name-shadow signal (the only case where
|
/// raw facts — the authoritative same-name-shadow signal (the only case where
|
||||||
/// distinct `nominal_id`s are needed). Module distinctness is by LEXICALLY
|
/// distinct `nominal_id`s are needed). Module distinctness is by LEXICALLY
|
||||||
/// NORMALIZED path: one logical file reached through several spellings
|
/// NORMALIZED path: one logical file reached through several spellings
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ pub fn lowerObjcDefinedClassMethods(self: *Lowering) void {
|
|||||||
self.emitObjcDefinedClassImps();
|
self.emitObjcDefinedClassImps();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `obj_expr` is typed as a pointer to a foreign Obj-C class
|
/// If `obj_expr` is typed as a pointer to a runtime Obj-C class
|
||||||
/// and that class (or any of its `#extends` ancestors) declares a
|
/// and that class (or any of its `#extends` ancestors) declares a
|
||||||
/// `#property` field with the given name, return the
|
/// `#property` field with the given name, return the
|
||||||
/// `RuntimeFieldDecl`. M2.2 + M2.3.
|
/// `RuntimeFieldDecl`. M2.2 + M2.3.
|
||||||
@@ -257,7 +257,7 @@ pub fn lowerObjcPropertySetter(self: *Lowering, obj_expr: *const ast.Node, field
|
|||||||
|
|
||||||
/// Get a FuncId for an external C-callconv function. If a function
|
/// Get a FuncId for an external C-callconv function. If a function
|
||||||
/// with this exported name already exists in the module (e.g.
|
/// with this exported name already exists in the module (e.g.
|
||||||
/// declared by stdlib `#foreign` decl), return it; otherwise
|
/// declared by stdlib `extern` decl), return it; otherwise
|
||||||
/// declare it fresh with the given signature.
|
/// declare it fresh with the given signature.
|
||||||
///
|
///
|
||||||
/// One helper instead of a `get<Name>Fid` per runtime function —
|
/// One helper instead of a `get<Name>Fid` per runtime function —
|
||||||
@@ -729,7 +729,7 @@ pub fn emitObjcDefinedClassImp(self: *Lowering, fcd: *const ast.RuntimeClassDecl
|
|||||||
|
|
||||||
// Pass the Obj-C receiver pointer through to the sx body as
|
// Pass the Obj-C receiver pointer through to the sx body as
|
||||||
// `self`. The body's `self: *Self` type resolves to the
|
// `self`. The body's `self: *Self` type resolves to the
|
||||||
// foreign-class stub (the opaque Obj-C type), matching Apple's
|
// runtime-class stub (the opaque Obj-C type), matching Apple's
|
||||||
// Obj-C semantics where `self` IS the object. `self.field`
|
// Obj-C semantics where `self` IS the object. `self.field`
|
||||||
// access on a sx-defined class is rewritten by lowerFieldAccess
|
// access on a sx-defined class is rewritten by lowerFieldAccess
|
||||||
// to go through `object_getIvar(self, __sx_state_ivar)` and
|
// to go through `object_getIvar(self, __sx_state_ivar)` and
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pub const Module = struct {
|
|||||||
/// the class object through this cache once per module.
|
/// the class object through this cache once per module.
|
||||||
objc_class_cache: std.ArrayList(ObjcClassEntry),
|
objc_class_cache: std.ArrayList(ObjcClassEntry),
|
||||||
/// sx-defined Obj-C classes — every `Cls :: #objc_class("Cls") { ... }`
|
/// sx-defined Obj-C classes — every `Cls :: #objc_class("Cls") { ... }`
|
||||||
/// declaration WITHOUT `#foreign`. Insertion-ordered so the
|
/// declaration WITHOUT `extern`. Insertion-ordered so the
|
||||||
/// class-registration constructors (M1.2 A.4) emit in source order
|
/// class-registration constructors (M1.2 A.4) emit in source order
|
||||||
/// — parent classes register before children, which matters because
|
/// — parent classes register before children, which matters because
|
||||||
/// `objc_allocateClassPair(super, ...)` resolves `super` by lookup.
|
/// `objc_allocateClassPair(super, ...)` resolves `super` by lookup.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub const VisibilityMode = enum {
|
|||||||
/// own scope ∪ `flat_import_graph`. The PERMANENT core for bare-name lookup
|
/// own scope ∪ `flat_import_graph`. The PERMANENT core for bare-name lookup
|
||||||
/// under flat imports (Agra constraint) — never a transitional path.
|
/// under flat imports (Agra constraint) — never a transitional path.
|
||||||
user_bare_flat,
|
user_bare_flat,
|
||||||
/// `user_bare_flat` plus the foreign-C gate (today's `isCImportVisible`):
|
/// `user_bare_flat` plus the extern-C gate (today's `isCImportVisible`):
|
||||||
/// only C-import `fn_decl`s without a `library_ref` are policed; everything
|
/// only C-import `fn_decl`s without a `library_ref` are policed; everything
|
||||||
/// else is unconditionally visible.
|
/// else is unconditionally visible.
|
||||||
c_import_bare,
|
c_import_bare,
|
||||||
@@ -172,13 +172,13 @@ pub fn fnDeclOf(raw: RawDeclRef) ?*const ast.FnDecl {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A PLAIN free function — no type params, an ordinary (non-`#foreign`/
|
/// A PLAIN free function — no type params, an ordinary (non-`extern`/
|
||||||
/// `#builtin`/`#compiler`/`extern`) body — the only callable kind the bare-call
|
/// `#builtin`/`#compiler`/`extern`) body — the only callable kind the bare-call
|
||||||
/// verdict counts.
|
/// verdict counts.
|
||||||
pub fn isPlainFreeFnDecl(fd: *const ast.FnDecl) bool {
|
pub fn isPlainFreeFnDecl(fd: *const ast.FnDecl) bool {
|
||||||
if (fd.type_params.len > 0) return false;
|
if (fd.type_params.len > 0) return false;
|
||||||
// An `extern` import is an external C symbol with no sx-lowerable body —
|
// An `extern` import is an external C symbol with no sx-lowerable body —
|
||||||
// dispatched name-keyed first-wins, exactly like a `#foreign` body, so it
|
// dispatched name-keyed first-wins, exactly like a `extern` body, so it
|
||||||
// is NOT a plain free fn (excluded from the bare-call ambiguity verdict and
|
// is NOT a plain free fn (excluded from the bare-call ambiguity verdict and
|
||||||
// the out-of-line-slot / shadow-author pass). `export` DEFINES a real sx
|
// the out-of-line-slot / shadow-author pass). `export` DEFINES a real sx
|
||||||
// body, so it stays plain-free.
|
// body, so it stays plain-free.
|
||||||
@@ -227,8 +227,8 @@ pub fn classifyHeadKind(raw: RawDeclRef, gs: *bool, tf: *bool, pr: *bool) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True when the bare-type verdict selected a foreign-class author
|
/// True when the bare-type verdict selected a runtime-class author
|
||||||
/// unambiguously. Used by lowering to route to the foreign-class path.
|
/// unambiguously. Used by lowering to route to the runtime-class path.
|
||||||
pub fn runtimeClassWinsType(set: AuthorSet, verdict: Verdict) bool {
|
pub fn runtimeClassWinsType(set: AuthorSet, verdict: Verdict) bool {
|
||||||
return switch (verdict) {
|
return switch (verdict) {
|
||||||
.own_wins => if (set.own) |a| std.meta.activeTag(a.raw) == .runtime_class_decl else false,
|
.own_wins => if (set.own) |a| std.meta.activeTag(a.raw) == .runtime_class_decl else false,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const TypeResolver = type_resolver.TypeResolver;
|
|||||||
/// imported / library modules are trusted, matching `checkErrorFlow`.
|
/// imported / library modules are trusted, matching `checkErrorFlow`.
|
||||||
///
|
///
|
||||||
/// Queries the canonical facts rather than maintaining a parallel authoritative
|
/// Queries the canonical facts rather than maintaining a parallel authoritative
|
||||||
/// list: declared top-level names come from `ProgramIndex` (foreign classes,
|
/// list: declared top-level names come from `ProgramIndex` (runtime classes,
|
||||||
/// generic templates, protocols, aliases) plus the AST decl/scope walk (for
|
/// generic templates, protocols, aliases) plus the AST decl/scope walk (for
|
||||||
/// LOCAL type decls, which `ProgramIndex` doesn't track); primitives come from
|
/// LOCAL type decls, which `ProgramIndex` doesn't track); primitives come from
|
||||||
/// `TypeResolver.resolvePrimitive`; registered concrete types from the
|
/// `TypeResolver.resolvePrimitive`; registered concrete types from the
|
||||||
@@ -138,7 +138,7 @@ pub const UnknownTypeChecker = struct {
|
|||||||
// A function NAME is a binding site too: a bare reserved-name
|
// A function NAME is a binding site too: a bare reserved-name
|
||||||
// `i2 :: (…) {…}` (free fn or struct/impl method) is rejected,
|
// `i2 :: (…) {…}` (free fn or struct/impl method) is rejected,
|
||||||
// exactly like `i2 := …`. Backtick (`` `i2 :: … ``) and
|
// exactly like `i2 := …`. Backtick (`` `i2 :: … ``) and
|
||||||
// `#import c` foreign fns set `is_raw` and are exempt (0089).
|
// `#import c` extern fns set `is_raw` and are exempt (0089).
|
||||||
self.checkBindingName(fd.name, fd.name_span, fd.is_raw);
|
self.checkBindingName(fd.name, fd.name_span, fd.is_raw);
|
||||||
self.checkParamNames(fd.params);
|
self.checkParamNames(fd.params);
|
||||||
self.checkBindingNames(fd.body);
|
self.checkBindingNames(fd.body);
|
||||||
@@ -194,7 +194,7 @@ pub const UnknownTypeChecker = struct {
|
|||||||
if (os.binding) |b| self.checkBindingName(b, os.binding_span, os.binding_is_raw);
|
if (os.binding) |b| self.checkBindingName(b, os.binding_span, os.binding_is_raw);
|
||||||
self.checkBindingNames(os.body);
|
self.checkBindingNames(os.body);
|
||||||
},
|
},
|
||||||
// impl / protocol-default / foreign-class method bodies: each
|
// impl / protocol-default / runtime-class method bodies: each
|
||||||
// method introduces its own params + locals. A `#jni_main` /
|
// method introduces its own params + locals. A `#jni_main` /
|
||||||
// `#objc_class` bodied method is lowered (M1.2), so its reserved
|
// `#objc_class` bodied method is lowered (M1.2), so its reserved
|
||||||
// param/local names mis-lower the same as any other.
|
// param/local names mis-lower the same as any other.
|
||||||
@@ -365,7 +365,7 @@ pub const UnknownTypeChecker = struct {
|
|||||||
/// (a lambda default), so recurse into it.
|
/// (a lambda default), so recurse into it.
|
||||||
fn checkParamNames(self: UnknownTypeChecker, params: []const ast.Param) void {
|
fn checkParamNames(self: UnknownTypeChecker, params: []const ast.Param) void {
|
||||||
for (params) |p| {
|
for (params) |p| {
|
||||||
// A backtick raw param (`` (`i2: T) ``) or a `#import c` foreign
|
// A backtick raw param (`` (`i2: T) ``) or a `#import c` extern
|
||||||
// param is exempt from the reserved-type-name rule —
|
// param is exempt from the reserved-type-name rule —
|
||||||
// the exemption is honored inside `checkBindingName` via `p.is_raw`.
|
// the exemption is honored inside `checkBindingName` via `p.is_raw`.
|
||||||
self.checkBindingName(p.name, p.name_span, p.is_raw);
|
self.checkBindingName(p.name, p.name_span, p.is_raw);
|
||||||
@@ -375,7 +375,7 @@ pub const UnknownTypeChecker = struct {
|
|||||||
|
|
||||||
/// Collect every top-level name that can legitimately appear in a type
|
/// Collect every top-level name that can legitimately appear in a type
|
||||||
/// position: const-decl names (covers `T :: struct/enum/union/error/alias`
|
/// position: const-decl names (covers `T :: struct/enum/union/error/alias`
|
||||||
/// and value consts), plus the scan-populated foreign-class / generic-
|
/// and value consts), plus the scan-populated runtime-class / generic-
|
||||||
/// template / protocol / alias maps from `ProgramIndex`. Built across ALL
|
/// template / protocol / alias maps from `ProgramIndex`. Built across ALL
|
||||||
/// files so a main-file reference to an imported type isn't flagged.
|
/// files so a main-file reference to an imported type isn't flagged.
|
||||||
fn collectDeclaredTypeNames(self: UnknownTypeChecker, decls: []const *const Node, out: *std.StringHashMap(void)) void {
|
fn collectDeclaredTypeNames(self: UnknownTypeChecker, decls: []const *const Node, out: *std.StringHashMap(void)) void {
|
||||||
@@ -860,7 +860,7 @@ pub const UnknownTypeChecker = struct {
|
|||||||
/// correct without any lowering special-case.
|
/// correct without any lowering special-case.
|
||||||
/// `is_raw` is a REQUIRED argument, not a call-site guard: the exemption
|
/// `is_raw` is a REQUIRED argument, not a call-site guard: the exemption
|
||||||
/// lives INSIDE the check so no caller can validate a name without also
|
/// lives INSIDE the check so no caller can validate a name without also
|
||||||
/// honoring the backtick / `#import c` foreign exemption. This is what keeps
|
/// honoring the backtick / `#import c` extern exemption. This is what keeps
|
||||||
/// the check and the exemption from desyncing — the recurring failure of the
|
/// the check and the exemption from desyncing — the recurring failure of the
|
||||||
/// earlier attempts, where each site threaded an `if (!is_raw)` guard
|
/// earlier attempts, where each site threaded an `if (!is_raw)` guard
|
||||||
/// separately and one was forgotten.
|
/// separately and one was forgotten.
|
||||||
@@ -872,12 +872,12 @@ pub const UnknownTypeChecker = struct {
|
|||||||
|
|
||||||
/// Reserved-name check for a `::` declaration whose own name binds an
|
/// Reserved-name check for a `::` declaration whose own name binds an
|
||||||
/// identifier but carries no dedicated `name_span` field — struct / enum /
|
/// identifier but carries no dedicated `name_span` field — struct / enum /
|
||||||
/// union / error-set / protocol / foreign-class type decls, ufcs aliases,
|
/// union / error-set / protocol / runtime-class type decls, ufcs aliases,
|
||||||
/// and namespaced imports. Each such node begins at its name
|
/// and namespaced imports. Each such node begins at its name
|
||||||
/// token (`createNode(name_start, …)`), so the name's length isolates the
|
/// token (`createNode(name_start, …)`), so the name's length isolates the
|
||||||
/// caret onto the name — a single source for the span, no separate stored
|
/// caret onto the name — a single source for the span, no separate stored
|
||||||
/// field to drift from `node.span`. `is_raw` is REQUIRED, exactly as in
|
/// field to drift from `node.span`. `is_raw` is REQUIRED, exactly as in
|
||||||
/// `checkBindingName`: a backtick raw / `#import c` foreign name is exempt
|
/// `checkBindingName`: a backtick raw / `#import c` extern name is exempt
|
||||||
/// by construction.
|
/// by construction.
|
||||||
fn checkDeclName(self: UnknownTypeChecker, node: *const Node, name: []const u8, is_raw: bool) void {
|
fn checkDeclName(self: UnknownTypeChecker, node: *const Node, name: []const u8, is_raw: bool) void {
|
||||||
const span = ast.Span{ .start = node.span.start, .end = node.span.start + @as(u32, @intCast(name.len)) };
|
const span = ast.Span{ .start = node.span.start, .end = node.span.start + @as(u32, @intCast(name.len)) };
|
||||||
|
|||||||
@@ -1322,7 +1322,7 @@ pub const Server = struct {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Skip functions, types, structs, enums, unions, comptime, foreign, library
|
// Skip functions, types, structs, enums, unions, comptime, extern, library
|
||||||
switch (cd.value.data) {
|
switch (cd.value.data) {
|
||||||
.fn_decl, .type_expr, .struct_decl, .enum_decl, .union_decl,
|
.fn_decl, .type_expr, .struct_decl, .enum_decl, .union_decl,
|
||||||
.comptime_expr, .library_decl,
|
.comptime_expr, .library_decl,
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
defer c_handle.unload(io);
|
defer c_handle.unload(io);
|
||||||
timer.record("c-import");
|
timer.record("c-import");
|
||||||
|
|
||||||
// dlopen #library dependencies so JIT can resolve foreign symbols.
|
// dlopen #library dependencies so JIT can resolve extern symbols.
|
||||||
// Program-owned dylibs (the #import c unit first, then #library
|
// Program-owned dylibs (the #import c unit first, then #library
|
||||||
// deps in declaration order) also become PRIORITY search targets
|
// deps in declaration order) also become PRIORITY search targets
|
||||||
// for the JIT, consulted before the process-wide fallback.
|
// for the JIT, consulted before the process-wide fallback.
|
||||||
|
|||||||
@@ -250,19 +250,20 @@ pub const Parser = struct {
|
|||||||
return self.parseProtocolDecl(name, start_pos, name_is_raw);
|
return self.parseProtocolDecl(name, start_pos, name_is_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Foreign-type binding with optional prefix modifiers:
|
// Runtime-class binding with an optional `#jni_main` prefix modifier:
|
||||||
// [#foreign | #jni_main]* (#jni_class / #jni_interface / #objc_class /
|
// [#jni_main]* (#jni_class / #jni_interface / #objc_class /
|
||||||
// #objc_protocol / #swift_class / #swift_struct / #swift_protocol) ("path") { body }
|
// #objc_protocol / #swift_class / #swift_struct / #swift_protocol) ("path") { body }
|
||||||
//
|
//
|
||||||
// Define-by-default: bare `#jni_class("...")` declares a new class (sx-defined).
|
// Define-by-default: bare `#jni_class("...")` declares a new class (sx-defined).
|
||||||
// `#foreign` flips that to "reference an existing class on the foreign side."
|
// Postfix `extern` flips that to "reference an existing class on the runtime
|
||||||
// `#jni_main` flags the class as the launchable entry (Android Activity).
|
// side". `#jni_main` flags the class as the launchable entry (Android Activity).
|
||||||
const prefix_loc = self.current.loc;
|
const prefix_loc = self.current.loc;
|
||||||
if (self.tryParseRuntimeClassPrefix()) |prefix| {
|
if (self.tryParseRuntimeClassPrefix()) |prefix| {
|
||||||
// Phase 8 cutover: the prefix `#foreign` on a runtime-class directive is
|
// Phase 8 cutover: the removed prefix linkage directive on a
|
||||||
// removed — reference an existing class via the postfix `extern` modifier
|
// runtime-class directive — reference an existing class via the
|
||||||
// (`X :: #objc_class("…") extern { … }`) instead. `prefix_loc` pins the
|
// postfix `extern` modifier (`X :: #objc_class("…") extern { … }`)
|
||||||
// diagnostic to the `#foreign` token (already consumed by the lookahead).
|
// instead. `prefix_loc` pins the diagnostic to the removed-directive
|
||||||
|
// token (already consumed by the lookahead).
|
||||||
if (prefix.is_extern) {
|
if (prefix.is_extern) {
|
||||||
return self.failAt(prefix_loc, "`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
return self.failAt(prefix_loc, "`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||||
}
|
}
|
||||||
@@ -320,9 +321,9 @@ pub const Parser = struct {
|
|||||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = value, .value = bi, .name_span = name_span, .is_raw = name_is_raw } });
|
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = value, .value = bi, .name_span = name_span, .is_raw = name_is_raw } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 8 cutover: the prefix `#foreign` linkage directive has been
|
// Phase 8 cutover: the removed prefix linkage directive on a
|
||||||
// removed — reject it with a migration message (was: `name :: type
|
// const-with-type decl — reject it with a migration message (the
|
||||||
// #foreign [lib] ["c_name"];`, now `name :: type extern [lib] ["c_name"];`).
|
// postfix form is `name :: type extern [lib] ["c_name"];`).
|
||||||
if (self.current.tag == .hash_foreign) {
|
if (self.current.tag == .hash_foreign) {
|
||||||
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||||
}
|
}
|
||||||
@@ -411,16 +412,16 @@ pub const Parser = struct {
|
|||||||
return try self.createNode(start_pos, .{ .var_decl = .{ .name = name, .name_span = name_span, .type_annotation = type_node, .value = null, .is_raw = name_is_raw } });
|
return try self.createNode(start_pos, .{ .var_decl = .{ .name = name, .name_span = name_span, .type_annotation = type_node, .value = null, .is_raw = name_is_raw } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 8 cutover: prefix `#foreign` on a data global is removed —
|
// Phase 8 cutover: the removed prefix linkage directive on a data
|
||||||
// reject it (was `name : type #foreign [lib] ["c_name"];`, now
|
// global — reject it (the postfix form `name : type extern [lib]
|
||||||
// `name : type extern [lib] ["c_name"];`, handled by the `kw_extern` arm).
|
// ["c_name"];` is handled by the `kw_extern` arm below).
|
||||||
if (self.current.tag == .hash_foreign) {
|
if (self.current.tag == .hash_foreign) {
|
||||||
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.current.tag == .kw_extern) {
|
if (self.current.tag == .kw_extern) {
|
||||||
// name : type extern [LIB] ["csym"]; (extern data global — the
|
// name : type extern [LIB] ["csym"]; (extern data global — the
|
||||||
// extern-named counterpart of `#foreign`; resolved at link time)
|
// extern-named counterpart of `extern`; resolved at link time)
|
||||||
self.advance();
|
self.advance();
|
||||||
var ext_lib: ?[]const u8 = null;
|
var ext_lib: ?[]const u8 = null;
|
||||||
if (self.current.tag == .identifier) {
|
if (self.current.tag == .identifier) {
|
||||||
@@ -446,7 +447,7 @@ pub const Parser = struct {
|
|||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.fail("expected ':', '=', ';', '#foreign', or 'extern' after type annotation");
|
return self.fail("expected ':', '=', ';', or 'extern' after type annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseTypeExpr(self: *Parser) anyerror!*Node {
|
fn parseTypeExpr(self: *Parser) anyerror!*Node {
|
||||||
@@ -1299,9 +1300,9 @@ pub const Parser = struct {
|
|||||||
is_main: bool,
|
is_main: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Recognise an optional sequence of `#foreign` / `#jni_main` modifiers
|
/// Recognise an optional sequence of `extern` / `#jni_main` modifiers
|
||||||
/// followed by a type-introducer directive (`#jni_class`, `#objc_class`,
|
/// followed by a type-introducer directive (`#jni_class`, `#objc_class`,
|
||||||
/// ...). Returns null if the current position isn't a foreign-class
|
/// ...). Returns null if the current position isn't a runtime-class
|
||||||
/// directive (possibly after modifiers). Consumes the modifier tokens
|
/// directive (possibly after modifiers). Consumes the modifier tokens
|
||||||
/// only when a runtime directive follows; otherwise leaves the parser
|
/// only when a runtime directive follows; otherwise leaves the parser
|
||||||
/// state untouched.
|
/// state untouched.
|
||||||
@@ -1368,31 +1369,22 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
try self.expect(.l_paren);
|
try self.expect(.l_paren);
|
||||||
if (self.current.tag != .string_literal) {
|
if (self.current.tag != .string_literal) {
|
||||||
return self.fail("expected string literal foreign-type path after directive");
|
return self.fail("expected string literal runtime-class type path after directive");
|
||||||
}
|
}
|
||||||
const raw = self.tokenSlice(self.current);
|
const raw = self.tokenSlice(self.current);
|
||||||
const runtime_path = raw[1 .. raw.len - 1];
|
const runtime_path = raw[1 .. raw.len - 1];
|
||||||
self.advance();
|
self.advance();
|
||||||
try self.expect(.r_paren);
|
try self.expect(.r_paren);
|
||||||
|
|
||||||
// Phase 3 (FFI-linkage): optional postfix `extern` / `export` after the
|
// The postfix `extern` / `export` modifier after the `#objc_class("X")`
|
||||||
// `#objc_class("X")` directive — the new spelling that replaces the prefix
|
// directive (mirrors `struct #compiler` postfix placement):
|
||||||
// `#foreign` modifier (mirrors `struct #compiler` postfix placement).
|
// `… extern { … }` ⇒ reference an existing runtime class.
|
||||||
// `… extern { … }` ⇒ reference an existing runtime class (== `#foreign`).
|
// `… export { … }` ⇒ define + register a new sx class (the default).
|
||||||
// `… export { … }` ⇒ define + register a new sx class (== no `#foreign`).
|
// Maps onto `is_extern`, threaded into the runtime_class_decl node. (The
|
||||||
// Maps straight onto the existing `is_extern` decision so lowering is
|
// passed `is_extern` is always false here — the removed prefix linkage
|
||||||
// unchanged. The legacy prefix `#foreign` form still works via the
|
// form is rejected by the caller before this point.)
|
||||||
// `is_extern` argument; interplay/diagnostics for combining them is Phase 4.
|
|
||||||
var is_extern_eff = is_extern;
|
var is_extern_eff = is_extern;
|
||||||
if (self.current.tag == .kw_extern or self.current.tag == .kw_export) {
|
if (self.current.tag == .kw_extern or self.current.tag == .kw_export) {
|
||||||
// Prefix `#foreign` and the postfix `extern`/`export` keyword are two
|
|
||||||
// spellings of the same linkage axis — combining them is contradictory
|
|
||||||
// (`#foreign`=import vs `export`=define) or redundant (`#foreign … extern`).
|
|
||||||
// Reject at the postfix keyword rather than let it silently override.
|
|
||||||
if (is_extern) {
|
|
||||||
const kw = if (self.current.tag == .kw_export) "export" else "extern";
|
|
||||||
return self.failFmt("conflicting linkage: prefix '#foreign' cannot be combined with postfix '{s}'; use either '#foreign' or postfix 'extern'/'export', not both", .{kw});
|
|
||||||
}
|
|
||||||
is_extern_eff = self.current.tag == .kw_extern;
|
is_extern_eff = self.current.tag == .kw_extern;
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
@@ -1586,7 +1578,7 @@ pub const Parser = struct {
|
|||||||
try self.expect(.r_paren);
|
try self.expect(.r_paren);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method body is optional: `;` → declaration (foreign or inherited
|
// Method body is optional: `;` → declaration (extern or inherited
|
||||||
// method we just want to call); `{ ... }` → sx-side block body
|
// method we just want to call); `{ ... }` → sx-side block body
|
||||||
// for sx-defined classes; `=> expr;` → expression-body form
|
// for sx-defined classes; `=> expr;` → expression-body form
|
||||||
// (M1.0), lowered as a single-statement block holding `expr`.
|
// (M1.0), lowered as a single-statement block holding `expr`.
|
||||||
@@ -1981,7 +1973,7 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
// Optional `[LIB] ["csym"]` tail after extern/export — a library-alias
|
// Optional `[LIB] ["csym"]` tail after extern/export — a library-alias
|
||||||
// ident then a C symbol-name string, both optional (mirrors
|
// ident then a C symbol-name string, both optional (mirrors
|
||||||
// `#foreign LIB "csym"`). Stored on extern_lib/extern_name; the rename
|
// `extern LIB "csym"`). Stored on extern_lib/extern_name; the rename
|
||||||
// is consumed in `declareFunction`, the lib reference in Part B.
|
// is consumed in `declareFunction`, the lib reference in Part B.
|
||||||
var extern_lib: ?[]const u8 = null;
|
var extern_lib: ?[]const u8 = null;
|
||||||
var extern_name: ?[]const u8 = null;
|
var extern_name: ?[]const u8 = null;
|
||||||
@@ -1997,7 +1989,7 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, #compiler, or #foreign marker.
|
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, or #compiler marker.
|
||||||
// An `extern` import has NO body — just `;`. The extern_export modifier
|
// An `extern` import has NO body — just `;`. The extern_export modifier
|
||||||
// carries the linkage; we synthesize an empty block as the (non-optional)
|
// carries the linkage; we synthesize an empty block as the (non-optional)
|
||||||
// body placeholder, and lowering routes on the modifier rather than this
|
// body placeholder, and lowering routes on the modifier rather than this
|
||||||
@@ -2026,7 +2018,7 @@ pub const Parser = struct {
|
|||||||
self.advance();
|
self.advance();
|
||||||
break :blk try self.createNode(ci_start, .{ .compiler_expr = {} });
|
break :blk try self.createNode(ci_start, .{ .compiler_expr = {} });
|
||||||
} else if (self.current.tag == .hash_foreign) {
|
} else if (self.current.tag == .hash_foreign) {
|
||||||
// Phase 8 cutover: the fn-body `#foreign` marker is removed — reject it.
|
// Phase 8 cutover: the removed fn-body linkage marker — reject it.
|
||||||
// The import form is now the postfix `extern` keyword handled above
|
// The import form is now the postfix `extern` keyword handled above
|
||||||
// (`f :: (…) -> R extern [LIB] ["csym"];`).
|
// (`f :: (…) -> R extern [LIB] ["csym"];`).
|
||||||
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||||
@@ -3650,7 +3642,7 @@ pub const Parser = struct {
|
|||||||
// ends with `;` directly after the param list — recognise it as a
|
// ends with `;` directly after the param list — recognise it as a
|
||||||
// function def (not a constant) so it goes through parseFnDecl.
|
// function def (not a constant) so it goes through parseFnDecl.
|
||||||
if (self.struct_default_compiler and tag == .semicolon) return true;
|
if (self.struct_default_compiler and tag == .semicolon) return true;
|
||||||
// `(T1, T2) -> R` without a trailing body (`{`, `=>`, or a foreign/
|
// `(T1, T2) -> R` without a trailing body (`{`, `=>`, or an extern/
|
||||||
// builtin marker) is a function-type literal, not a function def.
|
// builtin marker) is a function-type literal, not a function def.
|
||||||
if (tag == .arrow) return self.hasFnBodyAfterArrow();
|
if (tag == .arrow) return self.hasFnBodyAfterArrow();
|
||||||
// `kw_extern`/`kw_export`: a postfix linkage modifier (e.g. `f :: () extern;`
|
// `kw_extern`/`kw_export`: a postfix linkage modifier (e.g. `f :: () extern;`
|
||||||
@@ -4216,7 +4208,7 @@ test "parse void function with builtin body" {
|
|||||||
test "parse void function with extern import" {
|
test "parse void function with extern import" {
|
||||||
// A postfix `extern LIB` fn import builds an empty-block body +
|
// A postfix `extern LIB` fn import builds an empty-block body +
|
||||||
// extern_export = .extern_ + extern_lib. (Phase 8 removed the legacy
|
// extern_export = .extern_ + extern_lib. (Phase 8 removed the legacy
|
||||||
// prefix `#foreign` spelling that used to produce this same shape.)
|
// prefix `extern` spelling that used to produce this same shape.)
|
||||||
const source = "InitWindow :: (width: i32, height: i32, title: *u8) -> void extern rl;";
|
const source = "InitWindow :: (width: i32, height: i32, title: *u8) -> void extern rl;";
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|||||||
@@ -1337,7 +1337,7 @@ pub const Analyzer = struct {
|
|||||||
if (fd.is_extern and fd.is_main) {
|
if (fd.is_extern and fd.is_main) {
|
||||||
try self.diagnostics.append(self.allocator, .{
|
try self.diagnostics.append(self.allocator, .{
|
||||||
.level = .err,
|
.level = .err,
|
||||||
.message = "'#foreign' and '#jni_main' / '#objc_main' are mutually exclusive — a foreign-referenced class can't be the app's main entry",
|
.message = "'extern' and '#jni_main' / '#objc_main' are mutually exclusive — a extern-referenced class can't be the app's main entry",
|
||||||
.span = node.span,
|
.span = node.span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1346,7 +1346,7 @@ pub const Analyzer = struct {
|
|||||||
.method => |md| if (md.body != null) {
|
.method => |md| if (md.body != null) {
|
||||||
try self.diagnostics.append(self.allocator, .{
|
try self.diagnostics.append(self.allocator, .{
|
||||||
.level = .err,
|
.level = .err,
|
||||||
.message = "methods on a '#foreign' class can't have bodies — they reference foreign-runtime implementations",
|
.message = "methods on a 'extern' class can't have bodies — they reference runtime implementations",
|
||||||
.span = node.span,
|
.span = node.span,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -535,7 +535,7 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
|
|||||||
try argv.append(allocator, try std.fmt.allocPrint(allocator, "-L{s}", .{lp}));
|
try argv.append(allocator, try std.fmt.allocPrint(allocator, "-L{s}", .{lp}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-detect host OS library paths when linking foreign libraries
|
// Auto-detect host OS library paths when linking external libraries
|
||||||
if (libraries.len > 0 and target_config.triple == null) {
|
if (libraries.len > 0 and target_config.triple == null) {
|
||||||
for (host_lib_paths) |path| {
|
for (host_lib_paths) |path| {
|
||||||
try argv.append(allocator, try std.fmt.allocPrint(allocator, "-L{s}", .{path}));
|
try argv.append(allocator, try std.fmt.allocPrint(allocator, "-L{s}", .{path}));
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ pub const Tag = enum {
|
|||||||
hash_swift_class, // Foo :: #swift_class("Module.Type") { ...body... }
|
hash_swift_class, // Foo :: #swift_class("Module.Type") { ...body... }
|
||||||
hash_swift_struct, // Foo :: #swift_struct("Module.Type") { ...body... }
|
hash_swift_struct, // Foo :: #swift_struct("Module.Type") { ...body... }
|
||||||
hash_swift_protocol, // Foo :: #swift_protocol("Module.Proto") { ...body... }
|
hash_swift_protocol, // Foo :: #swift_protocol("Module.Proto") { ...body... }
|
||||||
hash_extends, // `#extends Alias;` inside a foreign-class body
|
hash_extends, // `#extends Alias;` inside a runtime-class body
|
||||||
hash_implements, // `#implements Alias;` inside a foreign-class body
|
hash_implements, // `#implements Alias;` inside a runtime-class body
|
||||||
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
||||||
hash_selector, // `#selector("explicit:string")` per-method Obj-C selector override (Phase 3.2)
|
hash_selector, // `#selector("explicit:string")` per-method Obj-C selector override (Phase 3.2)
|
||||||
hash_property, // `#property[(modifier, ...)]` field directive — synthesizes getter/setter dispatch (M2.2)
|
hash_property, // `#property[(modifier, ...)]` field directive — synthesizes getter/setter dispatch (M2.2)
|
||||||
|
|||||||
Reference in New Issue
Block a user