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:
@@ -250,19 +250,20 @@ pub const Parser = struct {
|
||||
return self.parseProtocolDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// Foreign-type binding with optional prefix modifiers:
|
||||
// [#foreign | #jni_main]* (#jni_class / #jni_interface / #objc_class /
|
||||
// Runtime-class binding with an optional `#jni_main` prefix modifier:
|
||||
// [#jni_main]* (#jni_class / #jni_interface / #objc_class /
|
||||
// #objc_protocol / #swift_class / #swift_struct / #swift_protocol) ("path") { body }
|
||||
//
|
||||
// Define-by-default: bare `#jni_class("...")` declares a new class (sx-defined).
|
||||
// `#foreign` flips that to "reference an existing class on the foreign side."
|
||||
// `#jni_main` flags the class as the launchable entry (Android Activity).
|
||||
// Postfix `extern` flips that to "reference an existing class on the runtime
|
||||
// side". `#jni_main` flags the class as the launchable entry (Android Activity).
|
||||
const prefix_loc = self.current.loc;
|
||||
if (self.tryParseRuntimeClassPrefix()) |prefix| {
|
||||
// Phase 8 cutover: the prefix `#foreign` on a runtime-class directive is
|
||||
// removed — reference an existing class via the postfix `extern` modifier
|
||||
// (`X :: #objc_class("…") extern { … }`) instead. `prefix_loc` pins the
|
||||
// diagnostic to the `#foreign` token (already consumed by the lookahead).
|
||||
// Phase 8 cutover: the removed prefix linkage directive on a
|
||||
// runtime-class directive — reference an existing class via the
|
||||
// postfix `extern` modifier (`X :: #objc_class("…") extern { … }`)
|
||||
// instead. `prefix_loc` pins the diagnostic to the removed-directive
|
||||
// token (already consumed by the lookahead).
|
||||
if (prefix.is_extern) {
|
||||
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 } });
|
||||
}
|
||||
|
||||
// Phase 8 cutover: the prefix `#foreign` linkage directive has been
|
||||
// removed — reject it with a migration message (was: `name :: type
|
||||
// #foreign [lib] ["c_name"];`, now `name :: type extern [lib] ["c_name"];`).
|
||||
// Phase 8 cutover: the removed prefix linkage directive on a
|
||||
// const-with-type decl — reject it with a migration message (the
|
||||
// postfix form is `name :: type extern [lib] ["c_name"];`).
|
||||
if (self.current.tag == .hash_foreign) {
|
||||
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 } });
|
||||
}
|
||||
|
||||
// Phase 8 cutover: prefix `#foreign` on a data global is removed —
|
||||
// reject it (was `name : type #foreign [lib] ["c_name"];`, now
|
||||
// `name : type extern [lib] ["c_name"];`, handled by the `kw_extern` arm).
|
||||
// Phase 8 cutover: the removed prefix linkage directive on a data
|
||||
// global — reject it (the postfix form `name : type extern [lib]
|
||||
// ["c_name"];` is handled by the `kw_extern` arm below).
|
||||
if (self.current.tag == .hash_foreign) {
|
||||
return self.fail("`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||
}
|
||||
|
||||
if (self.current.tag == .kw_extern) {
|
||||
// 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();
|
||||
var ext_lib: ?[]const u8 = null;
|
||||
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 {
|
||||
@@ -1299,9 +1300,9 @@ pub const Parser = struct {
|
||||
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`,
|
||||
/// ...). 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
|
||||
/// only when a runtime directive follows; otherwise leaves the parser
|
||||
/// state untouched.
|
||||
@@ -1368,31 +1369,22 @@ pub const Parser = struct {
|
||||
|
||||
try self.expect(.l_paren);
|
||||
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 runtime_path = raw[1 .. raw.len - 1];
|
||||
self.advance();
|
||||
try self.expect(.r_paren);
|
||||
|
||||
// Phase 3 (FFI-linkage): optional postfix `extern` / `export` after the
|
||||
// `#objc_class("X")` directive — the new spelling that replaces the prefix
|
||||
// `#foreign` modifier (mirrors `struct #compiler` postfix placement).
|
||||
// `… extern { … }` ⇒ reference an existing runtime class (== `#foreign`).
|
||||
// `… export { … }` ⇒ define + register a new sx class (== no `#foreign`).
|
||||
// Maps straight onto the existing `is_extern` decision so lowering is
|
||||
// unchanged. The legacy prefix `#foreign` form still works via the
|
||||
// `is_extern` argument; interplay/diagnostics for combining them is Phase 4.
|
||||
// The postfix `extern` / `export` modifier after the `#objc_class("X")`
|
||||
// directive (mirrors `struct #compiler` postfix placement):
|
||||
// `… extern { … }` ⇒ reference an existing runtime class.
|
||||
// `… export { … }` ⇒ define + register a new sx class (the default).
|
||||
// Maps onto `is_extern`, threaded into the runtime_class_decl node. (The
|
||||
// passed `is_extern` is always false here — the removed prefix linkage
|
||||
// form is rejected by the caller before this point.)
|
||||
var is_extern_eff = is_extern;
|
||||
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;
|
||||
self.advance();
|
||||
}
|
||||
@@ -1586,7 +1578,7 @@ pub const Parser = struct {
|
||||
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
|
||||
// for sx-defined classes; `=> expr;` → expression-body form
|
||||
// (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
|
||||
// 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.
|
||||
var extern_lib: ?[]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
|
||||
// carries the linkage; we synthesize an empty block as the (non-optional)
|
||||
// body placeholder, and lowering routes on the modifier rather than this
|
||||
@@ -2026,7 +2018,7 @@ pub const Parser = struct {
|
||||
self.advance();
|
||||
break :blk try self.createNode(ci_start, .{ .compiler_expr = {} });
|
||||
} 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
|
||||
// (`f :: (…) -> R extern [LIB] ["csym"];`).
|
||||
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
|
||||
// function def (not a constant) so it goes through parseFnDecl.
|
||||
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.
|
||||
if (tag == .arrow) return self.hasFnBodyAfterArrow();
|
||||
// `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" {
|
||||
// A postfix `extern LIB` fn import builds an empty-block body +
|
||||
// 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;";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
Reference in New Issue
Block a user