refactor(ffi-linkage)!: Phase 9.0 — delete the hash_foreign token (src is foreign-free)

Per user directive (total purge): remove the hash_foreign token entirely rather than
keep it for a friendly deprecation message. Deleted: the token enum (token.zig), the
lexer keyword entry + directive-list mention + lex test (lexer.zig), the 4 parser
rejection sites + 2 lookahead clauses + the runtime-class prefix #foreign peek arm
(parser.zig), and the lsp completion arm (server.zig). '#foreign' now lexes as an
invalid '#' token → a generic 'expected ;' parse error (no migration hint — the
accepted UX cost of zero-foreign). Deleted examples/1176-diagnostics-foreign-removed
(its purpose, the friendly rejection, no longer exists).

src/ now contains ZERO 'foreign' (case-insensitive). Suite green (645 corpus / 443
unit, 0 failed). Remaining for the 9.4 gate: issues/*.md prose + example filenames.
This commit is contained in:
agra
2026-06-15 10:59:59 +03:00
parent 811a280517
commit dfae690b31
8 changed files with 10 additions and 67 deletions

View File

@@ -1,10 +0,0 @@
// Phase 8 (FFI-linkage) cutover: the prefix `#foreign` linkage directive has
// been removed. A declaration imports an external C symbol via the postfix
// `extern` keyword (or defines + exposes one via `export`). The parser rejects
// `#foreign` with a clear migration message instead of routing it onto `extern`.
//
// Expected: one error on the `#foreign` token; exit 1.
abs_c :: (n: i32) -> i32 #foreign;
main :: () -> i32 { 0 }

View File

@@ -1,5 +0,0 @@
error: `#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead
--> examples/1176-diagnostics-foreign-removed.sx:8:26
|
8 | abs_c :: (n: i32) -> i32 #foreign;
| ^^^^^^^^

View File

@@ -69,7 +69,7 @@ pub const Lexer = struct {
}
// Directives: #import, #insert, #run, #builtin, #foreign, #library, #string
// Directives: #import, #insert, #run, #builtin, #library, #string
if (c == '#') {
// #string needs special handling (heredoc)
const str_kw = "#string";
@@ -88,7 +88,6 @@ pub const Lexer = struct {
.{ "#run", Tag.hash_run },
.{ "#builtin", Tag.hash_builtin },
.{ "#compiler", Tag.hash_compiler },
.{ "#foreign", Tag.hash_foreign },
.{ "#library", Tag.hash_library },
.{ "#framework", Tag.hash_framework },
.{ "#using", Tag.hash_using },
@@ -623,15 +622,6 @@ test "lex hash_insert" {
try std.testing.expectEqual(Tag.invalid, lex2.next().tag);
}
test "lex hash_foreign" {
var lex = Lexer.init("#foreign");
try std.testing.expectEqual(Tag.hash_foreign, lex.next().tag);
try std.testing.expectEqual(Tag.eof, lex.next().tag);
var lex2 = Lexer.init("#foreignx");
try std.testing.expectEqual(Tag.invalid, lex2.next().tag);
}
test "lex hash_library" {
var lex = Lexer.init("#library \"raylib\"");
try std.testing.expectEqual(Tag.hash_library, lex.next().tag);

View File

@@ -1690,7 +1690,6 @@ pub const Server = struct {
.hash_insert,
.hash_builtin,
.hash_compiler,
.hash_foreign,
.hash_library,
.hash_framework,
.hash_using,

View File

@@ -257,16 +257,7 @@ pub const Parser = struct {
// Define-by-default: bare `#jni_class("...")` declares a new class (sx-defined).
// 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 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");
}
return self.parseRuntimeClassDecl(name, start_pos, prefix.runtime, prefix.is_extern, prefix.is_main, name_is_raw);
}
@@ -321,13 +312,6 @@ 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 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");
}
try self.expect(.semicolon);
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = null, .value = value, .name_span = name_span, .is_raw = name_is_raw } });
}
@@ -412,16 +396,9 @@ 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: 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 `extern`; resolved at link time)
// name : type extern [LIB] ["csym"]; (extern data global, resolved
// at link time)
self.advance();
var ext_lib: ?[]const u8 = null;
if (self.current.tag == .identifier) {
@@ -1307,17 +1284,16 @@ pub const Parser = struct {
/// only when a runtime directive follows; otherwise leaves the parser
/// state untouched.
fn tryParseRuntimeClassPrefix(self: *Parser) ?RuntimeClassPrefix {
// Peek ahead through modifier tokens to confirm a directive follows.
// Peek ahead through the optional `#jni_main` modifier to confirm a
// runtime-class directive follows. (`is_extern` — reference vs define —
// is decided by the POSTFIX `extern`/`export` keyword in parseRuntimeClassDecl,
// never a prefix; it stays false here.)
var lookahead_idx: usize = 0;
var is_extern = false;
const is_extern = false;
var is_main = false;
while (true) {
const tag = self.peekTag(lookahead_idx);
switch (tag) {
.hash_foreign => {
is_extern = true;
lookahead_idx += 1;
},
.hash_jni_main => {
is_main = true;
lookahead_idx += 1;
@@ -2017,11 +1993,6 @@ pub const Parser = struct {
const ci_start = self.current.loc.start;
self.advance();
break :blk try self.createNode(ci_start, .{ .compiler_expr = {} });
} else if (self.current.tag == .hash_foreign) {
// 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");
} else if (self.current.tag == .fat_arrow) blk: {
is_arrow = true;
self.advance();
@@ -3647,7 +3618,7 @@ pub const Parser = struct {
if (tag == .arrow) return self.hasFnBodyAfterArrow();
// `kw_extern`/`kw_export`: a postfix linkage modifier (e.g. `f :: () extern;`
// with no return type) marks a fn decl just like `callconv`.
return tag == .l_brace or tag == .hash_builtin or tag == .hash_compiler or tag == .hash_foreign or tag == .fat_arrow or tag == .kw_callconv or tag == .kw_extern or tag == .kw_export;
return tag == .l_brace or tag == .hash_builtin or tag == .hash_compiler or tag == .fat_arrow or tag == .kw_callconv or tag == .kw_extern or tag == .kw_export;
}
fn hasFnBodyAfterArrow(self: *Parser) bool {
@@ -3673,7 +3644,7 @@ pub const Parser = struct {
while (self.current.tag != .eof) {
if (self.current.tag == .fat_arrow) return true;
if (self.current.tag == .l_brace) return true;
if (self.current.tag == .hash_builtin or self.current.tag == .hash_compiler or self.current.tag == .hash_foreign) return true;
if (self.current.tag == .hash_builtin or self.current.tag == .hash_compiler) return true;
if (self.current.tag == .kw_callconv) return true;
// Postfix linkage modifier after the return type: `-> R extern;` /
// `-> R export { … }` (and `-> R callconv(.c) extern`). Marks a fn def.

View File

@@ -118,7 +118,6 @@ pub const Tag = enum {
hash_insert, // #insert
hash_builtin, // #builtin
hash_compiler, // #compiler
hash_foreign, // #foreign
hash_library, // #library
hash_framework, // #framework
hash_using, // #using