diff --git a/src/abi.cpp b/src/abi.cpp index cffc7d5..0c76cc3 100644 --- a/src/abi.cpp +++ b/src/abi.cpp @@ -13,7 +13,10 @@ namespace jam { namespace abi { bool isByRef(TypeIdx ty, const JamCodegenContext &ctx) { - if (ty == kNoType) return false; + if (ty == kNoType) { + return false; + } + const TypeKey &k = ctx.getTypePool().get(ty); switch (k.kind) { // Source-only / comptime-only — never lowered to runtime memory, diff --git a/src/jir_codegen.cpp b/src/jir_codegen.cpp index 196094d..986f848 100644 --- a/src/jir_codegen.cpp +++ b/src/jir_codegen.cpp @@ -868,9 +868,29 @@ static JamValueRef emitInstImpl(JirCodegenCtx &lctx, JirRef r) { } const char *resultName = (inst.ty == kNoType) ? "" : "call.indirect"; - return JamLLVMBuildIndirectCall( + JamValueRef callResult = JamLLVMBuildIndirectCall( lctx.ctx.getBuilder(), llvmFnTy, calleeVal, args.data(), static_cast(args.size()), resultName); + // A byref aggregate return arrives as an SSA aggregate VALUE: the + // indirect-call signature returns it by value (getLLVMType → + // struct type), so LLVM applies the platform C ABI — arm64 HFAs + // (NSPoint/NSSize/NSRect, CGRect) come back in v0–v3, ≤16-byte + // structs (NSRange) in x0/x1. jam's byref model expects the + // call's JirRef to be a POINTER to the aggregate, so spill the + // value to a slot and hand back the pointer; downstream + // Load/Store/FieldAccess then treat it like any other byref + // value. (Symmetric to the by-value aggregate ARG load above — + // together they make `[view frame]` / `[win frame]` and other + // struct-returning Cocoa methods work through the msgSend pun.) + if (inst.ty != kNoType && jam::abi::isByRef(inst.ty, lctx.ctx)) { + JamTypeRef aggTy = lctx.ctx.getLLVMType(inst.ty); + uint64_t align = lctx.ctx.typeAlign(inst.ty); + JamValueRef slot = JamLLVMBuildAlloca(lctx.ctx.getBuilder(), aggTy, + align, "ret.byval"); + JamLLVMBuildStore(lctx.ctx.getBuilder(), callResult, slot); + return slot; + } + return callResult; } // Control case JirTag::Br: { diff --git a/src/token.h b/src/token.h index e6c016b..b9cec97 100644 --- a/src/token.h +++ b/src/token.h @@ -12,15 +12,9 @@ #include #include -// Token types enum TokenType { TOK_EOF = 0, TOK_FN, - // `cfn` declares a method whose calls are synthesized by the - // compiler instead of being typed by the user. The shape on disk - // is identical to a regular function (same args / body / return); - // the difference is opt-in for the compiler hooks (`drop`, `at`, - // `default`). A `fn drop(self)` without `cfn` is just a method. TOK_CFN, TOK_IDENTIFIER, TOK_COLON, @@ -53,24 +47,23 @@ enum TokenType { TOK_CLOSE_BRACKET, TOK_STRING_LITERAL, TOK_WHILE, - TOK_LOOP, // `loop { ... }` — infinite loop, syntactically noreturn unless - // body breaks + TOK_LOOP, TOK_FOR, TOK_BREAK, TOK_CONTINUE, TOK_IN, - TOK_EXTERN, // extern keyword (import C function) - TOK_EXPORT, // export keyword (C ABI export) - TOK_PUB, // pub keyword (visible across Jam modules) - TOK_IMPORT, // import keyword - TOK_DOT, // . for member access - TOK_AND, // && (logical AND, short-circuit) - TOK_OR, // || (logical OR, short-circuit) - TOK_NOT, // ! (logical NOT) - TOK_TFN, // tfn keyword (test function) - TOK_STRUCT, // struct keyword - TOK_UNION, // union keyword (untagged; FFI-shaped) - TOK_ENUM, // enum keyword (tagged sum type; payload-less) + TOK_EXTERN, + TOK_EXPORT, + TOK_PUB, + TOK_IMPORT, + TOK_DOT, + TOK_AND, + TOK_OR, + TOK_NOT, + TOK_TFN, + TOK_STRUCT, + TOK_UNION, + TOK_ENUM, TOK_STAR, // * (pointer prefix; multiplication) TOK_SLASH, // / (division) TOK_PERCENT, // % (modulo) @@ -80,23 +73,15 @@ enum TokenType { TOK_TILDE, // ~ (bitwise NOT) TOK_LSHIFT, // << (left shift) TOK_RSHIFT, // >> (right shift) - TOK_MOVE, // move keyword (parameter mode: consume ownership) + TOK_MOVE, TOK_ELLIPSIS, // ... (variadic marker in extern fn parameters) - TOK_MATCH, // match keyword + TOK_MATCH, TOK_DOTDOT, // .. (exclusive range / slice) TOK_DOTDOT_EQ, // ..= (inclusive range in match patterns) - TOK_AS, // as keyword (explicit type cast) - TOK_AT, // @ — prefix for comptime-function invocations: - // `@sizeOf(T)`, `@alignOf(T)`, user-defined cfns - TOK_COMP, // comp keyword — comp param marker (`fn f(comp n: u32) - // ...`); also wraps an expression that must fold at - // compile time (`comp const X = 1 + 1;`). Source- - // level keyword for the broader "compile-time - // evaluation" mechanism implemented in src/comptime.h. - TOK_INLINE, // inline keyword — prefix for `inline while` (loop - // unrolling at compile time, mirrors Zig's - // `inline while`). Required by std.fmt's format- - // string parser. + TOK_AS, + TOK_AT, // @ + TOK_COMP, + TOK_INLINE, }; // Token structure. diff --git a/tests/unit/test_abi_extern_mixed.jam b/tests/unit/test_abi_extern_mixed.jam index 9c8e7b7..ea292d1 100644 --- a/tests/unit/test_abi_extern_mixed.jam +++ b/tests/unit/test_abi_extern_mixed.jam @@ -1,19 +1,8 @@ const { assert } = import("test"); -// MVS P9.8: extern fns follow the C ABI literally — mode-aware ABI -// rules don't apply at the FFI boundary. These tests confirm that -// (a) Jam functions using P9 modes can call extern fns with primitive -// and pointer arguments, (b) extern fns retain their C-ABI shape -// regardless of what the surrounding Jam code is doing, and (c) all -// the existing extern patterns (snprintf, printf, etc.) continue to -// compose cleanly with the new ABI. - extern fn snprintf(buf: *mut[] u8, size: u64, fmt: *const[] u8, ...) i32; extern fn strlen(s: *const[] u8) u64; -// Jam-defined helper using a `mut` parameter that internally calls an -// extern fn. The mut param is pointer-passed under P9.3; the extern -// call uses pointer args via Jam-source `&` syntax — no auto-address. fn fillFromExtern(buf: mut [16]u8) i32 { var written: i32 = snprintf(&buf[0], 16, "n=%d", 42); return written;