kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

parser.c (66062B)


      1 #include <stddef.h>
      2 #include <stdint.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 #include "internal.h"
      7 
      8 /* Public CG API coverage goals for this frontend are tracked in
      9  * doc/toy-todo.md. Keep this file aligned with include/kit/cg.h rather than
     10  * private CG implementation details. */
     11 
     12 /* ============================================================
     13  * Parser (single-pass parse -> codegen)
     14  * ============================================================ */
     15 
     16 static int toy_parse_stmt(ToyParser* p);
     17 
     18 int toy_parse_block(ToyParser* p) {
     19   size_t saved_nvars = p->nvars;
     20   if (!toy_parser_expect(p, TOK_LBRACE)) {
     21     toy_error(p, p->cur.loc, "expected '{'");
     22     return 0;
     23   }
     24   while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
     25     if (!toy_parse_stmt(p)) return 0;
     26   }
     27   if (!toy_parser_expect(p, TOK_RBRACE)) {
     28     toy_error(p, p->cur.loc, "expected '}'");
     29     return 0;
     30   }
     31   p->nvars = saved_nvars;
     32   return 1;
     33 }
     34 
     35 /* Pushes [base_ptr, index] for kit_cg_elem: the array local is decayed to a
     36  * pointer to its element type (its address, bitcast to *elem), then the index
     37  * value is pushed on top. The following kit_cg_elem(0) yields the element
     38  * PLACE. */
     39 static void toy_push_local_indexed(ToyParser* p, KitCgLocal slot,
     40                                    KitCgTypeId elem_ty, uint64_t index) {
     41   kit_cg_push_local(p->cg, slot);
     42   kit_cg_addr(p->cg);
     43   kit_cg_bitcast(p->cg, kit_cg_type_ptr(p->c, elem_ty, 0));
     44   kit_cg_push_int(p->cg, index, p->size_type);
     45 }
     46 
     47 static int toy_check_source_value(ToyParser* p, KitCgTypeId expected_cg,
     48                                   ToyTypeId expected_toy, KitCgTypeId actual_cg,
     49                                   ToyTypeId actual_toy, const char* message) {
     50   if (expected_toy != TOY_TYPE_NONE) {
     51     if (!toy_type_accepts_type(p, expected_toy, actual_toy)) {
     52       toy_error(p, p->cur.loc, message);
     53       return 0;
     54     }
     55     return 1;
     56   }
     57   if (!toy_type_can_implicitly_cast(p, actual_cg, expected_cg)) {
     58     toy_error(p, p->cur.loc, message);
     59     return 0;
     60   }
     61   return 1;
     62 }
     63 
     64 static int toy_records_have_matching_storage(ToyParser* p, KitCgTypeId expected,
     65                                              KitCgTypeId actual) {
     66   uint32_t i;
     67   uint32_t nfields;
     68   if (kit_cg_type_kind(p->c, expected) != KIT_CG_TYPE_RECORD ||
     69       kit_cg_type_kind(p->c, actual) != KIT_CG_TYPE_RECORD) {
     70     return 0;
     71   }
     72   nfields = kit_cg_type_record_nfields(p->c, expected);
     73   if (nfields != kit_cg_type_record_nfields(p->c, actual)) return 0;
     74   for (i = 0; i < nfields; ++i) {
     75     KitCgField expected_field;
     76     KitCgField actual_field;
     77     if (kit_cg_type_record_field(p->c, expected, i, &expected_field, NULL) ||
     78         kit_cg_type_record_field(p->c, actual, i, &actual_field, NULL)) {
     79       return 0;
     80     }
     81     if (expected_field.name != actual_field.name ||
     82         expected_field.type != actual_field.type) {
     83       return 0;
     84     }
     85   }
     86   return 1;
     87 }
     88 
     89 static int toy_copy_record_lvalue_to_local(ToyParser* p, KitCgTypeId src_ty,
     90                                            KitCgLocal dst_slot,
     91                                            KitCgTypeId dst_ty) {
     92   uint32_t i;
     93   uint32_t nfields;
     94   if (!toy_records_have_matching_storage(p, dst_ty, src_ty)) {
     95     toy_error(p, p->cur.loc, "record storage mismatch");
     96     return 0;
     97   }
     98   nfields = kit_cg_type_record_nfields(p->c, dst_ty);
     99   for (i = 0; i < nfields; ++i) {
    100     KitCgField field;
    101     uint64_t offset = 0;
    102     if (kit_cg_type_record_field(p->c, dst_ty, i, &field, &offset)) return 0;
    103     kit_cg_dup(p->cg);
    104     kit_cg_deref(p->cg, (int64_t)offset);
    105     kit_cg_load(p->cg, toy_mem_access(p, field.type));
    106     kit_cg_push_local(p->cg, dst_slot);
    107     kit_cg_addr(p->cg);
    108     kit_cg_deref(p->cg, (int64_t)offset);
    109     kit_cg_swap(p->cg);
    110     kit_cg_store(p->cg, toy_mem_access(p, field.type));
    111   }
    112   kit_cg_drop(p->cg);
    113   return 1;
    114 }
    115 
    116 static int toy_copy_record_lvalue_to_var(ToyParser* p, KitCgTypeId src_ty,
    117                                          const ToyVar* dst_var,
    118                                          const ToyGlobal* dst_global) {
    119   KitCgTypeId dst_ty = dst_var ? dst_var->type : dst_global->type;
    120   uint32_t i;
    121   uint32_t nfields;
    122   if (!toy_records_have_matching_storage(p, dst_ty, src_ty)) {
    123     toy_error(p, p->cur.loc, "record storage mismatch");
    124     return 0;
    125   }
    126   nfields = kit_cg_type_record_nfields(p->c, dst_ty);
    127   for (i = 0; i < nfields; ++i) {
    128     KitCgField field;
    129     uint64_t offset = 0;
    130     if (kit_cg_type_record_field(p->c, dst_ty, i, &field, &offset)) return 0;
    131     kit_cg_dup(p->cg);
    132     kit_cg_deref(p->cg, (int64_t)offset);
    133     kit_cg_load(p->cg, toy_mem_access(p, field.type));
    134     if (dst_var) {
    135       toy_push_var_lvalue(p, dst_var);
    136     } else {
    137       kit_cg_push_symbol_addr(p->cg, toy_global_cur_sym(p, dst_global), 0);
    138     }
    139     kit_cg_deref(p->cg, (int64_t)offset);
    140     kit_cg_swap(p->cg);
    141     kit_cg_store(p->cg, toy_mem_access(p, field.type));
    142   }
    143   kit_cg_drop(p->cg);
    144   return 1;
    145 }
    146 
    147 static int toy_parse_array_initializer(ToyParser* p, KitCgLocal slot,
    148                                        KitCgTypeId arr_ty,
    149                                        ToyTypeId arr_toy_type) {
    150   KitCgTypeId elem_ty = kit_cg_type_array_elem(p->c, arr_ty);
    151   ToyTypeId elem_toy_type = toy_type_array_elem(p, arr_toy_type);
    152   uint64_t count = kit_cg_type_array_count(p->c, arr_ty);
    153   uint64_t index = 0;
    154 
    155   if (!toy_parser_expect(p, TOK_LBRACKET)) {
    156     toy_error(p, p->cur.loc, "expected array literal");
    157     return 0;
    158   }
    159   while (p->cur.kind != TOK_RBRACKET && p->cur.kind != TOK_EOF) {
    160     KitCgTypeId expr_ty;
    161     if (index >= count) {
    162       toy_error(p, p->cur.loc, "too many array elements");
    163       return 0;
    164     }
    165     toy_push_local_indexed(p, slot, elem_ty, index);
    166     kit_cg_elem(p->cg, 0);
    167     expr_ty = toy_parse_expr(p);
    168     if (expr_ty == KIT_CG_TYPE_NONE) return 0;
    169     if (!toy_check_source_value(p, elem_ty, elem_toy_type, expr_ty,
    170                                 p->last_type, "array element type mismatch")) {
    171       return 0;
    172     }
    173     if (expr_ty != elem_ty && !toy_emit_checked_cast(p, expr_ty, elem_ty))
    174       return 0;
    175     kit_cg_store(p->cg, toy_mem_access(p, elem_ty));
    176     index++;
    177     if (!toy_parser_match(p, TOK_COMMA)) break;
    178   }
    179   if (!toy_parser_expect(p, TOK_RBRACKET)) {
    180     toy_error(p, p->cur.loc, "expected ']' after array literal");
    181     return 0;
    182   }
    183   while (index < count) {
    184     toy_push_local_indexed(p, slot, elem_ty, index);
    185     kit_cg_elem(p->cg, 0);
    186     kit_cg_push_int(p->cg, 0, elem_ty);
    187     kit_cg_store(p->cg, toy_mem_access(p, elem_ty));
    188     index++;
    189   }
    190   return 1;
    191 }
    192 
    193 static int toy_parse_record_initializer(ToyParser* p, KitCgLocal slot,
    194                                         KitCgTypeId record_ty,
    195                                         ToyTypeId record_toy_type) {
    196   uint32_t i, nfields = kit_cg_type_record_nfields(p->c, record_ty);
    197   ToyNamedType* named = toy_find_named_type_by_type(p, record_ty);
    198   int positional = named && named->kind == TOY_NAMED_TUPLE;
    199   (void)record_toy_type;
    200   if (p->cur.kind == TOK_IDENT) toy_parser_advance(p);
    201   if (!toy_parser_expect(p, TOK_LBRACE)) {
    202     toy_error(p, p->cur.loc, "expected record literal");
    203     return 0;
    204   }
    205 
    206   for (i = 0; i < nfields; ++i) {
    207     KitCgField field;
    208     uint64_t foff = 0;
    209     if (kit_cg_type_record_field(p->c, record_ty, i, &field, &foff) != 0)
    210       return 0;
    211     kit_cg_push_local(p->cg, slot);
    212     kit_cg_addr(p->cg);
    213     kit_cg_deref(p->cg, (int64_t)foff);
    214     kit_cg_push_int(p->cg, 0, field.type);
    215     kit_cg_store(p->cg, toy_mem_access(p, field.type));
    216   }
    217 
    218   if (positional) {
    219     uint32_t field_index = 0;
    220     while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
    221       KitCgField field;
    222       KitCgTypeId expr_ty;
    223       uint64_t foff = 0;
    224       if (field_index >= nfields) {
    225         toy_error(p, p->cur.loc, "too many tuple fields");
    226         return 0;
    227       }
    228       if (kit_cg_type_record_field(p->c, record_ty, field_index, &field,
    229                                    &foff) != 0)
    230         return 0;
    231       kit_cg_push_local(p->cg, slot);
    232       kit_cg_addr(p->cg);
    233       kit_cg_deref(p->cg, (int64_t)foff);
    234       expr_ty = toy_parse_expr(p);
    235       if (expr_ty == KIT_CG_TYPE_NONE) return 0;
    236       {
    237         ToyTypeId expected = (named && field_index < named->nfields)
    238                                  ? named->fields[field_index].toy_type
    239                                  : TOY_TYPE_NONE;
    240         if (!toy_check_source_value(p, field.type, expected, expr_ty,
    241                                     p->last_type,
    242                                     "tuple field type mismatch")) {
    243           return 0;
    244         }
    245       }
    246       if (expr_ty != field.type &&
    247           !toy_emit_checked_cast(p, expr_ty, field.type))
    248         return 0;
    249       kit_cg_store(p->cg, toy_mem_access(p, field.type));
    250       field_index++;
    251       if (!toy_parser_match(p, TOK_COMMA)) break;
    252     }
    253     if (!toy_parser_expect(p, TOK_RBRACE)) {
    254       toy_error(p, p->cur.loc, "expected '}' after tuple literal");
    255       return 0;
    256     }
    257     return 1;
    258   }
    259 
    260   while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
    261     KitSym field_name;
    262     KitCgField field;
    263     uint32_t field_index;
    264     KitCgTypeId expr_ty;
    265     uint64_t foff = 0;
    266     if (p->cur.kind != TOK_IDENT) {
    267       toy_error(p, p->cur.loc, "expected field name");
    268       return 0;
    269     }
    270     field_name = toy_tok_sym(p, p->cur);
    271     toy_parser_advance(p);
    272     if (!toy_parser_expect(p, TOK_COLON)) {
    273       toy_error(p, p->cur.loc, "expected ':' in record literal");
    274       return 0;
    275     }
    276     if (!toy_record_field_index(p, record_ty, field_name, &field_index,
    277                                 &field)) {
    278       toy_error(p, p->cur.loc, "unknown record field");
    279       return 0;
    280     }
    281     if (kit_cg_type_record_field(p->c, record_ty, field_index, NULL, &foff) !=
    282         0)
    283       return 0;
    284     kit_cg_push_local(p->cg, slot);
    285     kit_cg_addr(p->cg);
    286     kit_cg_deref(p->cg, (int64_t)foff);
    287     expr_ty = toy_parse_expr(p);
    288     if (expr_ty == KIT_CG_TYPE_NONE) return 0;
    289     {
    290       ToyTypeId expected = (named && field_index < named->nfields)
    291                                ? named->fields[field_index].toy_type
    292                                : TOY_TYPE_NONE;
    293       if (!toy_check_source_value(p, field.type, expected, expr_ty,
    294                                   p->last_type, "record field type mismatch")) {
    295         return 0;
    296       }
    297     }
    298     if (expr_ty != field.type &&
    299         !toy_emit_checked_cast(p, expr_ty, field.type))
    300       return 0;
    301     kit_cg_store(p->cg, toy_mem_access(p, field.type));
    302     if (!toy_parser_match(p, TOK_COMMA)) break;
    303   }
    304   if (!toy_parser_expect(p, TOK_RBRACE)) {
    305     toy_error(p, p->cur.loc, "expected '}' after record literal");
    306     return 0;
    307   }
    308   return 1;
    309 }
    310 
    311 static int toy_parse_value_block_body_to_local(ToyParser* p, KitCgLocal slot,
    312                                                KitCgTypeId result_ty,
    313                                                ToyTypeId result_toy_type);
    314 
    315 static int toy_parse_if_initializer(ToyParser* p, KitCgLocal slot,
    316                                     KitCgTypeId result_ty,
    317                                     ToyTypeId result_toy_type) {
    318   KitCgTypeId cond_ty;
    319   KitCgIf it;
    320   if (!toy_parser_match(p, TOK_IF)) return 0;
    321   cond_ty = toy_parse_expr(p);
    322   if (cond_ty == KIT_CG_TYPE_NONE) return 0;
    323   if (!toy_emit_truthy(p, cond_ty)) return 0;
    324   it = kit_cg_if_begin(p->cg);
    325 
    326   if (!toy_parser_expect(p, TOK_LBRACE)) {
    327     toy_error(p, p->cur.loc, "expected '{' in if expression");
    328     return 0;
    329   }
    330   if (!toy_parse_value_block_body_to_local(p, slot, result_ty, result_toy_type))
    331     return 0;
    332 
    333   kit_cg_if_else(p->cg, it);
    334 
    335   if (!toy_parser_expect(p, TOK_ELSE) || !toy_parser_expect(p, TOK_LBRACE)) {
    336     toy_error(p, p->cur.loc, "expected else block in if expression");
    337     return 0;
    338   }
    339   if (!toy_parse_value_block_body_to_local(p, slot, result_ty, result_toy_type))
    340     return 0;
    341 
    342   kit_cg_if_end(p->cg, it);
    343   return 1;
    344 }
    345 
    346 static int toy_parse_value_block_body_to_local(ToyParser* p, KitCgLocal slot,
    347                                                KitCgTypeId result_ty,
    348                                                ToyTypeId result_toy_type) {
    349   size_t saved_nvars = p->nvars;
    350   while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
    351     KitCgTypeId arm_ty;
    352     if (p->cur.kind == TOK_LET || p->cur.kind == TOK_VAR ||
    353         p->cur.kind == TOK_IF || p->cur.kind == TOK_WHILE ||
    354         p->cur.kind == TOK_SWITCH || p->cur.kind == TOK_LABEL ||
    355         p->cur.kind == TOK_GOTO || p->cur.kind == TOK_BREAK ||
    356         p->cur.kind == TOK_CONTINUE || p->cur.kind == TOK_RETURN ||
    357         p->cur.kind == TOK_LBRACE) {
    358       if (!toy_parse_stmt(p)) {
    359         p->nvars = saved_nvars;
    360         return 0;
    361       }
    362       continue;
    363     }
    364     arm_ty = toy_parse_expr(p);
    365     if (arm_ty == KIT_CG_TYPE_NONE) {
    366       p->nvars = saved_nvars;
    367       return 0;
    368     }
    369     if (toy_parser_match(p, TOK_SEMI)) {
    370       if (arm_ty != toy_builtin_type(p, KIT_CG_BUILTIN_VOID))
    371         kit_cg_drop(p->cg);
    372       continue;
    373     }
    374     if (!toy_check_source_value(p, result_ty, result_toy_type, arm_ty,
    375                                 p->last_type, "block value type mismatch")) {
    376       p->nvars = saved_nvars;
    377       return 0;
    378     }
    379     if (arm_ty != result_ty && !toy_emit_checked_cast(p, arm_ty, result_ty)) {
    380       p->nvars = saved_nvars;
    381       return 0;
    382     }
    383     kit_cg_push_local(p->cg, slot);
    384     kit_cg_swap(p->cg);
    385     kit_cg_store(p->cg, toy_mem_access(p, result_ty));
    386     if (!toy_parser_expect(p, TOK_RBRACE)) {
    387       toy_error(p, p->cur.loc, "expected '}' after value block");
    388       p->nvars = saved_nvars;
    389       return 0;
    390     }
    391     p->nvars = saved_nvars;
    392     return 1;
    393   }
    394   toy_error(p, p->cur.loc, "missing block value");
    395   p->nvars = saved_nvars;
    396   return 0;
    397 }
    398 
    399 static int toy_parse_value_block_to_local(ToyParser* p, KitCgLocal slot,
    400                                           KitCgTypeId result_ty,
    401                                           ToyTypeId result_toy_type) {
    402   if (!toy_parser_expect(p, TOK_LBRACE)) {
    403     toy_error(p, p->cur.loc, "expected value block");
    404     return 0;
    405   }
    406   return toy_parse_value_block_body_to_local(p, slot, result_ty,
    407                                              result_toy_type);
    408 }
    409 
    410 static int toy_parse_switch_strategy_hint(ToyParser* p,
    411                                           KitCgSwitchHint* hint_out) {
    412   *hint_out = KIT_CG_SWITCH_TARGET_DEFAULT;
    413   if (p->cur.kind != TOK_AT || toy_lexer_peek(&p->lex).kind != TOK_LBRACKET)
    414     return 1;
    415   toy_parser_advance(p);
    416   if (!toy_parser_expect(p, TOK_LBRACKET)) {
    417     toy_error(p, p->cur.loc, "expected switch strategy list");
    418     return 0;
    419   }
    420   while (p->cur.kind != TOK_RBRACKET && p->cur.kind != TOK_EOF) {
    421     KitSym name;
    422     if (!toy_parse_attr_dot_name(p, &name)) return 0;
    423     if (toy_sym_is(p, name, "branch_chain")) {
    424       *hint_out = KIT_CG_SWITCH_BRANCH_CHAIN;
    425     } else if (toy_sym_is(p, name, "jump_table")) {
    426       *hint_out = KIT_CG_SWITCH_JUMP_TABLE;
    427     } else {
    428       toy_error(p, p->cur.loc, "unknown switch strategy");
    429       return 0;
    430     }
    431     if (!toy_parser_match(p, TOK_COMMA)) break;
    432   }
    433   if (!toy_parser_expect(p, TOK_RBRACKET)) {
    434     toy_error(p, p->cur.loc, "expected ']' after switch strategy list");
    435     return 0;
    436   }
    437   return 1;
    438 }
    439 
    440 static int toy_parse_switch_initializer(ToyParser* p, KitCgLocal slot,
    441                                         KitCgTypeId result_ty,
    442                                         ToyTypeId result_toy_type) {
    443   KitCgTypeId selector_ty;
    444   KitCgLocal selector_slot;
    445   KitCgLabel end_label;
    446   KitCgLabel dispatch_label;
    447   KitCgLabel default_arm_label = KIT_CG_LABEL_NONE;
    448   KitCgLabel unreachable_label = KIT_CG_LABEL_NONE;
    449   ToyNamedType* selector_enum;
    450   unsigned char* enum_seen = NULL;
    451   size_t enum_seen_size = 0;
    452   int saw_default = 0;
    453   size_t enum_seen_count = 0;
    454   KitCgSwitchCase* cases = NULL;
    455   size_t ncases = 0;
    456   size_t cap_cases = 0;
    457   KitCgSwitchHint hint;
    458   KitCgSwitch sw;
    459 
    460   if (!toy_parser_match(p, TOK_SWITCH)) return 0;
    461   if (!toy_parse_switch_strategy_hint(p, &hint)) return 0;
    462   selector_ty = toy_parse_expr(p);
    463   if (selector_ty == KIT_CG_TYPE_NONE) return 0;
    464   if (!toy_type_is_intlike(p, selector_ty)) {
    465     toy_error(p, p->cur.loc, "switch selector must be integer-like");
    466     return 0;
    467   }
    468   selector_slot = kit_cg_local(p->cg, selector_ty, toy_slot_attrs(0));
    469   kit_cg_push_local(p->cg, selector_slot);
    470   kit_cg_swap(p->cg);
    471   kit_cg_store(p->cg, toy_mem_access(p, selector_ty));
    472   end_label = kit_cg_label_new(p->cg);
    473   dispatch_label = kit_cg_label_new(p->cg);
    474   /* Skip the arm bodies on entry; come back through the dispatch label. */
    475   kit_cg_jump(p->cg, dispatch_label);
    476   selector_enum = toy_find_named_type_by_type(p, selector_ty);
    477   if (!selector_enum || selector_enum->kind != TOY_NAMED_ENUM)
    478     selector_enum = NULL;
    479   if (selector_enum) {
    480     enum_seen_size =
    481         (selector_enum->nenum_values ? selector_enum->nenum_values : 1u) *
    482         sizeof *enum_seen;
    483     enum_seen = (unsigned char*)toy_parser_zalloc(
    484         p, selector_enum->nenum_values ? selector_enum->nenum_values : 1u,
    485         sizeof *enum_seen, "enum switch state");
    486     if (!enum_seen) return 0;
    487   }
    488 
    489   if (!toy_parser_expect(p, TOK_LBRACE)) {
    490     toy_error(p, p->cur.loc, "expected '{' after switch selector");
    491     toy_parser_free_mem(p, enum_seen, enum_seen_size);
    492     return 0;
    493   }
    494   while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
    495     KitCgLabel arm_label = kit_cg_label_new(p->cg);
    496     if (p->cur.kind == TOK_DEFAULT) {
    497       saw_default = 1;
    498       default_arm_label = arm_label;
    499       toy_parser_advance(p);
    500     } else {
    501       for (;;) {
    502         int64_t value;
    503         size_t i;
    504         if (!toy_parse_switch_label_value(p, selector_ty, &value)) {
    505           toy_parser_free_mem(p, enum_seen, enum_seen_size);
    506           toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
    507           return 0;
    508         }
    509         if (selector_enum) {
    510           for (i = 0; i < selector_enum->nenum_values; ++i) {
    511             if (selector_enum->enum_values[i].value == value) {
    512               if (!enum_seen[i]) {
    513                 enum_seen[i] = 1;
    514                 enum_seen_count++;
    515               }
    516               break;
    517             }
    518           }
    519         }
    520         if (!toy_parser_reserve(p, (void**)&cases, &cap_cases, ncases + 1u,
    521                                 sizeof *cases, "switch cases")) {
    522           toy_parser_free_mem(p, enum_seen, enum_seen_size);
    523           return 0;
    524         }
    525         cases[ncases].value = (uint64_t)value;
    526         cases[ncases].label = arm_label;
    527         ncases++;
    528         if (!toy_parser_match(p, TOK_COMMA)) break;
    529       }
    530     }
    531     kit_cg_label_place(p->cg, arm_label);
    532     if (!toy_parse_value_block_to_local(p, slot, result_ty, result_toy_type)) {
    533       toy_parser_free_mem(p, enum_seen, enum_seen_size);
    534       toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
    535       return 0;
    536     }
    537     kit_cg_jump(p->cg, end_label);
    538   }
    539   if (!toy_parser_expect(p, TOK_RBRACE)) {
    540     toy_error(p, p->cur.loc, "expected '}' after switch expression");
    541     toy_parser_free_mem(p, enum_seen, enum_seen_size);
    542     toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
    543     return 0;
    544   }
    545   if (!saw_default &&
    546       (!selector_enum || enum_seen_count != selector_enum->nenum_values)) {
    547     toy_error(p, p->cur.loc, "expression switch requires default");
    548     toy_parser_free_mem(p, enum_seen, enum_seen_size);
    549     toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
    550     return 0;
    551   }
    552   kit_cg_label_place(p->cg, dispatch_label);
    553   kit_cg_push_local(p->cg, selector_slot);
    554   kit_cg_load(p->cg, toy_mem_access(p, selector_ty));
    555   if (saw_default) {
    556     sw.default_label = default_arm_label;
    557   } else {
    558     /* Enum-exhaustive switch with no source default: any value outside the
    559      * covered set is undefined behavior — emit an unreachable landing pad. */
    560     unreachable_label = kit_cg_label_new(p->cg);
    561     sw.default_label = unreachable_label;
    562   }
    563   sw.selector_type = selector_ty;
    564   sw.cases = cases;
    565   sw.ncases = (uint32_t)ncases;
    566   sw.hint = hint;
    567   kit_cg_switch(p->cg, sw);
    568   if (unreachable_label != KIT_CG_LABEL_NONE) {
    569     kit_cg_label_place(p->cg, unreachable_label);
    570     kit_cg_unreachable(p->cg);
    571   }
    572   kit_cg_label_place(p->cg, end_label);
    573   toy_parser_free_mem(p, enum_seen, enum_seen_size);
    574   toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
    575   return 1;
    576 }
    577 
    578 static int toy_parse_while_initializer_named(ToyParser* p, KitCgLocal slot,
    579                                              KitCgTypeId result_ty,
    580                                              ToyTypeId result_toy_type,
    581                                              KitSym label_name) {
    582   KitCgTypeId explicit_ty;
    583   ToyTypeId explicit_toy_type;
    584   KitCgTypeId cond_ty;
    585   KitCgScope scope;
    586   KitCgLabel else_label;
    587 
    588   if (!toy_parser_match(p, TOK_WHILE)) return 0;
    589   if (!toy_parser_expect(p, TOK_LT)) {
    590     toy_error(p, p->cur.loc, "expected result type in while expression");
    591     return 0;
    592   }
    593   explicit_ty = toy_parse_type(p);
    594   if (explicit_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT))
    595     return 0;
    596   explicit_toy_type = p->last_type;
    597   if (!toy_check_source_value(p, result_ty, result_toy_type, explicit_ty,
    598                               explicit_toy_type,
    599                               "while result type mismatch")) {
    600     return 0;
    601   }
    602   if (!toy_parser_reserve(p, (void**)&p->scopes, &p->cap_scopes,
    603                           p->nscopes + 1u, sizeof *p->scopes, "scopes")) {
    604     return 0;
    605   }
    606   scope = kit_cg_scope_begin(p->cg, result_ty);
    607   p->scopes[p->nscopes].name = label_name;
    608   p->scopes[p->nscopes].kind = TOY_SCOPE_LOOP;
    609   p->scopes[p->nscopes].cg_scope = scope;
    610   p->scopes[p->nscopes].result_type = result_ty;
    611   p->scopes[p->nscopes].result_toy_type = result_toy_type;
    612   p->nscopes++;
    613   else_label = kit_cg_label_new(p->cg);
    614 
    615   cond_ty = toy_parse_expr(p);
    616   if (cond_ty == KIT_CG_TYPE_NONE) {
    617     p->nscopes--;
    618     return 0;
    619   }
    620   if (!toy_emit_truthy(p, cond_ty)) {
    621     p->nscopes--;
    622     return 0;
    623   }
    624   kit_cg_branch_false(p->cg, else_label);
    625   if (!toy_parse_block(p)) {
    626     p->nscopes--;
    627     return 0;
    628   }
    629   kit_cg_continue(p->cg, scope);
    630   kit_cg_label_place(p->cg, else_label);
    631   if (!toy_parser_expect(p, TOK_ELSE)) {
    632     toy_error(p, p->cur.loc, "expected else in while expression");
    633     p->nscopes--;
    634     return 0;
    635   }
    636   if (!toy_parser_expect(p, TOK_LBRACE)) {
    637     toy_error(p, p->cur.loc, "expected else block in while expression");
    638     p->nscopes--;
    639     return 0;
    640   }
    641   {
    642     KitCgTypeId else_ty = toy_parse_expr(p);
    643     if (else_ty == KIT_CG_TYPE_NONE) {
    644       p->nscopes--;
    645       return 0;
    646     }
    647     if (!toy_check_source_value(p, result_ty, result_toy_type, else_ty,
    648                                 p->last_type, "while else type mismatch")) {
    649       p->nscopes--;
    650       return 0;
    651     }
    652     if (else_ty != result_ty &&
    653         !toy_emit_checked_cast(p, else_ty, result_ty)) {
    654       p->nscopes--;
    655       return 0;
    656     }
    657   }
    658   if (!toy_parser_expect(p, TOK_RBRACE)) {
    659     toy_error(p, p->cur.loc, "expected '}' after while else");
    660     p->nscopes--;
    661     return 0;
    662   }
    663   kit_cg_scope_end(p->cg, scope);
    664   p->nscopes--;
    665   kit_cg_push_local(p->cg, slot);
    666   kit_cg_swap(p->cg);
    667   kit_cg_store(p->cg, toy_mem_access(p, result_ty));
    668   return 1;
    669 }
    670 
    671 static int toy_parse_while_initializer(ToyParser* p, KitCgLocal slot,
    672                                        KitCgTypeId result_ty,
    673                                        ToyTypeId result_toy_type) {
    674   return toy_parse_while_initializer_named(p, slot, result_ty, result_toy_type,
    675                                            0);
    676 }
    677 
    678 static int toy_parse_let_stmt(ToyParser* p) {
    679   KitSym name;
    680   KitCgTypeId ty = KIT_CG_TYPE_NONE;
    681   KitCgTypeId init_ty = KIT_CG_TYPE_NONE;
    682   ToyTypeId toy_ty = TOY_TYPE_NONE;
    683   ToyTypeId init_toy_type = TOY_TYPE_NONE;
    684   KitCgLocal slot;
    685   int has_init = 0;
    686   int inferred = 0;
    687   int is_var = p->cur.kind == TOK_VAR;
    688   int is_static = 0;
    689   int copy_record_init = 0;
    690   toy_parser_advance(p); /* let/var */
    691   if (!toy_skip_attr_list_ex(p, &is_static)) return 0;
    692   if (p->cur.kind != TOK_IDENT) {
    693     toy_error(p, p->cur.loc, "expected identifier after 'let'");
    694     return 0;
    695   }
    696   name = toy_tok_sym(p, p->cur);
    697   toy_parser_advance(p);
    698 
    699   if (toy_parser_match(p, TOK_COLON)) {
    700     ty = toy_parse_type(p);
    701     if (ty == KIT_CG_TYPE_NONE) return 0;
    702     toy_ty = p->last_type;
    703   } else {
    704     inferred = 1;
    705     if (p->cur.kind != TOK_EQ) {
    706       toy_error(p, p->cur.loc, "expected ':' or initializer after identifier");
    707       return 0;
    708     }
    709   }
    710 
    711   if (is_static) {
    712     KitCgDecl decl;
    713     KitCgDataDefAttrs data_attrs;
    714     KitCgSym sym;
    715     KitSym linkage_name;
    716     char sym_name[64];
    717     uint8_t buf[1024];
    718     size_t n = (size_t)kit_cg_type_size(p->c, ty);
    719     int array_data_init = 0;
    720     if (inferred) {
    721       toy_error(p, p->cur.loc, "static local requires explicit type");
    722       return 0;
    723     }
    724     if (n > sizeof buf) {
    725       toy_error(p, p->cur.loc, "static initializer too large");
    726       return 0;
    727     }
    728     memset(buf, 0, n);
    729     if (toy_parser_match(p, TOK_EQ)) {
    730       if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
    731         toy_emit_int_bytes(p, (uint64_t)p->cur.int_value, buf, n);
    732         toy_parser_advance(p);
    733       } else if (p->cur.kind == TOK_STRING &&
    734                  kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_ARRAY &&
    735                  kit_cg_type_array_elem(p->c, ty) ==
    736                      toy_builtin_type(p, KIT_CG_BUILTIN_I8)) {
    737         size_t len = 0;
    738         if (!toy_parse_string_bytes(p, buf, sizeof buf, &len)) return 0;
    739         if (len > n) {
    740           toy_error(p, p->cur.loc, "string initializer too large");
    741           return 0;
    742         }
    743       } else if (p->cur.kind == TOK_LBRACKET &&
    744                  kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_ARRAY &&
    745                  toy_lexer_peek(&p->lex).kind == TOK_AT) {
    746         array_data_init = 1;
    747       } else {
    748         toy_error(p, p->cur.loc, "expected constant static initializer");
    749         return 0;
    750       }
    751     }
    752     snprintf(sym_name, sizeof sym_name, ".Ltoy_static_%u",
    753              p->module->static_counter++);
    754     linkage_name = kit_sym_intern(p->c, kit_slice_cstr(sym_name));
    755     memset(&decl, 0, sizeof decl);
    756     decl.kind = KIT_CG_DECL_OBJECT;
    757     decl.linkage_name = linkage_name;
    758     decl.display_name = name;
    759     decl.type = ty;
    760     decl.sym.bind = KIT_SB_LOCAL;
    761     decl.sym.visibility = KIT_CG_VIS_DEFAULT;
    762     decl.as.object.tls_model = KIT_CG_TLS_AUTO;
    763     if (!is_var) decl.as.object.flags |= KIT_CG_OBJ_READONLY;
    764     sym = kit_cg_decl(p->cg, decl);
    765     if (sym == KIT_CG_SYM_NONE) {
    766       toy_error(p, p->cur.loc, "failed to declare static local");
    767       return 0;
    768     }
    769     if (!toy_add_static_local_typed(p, name, ty, toy_ty, sym, is_var)) return 0;
    770     memset(&data_attrs, 0, sizeof data_attrs);
    771     data_attrs.flags |= KIT_CG_DATADEF_FUNCTION_LOCAL;
    772     if (!is_var) data_attrs.flags |= KIT_CG_DATADEF_READONLY;
    773     kit_cg_data_begin(p->cg, sym, data_attrs);
    774     if (array_data_init) {
    775       KitCgTypeId elem_ty = kit_cg_type_array_elem(p->c, ty);
    776       uint64_t total_size = kit_cg_type_size(p->c, ty);
    777       uint64_t pos = 0;
    778       toy_parser_advance(p); /* '[' */
    779       while (p->cur.kind != TOK_RBRACKET && p->cur.kind != TOK_EOF) {
    780         if (!toy_parse_data_array_builtin(p, elem_ty, total_size, &pos))
    781           return 0;
    782         if (!toy_parser_match(p, TOK_COMMA)) break;
    783       }
    784       if (!toy_parser_expect(p, TOK_RBRACKET)) {
    785         toy_error(p, p->cur.loc, "expected ']' after static initializer");
    786         return 0;
    787       }
    788       if (pos < total_size) kit_cg_data_zero(p->cg, total_size - pos);
    789     } else {
    790       kit_cg_data_bytes(p->cg, buf, n);
    791     }
    792     kit_cg_data_end(p->cg);
    793     if (!toy_parser_expect(p, TOK_SEMI)) {
    794       toy_error(p, p->cur.loc, "expected ';' after static local");
    795       return 0;
    796     }
    797     return 1;
    798   }
    799 
    800   if (!inferred && p->cur.kind == TOK_EQ &&
    801       ((kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_ARRAY &&
    802         toy_lexer_peek(&p->lex).kind == TOK_LBRACKET) ||
    803        (kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_RECORD &&
    804         !toy_type_is_slice(p, toy_ty) &&
    805         (toy_lexer_peek(&p->lex).kind == TOK_IDENT ||
    806          toy_lexer_peek(&p->lex).kind == TOK_LBRACE)))) {
    807     toy_parser_advance(p); /* = */
    808     slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    809     if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    810     if (kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_ARRAY) {
    811       if (!toy_parse_array_initializer(p, slot, ty, toy_ty)) return 0;
    812     } else {
    813       if (!toy_parse_record_initializer(p, slot, ty, toy_ty)) return 0;
    814     }
    815     if (!toy_parser_expect(p, TOK_SEMI)) {
    816       toy_error(p, p->cur.loc, "expected ';' after let");
    817       return 0;
    818     }
    819     return 1;
    820   }
    821 
    822   if (!inferred && kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_ENUM &&
    823       p->cur.kind == TOK_EQ && toy_lexer_peek(&p->lex).kind == TOK_DOT) {
    824     ToyNamedType* named = toy_find_named_type_by_type(p, ty);
    825     KitSym value_name;
    826     size_t i;
    827     int found = 0;
    828     int64_t value = 0;
    829     toy_parser_advance(p); /* = */
    830     toy_parser_advance(p); /* . */
    831     if (p->cur.kind != TOK_IDENT) {
    832       toy_error(p, p->cur.loc, "expected enum value");
    833       return 0;
    834     }
    835     value_name = toy_tok_sym(p, p->cur);
    836     toy_parser_advance(p);
    837     if (!named || named->kind != TOY_NAMED_ENUM) {
    838       toy_error(p, p->cur.loc, "unknown enum type");
    839       return 0;
    840     }
    841     for (i = 0; i < named->nenum_values; ++i) {
    842       if (named->enum_values[i].name == value_name) {
    843         found = 1;
    844         value = named->enum_values[i].value;
    845         break;
    846       }
    847     }
    848     if (!found) {
    849       toy_error(p, p->cur.loc, "unknown enum value");
    850       return 0;
    851     }
    852     slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    853     if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    854     kit_cg_push_local(p->cg, slot);
    855     kit_cg_push_int(p->cg, (uint64_t)value, ty);
    856     kit_cg_store(p->cg, toy_mem_access(p, ty));
    857     if (!toy_parser_expect(p, TOK_SEMI)) {
    858       toy_error(p, p->cur.loc, "expected ';' after let");
    859       return 0;
    860     }
    861     return 1;
    862   }
    863 
    864   if (!inferred && p->cur.kind == TOK_EQ &&
    865       toy_lexer_peek(&p->lex).kind == TOK_IF) {
    866     toy_parser_advance(p); /* = */
    867     slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    868     if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    869     if (!toy_parse_if_initializer(p, slot, ty, toy_ty)) return 0;
    870     if (!toy_parser_expect(p, TOK_SEMI)) {
    871       toy_error(p, p->cur.loc, "expected ';' after let");
    872       return 0;
    873     }
    874     return 1;
    875   }
    876 
    877   if (!inferred && p->cur.kind == TOK_EQ &&
    878       toy_lexer_peek(&p->lex).kind == TOK_SWITCH) {
    879     toy_parser_advance(p); /* = */
    880     slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    881     if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    882     if (!toy_parse_switch_initializer(p, slot, ty, toy_ty)) return 0;
    883     if (!toy_parser_expect(p, TOK_SEMI)) {
    884       toy_error(p, p->cur.loc, "expected ';' after let");
    885       return 0;
    886     }
    887     return 1;
    888   }
    889 
    890   if (!inferred && p->cur.kind == TOK_EQ &&
    891       toy_lexer_peek(&p->lex).kind == TOK_WHILE) {
    892     toy_parser_advance(p); /* = */
    893     slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    894     if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    895     if (!toy_parse_while_initializer(p, slot, ty, toy_ty)) return 0;
    896     if (!toy_parser_expect(p, TOK_SEMI)) {
    897       toy_error(p, p->cur.loc, "expected ';' after let");
    898       return 0;
    899     }
    900     return 1;
    901   }
    902 
    903   if (!inferred && p->cur.kind == TOK_EQ) {
    904     ToyLexer tmp_lex = p->lex;
    905     ToyToken label_tok = toy_lexer_next(&tmp_lex);
    906     ToyToken colon_tok = toy_lexer_next(&tmp_lex);
    907     ToyToken while_tok = toy_lexer_next(&tmp_lex);
    908     if (label_tok.kind == TOK_IDENT && colon_tok.kind == TOK_COLON &&
    909         while_tok.kind == TOK_WHILE) {
    910       KitSym loop_label;
    911       toy_parser_advance(p); /* = */
    912       loop_label = toy_tok_sym(p, p->cur);
    913       toy_parser_advance(p); /* label */
    914       if (!toy_parser_expect(p, TOK_COLON)) {
    915         toy_error(p, p->cur.loc, "expected ':' after loop label");
    916         return 0;
    917       }
    918       slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    919       if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    920       if (!toy_parse_while_initializer_named(p, slot, ty, toy_ty, loop_label))
    921         return 0;
    922       if (!toy_parser_expect(p, TOK_SEMI)) {
    923         toy_error(p, p->cur.loc, "expected ';' after let");
    924         return 0;
    925       }
    926       return 1;
    927     }
    928   }
    929 
    930   if (toy_parser_match(p, TOK_EQ)) {
    931     has_init = 1;
    932     if (!inferred && toy_type_is_ptr(p, ty) && p->cur.kind == TOK_IDENT &&
    933         toy_sym_is(p, toy_tok_sym(p, p->cur), "NULL") &&
    934         toy_lexer_peek(&p->lex).kind == TOK_SEMI) {
    935       toy_parser_advance(p);
    936       kit_cg_push_int(p->cg, 0, p->int_type);
    937       kit_cg_int_to_ptr(p->cg, ty);
    938       init_ty = ty;
    939       init_toy_type = toy_ty;
    940     } else {
    941       init_ty = toy_parse_expr(p);
    942       if (init_ty == KIT_CG_TYPE_NONE) return 0;
    943       init_toy_type = p->last_type;
    944     }
    945     if (inferred) {
    946       ty = init_ty;
    947       toy_ty = init_toy_type;
    948     } else {
    949       if (!toy_check_source_value(p, ty, toy_ty, init_ty, init_toy_type,
    950                                   "type mismatch in let initializer")) {
    951         return 0;
    952       }
    953       if (init_ty != ty) {
    954         if (toy_records_have_matching_storage(p, ty, init_ty)) {
    955           copy_record_init = 1;
    956         } else {
    957           if (!toy_emit_checked_cast(p, init_ty, ty)) return 0;
    958         }
    959       }
    960     }
    961   }
    962 
    963   slot = kit_cg_local(p->cg, ty, toy_slot_attrs(name));
    964   if (!toy_add_local_typed(p, name, ty, toy_ty, slot, is_var)) return 0;
    965 
    966   if (has_init) {
    967     if (toy_type_is_slice(p, init_toy_type)) {
    968       if (!toy_copy_record_lvalue_to_local(p, init_ty, slot, ty)) return 0;
    969     } else if (copy_record_init ||
    970                kit_cg_type_kind(p->c, ty) == KIT_CG_TYPE_RECORD) {
    971       if (!toy_copy_record_lvalue_to_local(p, init_ty, slot, ty)) return 0;
    972     } else {
    973       kit_cg_push_local(p->cg, slot);
    974       kit_cg_swap(p->cg);
    975       kit_cg_store(p->cg, toy_mem_access(p, ty));
    976     }
    977   }
    978   if (!toy_parser_expect(p, TOK_SEMI)) {
    979     toy_error(p, p->cur.loc, "expected ';' after let");
    980     return 0;
    981   }
    982   return 1;
    983 }
    984 
    985 static int toy_parse_if_stmt(ToyParser* p) {
    986   KitCgIf it;
    987   KitCgTypeId cond_ty;
    988   toy_parser_advance(p); /* if */
    989   cond_ty = toy_parse_expr(p);
    990   if (cond_ty == KIT_CG_TYPE_NONE) return 0;
    991   if (!toy_emit_truthy(p, cond_ty)) return 0;
    992 
    993   it = kit_cg_if_begin(p->cg);
    994 
    995   if (!toy_parse_block(p)) return 0;
    996 
    997   kit_cg_if_else(p->cg, it);
    998 
    999   if (p->cur.kind == TOK_ELSE) {
   1000     toy_parser_advance(p); /* else */
   1001     if (p->cur.kind == TOK_LBRACE) {
   1002       if (!toy_parse_block(p)) return 0;
   1003     } else {
   1004       if (!toy_parse_stmt(p)) return 0;
   1005     }
   1006   }
   1007 
   1008   kit_cg_if_end(p->cg, it);
   1009   return 1;
   1010 }
   1011 
   1012 static int toy_parse_while_stmt_named(ToyParser* p, KitSym label_name) {
   1013   KitCgScope scope;
   1014   KitCgTypeId cond_ty;
   1015 
   1016   toy_parser_advance(p); /* while */
   1017 
   1018   if (!toy_parser_reserve(p, (void**)&p->scopes, &p->cap_scopes,
   1019                           p->nscopes + 1u, sizeof *p->scopes, "scopes")) {
   1020     return 0;
   1021   }
   1022   scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
   1023   p->scopes[p->nscopes].name = label_name;
   1024   p->scopes[p->nscopes].kind = TOY_SCOPE_LOOP;
   1025   p->scopes[p->nscopes].cg_scope = scope;
   1026   p->scopes[p->nscopes].result_type = KIT_CG_TYPE_NONE;
   1027   p->scopes[p->nscopes].result_toy_type = TOY_TYPE_NONE;
   1028   p->nscopes++;
   1029 
   1030   cond_ty = toy_parse_expr(p);
   1031   if (cond_ty == KIT_CG_TYPE_NONE) {
   1032     p->nscopes--;
   1033     return 0;
   1034   }
   1035   if (!toy_emit_truthy(p, cond_ty)) {
   1036     p->nscopes--;
   1037     return 0;
   1038   }
   1039   kit_cg_break_false(p->cg, scope);
   1040 
   1041   if (!toy_parse_block(p)) {
   1042     p->nscopes--;
   1043     return 0;
   1044   }
   1045 
   1046   kit_cg_continue(p->cg, scope);
   1047   kit_cg_scope_end(p->cg, scope);
   1048   p->nscopes--;
   1049   return 1;
   1050 }
   1051 
   1052 static int toy_parse_while_stmt(ToyParser* p) {
   1053   return toy_parse_while_stmt_named(p, 0);
   1054 }
   1055 
   1056 static int toy_parse_switch_stmt_named(ToyParser* p, KitSym label_name) {
   1057   KitCgTypeId selector_ty;
   1058   KitCgLocal selector_slot;
   1059   KitCgLabel end_label;
   1060   KitCgLabel dispatch_label;
   1061   KitCgLabel default_arm_label = KIT_CG_LABEL_NONE;
   1062   KitCgScope scope;
   1063   KitCgSwitchCase* cases = NULL;
   1064   size_t ncases = 0;
   1065   size_t cap_cases = 0;
   1066   KitCgSwitchHint hint;
   1067   KitCgSwitch sw;
   1068 
   1069   toy_parser_advance(p); /* switch */
   1070   if (!toy_parse_switch_strategy_hint(p, &hint)) return 0;
   1071   selector_ty = toy_parse_expr(p);
   1072   if (selector_ty == KIT_CG_TYPE_NONE) return 0;
   1073   if (!toy_type_is_intlike(p, selector_ty)) {
   1074     toy_error(p, p->cur.loc, "switch selector must be integer-like");
   1075     return 0;
   1076   }
   1077   selector_slot = kit_cg_local(p->cg, selector_ty, toy_slot_attrs(0));
   1078   kit_cg_push_local(p->cg, selector_slot);
   1079   kit_cg_swap(p->cg);
   1080   kit_cg_store(p->cg, toy_mem_access(p, selector_ty));
   1081   end_label = kit_cg_label_new(p->cg);
   1082   dispatch_label = kit_cg_label_new(p->cg);
   1083   if (!toy_parser_reserve(p, (void**)&p->scopes, &p->cap_scopes,
   1084                           p->nscopes + 1u, sizeof *p->scopes, "scopes")) {
   1085     return 0;
   1086   }
   1087   /* scope_begin must precede the jump-to-dispatch so the scope's entry
   1088    * block is reachable from the straight-line predecessor. With the
   1089    * jump before scope_begin, opt's CFG sees the scope_begin block as
   1090    * unreachable and prunes it; scope_end then can't find its handle. */
   1091   scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
   1092   p->scopes[p->nscopes].name = label_name;
   1093   p->scopes[p->nscopes].kind = TOY_SCOPE_SWITCH;
   1094   p->scopes[p->nscopes].cg_scope = scope;
   1095   p->scopes[p->nscopes].result_type = KIT_CG_TYPE_NONE;
   1096   p->scopes[p->nscopes].result_toy_type = TOY_TYPE_NONE;
   1097   p->nscopes++;
   1098   /* Skip the arm bodies on entry; come back through the dispatch label. */
   1099   kit_cg_jump(p->cg, dispatch_label);
   1100   if (!toy_parser_expect(p, TOK_LBRACE)) {
   1101     toy_error(p, p->cur.loc, "expected '{' after switch selector");
   1102     p->nscopes--;
   1103     return 0;
   1104   }
   1105   while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) {
   1106     KitCgLabel arm_label = kit_cg_label_new(p->cg);
   1107     if (p->cur.kind == TOK_DEFAULT) {
   1108       default_arm_label = arm_label;
   1109       toy_parser_advance(p);
   1110     } else {
   1111       for (;;) {
   1112         int64_t value;
   1113         if (!toy_parse_switch_label_value(p, selector_ty, &value)) {
   1114           toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
   1115           p->nscopes--;
   1116           return 0;
   1117         }
   1118         if (!toy_parser_reserve(p, (void**)&cases, &cap_cases, ncases + 1u,
   1119                                 sizeof *cases, "switch cases")) {
   1120           p->nscopes--;
   1121           return 0;
   1122         }
   1123         cases[ncases].value = (uint64_t)value;
   1124         cases[ncases].label = arm_label;
   1125         ncases++;
   1126         if (!toy_parser_match(p, TOK_COMMA)) break;
   1127       }
   1128     }
   1129     kit_cg_label_place(p->cg, arm_label);
   1130     if (!toy_parse_block(p)) {
   1131       toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
   1132       p->nscopes--;
   1133       return 0;
   1134     }
   1135     kit_cg_jump(p->cg, end_label);
   1136   }
   1137   if (!toy_parser_expect(p, TOK_RBRACE)) {
   1138     toy_error(p, p->cur.loc, "expected '}' after switch");
   1139     toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
   1140     p->nscopes--;
   1141     return 0;
   1142   }
   1143   kit_cg_label_place(p->cg, dispatch_label);
   1144   kit_cg_push_local(p->cg, selector_slot);
   1145   kit_cg_load(p->cg, toy_mem_access(p, selector_ty));
   1146   sw.selector_type = selector_ty;
   1147   sw.default_label =
   1148       default_arm_label != KIT_CG_LABEL_NONE ? default_arm_label : end_label;
   1149   sw.cases = cases;
   1150   sw.ncases = (uint32_t)ncases;
   1151   sw.hint = hint;
   1152   kit_cg_switch(p->cg, sw);
   1153   kit_cg_label_place(p->cg, end_label);
   1154   kit_cg_scope_end(p->cg, scope);
   1155   p->nscopes--;
   1156   toy_parser_free_mem(p, cases, cap_cases * sizeof *cases);
   1157   return 1;
   1158 }
   1159 
   1160 static int toy_parse_switch_stmt(ToyParser* p) {
   1161   return toy_parse_switch_stmt_named(p, 0);
   1162 }
   1163 
   1164 static int toy_add_goto_target(ToyParser* p, uint32_t* ntargets,
   1165                                KitCgLabel label) {
   1166   if (!toy_parser_reserve(p, (void**)&p->goto_targets, &p->cap_goto_targets,
   1167                           (size_t)*ntargets + 1u, sizeof *p->goto_targets,
   1168                           "goto targets")) {
   1169     return 0;
   1170   }
   1171   p->goto_targets[*ntargets] = label;
   1172   (*ntargets)++;
   1173   return 1;
   1174 }
   1175 
   1176 static int toy_parse_label_decl_stmt(ToyParser* p) {
   1177   KitSym name;
   1178   toy_parser_advance(p); /* label */
   1179   if (p->cur.kind != TOK_IDENT) {
   1180     toy_error(p, p->cur.loc, "expected label name");
   1181     return 0;
   1182   }
   1183   name = toy_tok_sym(p, p->cur);
   1184   toy_parser_advance(p);
   1185   if (!toy_declare_label(p, name)) return 0;
   1186   if (!toy_parser_expect(p, TOK_SEMI)) {
   1187     toy_error(p, p->cur.loc, "expected ';' after label declaration");
   1188     return 0;
   1189   }
   1190   return 1;
   1191 }
   1192 
   1193 static int toy_parse_goto_stmt(ToyParser* p) {
   1194   KitCgTypeId target_ty;
   1195   KitCgTypeId void_ptr_ty =
   1196       kit_cg_type_ptr(p->c, toy_builtin_type(p, KIT_CG_BUILTIN_VOID), 0);
   1197   uint32_t ntargets = 0;
   1198   uint32_t i;
   1199   toy_parser_advance(p); /* goto */
   1200   if (!toy_parser_expect(p, TOK_STAR)) {
   1201     toy_error(p, p->cur.loc, "expected computed goto target");
   1202     return 0;
   1203   }
   1204   target_ty = toy_parse_expr(p);
   1205   if (target_ty == KIT_CG_TYPE_NONE) return 0;
   1206   if (target_ty != void_ptr_ty) {
   1207     toy_error(p, p->cur.loc, "computed goto target must be *void");
   1208     return 0;
   1209   }
   1210   if (toy_parser_match(p, TOK_WITHIN)) {
   1211     if (!toy_parser_expect(p, TOK_LPAREN)) {
   1212       toy_error(p, p->cur.loc, "expected target list");
   1213       return 0;
   1214     }
   1215     while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
   1216       KitSym name;
   1217       ToyLabel* label;
   1218       if (p->cur.kind != TOK_IDENT) {
   1219         toy_error(p, p->cur.loc, "expected label in target list");
   1220         return 0;
   1221       }
   1222       name = toy_tok_sym(p, p->cur);
   1223       toy_parser_advance(p);
   1224       label = toy_find_label(p, name);
   1225       if (!label) {
   1226         toy_error(p, p->cur.loc, "unknown label in target list");
   1227         return 0;
   1228       }
   1229       if (!toy_add_goto_target(p, &ntargets, label->label)) return 0;
   1230       if (!toy_parser_match(p, TOK_COMMA)) break;
   1231     }
   1232     if (!toy_parser_expect(p, TOK_RPAREN)) {
   1233       toy_error(p, p->cur.loc, "expected ')' after target list");
   1234       return 0;
   1235     }
   1236   }
   1237   if (ntargets == 0) {
   1238     for (i = 0; i < p->nlabels; ++i) {
   1239       if (!toy_add_goto_target(p, &ntargets, p->labels[i].label)) return 0;
   1240     }
   1241   }
   1242   kit_cg_computed_goto(p->cg, p->goto_targets, ntargets);
   1243   if (!toy_parser_expect(p, TOK_SEMI)) {
   1244     toy_error(p, p->cur.loc, "expected ';' after goto");
   1245     return 0;
   1246   }
   1247   return 1;
   1248 }
   1249 
   1250 static int toy_parse_break_stmt(ToyParser* p) {
   1251   KitSym target_name = 0;
   1252   ToyScope* target_scope;
   1253   toy_parser_advance(p); /* break */
   1254   if (p->nscopes == 0) {
   1255     toy_error(p, p->cur.loc, "break outside loop");
   1256     return 0;
   1257   }
   1258   if (p->cur.kind == TOK_IDENT) {
   1259     KitSym maybe_target = toy_tok_sym(p, p->cur);
   1260     if (toy_find_scope(p, maybe_target)) {
   1261       target_name = maybe_target;
   1262       toy_parser_advance(p);
   1263     }
   1264   }
   1265   target_scope = toy_find_scope(p, target_name);
   1266   if (!target_scope) {
   1267     toy_error(p, p->cur.loc, "unknown break target");
   1268     return 0;
   1269   }
   1270   if (p->cur.kind != TOK_SEMI) {
   1271     KitCgTypeId expr_ty = toy_parse_expr(p);
   1272     KitCgTypeId result_ty = target_scope->result_type;
   1273     if (expr_ty == KIT_CG_TYPE_NONE) return 0;
   1274     if (result_ty == KIT_CG_TYPE_NONE) {
   1275       toy_error(p, p->cur.loc, "break value type mismatch");
   1276       return 0;
   1277     }
   1278     if (!toy_check_source_value(p, result_ty, target_scope->result_toy_type,
   1279                                 expr_ty, p->last_type,
   1280                                 "break value type mismatch"))
   1281       return 0;
   1282     if (expr_ty != result_ty &&
   1283         !toy_emit_checked_cast(p, expr_ty, result_ty))
   1284       return 0;
   1285     kit_cg_break(p->cg, target_scope->cg_scope);
   1286     if (!toy_parser_expect(p, TOK_SEMI)) {
   1287       toy_error(p, p->cur.loc, "expected ';' after break");
   1288       return 0;
   1289     }
   1290     return 1;
   1291   }
   1292   kit_cg_break(p->cg, target_scope->cg_scope);
   1293   if (!toy_parser_expect(p, TOK_SEMI)) {
   1294     toy_error(p, p->cur.loc, "expected ';' after break");
   1295     return 0;
   1296   }
   1297   return 1;
   1298 }
   1299 
   1300 static int toy_parse_continue_stmt(ToyParser* p) {
   1301   KitSym target_name = 0;
   1302   ToyScope* target_scope;
   1303   toy_parser_advance(p); /* continue */
   1304   if (p->nscopes == 0) {
   1305     toy_error(p, p->cur.loc, "continue outside loop");
   1306     return 0;
   1307   }
   1308   if (p->cur.kind == TOK_IDENT) {
   1309     target_name = toy_tok_sym(p, p->cur);
   1310     toy_parser_advance(p);
   1311     target_scope = toy_find_scope(p, target_name);
   1312     if (!target_scope) {
   1313       toy_error(p, p->cur.loc, "unknown continue target");
   1314       return 0;
   1315     }
   1316     if (target_scope->kind != TOY_SCOPE_LOOP) {
   1317       toy_error(p, p->cur.loc, "continue target is not a loop");
   1318       return 0;
   1319     }
   1320   } else {
   1321     target_scope = toy_find_innermost_loop_scope(p);
   1322     if (!target_scope) {
   1323       toy_error(p, p->cur.loc, "continue outside loop");
   1324       return 0;
   1325     }
   1326   }
   1327   kit_cg_continue(p->cg, target_scope->cg_scope);
   1328   if (!toy_parser_expect(p, TOK_SEMI)) {
   1329     toy_error(p, p->cur.loc, "expected ';' after continue");
   1330     return 0;
   1331   }
   1332   return 1;
   1333 }
   1334 
   1335 static int toy_parse_return_stmt(ToyParser* p) {
   1336   KitCgTypeId ty;
   1337   toy_parser_advance(p); /* return */
   1338   int is_tail = p->cur.kind == TOK_TAIL;
   1339   int is_musttail = p->cur.kind == TOK_MUSTTAIL;
   1340   if (is_tail || is_musttail) {
   1341     int must_tail = is_musttail;
   1342     KitSym name;
   1343     ToyFn* fn;
   1344     ToyToken call_tok;
   1345     KitCgTypeId fn_ty;
   1346     size_t nargs = 0;
   1347     toy_parser_advance(p); /* tail | musttail */
   1348     if (p->cur.kind != TOK_IDENT) {
   1349       toy_error(p, p->cur.loc, "expected function name after %s",
   1350                 must_tail ? "musttail" : "tail");
   1351       return 0;
   1352     }
   1353     call_tok = p->cur;
   1354     name = toy_tok_sym(p, p->cur);
   1355     fn = toy_find_fn(p, name);
   1356     toy_parser_advance(p);
   1357     if (!toy_parser_expect(p, TOK_LPAREN)) return 0;
   1358     if (fn) {
   1359       fn_ty = fn->type;
   1360     } else {
   1361       KitCgTypeId callee_ty = toy_push_named_rvalue(p, name);
   1362       if (callee_ty == KIT_CG_TYPE_NONE) {
   1363         toy_error(p, call_tok.loc, "undefined function in tail call");
   1364         return 0;
   1365       }
   1366       fn_ty = toy_ptr_pointee_func_type(p, callee_ty);
   1367       if (fn_ty == KIT_CG_TYPE_NONE) {
   1368         toy_error(p, call_tok.loc, "tail callee is not a function pointer");
   1369         return 0;
   1370       }
   1371     }
   1372     /* Variadic tail calls are not rejected here: realizability is a per-target
   1373      * decision owned by CG's precondition and the target's tail_call hook
   1374      * (e.g. native fits varargs in the caller's incoming area; wasm cannot,
   1375      * since its vararg buffer lives in the frame a sibling call tears down). */
   1376     if (!toy_parse_call_args(p, call_tok, fn_ty, fn ? fn->toy_params : NULL,
   1377                              fn ? fn->nparams : 0, &nargs))
   1378       return 0;
   1379     if (fn && !toy_type_accepts_storage(p, p->cur_fn_ret_toy, fn->toy_ret)) {
   1380       toy_error(p, p->cur.loc, "tail call signature mismatch");
   1381       return 0;
   1382     }
   1383     if (!fn && toy_cg_func_ret(p, fn_ty) != p->cur_fn_ret) {
   1384       toy_error(p, p->cur.loc, "tail call signature mismatch");
   1385       return 0;
   1386     }
   1387     if (fn) {
   1388       if (must_tail)
   1389         kit_cg_musttail_call_symbol(p->cg, toy_fn_cur_sym(p, fn),
   1390                                     (uint32_t)nargs);
   1391       else
   1392         kit_cg_tail_call_symbol(p->cg, toy_fn_cur_sym(p, fn), (uint32_t)nargs);
   1393     } else {
   1394       if (must_tail)
   1395         kit_cg_musttail_call(p->cg, (uint32_t)nargs, fn_ty);
   1396       else
   1397         kit_cg_tail_call(p->cg, (uint32_t)nargs, fn_ty);
   1398     }
   1399     if (!toy_parser_expect(p, TOK_SEMI)) return 0;
   1400     return 1;
   1401   }
   1402   if (p->cur.kind == TOK_SEMI) {
   1403     toy_parser_advance(p);
   1404     if (p->cur_fn_ret != toy_builtin_type(p, KIT_CG_BUILTIN_VOID)) {
   1405       toy_error(p, p->cur.loc, "return without value in non-void function");
   1406       return 0;
   1407     }
   1408     kit_cg_ret(p->cg);
   1409     return 1;
   1410   }
   1411   if (p->cur_fn_ret != toy_builtin_type(p, KIT_CG_BUILTIN_VOID)) {
   1412     int is_control_expr = p->cur.kind == TOK_IF || p->cur.kind == TOK_SWITCH ||
   1413                           p->cur.kind == TOK_WHILE;
   1414     KitSym loop_label = 0;
   1415     if (!is_control_expr && p->cur.kind == TOK_IDENT) {
   1416       ToyLexer tmp_lex = p->lex;
   1417       ToyToken colon_tok = toy_lexer_next(&tmp_lex);
   1418       ToyToken while_tok = toy_lexer_next(&tmp_lex);
   1419       if (colon_tok.kind == TOK_COLON && while_tok.kind == TOK_WHILE) {
   1420         is_control_expr = 1;
   1421         loop_label = toy_tok_sym(p, p->cur);
   1422       }
   1423     }
   1424     if (is_control_expr) {
   1425       KitCgLocal slot = kit_cg_local(p->cg, p->cur_fn_ret, toy_slot_attrs(0));
   1426       if (p->cur.kind == TOK_IF) {
   1427         if (!toy_parse_if_initializer(p, slot, p->cur_fn_ret,
   1428                                       p->cur_fn_ret_toy))
   1429           return 0;
   1430       } else if (p->cur.kind == TOK_SWITCH) {
   1431         if (!toy_parse_switch_initializer(p, slot, p->cur_fn_ret,
   1432                                           p->cur_fn_ret_toy))
   1433           return 0;
   1434       } else if (p->cur.kind == TOK_WHILE) {
   1435         if (!toy_parse_while_initializer(p, slot, p->cur_fn_ret,
   1436                                          p->cur_fn_ret_toy))
   1437           return 0;
   1438       } else {
   1439         toy_parser_advance(p); /* label */
   1440         if (!toy_parser_expect(p, TOK_COLON)) {
   1441           toy_error(p, p->cur.loc, "expected ':' after loop label");
   1442           return 0;
   1443         }
   1444         if (!toy_parse_while_initializer_named(p, slot, p->cur_fn_ret,
   1445                                                p->cur_fn_ret_toy, loop_label)) {
   1446           return 0;
   1447         }
   1448       }
   1449       kit_cg_push_local(p->cg, slot);
   1450       kit_cg_load(p->cg, toy_mem_access(p, p->cur_fn_ret));
   1451       kit_cg_ret(p->cg);
   1452       if (!toy_parser_expect(p, TOK_SEMI)) {
   1453         toy_error(p, p->cur.loc, "expected ';' after return");
   1454         return 0;
   1455       }
   1456       return 1;
   1457     }
   1458   }
   1459   p->allow_tail_call_expr = 1;
   1460   p->tail_call_expr = 0;
   1461   p->tail_call_ret_toy = TOY_TYPE_NONE;
   1462   ty = toy_parse_expr(p);
   1463   p->allow_tail_call_expr = 0;
   1464   if (ty == KIT_CG_TYPE_NONE) return 0;
   1465   if (p->tail_call_expr) {
   1466     ToyTypeId tail_toy = p->tail_call_ret_toy;
   1467     if (!tail_toy) tail_toy = p->last_type;
   1468     if (!toy_check_source_value(p, p->cur_fn_ret, p->cur_fn_ret_toy, ty,
   1469                                 tail_toy, "return type mismatch"))
   1470       return 0;
   1471     if (!toy_parser_expect(p, TOK_SEMI)) {
   1472       toy_error(p, p->cur.loc, "expected ';' after return");
   1473       return 0;
   1474     }
   1475     p->tail_call_expr = 0;
   1476     p->tail_call_ret_toy = TOY_TYPE_NONE;
   1477     return 1;
   1478   }
   1479   if (!toy_check_source_value(p, p->cur_fn_ret, p->cur_fn_ret_toy, ty,
   1480                               p->last_type, "return type mismatch"))
   1481     return 0;
   1482   if (ty != p->cur_fn_ret && !toy_emit_checked_cast(p, ty, p->cur_fn_ret))
   1483     return 0;
   1484   kit_cg_ret(p->cg);
   1485   if (!toy_parser_expect(p, TOK_SEMI)) {
   1486     toy_error(p, p->cur.loc, "expected ';' after return");
   1487     return 0;
   1488   }
   1489   return 1;
   1490 }
   1491 
   1492 static int toy_parse_expr_stmt(ToyParser* p) {
   1493   KitCgTypeId ty = toy_parse_expr(p);
   1494   if (ty == KIT_CG_TYPE_NONE) return 0;
   1495   if (p->tail_call_expr) {
   1496     toy_error(p, p->cur.loc, "tail @call requires return");
   1497     return 0;
   1498   }
   1499   if (!toy_parser_expect(p, TOK_SEMI)) {
   1500     toy_error(p, p->cur.loc, "expected ';' after expression");
   1501     return 0;
   1502   }
   1503   if (ty != toy_builtin_type(p, KIT_CG_BUILTIN_VOID)) kit_cg_drop(p->cg);
   1504   return 1;
   1505 }
   1506 
   1507 static int toy_parse_stmt(ToyParser* p) {
   1508   toy_set_loc(p);
   1509   if (p->cur.kind == TOK_LET || p->cur.kind == TOK_VAR)
   1510     return toy_parse_let_stmt(p);
   1511   if (p->cur.kind == TOK_IF) return toy_parse_if_stmt(p);
   1512   if (p->cur.kind == TOK_WHILE) return toy_parse_while_stmt(p);
   1513   if (p->cur.kind == TOK_SWITCH) return toy_parse_switch_stmt(p);
   1514   if (p->cur.kind == TOK_LABEL) return toy_parse_label_decl_stmt(p);
   1515   if (p->cur.kind == TOK_GOTO) return toy_parse_goto_stmt(p);
   1516   if (p->cur.kind == TOK_BREAK) return toy_parse_break_stmt(p);
   1517   if (p->cur.kind == TOK_CONTINUE) return toy_parse_continue_stmt(p);
   1518   if (p->cur.kind == TOK_RETURN) return toy_parse_return_stmt(p);
   1519   if (p->cur.kind == TOK_LBRACE) return toy_parse_block(p);
   1520 
   1521   /* Assignment or expression statement */
   1522   if (p->cur.kind == TOK_IDENT && toy_lexer_peek(&p->lex).kind == TOK_COLON) {
   1523     KitSym name = toy_tok_sym(p, p->cur);
   1524     toy_parser_advance(p); /* ident */
   1525     toy_parser_advance(p); /* : */
   1526     if (p->cur.kind == TOK_WHILE) return toy_parse_while_stmt_named(p, name);
   1527     if (p->cur.kind == TOK_SWITCH) return toy_parse_switch_stmt_named(p, name);
   1528     {
   1529       ToyLabel* label = toy_declare_label(p, name);
   1530       if (!label) return 0;
   1531       kit_cg_label_place(p->cg, label->label);
   1532     }
   1533     return 1;
   1534   }
   1535 
   1536   if (p->cur.kind == TOK_IDENT &&
   1537       (toy_lexer_peek(&p->lex).kind == TOK_LBRACKET ||
   1538        toy_lexer_peek(&p->lex).kind == TOK_DOTSTAR ||
   1539        toy_lexer_peek(&p->lex).kind == TOK_DOT)) {
   1540     KitSym name = toy_tok_sym(p, p->cur);
   1541     KitCgTypeId lhs_ty;
   1542     ToyTypeId lhs_toy_type = TOY_TYPE_NONE;
   1543     KitCgTypeId root_ty = KIT_CG_TYPE_NONE;
   1544     int root_mutable = 1;
   1545     int lhs_slice_metadata = 0;
   1546     toy_parser_advance(p);
   1547     /* Chain invariant: TOS holds a pointer-rvalue of type `*lhs_ty`.
   1548      * Intermediate field / index / dereference steps materialize the
   1549      * address via toy_addr_offset / toy_addr_index / explicit loads.
   1550      * The final store consumes the pointer with EA {0, 0}. */
   1551     {
   1552       ToyVar* v = toy_find_var(p, name);
   1553       ToyGlobal* g = toy_find_global(p, name);
   1554       if (v) {
   1555         root_ty = v->type;
   1556         lhs_toy_type = v->toy_type;
   1557         root_mutable = v->mutable;
   1558       } else if (g) {
   1559         root_ty = g->type;
   1560         lhs_toy_type = g->toy_type;
   1561         root_mutable = g->mutable;
   1562       }
   1563       if (v) {
   1564         toy_push_var_addr(p, v);
   1565         lhs_ty = v->type;
   1566       } else if (g) {
   1567         kit_cg_push_symbol_addr(p->cg, toy_global_cur_sym(p, g), 0);
   1568         lhs_ty = g->type;
   1569       } else {
   1570         lhs_ty = KIT_CG_TYPE_NONE;
   1571       }
   1572     }
   1573     if (lhs_ty == KIT_CG_TYPE_NONE) {
   1574       toy_error(p, p->cur.loc, "undefined variable in assignment");
   1575       return 0;
   1576     }
   1577 
   1578     for (;;) {
   1579       if (toy_parser_match(p, TOK_LBRACKET)) {
   1580         KitCgTypeId idx_ty = toy_parse_expr(p);
   1581         lhs_slice_metadata = 0;
   1582         if (idx_ty == KIT_CG_TYPE_NONE) return 0;
   1583         if (!toy_type_can_implicitly_cast(p, idx_ty, p->size_type)) {
   1584           toy_error(p, p->cur.loc, "index must be isize");
   1585           return 0;
   1586         }
   1587         if (!toy_emit_implicit_cast(p, idx_ty, p->size_type)) return 0;
   1588         idx_ty = p->size_type;
   1589         if (!toy_parser_expect(p, TOK_RBRACKET)) {
   1590           toy_error(p, p->cur.loc, "expected ']' after index");
   1591           return 0;
   1592         }
   1593         if (kit_cg_type_kind(p->c, lhs_ty) == KIT_CG_TYPE_PTR) {
   1594           /* TOS = **T_chain (where lhs_ty = *T_chain). Load the pointer
   1595            * value so the index applies to the pointee. */
   1596           KitCgTypeId pointee = kit_cg_type_ptr_pointee(p->c, lhs_ty);
   1597           ToyTypeId source_pointee = toy_type_pointee(p, lhs_toy_type);
   1598           /* index is currently on top; stash so we can load the pointer. */
   1599           {
   1600             KitCgLocal idx_slot =
   1601                 kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
   1602             kit_cg_push_local(p->cg, idx_slot);
   1603             kit_cg_swap(p->cg);
   1604             kit_cg_store(p->cg, toy_mem_access(p, p->size_type));
   1605             /* TOS is the chain pointer VALUE (**T); deref to a PLACE and
   1606              * load the inner pointer it addresses. */
   1607             kit_cg_deref(p->cg, 0);
   1608             kit_cg_load(p->cg, toy_mem_access(p, lhs_ty));
   1609             kit_cg_push_local(p->cg, idx_slot);
   1610             kit_cg_load(p->cg, toy_mem_access(p, p->size_type));
   1611           }
   1612           if (kit_cg_type_kind(p->c, pointee) == KIT_CG_TYPE_ARRAY) {
   1613             KitCgTypeId elem_ty = kit_cg_type_array_elem(p->c, pointee);
   1614             lhs_ty = elem_ty;
   1615             lhs_toy_type = toy_type_array_elem(p, source_pointee);
   1616             toy_addr_index(p, kit_cg_type_size(p->c, elem_ty),
   1617                            kit_cg_type_ptr(p->c, elem_ty, 0));
   1618           } else {
   1619             lhs_ty = pointee;
   1620             lhs_toy_type = source_pointee;
   1621             toy_addr_index(p, kit_cg_type_size(p->c, lhs_ty),
   1622                            kit_cg_type_ptr(p->c, lhs_ty, 0));
   1623           }
   1624         } else if (kit_cg_type_kind(p->c, lhs_ty) == KIT_CG_TYPE_ARRAY) {
   1625           KitCgTypeId elem_ty = kit_cg_type_array_elem(p->c, lhs_ty);
   1626           lhs_ty = elem_ty;
   1627           lhs_toy_type = toy_type_array_elem(p, lhs_toy_type);
   1628           toy_addr_index(p, kit_cg_type_size(p->c, elem_ty),
   1629                          kit_cg_type_ptr(p->c, elem_ty, 0));
   1630         } else if (toy_type_is_slice(p, lhs_toy_type)) {
   1631           lhs_ty = toy_emit_slice_index_lvalue(p, lhs_ty, lhs_toy_type,
   1632                                                &lhs_toy_type);
   1633           if (lhs_ty == KIT_CG_TYPE_NONE) return 0;
   1634         } else {
   1635           toy_error(p, p->cur.loc, "cannot index non-array/non-pointer");
   1636           return 0;
   1637         }
   1638         continue;
   1639       }
   1640       if (toy_parser_match(p, TOK_DOTSTAR)) {
   1641         lhs_slice_metadata = 0;
   1642         if (kit_cg_type_kind(p->c, lhs_ty) != KIT_CG_TYPE_PTR) {
   1643           toy_error(p, p->cur.loc, "cannot dereference non-pointer");
   1644           return 0;
   1645         }
   1646         /* TOS = `**T` (pointer VALUE); deref to PLACE and load to `*T`. */
   1647         kit_cg_deref(p->cg, 0);
   1648         kit_cg_load(p->cg, toy_mem_access(p, lhs_ty));
   1649         lhs_ty = kit_cg_type_ptr_pointee(p->c, lhs_ty);
   1650         lhs_toy_type = toy_type_pointee(p, lhs_toy_type);
   1651         continue;
   1652       }
   1653       if (toy_parser_match(p, TOK_DOT)) {
   1654         KitCgField field;
   1655         uint32_t field_index = 0;
   1656         ToyNamedType* named;
   1657         uint64_t foff = 0;
   1658         if (kit_cg_type_kind(p->c, lhs_ty) == KIT_CG_TYPE_PTR &&
   1659             kit_cg_type_kind(p->c, kit_cg_type_ptr_pointee(p->c, lhs_ty)) ==
   1660                 KIT_CG_TYPE_RECORD) {
   1661           /* `p.field`: deref to PLACE and load the pointer value that
   1662            * addresses the record. */
   1663           kit_cg_deref(p->cg, 0);
   1664           kit_cg_load(p->cg, toy_mem_access(p, lhs_ty));
   1665           lhs_ty = kit_cg_type_ptr_pointee(p->c, lhs_ty);
   1666           lhs_toy_type = toy_type_pointee(p, lhs_toy_type);
   1667         }
   1668         if (kit_cg_type_kind(p->c, lhs_ty) != KIT_CG_TYPE_RECORD) {
   1669           toy_error(p, p->cur.loc, "field assignment on non-record");
   1670           return 0;
   1671         }
   1672         named = toy_find_named_type_by_type(p, lhs_ty);
   1673         if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
   1674           if (p->cur.int_value < 0 ||
   1675               p->cur.int_value >=
   1676                   (int64_t)kit_cg_type_record_nfields(p->c, lhs_ty)) {
   1677             toy_error(p, p->cur.loc, "invalid tuple field");
   1678             return 0;
   1679           }
   1680           field_index = (uint32_t)p->cur.int_value;
   1681           toy_parser_advance(p);
   1682           if (kit_cg_type_record_field(p->c, lhs_ty, field_index, &field,
   1683                                        &foff) != 0)
   1684             return 0;
   1685         } else {
   1686           KitSym field_name;
   1687           if (p->cur.kind != TOK_IDENT) {
   1688             toy_error(p, p->cur.loc, "expected field name");
   1689             return 0;
   1690           }
   1691           field_name = toy_tok_sym(p, p->cur);
   1692           toy_parser_advance(p);
   1693           if (!toy_record_field_index(p, lhs_ty, field_name, &field_index,
   1694                                       &field)) {
   1695             toy_error(p, p->cur.loc, "unknown record field");
   1696             return 0;
   1697           }
   1698           if (kit_cg_type_record_field(p->c, lhs_ty, field_index, NULL,
   1699                                        &foff) != 0)
   1700             return 0;
   1701         }
   1702         lhs_slice_metadata = toy_type_is_slice(p, lhs_toy_type) &&
   1703                              (field_index == 0 || field_index == 1);
   1704         lhs_ty = field.type;
   1705         lhs_toy_type = (named && field_index < named->nfields)
   1706                            ? named->fields[field_index].toy_type
   1707                            : TOY_TYPE_NONE;
   1708         toy_addr_offset(p, (int64_t)foff, kit_cg_type_ptr(p->c, lhs_ty, 0));
   1709         continue;
   1710       }
   1711       break;
   1712     }
   1713 
   1714     if (!toy_parser_expect(p, TOK_EQ)) {
   1715       toy_error(p, p->cur.loc, "expected '=' in assignment");
   1716       return 0;
   1717     }
   1718     if (!root_mutable && kit_cg_type_kind(p->c, root_ty) != KIT_CG_TYPE_PTR) {
   1719       toy_error(p, p->cur.loc, "cannot assign to immutable local");
   1720       return 0;
   1721     }
   1722     if (lhs_slice_metadata) {
   1723       toy_error(p, p->cur.loc, "cannot assign to slice metadata");
   1724       return 0;
   1725     }
   1726     /* The address chain leaves a pointer VALUE (`*lhs_ty`) on the stack; turn
   1727      * it into a PLACE before pushing the rhs so store sees [place, value]. */
   1728     kit_cg_deref(p->cg, 0);
   1729     {
   1730       KitCgTypeId expr_ty = toy_parse_expr(p);
   1731       ToyTypeId expr_toy_type = p->last_type;
   1732       if (expr_ty == KIT_CG_TYPE_NONE) return 0;
   1733       if (lhs_toy_type != TOY_TYPE_NONE) {
   1734         if (!toy_type_accepts_type(p, lhs_toy_type, expr_toy_type)) {
   1735           toy_error(p, p->cur.loc, "type mismatch in assignment");
   1736           return 0;
   1737         }
   1738         if (expr_ty != lhs_ty &&
   1739             !toy_emit_checked_cast(p, expr_ty, lhs_ty))
   1740           return 0;
   1741       } else if (!toy_type_can_implicitly_cast(p, expr_ty, lhs_ty)) {
   1742         toy_error(p, p->cur.loc, "type mismatch in assignment");
   1743         return 0;
   1744       } else if (expr_ty != lhs_ty) {
   1745         if (!toy_emit_implicit_cast(p, expr_ty, lhs_ty)) return 0;
   1746       }
   1747       kit_cg_store(p->cg, toy_mem_access(p, lhs_ty));
   1748     }
   1749     if (!toy_parser_expect(p, TOK_SEMI)) {
   1750       toy_error(p, p->cur.loc, "expected ';' after assignment");
   1751       return 0;
   1752     }
   1753     return 1;
   1754   }
   1755 
   1756   if (p->cur.kind == TOK_IDENT && toy_lexer_peek(&p->lex).kind == TOK_EQ) {
   1757     KitSym name = toy_tok_sym(p, p->cur);
   1758     toy_parser_advance(p); /* ident */
   1759     toy_parser_advance(p); /* = */
   1760     KitCgTypeId expr_ty = toy_parse_expr(p);
   1761     ToyTypeId expr_toy_type = p->last_type;
   1762     if (expr_ty == KIT_CG_TYPE_NONE) return 0;
   1763     {
   1764       ToyVar* v = toy_find_var(p, name);
   1765       if (v) {
   1766         if (!v->mutable) {
   1767           toy_error(p, p->cur.loc, "cannot assign to immutable local");
   1768           return 0;
   1769         }
   1770         if (!toy_type_accepts_type(p, v->toy_type, expr_toy_type)) {
   1771           toy_error(p, p->cur.loc, "type mismatch in assignment");
   1772           return 0;
   1773         }
   1774         if (toy_type_is_slice(p, v->toy_type) ||
   1775             kit_cg_type_kind(p->c, v->type) == KIT_CG_TYPE_RECORD) {
   1776           if (!toy_copy_record_lvalue_to_var(p, expr_ty, v, NULL)) return 0;
   1777           if (!toy_parser_expect(p, TOK_SEMI)) {
   1778             toy_error(p, p->cur.loc, "expected ';' after assignment");
   1779             return 0;
   1780           }
   1781           return 1;
   1782         }
   1783         toy_push_var_lvalue(p, v);
   1784         kit_cg_deref(p->cg, 0); /* pointer VALUE -> PLACE for store */
   1785         kit_cg_swap(p->cg);
   1786         if (expr_ty != v->type &&
   1787             !toy_emit_checked_cast(p, expr_ty, v->type))
   1788           return 0;
   1789         kit_cg_store(p->cg, toy_mem_access(p, v->type));
   1790       } else {
   1791         ToyGlobal* g = toy_find_global(p, name);
   1792         if (!g) {
   1793           toy_error(p, p->cur.loc, "undefined variable in assignment");
   1794           return 0;
   1795         }
   1796         if (!g->mutable) {
   1797           toy_error(p, p->cur.loc, "cannot assign to immutable global");
   1798           return 0;
   1799         }
   1800         if (!toy_type_accepts_type(p, g->toy_type, expr_toy_type)) {
   1801           toy_error(p, p->cur.loc, "type mismatch in assignment");
   1802           return 0;
   1803         }
   1804         if (toy_type_is_slice(p, g->toy_type) ||
   1805             kit_cg_type_kind(p->c, g->type) == KIT_CG_TYPE_RECORD) {
   1806           if (!toy_copy_record_lvalue_to_var(p, expr_ty, NULL, g)) return 0;
   1807           if (!toy_parser_expect(p, TOK_SEMI)) {
   1808             toy_error(p, p->cur.loc, "expected ';' after assignment");
   1809             return 0;
   1810           }
   1811           return 1;
   1812         }
   1813         kit_cg_push_symbol_addr(p->cg, toy_global_cur_sym(p, g), 0);
   1814         kit_cg_deref(p->cg, 0); /* pointer VALUE -> PLACE for store */
   1815         kit_cg_swap(p->cg);
   1816         if (expr_ty != g->type &&
   1817             !toy_emit_checked_cast(p, expr_ty, g->type))
   1818           return 0;
   1819         kit_cg_store(p->cg, toy_mem_access(p, g->type));
   1820       }
   1821     }
   1822     if (!toy_parser_expect(p, TOK_SEMI)) {
   1823       toy_error(p, p->cur.loc, "expected ';' after assignment");
   1824       return 0;
   1825     }
   1826     return 1;
   1827   }
   1828 
   1829   return toy_parse_expr_stmt(p);
   1830 }
   1831 
   1832 /* ============================================================
   1833  * Program parsing
   1834  * ============================================================ */
   1835 
   1836 int toy_parse_program(ToyParser* p) {
   1837   while (p->cur.kind != TOK_EOF) {
   1838     int is_extern = 0;
   1839     int is_pub = 0;
   1840     if (toy_parser_match(p, TOK_PUB)) {
   1841       is_pub = 1;
   1842       if (p->cur.kind != TOK_FN && p->cur.kind != TOK_LET &&
   1843           p->cur.kind != TOK_VAR && p->cur.kind != TOK_ALIAS) {
   1844         toy_error(p, p->cur.loc, "expected declaration after pub");
   1845         return 0;
   1846       }
   1847     }
   1848     if (toy_parser_match(p, TOK_EXTERN)) {
   1849       is_extern = 1;
   1850       if (p->cur.kind != TOK_FN && p->cur.kind != TOK_VAR &&
   1851           p->cur.kind != TOK_LET) {
   1852         toy_error(p, p->cur.loc, "expected extern declaration");
   1853         return 0;
   1854       }
   1855     }
   1856     if (p->cur.kind == TOK_FN) {
   1857       int r = toy_parse_fn(p, is_extern, is_pub);
   1858       if (r < 0) return 0;
   1859       if (r == 0) {
   1860         toy_error(p, p->cur.loc, "expected function declaration");
   1861         return 0;
   1862       }
   1863     } else if (p->cur.kind == TOK_LET || p->cur.kind == TOK_VAR) {
   1864       if (!toy_parse_global_var(p, is_extern, is_pub)) return 0;
   1865     } else if (p->cur.kind == TOK_TYPE && !is_extern) {
   1866       if (!toy_parse_type_alias_decl(p)) return 0;
   1867     } else if (p->cur.kind == TOK_RECORD) {
   1868       if (!toy_parse_record_decl(p)) return 0;
   1869     } else if (p->cur.kind == TOK_TUPLE && !is_extern) {
   1870       if (!toy_parse_tuple_decl(p)) return 0;
   1871     } else if (p->cur.kind == TOK_ENUM && !is_extern) {
   1872       if (!toy_parse_enum_decl(p)) return 0;
   1873     } else if (p->cur.kind == TOK_ALIAS && !is_extern) {
   1874       if (!toy_parse_alias_decl(p, is_pub)) return 0;
   1875     } else {
   1876       toy_error(p, p->cur.loc, "expected function or global declaration");
   1877       return 0;
   1878     }
   1879   }
   1880   return 1;
   1881 }