diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index 4795531..a8fc133 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -294,6 +294,7 @@ static int spl_type_size(spl_type_t t) { case SPL_U64: \ case SPL_USIZE: \ case SPL_ISIZE: \ + case SPL_PTR: \ _r = _a OP _b; \ break; \ case SPL_F32: { \ diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index 1c401e2..ac04a92 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -133,9 +133,34 @@ spl_type_t spl_type_emit_type(spl_type_info_t *type) { return type->basic_type; if (type->kind == TYPE_PTR) return SPL_PTR; + if (type->kind == TYPE_ENUM) { + /* Simple enum (no data variants): tag is I32 */ + vec_for(type->variants, i) { + if (vec_at(type->variants, i).data_type) + return SPL_PTR; /* has data — treat as aggregate */ + } + return SPL_I32; + } return SPL_PTR; } +/* Scalar types fit in one slot, are auto-loaded on field access, + * and support == / != comparison directly. */ +int spl_type_is_scalar(spl_type_info_t *type) { + if (!type) + return 1; + if (type->kind == TYPE_BASIC || type->kind == TYPE_PTR) + return 1; + if (type->kind == TYPE_ENUM) { + vec_for(type->variants, i) { + if (vec_at(type->variants, i).data_type) + return 0; /* has data — aggregate */ + } + return 1; /* simple enum — scalar */ + } + return 0; +} + /* ---- Load from [saved_ptr+offset], store to local var ---- */ void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index 60b7cb2..e30ba79 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -91,8 +91,10 @@ const char *spl_type_str(spl_type_info_t *t); int spl_type_is_integer(spl_type_t bt); spl_type_info_t *spl_type_clone(spl_type_info_t *t); -/* Determine the uniform type for LOAD/STORE codegen (i32→SPL_I32, ptr→SPL_PTR, else→SPL_PTR) */ +/* Determine the uniform type for LOAD/STORE codegen */ spl_type_t spl_type_emit_type(spl_type_info_t *type); +/* Returns 1 if type is scalar (fits in one slot, auto-loadable, comparable with ==/!=) */ +int spl_type_is_scalar(spl_type_info_t *type); /* Emit LOAD from [saved_ptr + byte_offset] and STORE to local var at var_offset. * Stack: [] → [] @@ -259,7 +261,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ * For enum: parses .VariantName, emits tag comparison, returns variant. * For value: parses expression, emits equality comparison. */ spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset); + int val_offset, int by_value); void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset); /* ============================================================ diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index d03ee86..ab0cda4 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -128,7 +128,7 @@ static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const vec_for(type->variants, i) { if (strcmp(vec_at(type->variants, i).name, field) == 0) { spl_emit(ctx, SPL_PUSH, SPL_I32, vec_at(type->variants, i).value); - *result = (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + *result = (spl_expr_result_t){type, 0}; return 1; } } @@ -633,7 +633,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ if (sz <= sizeof(spl_val_t)) { spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_LOAD, spl_type_emit_type(type), 0); return (spl_expr_result_t){type, 0}; } else { spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); @@ -698,7 +698,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { if (v) { spl_val_t cv = 0; if (map_get(ctx->const_values, name, &cv)) { - spl_emit(ctx, SPL_PUSH, SPL_I32, cv); + spl_emit(ctx, SPL_PUSH, spl_type_emit_type(v->type), cv); spl_expr_result_t r = {v->type, 0}; return r; } @@ -736,10 +736,9 @@ static spl_expr_result_t parse_group(spl_comp_t *ctx) { static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, spl_type_info_t *type) { - if (type->kind == TYPE_BASIC || type->kind == TYPE_PTR) { + if (spl_type_is_scalar(type)) { if (!ctx->addr_of_mode) { - spl_type_t bt = (type->kind == TYPE_BASIC) ? type->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); + spl_emit(ctx, SPL_LOAD, spl_type_emit_type(type), 0); *result = (spl_expr_result_t){type, 0}; return; } @@ -758,14 +757,14 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { switch (op->type) { case TOK_SUB: - spl_emit(ctx, SPL_NEG, SPL_I32, 0); + spl_emit(ctx, SPL_NEG, spl_type_emit_type(right.type), 0); break; case TOK_NOT: spl_emit(ctx, SPL_PUSH, SPL_I32, 0); - spl_emit(ctx, SPL_EQ, SPL_I32, 0); + spl_emit(ctx, SPL_EQ, spl_type_emit_type(right.type), 0); break; case TOK_BIT_NOT: - spl_emit(ctx, SPL_NOT, SPL_I32, 0); + spl_emit(ctx, SPL_NOT, spl_type_emit_type(right.type), 0); break; case TOK_AND: if (!right.is_lvalue) { @@ -1193,9 +1192,7 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp ctx->addr_of_mode = saved_addr_of_mode; if (left.is_lvalue) { - spl_type_t bt = left.type - ? (left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_PTR) - : SPL_I32; + spl_type_t bt = spl_type_emit_type(left.type); if (op == TOK_ASSIGN) { spl_emit(ctx, SPL_STORE, bt, 0); } else { @@ -1213,12 +1210,20 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp /* Regular binary op */ spl_parse_expr(ctx, next_prec); - int sop = binop_to_sir(op, left.type ? left.type->basic_type : SPL_I32); - spl_type_t bt = left.type && left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_I32; + /* Forbid comparison of non-scalar types (struct, enum-with-data) */ + if ((op == TOK_EQ || op == TOK_NEQ) && left.type && !spl_type_is_scalar(left.type)) { + spl_comp_error(ctx, "type '%s' does not support comparison", spl_type_str(left.type)); + } + spl_type_t bt = spl_type_emit_type(left.type); + int sop = binop_to_sir(op, bt); if (sop >= 0) { spl_emit(ctx, sop, bt, 0); } - return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + /* Comparisons return boolean (I32), other binary ops match operand type */ + int is_compare = (op == TOK_EQ || op == TOK_NEQ || op == TOK_LT || op == TOK_GT || + op == TOK_LE || op == TOK_GE); + spl_type_info_t *result_type = is_compare ? spl_type_basic(SPL_I32) : left.type; + return (spl_expr_result_t){result_type, 0}; } /* ============================================================ @@ -1231,7 +1236,7 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp * ============================================================ */ spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset) { + int val_offset, int by_value) { char vname[256]; /* Parse variant reference, reusing the same type resolution path @@ -1274,10 +1279,16 @@ lookup: spl_enum_variant_t *v = &vec_at(enum_type->variants, vi); if (strcmp(v->name, vname) == 0) { spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_emit(ctx, SPL_LOAD, SPL_I32, 0); + if (by_value) { + /* Scalar enum: raw tag stored directly in slot */ + spl_emit(ctx, SPL_LOAD, SPL_I32, 0); + } else { + /* Enum with data or *Enum: slot has a pointer, dereference it */ + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + spl_emit(ctx, SPL_LOAD, SPL_I32, 0); + } spl_emit(ctx, SPL_PUSH, SPL_I32, v->value); spl_emit(ctx, SPL_EQ, SPL_I32, 0); return v; @@ -1299,15 +1310,7 @@ 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 = SPL_VOID; - if (ret_type) { - if (ret_type->kind == TYPE_BASIC) { - rt = ret_type->basic_type; - } else { - /* Pointer, struct, enum, etc: return address of value */ - rt = SPL_PTR; - } - } + spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID; spl_emit(ctx, SPL_RET, rt, 0); } @@ -1321,14 +1324,14 @@ void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_t if (sz <= sizeof(spl_val_t)) { spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + spl_emit(ctx, SPL_STORE, spl_type_emit_type(var_type), 0); } else { usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); spl_emit_copy_slots(ctx, var_offset, nslots); } } else if (var_type && var_type->kind == TYPE_ARRAY) { spl_type_info_t *elem = var_type->elem; - spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32; + spl_type_t bt = spl_type_emit_type(elem); usize stride = spl_type_elem_stride(elem); for (int i = (int)var_type->array_len - 1; i >= 0; i--) { spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride)); @@ -1344,9 +1347,7 @@ void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_t spl_emit(ctx, SPL_STORE, SPL_PTR, 0); } else { spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_type_t bt = (var_type->kind == TYPE_BASIC) ? var_type->basic_type - : (var_type->kind == TYPE_PTR) ? SPL_PTR - : SPL_I32; + spl_type_t bt = spl_type_emit_type(var_type); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, bt, 0); } diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index 1d44f94..1b6c59e 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -182,8 +182,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) { skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) { /* Expression statement: drop value, consume ; */ - if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && - expr.type->basic_type != SPL_VOID) + if (!is_assign && expr.type && spl_type_emit_type(expr.type) != SPL_VOID) spl_emit(ctx, SPL_DROP, SPL_VOID, 0); while (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) advance(ctx); @@ -545,8 +544,8 @@ static void parse_defer_stmt(spl_comp_t *ctx) { * Delegates comparison to expr layer (spl_emit_match_enum_cmp). * Returns the variant for binding parsing, or NULL on error. */ static spl_enum_variant_t *parse_match_enum_variant(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset) { - return spl_emit_match_enum_cmp(ctx, enum_type, val_offset); + int val_offset, int by_value) { + return spl_emit_match_enum_cmp(ctx, enum_type, val_offset, by_value); } /* Parse a value pattern in a match arm (non-enum). @@ -642,6 +641,9 @@ static void parse_match_stmt(spl_comp_t *ctx) { return; } + /* Scalar enums store the raw tag directly; others store a pointer */ + int match_by_value = (t && t->kind == TYPE_ENUM && spl_type_is_scalar(t)); + /* Save match value to temp slot */ int val_offset = ctx->current_local_bytes; ctx->current_local_bytes += (int)sizeof(spl_val_t); @@ -649,7 +651,7 @@ static void parse_match_stmt(spl_comp_t *ctx) { ctx->peak_local_bytes = ctx->current_local_bytes; spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + spl_emit(ctx, SPL_STORE, spl_type_emit_type(t), 0); skip_nl(ctx); if (peek(ctx)->type == TOK_L_BRACE) @@ -684,7 +686,8 @@ static void parse_match_stmt(spl_comp_t *ctx) { } else { for (;;) { if (is_enum_match) { - arm_variant = parse_match_enum_variant(ctx, enum_type, val_offset); + arm_variant = + parse_match_enum_variant(ctx, enum_type, val_offset, match_by_value); if (ctx->has_error) break; skip_nl(ctx); @@ -887,8 +890,7 @@ static void parse_expr_stmt(spl_comp_t *ctx) { if (is_assign) ctx->addr_of_mode = 0; - if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && - expr.type->basic_type != SPL_VOID) + if (!is_assign && expr.type && spl_type_emit_type(expr.type) != SPL_VOID) spl_emit(ctx, SPL_DROP, SPL_VOID, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);