feat(ffi-linkage): fn-path accepts postfix extern/export + lib/name fields (Phase 1.0a)
parseFnDecl now calls parseOptionalExternExport() after the callconv
slot and stores the modifier on FnDecl.extern_export. For 'extern' the
body is ';' (an empty-block placeholder — the modifier carries the
linkage, no *_expr node, per the naming constraint). Both fn-decl
lookahead predicates (isFunctionDef, hasFnBodyAfterArrow) now treat
kw_extern/kw_export as fn-body markers beside kw_callconv, so
'(...) -> R extern;' is recognized as a fn def rather than a fn-type
const.
Per user feedback, decision 4 ("library separate") is REVISED: extern
carries an optional LIB + "csym" axis mirroring '#foreign LIB "csym"',
so it is a true #foreign superset (Gate A->B requirement — the Part B
migration of 466 #foreign uses across 6 libs must preserve each
symbol's library). Added FnDecl.extern_lib/extern_name and
VarDecl.extern_lib (beside is_extern/extern_name).
All unconsumed by lowering: extern parses, but a fn still errors at
sema (body produces no value). Suite green (443 unit / 633 corpus).
lock commit.
This commit is contained in:
22
src/ast.zig
22
src/ast.zig
@@ -143,6 +143,15 @@ pub const FnDecl = struct {
|
||||
/// `callconv(...)` slot. `.none` for an ordinary sx-internal function.
|
||||
/// Parsed in Phase 0.1; not consumed by the fn-decl path until Phase 1.
|
||||
extern_export: ExternExportModifier = .none,
|
||||
/// Optional library reference + symbol-name override for an `extern`/`export`
|
||||
/// function, mirroring `#foreign LIB "csym"` (foreign_lib/foreign_name). Both
|
||||
/// optional: `extern` alone resolves the sx name against the default-linked
|
||||
/// libs; `extern LIB` names the source library; `extern "csym"` renames the
|
||||
/// symbol. Required for `extern` to be a behavior-equivalent superset of
|
||||
/// `#foreign` (Gate A→B) — the migration of 466 `#foreign` uses across 6 libs
|
||||
/// must preserve each symbol's library. Parsed/consumed in Phase 1.2.
|
||||
extern_lib: ?[]const u8 = null,
|
||||
extern_name: ?[]const u8 = null,
|
||||
/// Span of the function's name token, for the reserved-type-name decl
|
||||
/// diagnostic. Synthesized decls (e.g. `#import c` foreign
|
||||
/// functions, lowering-time objc/protocol method synthesis) leave it zero.
|
||||
@@ -355,12 +364,15 @@ pub const VarDecl = struct {
|
||||
is_foreign: bool = false,
|
||||
foreign_lib: ?[]const u8 = null,
|
||||
foreign_name: ?[]const u8 = null,
|
||||
/// `extern`-global form `g : T extern ["csym"];` — a reference to a global
|
||||
/// defined elsewhere (external linkage, resolved at link time). The new
|
||||
/// extern-named surface; distinct from the legacy `#foreign` path above.
|
||||
/// `extern_name` is the optional symbol-name override. Parsed in Phase 0.1;
|
||||
/// not consumed by the var-decl path until Phase 1.2.
|
||||
/// `extern`-global form `g : T extern [LIB] ["csym"];` — a reference to a
|
||||
/// global defined elsewhere (external linkage, resolved at link time). The
|
||||
/// new extern-named surface; distinct from the legacy `#foreign` path above.
|
||||
/// `extern_lib` is the optional source-library reference and `extern_name`
|
||||
/// the optional symbol-name override (mirroring foreign_lib/foreign_name, so
|
||||
/// `extern` fully supersedes `#foreign`). Parsed in Phase 0.1; not consumed
|
||||
/// by the var-decl path until Phase 1.2.
|
||||
is_extern: bool = false,
|
||||
extern_lib: ?[]const u8 = null,
|
||||
extern_name: ?[]const u8 = null,
|
||||
/// True when the binding name was written as a backtick raw identifier
|
||||
/// (`` `i2 := … ``). A raw name is exempt from the reserved-type-name
|
||||
|
||||
@@ -1949,9 +1949,22 @@ pub const Parser = struct {
|
||||
// Optional calling convention: callconv(.c)
|
||||
const call_conv = try self.parseOptionalCallConv();
|
||||
|
||||
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, #compiler, or #foreign marker
|
||||
// Optional postfix linkage modifier: `extern` (import) / `export` (define).
|
||||
const extern_export = self.parseOptionalExternExport();
|
||||
|
||||
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, #compiler, or #foreign 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
|
||||
// block (no `*_expr` node — naming-constraint rule). `export` keeps its
|
||||
// `{ … }` body and flows through the normal chain below.
|
||||
var is_arrow = false;
|
||||
const body = if (self.current.tag == .hash_builtin) blk: {
|
||||
const body = if (extern_export == .extern_) blk: {
|
||||
const semi_start = self.current.loc.start;
|
||||
try self.expect(.semicolon);
|
||||
const stmts = try self.allocator.alloc(*Node, 0);
|
||||
break :blk try self.createNode(semi_start, .{ .block = .{ .stmts = stmts, .produces_value = false } });
|
||||
} else if (self.current.tag == .hash_builtin) blk: {
|
||||
const bi_start = self.current.loc.start;
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
@@ -2011,6 +2024,7 @@ pub const Parser = struct {
|
||||
.type_params = type_params,
|
||||
.is_arrow = is_arrow,
|
||||
.call_conv = call_conv,
|
||||
.extern_export = extern_export,
|
||||
.name_span = name_span,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
@@ -3609,7 +3623,9 @@ pub const Parser = struct {
|
||||
// `(T1, T2) -> R` without a trailing body (`{`, `=>`, or a foreign/
|
||||
// builtin marker) is a function-type literal, not a function def.
|
||||
if (tag == .arrow) return self.hasFnBodyAfterArrow();
|
||||
return tag == .l_brace or tag == .hash_builtin or tag == .hash_compiler or tag == .hash_foreign or tag == .fat_arrow or tag == .kw_callconv;
|
||||
// `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;
|
||||
}
|
||||
|
||||
fn hasFnBodyAfterArrow(self: *Parser) bool {
|
||||
@@ -3637,6 +3653,9 @@ pub const Parser = struct {
|
||||
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 == .kw_callconv) return true;
|
||||
// Postfix linkage modifier after the return type: `-> R extern;` /
|
||||
// `-> R export { … }` (and `-> R callconv(.c) extern`). Marks a fn def.
|
||||
if (self.current.tag == .kw_extern or self.current.tag == .kw_export) return true;
|
||||
// Inside a `struct #compiler` block, a `(...) -> Ret;` ending
|
||||
// with `;` after the return type is a `#compiler` method
|
||||
// declaration (body implicit). Outside that context, the same
|
||||
|
||||
Reference in New Issue
Block a user