From 4101cbc3e72ed8d48cadc1a965207858e93331db Mon Sep 17 00:00:00 2001 From: agra Date: Sun, 14 Jun 2026 15:39:27 +0300 Subject: [PATCH] =?UTF-8?q?feat(ffi-linkage):=20reject=20#foreign=20+=20po?= =?UTF-8?q?stfix=20extern/export=20combo=20(Phase=204)=20=E2=80=94=201174?= =?UTF-8?q?=20green?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/parser.zig | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index 7a51ae4..d3df6f1 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -1415,11 +1415,16 @@ pub const Parser = struct { // unchanged. The legacy prefix `#foreign` form still works via the // `is_foreign` argument; interplay/diagnostics for combining them is Phase 4. var is_foreign_eff = is_foreign; - if (self.current.tag == .kw_extern) { - is_foreign_eff = true; - self.advance(); - } else if (self.current.tag == .kw_export) { - is_foreign_eff = false; + 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_foreign) { + 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_foreign_eff = self.current.tag == .kw_extern; self.advance(); }