diff --git a/stage0/test_spl_vm.c b/stage0/test_spl_vm.c index 27e9705..d080d10 100644 --- a/stage0/test_spl_vm.c +++ b/stage0/test_spl_vm.c @@ -476,7 +476,7 @@ void test_call_add(void) { * ret i64 ip=10 */ spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0), - ins(SPL_LADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_I64, 0), + ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0), ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)}; spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20), ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2), @@ -583,7 +583,7 @@ void test_calli(void) { * ret i64 ip=11 */ spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0), - ins(SPL_LADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_I64, 0), + ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0), ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)}; spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20), ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0), diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index ac04a92..76e1fb8 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -107,7 +107,7 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) { spl_patch(ctx, addr, offset); } -/* ---- Multi-slot copy helper ---- */ +/* ---- Multi-slot copy helpers ---- */ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) { for (usize i = 0; i < nslots; i++) { @@ -124,6 +124,31 @@ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) { } } +/* Copy nslots from source address to destination address. + * Stack before: [..., depth_items..., dest_addr, src_addr] (src TOS) + * Stack after: [..., depth_items...] + * depth: number of items below dest_addr that must be preserved (e.g. 1 if + * there's a base pointer between the rest of the stack and dest_addr). */ +void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth) { + for (usize i = 0; i < nslots; i++) { + spl_emit(ctx, SPL_DUP, SPL_VOID, 0); + if (i > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_PICK, SPL_VOID, 2 + depth); + if (i > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + } + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); +} + /* ---- Uniform LOAD/STORE type helper ---- */ spl_type_t spl_type_emit_type(spl_type_info_t *type) { @@ -165,17 +190,22 @@ int spl_type_is_scalar(spl_type_info_t *type) { void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, spl_type_info_t *data_type, int var_offset) { - spl_type_t bt = spl_type_emit_type(data_type); spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset); spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); if (byte_offset > 0) { spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); } - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); + if (data_type && !spl_type_is_scalar(data_type) && + spl_type_size(data_type) > sizeof(spl_val_t)) { + spl_emit_copy_slots(ctx, var_offset, spl_type_slot_count(data_type)); + } else { + spl_type_t bt = spl_type_emit_type(data_type); + spl_emit(ctx, SPL_LOAD, bt, 0); + spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, bt, 0); + } } /* ---- Scope management ---- */ diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index e30ba79..e36dc4b 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -287,6 +287,7 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr); * Stack: [..., temp_addr] → [...] * Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots); +void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth); void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type); void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type); diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index ab0cda4..e79d5fb 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -457,7 +457,11 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int b spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); } spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, st, 0); + if (elem_type && !spl_type_is_scalar(elem_type)) { + spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem_type), 1); + } else { + spl_emit(ctx, SPL_STORE, st, 0); + } skip_nl(ctx); } if (peek(ctx)->type == TOK_COMMA) @@ -657,6 +661,67 @@ static int parse_call_args(spl_comp_t *ctx) { return nargs; } +/* Type check: warn when a value is passed where a pointer of the same type is expected. + * This catches bugs like passing `tok` (Token) where `*Token` is expected. */ +static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, spl_type_info_t *arg_type, + spl_type_info_t *param_type, int arg_idx) { + if (!arg_type || !param_type) + return; + if (param_type->kind == TYPE_PTR && param_type->elem && arg_type->kind != TYPE_PTR && + arg_type->name && param_type->elem->name && + strcmp(arg_type->name, param_type->elem->name) == 0) { + fprintf(stderr, + "%s: warning: argument %d of '%s' expects '%s*', " + "got '%s' (missing '&'?)\n", + ctx->fname, arg_idx + 1, fname, param_type->elem->name, arg_type->name); + } +} + +/* Wrapper: parse_call_args with type checking against parameter types */ +static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, + spl_type_info_t **param_types, int nparams) { + int nargs = 0; /* physical slot count (for CALL) */ + int nlogical = 0; /* logical arg index (for param_types lookup) */ + if (peek(ctx)->type != TOK_R_PAREN) { + for (;;) { + spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN); + if (param_types && nlogical < nparams && param_types[nlogical]) + spl_check_arg_type(ctx, fname, arg.type, param_types[nlogical], nlogical); + + /* For multi-slot struct/enum by-value parameters, expand + * the address left on the stack into multiple data slots. */ + int arg_slots = 1; + if (param_types && nlogical < nparams && param_types[nlogical] && arg.type && + param_types[nlogical]->kind != TYPE_PTR && + !spl_type_is_scalar(param_types[nlogical]) && + spl_type_size(param_types[nlogical]) > sizeof(spl_val_t)) { + usize nslots = (spl_type_size(param_types[nlogical]) + sizeof(spl_val_t) - 1) / + sizeof(spl_val_t); + for (usize i = 0; i < nslots; i++) { + spl_emit(ctx, SPL_DUP, SPL_VOID, 0); + if (i > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + } + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + arg_slots = (int)nslots; + } + + nargs += arg_slots; + nlogical++; + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + continue; + } + break; + } + } + return nargs; +} + static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); char name[256]; @@ -673,7 +738,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_func_info_t *f = &vec_at(ctx->funcs, fi); advance(ctx); /* skip ( */ - int nargs = parse_call_args(ctx); + int nargs = parse_call_args_checked(ctx, f->name, f->param_types, f->nparams); expect(ctx, TOK_R_PAREN); if (f->is_extern) { @@ -690,6 +755,23 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { } spl_expr_result_t r = {f->ret_type, 0}; + + /* Multi-slot struct return: copy from returned address to caller's + * temp space. The returned address points to fp[1] in callee's frame + * (fp[0] is a gap). After RET, sp = callee_fp, so the first PUSH in + * emit_copy_slots (DUP) writes to the gap slot, not data. */ + if (f->ret_type && !spl_type_is_scalar(f->ret_type) && + spl_type_size(f->ret_type) > sizeof(spl_val_t)) { + usize nslots = spl_type_slot_count(f->ret_type); + int temp_offset = ctx->current_local_bytes; + ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t)); + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; + spl_emit_copy_slots(ctx, temp_offset, nslots); + spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset); + r.is_lvalue = 1; + } + return r; } @@ -946,7 +1028,15 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l nargs = 0; } - nargs += parse_call_args(ctx); + { + spl_type_info_t **cp = func->param_types; + int cn = func->nparams; + if (is_instance && cn > 0) { + cp++; + cn--; + } + nargs += parse_call_args_checked(ctx, func->name, cp, cn); + } expect(ctx, TOK_R_PAREN); /* Record fixup for forward-reference resolution */ @@ -957,6 +1047,20 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l vec_push(ctx->call_fixup_funcs, func->func_idx); left = (spl_expr_result_t){func->ret_type, 0}; + + /* Multi-slot return: copy to temp, keep address */ + if (func->ret_type && !spl_type_is_scalar(func->ret_type) && + spl_type_size(func->ret_type) > sizeof(spl_val_t)) { + usize nslots = spl_type_slot_count(func->ret_type); + int temp_offset = ctx->current_local_bytes; + ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t)); + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; + spl_emit_copy_slots(ctx, temp_offset, nslots); + spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset); + left.is_lvalue = 1; + } + break; } } @@ -1193,7 +1297,10 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp if (left.is_lvalue) { spl_type_t bt = spl_type_emit_type(left.type); - if (op == TOK_ASSIGN) { + if (op == TOK_ASSIGN && left.type && !spl_type_is_scalar(left.type) && + right.is_lvalue) { + spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(left.type), 0); + } else if (op == TOK_ASSIGN) { spl_emit(ctx, SPL_STORE, bt, 0); } else { spl_emit(ctx, SPL_PICK, SPL_VOID, 1); @@ -1310,8 +1417,20 @@ void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset) { * ============================================================ */ void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type) { - spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID; - spl_emit(ctx, SPL_RET, rt, 0); + if (ret_type && !spl_type_is_scalar(ret_type) && spl_type_size(ret_type) > sizeof(spl_val_t)) { + /* Multi-slot struct return: copy to fp[0..nslots-1] (above saved_sp), + * then return address of fp[0]. Caller copies from that address to its + * own temp space (see parse_ident / method call handling). */ + usize nslots = spl_type_slot_count(ret_type); + /* Store at fp[1..nslots] (skip fp[0] as gap) so that RET's PUSH + * and caller's LADDR both write to the gap slot, not the data. */ + spl_emit_copy_slots(ctx, (int)sizeof(spl_val_t), nslots); + spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t)); + spl_emit(ctx, SPL_RET, SPL_PTR, 0); + } else { + spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID; + spl_emit(ctx, SPL_RET, rt, 0); + } } /* ============================================================ @@ -1336,7 +1455,11 @@ void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_t for (int i = (int)var_type->array_len - 1; i >= 0; i--) { spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride)); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); + if (elem && !spl_type_is_scalar(elem)) { + spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem), 0); + } else { + spl_emit(ctx, SPL_STORE, bt, 0); + } } } else if (var_type && var_type->kind == TYPE_SLICE) { spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)sizeof(spl_val_t)); diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index c653bdd..5e88e36 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -86,14 +86,49 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t * skip_nl(ctx); } - spl_patch(ctx, alloc_addr, ctx->peak_local_bytes / (int)sizeof(spl_val_t) - nparams); + /* Compute physical param slot count (1 slot = 8 bytes). + * Multi-slot struct params occupy ceil(size/8) slots; scalar params occupy 1 each. */ + int total_phys_slots = 0; + for (int i = 0; i < nparams; i++) { + usize psz = spl_type_size(ptypes[i]); + total_phys_slots += (int)((psz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t)); + } + /* Ensure ALLOC provides enough space for multi-slot return value + * pre-placed at fp[0..nslots-1] (above saved_sp). */ + if (ret_type && !spl_type_is_scalar(ret_type) && + spl_type_size(ret_type) > sizeof(spl_val_t)) { + usize min_bytes = spl_type_slot_count(ret_type) * sizeof(spl_val_t); + if ((usize)ctx->peak_local_bytes < min_bytes) + ctx->peak_local_bytes = (int)min_bytes; + } + spl_patch(ctx, alloc_addr, + ctx->peak_local_bytes / (int)sizeof(spl_val_t) - total_phys_slots); expect(ctx, TOK_R_BRACE); } spl_emit_defer_epilogue(ctx, ctx->scope_depth); spl_pop_scope(ctx); - spl_emit(ctx, SPL_RET, SPL_VOID, 0); + /* Store param_types for call-site type checking and multi-slot expansion */ + { + spl_func_info_t *f = &vec_at(ctx->funcs, fi); + f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); + f->param_names = calloc(nparams, sizeof(char *)); + for (int i = 0; i < nparams; i++) { + f->param_types[i] = ptypes[i]; + f->param_names[i] = strdup(pnames[i]); + } + } + + /* Epilogue return: for multi-slot returns, return address of fp[1] + * (fp[0] is a gap slot that absorbs the RET PUSH, data at fp[1..nslots]). */ + if (ctx->current_ret_type && !spl_type_is_scalar(ctx->current_ret_type) && + spl_type_size(ctx->current_ret_type) > sizeof(spl_val_t)) { + spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t)); + spl_emit(ctx, SPL_RET, SPL_PTR, 0); + } else { + spl_emit(ctx, SPL_RET, SPL_VOID, 0); + } spl_prog_end_func(&ctx->prog, fi); ctx->current_func_idx = -1; ctx->current_ret_type = NULL; @@ -183,16 +218,6 @@ static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) { int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0); - { - spl_func_info_t *f = &vec_at(ctx->funcs, fi); - f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); - f->param_names = calloc(nparams, sizeof(char *)); - for (int i = 0; i < nparams; i++) { - f->param_types[i] = ptypes[i]; - f->param_names[i] = strdup(pnames[i]); - } - } - spl_type_add_method(container, mname, fi); } diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index 1b6c59e..e71eb21 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -451,11 +451,16 @@ static void parse_for_stmt(spl_comp_t *ctx) { spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size); spl_emit(ctx, SPL_MUL, SPL_U64, 0); spl_emit(ctx, SPL_ADD, SPL_U64, 0); - spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32; - spl_emit(ctx, SPL_LOAD, elem_bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, elem_bt, 0); + if (elem_type && !spl_type_is_scalar(elem_type) && + spl_type_size(elem_type) > sizeof(spl_val_t)) { + spl_emit_copy_slots(ctx, val_offset, spl_type_slot_count(elem_type)); + } else { + spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32; + spl_emit(ctx, SPL_LOAD, elem_bt, 0); + spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, elem_bt, 0); + } skip_nl(ctx); spl_parse_block(ctx); /* body */ diff --git a/stage1/test10_struct.spl b/stage1/test10_struct.spl index e266387..74c8392 100644 --- a/stage1/test10_struct.spl +++ b/stage1/test10_struct.spl @@ -8,6 +8,133 @@ type Pair = struct { b: i32, } +type Three = struct { + a: i64, + b: i64, + c: i64, +} + +type Tag = enum { + Unknown, + BlockComment, + LineComment, + Ident, + Number, +} + +type Token = struct { + loc_start: i32, + loc_end: i32, + tag: Tag, +} + +fn make_three(a: i64, b: i64, c: i64) Three { + ret Three { .a = a, .b = b, .c = c }; +} + +fn sum_three(t: Three) i64 { + ret t.a + t.b + t.c; +} + +fn make_token(s: i32, e: i32, t: Tag) Token { + ret Token { .loc_start = s, .loc_end = e, .tag = t }; +} + +fn test_ret_enum_struct() i32 { + var tok := make_token(10, 20, Tag.BlockComment); + if tok.loc_start != 10 { ret 1; } + if tok.loc_end != 20 { ret 2; } + if tok.tag != Tag.BlockComment { ret 3; } + ret 0; +} + +fn test_nested_ret_enum() i32 { + var tok := make_token(1, 5, Tag.Ident); + if tok.tag != Tag.Ident { ret 1; } + var tok2 := make_token(6, 9, Tag.Number); + if tok2.tag != Tag.Number { ret 2; } + if tok.tag != Tag.Ident { ret 3; } + ret 0; +} + +fn test_copy_token() i32 { + var tok := make_token(0, 100, Tag.LineComment); + var c := tok; + if c.loc_start != 0 { ret 1; } + if c.tag != Tag.LineComment { ret 2; } + c.tag = Tag.Ident; + if tok.tag != Tag.LineComment { ret 3; } + if c.tag != Tag.Ident { ret 4; } + ret 0; +} + +fn test_pass_token(t: Token) i32 { + if t.tag != Tag.Number { ret 1; } + if t.loc_start != 42 { ret 2; } + ret 0; +} + +fn test_param_pass_token() i32 { + var tok := make_token(42, 99, Tag.Number); + var r := test_pass_token(tok); + if r != 0 { ret r; } + ret 0; +} + +fn test_get_tag(tok: Token) i32 { + ret tok.tag; +} + +fn test_chain_func_call() i32 { + var tok := make_token(1, 2, Tag.BlockComment); + var tg := test_get_tag(tok); + if tg != Tag.BlockComment { ret 1; } + var tok2 := make_token(3, 4, Tag.LineComment); + var tg2 := test_get_tag(tok2); + if tg2 != Tag.LineComment { ret 2; } + ret 0; +} + +fn test_ret_multi_slot_struct() i32 { + var t := make_three(10, 20, 30); + if t.a != 10 { ret 1; } + if t.b != 20 { ret 2; } + if t.c != 30 { ret 3; } + ret 0; +} + +fn test_pass_struct_by_value() i32 { + var t := make_three(10, 20, 30); + var sum := sum_three(t); + if sum != 60 { ret 1; } + ret 0; +} + +fn test_copy_struct() i32 { + var t := make_three(10, 20, 30); + var t2: Three; + t2 = t; + if t2.a != 10 { ret 1; } + t2.a = 99; + if t.a != 10 { ret 2; } + if t2.a != 99 { ret 3; } + ret 0; +} + +fn test_multi_calls() i32 { + var ta := make_three(5, 10, 15); + var tb := make_three(20, 25, 30); + if ta.a + ta.b + ta.c != 30 { ret 1; } + if tb.a + tb.b + tb.c != 75 { ret 2; } + ret 0; +} + +fn test_literal_arg() i32 { + var sum := sum_three(Three { .a = 7, .b = 8, .c = 9 }); + if sum != 24 { ret 1; } + ret 0; +} + fn main() i32 { /* struct 字段访问 */ var p: Pair; @@ -60,5 +187,29 @@ fn main() i32 { if o.inner.val != 42 { ret 7; } if o.extra != 58 { ret 8; } + /* 多 slot 结构体返回值测试 */ + var r9 := test_ret_multi_slot_struct(); + if r9 != 0 { ret 100; } + var r10 := test_pass_struct_by_value(); + if r10 != 0 { ret 101; } + var r11 := test_copy_struct(); + if r11 != 0 { ret 102; } + var r12 := test_multi_calls(); + if r12 != 0 { ret 103; } + var r13 := test_literal_arg(); + if r13 != 0 { ret 104; } + + /* 含 enum 字段的结构体返回值测试 */ + var r14 := test_ret_enum_struct(); + if r14 != 0 { ret 110; } + var r15 := test_nested_ret_enum(); + if r15 != 0 { ret 111; } + var r16 := test_copy_token(); + if r16 != 0 { ret 112; } + var r17 := test_param_pass_token(); + if r17 != 0 { ret 113; } + var r18 := test_chain_func_call(); + if r18 != 0 { ret 114; } + ret 0; }