diff --git a/src/ir/lower/decl.zig b/src/ir/lower/decl.zig index 6343964..3e95732 100644 --- a/src/ir/lower/decl.zig +++ b/src/ir/lower/decl.zig @@ -387,9 +387,9 @@ pub fn detectContextDecl(decls: []const *const Node) bool { pub fn funcWantsImplicitCtx(self: *const Lowering, fd: *const ast.FnDecl) bool { if (!self.implicit_ctx_enabled) return false; if (fd.call_conv == .c) return false; - // `extern` imports are external C symbols — C ABI, no sx context. - // (`export` defines get the same treatment in Phase 2, gap iv.) - if (fd.extern_export == .extern_) return false; + // `extern` imports and `export` defines are external C symbols — + // C ABI, no sx context (Phase 2, gap iv). + if (fd.extern_export != .none) return false; return switch (fd.body.data) { .foreign_expr, .builtin_expr, .compiler_expr => false, else => !isExportedEntryName(fd.name), @@ -1319,7 +1319,11 @@ pub fn lowerMainAndComptime(self: *Lowering, decls: []const *const Node) void { switch (decl.data) { .const_decl => |cd| { if (cd.value.data == .fn_decl) { - if (isExportedEntryName(cd.name)) { + // `export` defines are roots: their purpose is external + // consumption (often never called from sx), so force-lower + // them like OS-called entry points — else lazy lowering + // leaves them as bodiless `declare` stubs (Phase 2). + if (isExportedEntryName(cd.name) or cd.value.data.fn_decl.extern_export == .export_) { self.lazyLowerFunction(cd.name); } } else if (cd.value.data == .comptime_expr) { @@ -1327,7 +1331,7 @@ pub fn lowerMainAndComptime(self: *Lowering, decls: []const *const Node) void { } }, .fn_decl => |fd| { - if (isExportedEntryName(fd.name)) { + if (isExportedEntryName(fd.name) or fd.extern_export == .export_) { self.lazyLowerFunction(fd.name); } }, @@ -2117,7 +2121,7 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8) // typed by name as `(args) -> ret` of a `#foreign` decl can be // assigned to / passed as a `callconv(.c)` fn-pointer without a // call-convention mismatch. - const cc: Function.CallingConvention = if (fd.call_conv == .c or is_foreign or is_extern_decl) .c else .default; + const cc: Function.CallingConvention = if (fd.call_conv == .c or is_foreign or is_extern_decl or fd.extern_export == .export_) .c else .default; // Symbol-name override: `#foreign … "csym"` (foreign_expr.c_name) or the new // `extern … "csym"` (fd.extern_name). Declare under the C name and map the sx @@ -2399,8 +2403,9 @@ pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId return; } func.is_extern = false; // promote from extern stub to real function - func.linkage = if (isExportedEntryName(name)) .external else .internal; - if (fd.call_conv == .c) func.call_conv = .c; + // `export` defines force external linkage + C ABI (Phase 2, gaps i+ii). + func.linkage = if (isExportedEntryName(name) or fd.extern_export == .export_) .external else .internal; + if (fd.call_conv == .c or fd.extern_export == .export_) func.call_conv = .c; // Set inst_counter to param count (params occupy refs 0..N-1). IR params // = AST params + 1 if the function carries `__sx_ctx` at slot 0. const ctx_slots: usize = if (func.has_implicit_ctx) 1 else 0; @@ -2535,12 +2540,14 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i // matches C `static`). isExportedEntryName lists the names the OS // loader calls — `main`, Android NativeActivity hooks — which must // stay externally visible. - if (isExportedEntryName(name)) { + // `export` defines force external linkage (Phase 2, gap i) alongside + // the OS-called entry points. + if (isExportedEntryName(name) or fd.extern_export == .export_) { self.builder.currentFunc().linkage = .external; } - // Set calling convention - if (fd.call_conv == .c) { + // Set calling convention. `export` defines promote to C ABI (gap ii). + if (fd.call_conv == .c or fd.extern_export == .export_) { self.builder.currentFunc().call_conv = .c; }