diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-03-19 20:55:39 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-03-19 20:55:39 +0000 |
| commit | c26990c22daf6d2e09948b38366f457496f16cfe (patch) | |
| tree | ec7ffec4193ffb90fe4e18b3b2bcc41eec537770 /src | |
| parent | c34d839f9ffd110762270f071d7abbefaa41bc20 (diff) | |
Multiple type cases for `match in`
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_stmt.c | 99 | ||||
| -rw-r--r-- | src/checker.c | 11 | ||||
| -rw-r--r-- | src/ir.c | 236 | ||||
| -rw-r--r-- | src/parser.c | 18 |
4 files changed, 177 insertions, 187 deletions
diff --git a/src/check_stmt.c b/src/check_stmt.c index 5e8766f11..2eabaf798 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -942,7 +942,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } ast_node(cc, CaseClause, stmt); - for_array(j, cc->list) { AstNode *expr = cc->list.e[j]; Operand y = {0}; @@ -1058,7 +1057,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { break; } - // NOTE(bill): Check for multiple defaults AstNode *first_default = NULL; ast_node(bs, BlockStmt, ms->body); @@ -1093,7 +1091,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } - MapBool seen = {0}; + MapBool seen = {0}; // Multimap map_bool_init(&seen, heap_allocator()); for_array(i, bs->stmts) { @@ -1107,69 +1105,68 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { // TODO(bill): Make robust Type *bt = base_type(type_deref(x.type)); - AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL; Type *case_type = NULL; - if (type_expr != NULL) { // Otherwise it's a default expression - Operand y = {0}; - check_expr_or_type(c, &y, type_expr); - - if (match_type_kind == MatchType_Union) { - GB_ASSERT(is_type_union(bt)); - bool tag_type_found = false; - for (isize i = 0; i < bt->Record.variant_count; i++) { - Entity *f = bt->Record.variants[i]; - if (are_types_identical(f->type, y.type)) { - tag_type_found = true; - break; + for_array(type_index, cc->list) { + AstNode *type_expr = cc->list.e[type_index]; + if (type_expr != NULL) { // Otherwise it's a default expression + Operand y = {0}; + check_expr_or_type(c, &y, type_expr); + + if (match_type_kind == MatchType_Union) { + GB_ASSERT(is_type_union(bt)); + bool tag_type_found = false; + for (isize i = 0; i < bt->Record.variant_count; i++) { + Entity *f = bt->Record.variants[i]; + if (are_types_identical(f->type, y.type)) { + tag_type_found = true; + break; + } } + if (!tag_type_found) { + gbString type_str = type_to_string(y.type); + error_node(y.expr, "Unknown tag type, got `%s`", type_str); + gb_string_free(type_str); + continue; + } + case_type = y.type; + } else if (match_type_kind == MatchType_Any) { + case_type = y.type; + } else { + GB_PANIC("Unknown type to type match statement"); } - if (!tag_type_found) { - gbString type_str = type_to_string(y.type); - error_node(y.expr, "Unknown tag type, got `%s`", type_str); - gb_string_free(type_str); - continue; - } - case_type = y.type; - } else if (match_type_kind == MatchType_Any) { - case_type = y.type; - } else { - GB_PANIC("Unknown type to type match statement"); - } - HashKey key = hash_pointer(y.type); - bool *found = map_bool_get(&seen, key); - if (found) { - TokenPos pos = cc->token.pos; - gbString expr_str = expr_to_string(y.expr); - error_node(y.expr, - "Duplicate type case `%s`\n" - "\tprevious type case at %.*s(%td:%td)", - expr_str, - LIT(pos.file), pos.line, pos.column); - gb_string_free(expr_str); - break; + HashKey key = hash_pointer(y.type); + bool *found = map_bool_get(&seen, key); + if (found) { + TokenPos pos = cc->token.pos; + gbString expr_str = expr_to_string(y.expr); + error_node(y.expr, + "Duplicate type case `%s`\n" + "\tprevious type case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + break; + } + map_bool_set(&seen, key, cast(bool)true); } - map_bool_set(&seen, key, cast(bool)true); } - check_open_scope(c, stmt); + if (cc->list.count > 1) { + case_type = NULL; + } if (case_type == NULL) { - case_type = type_deref(x.type); + case_type = x.type; } - add_type_info_type(c, case_type); + check_open_scope(c, stmt); { - // NOTE(bill): Dummy type - Type *tt = case_type; - if (is_type_pointer(x.type)) { - tt = make_type_pointer(c->allocator, case_type); - add_type_info_type(c, tt); - } - Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true); + Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, true); tag_var->flags |= EntityFlag_Used; add_entity(c, c->context.scope, lhs, tag_var); add_entity_use(c, lhs, tag_var); + add_implicit_entity(c, stmt, tag_var); } check_stmt_list(c, cc->stmts, mod_flags); check_close_scope(c); diff --git a/src/checker.c b/src/checker.c index 4cd8e7ae9..93187d50c 100644 --- a/src/checker.c +++ b/src/checker.c @@ -165,7 +165,7 @@ bool is_operand_nil(Operand o) { typedef struct BlockLabel { String name; - AstNode *label; // AstNode_Label + AstNode *label; // AstNode_Label; } BlockLabel; // DeclInfo is used to store information of certain declarations to allow for "any order" usage @@ -286,6 +286,7 @@ typedef struct CheckerInfo { MapScope scopes; // Key: AstNode * | Node -> Scope MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo MapDeclInfo entities; // Key: Entity * + MapEntity implicits; // Key: AstNode * MapEntity foreigns; // Key: String MapAstFile files; // Key: String (full path) MapIsize type_info_map; // Key: Type * @@ -674,6 +675,7 @@ void init_checker_info(CheckerInfo *i) { map_decl_info_init(&i->entities, a); map_expr_info_init(&i->untyped, a); map_entity_init(&i->foreigns, a); + map_entity_init(&i->implicits, a); map_isize_init(&i->type_info_map, a); map_ast_file_init(&i->files, a); i->type_info_count = 0; @@ -688,6 +690,7 @@ void destroy_checker_info(CheckerInfo *i) { map_decl_info_destroy(&i->entities); map_expr_info_destroy(&i->untyped); map_entity_destroy(&i->foreigns); + map_entity_destroy(&i->implicits); map_isize_destroy(&i->type_info_map); map_ast_file_destroy(&i->files); } @@ -873,6 +876,12 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn } +void add_implicit_entity(Checker *c, AstNode *node, Entity *e) { + GB_ASSERT(node != NULL); + GB_ASSERT(e != NULL); + map_entity_set(&c->info.implicits, hash_pointer(node), e); +} + void add_type_info_type(Checker *c, Type *t) { if (t == NULL) { @@ -341,8 +341,8 @@ typedef struct irValueTypeName { typedef struct irValueGlobal { Entity * entity; Type * type; - irValue * value; - irValueArray referrers; + irValue * value; + irValueArray referrers; bool is_constant; bool is_private; bool is_thread_local; @@ -359,7 +359,8 @@ typedef struct irValueParam { typedef struct irValue { irValueKind kind; - i32 index; + i32 index; + bool index_set; union { irValueConstant Constant; irValueConstantSlice ConstantSlice; @@ -1015,6 +1016,8 @@ irValue *ir_emit(irProcedure *proc, irValue *instr) { if (!ir_is_instr_terminating(i)) { array_add(&b->instrs, instr); } + } else if (instr->Instr.kind != irInstr_Unreachable) { + GB_PANIC("ir_emit: Instruction missing parent block"); } return instr; } @@ -1108,7 +1111,9 @@ irBlock *ir_new_block(irProcedure *proc, AstNode *node, char *label) { void ir_add_block_to_proc(irProcedure *proc, irBlock *b) { for_array(i, proc->blocks) { - GB_ASSERT(proc->blocks.e[i] != b); + if (proc->blocks.e[i] == b) { + return; + } } array_add(&proc->blocks, b); b->index = proc->block_count++; @@ -4876,7 +4881,25 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type if (done_) *done_ = done; } +void ir_store_type_case_implicit(irProcedure *proc, AstNode *clause, irValue *value) { + Entity **found = map_entity_get(&proc->module->info->implicits, hash_pointer(clause)); + GB_ASSERT(found != NULL); + Entity *e = *found; GB_ASSERT(e != NULL); + irValue *x = ir_add_local(proc, e, NULL); + ir_emit_store(proc, x, value); +} + +void ir_type_case_body(irProcedure *proc, AstNode *label, AstNode *clause, irBlock *body, irBlock *done) { + ast_node(cc, CaseClause, clause); + ir_push_target_list(proc, label, done, NULL, NULL); + ir_open_scope(proc); + ir_build_stmt_list(proc, cc->stmts); + ir_close_scope(proc, irDeferExit_Default, body); + ir_pop_target_list(proc); + + ir_emit_jump(proc, done); +} void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { @@ -5504,166 +5527,116 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ast_node(as, AssignStmt, ms->tag); GB_ASSERT(as->lhs.count == 1); GB_ASSERT(as->rhs.count == 1); - AstNode *lhs = as->lhs.e[0]; - AstNode *rhs = as->rhs.e[0]; - irValue *parent = ir_build_expr(proc, rhs); + irValue *parent = ir_build_expr(proc, as->rhs.e[0]); + Type *parent_type = ir_type(parent); bool is_parent_ptr = is_type_pointer(ir_type(parent)); + MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent)); GB_ASSERT(match_type_kind != MatchType_Invalid); + irValue *parent_value = parent; + + irValue *parent_ptr = parent; + if (!is_parent_ptr) { + parent_ptr = ir_address_from_load_or_generate_local(proc, parent_ptr); + } + irValue *tag_index = NULL; irValue *union_data = NULL; if (match_type_kind == MatchType_Union) { - if (!is_parent_ptr) { - parent = ir_address_from_load_or_generate_local(proc, parent); - } ir_emit_comment(proc, str_lit("get union's tag")); - tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent)); - union_data = ir_emit_conv(proc, parent, t_rawptr); - } else if (match_type_kind == MatchType_Any) { - if (!is_parent_ptr) { - parent = ir_address_from_load_or_generate_local(proc, parent); - } + tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent_ptr)); + union_data = ir_emit_conv(proc, parent_ptr, t_rawptr); } - irBlock *start_block = ir_new_block(proc, node, "type-match.case.first"); + irBlock *start_block = ir_new_block(proc, node, "typematch.case.first"); ir_emit_jump(proc, start_block); ir_start_block(proc, start_block); - irBlock *done = ir_new_block(proc, node, "type-match.done"); // NOTE(bill): Append later + // NOTE(bill): Append this later + irBlock *done = ir_new_block(proc, node, "typematch.done"); + AstNode *default_ = NULL; ast_node(body, BlockStmt, ms->body); - String tag_var_name = lhs->Ident.string; - - - AstNodeArray default_stmts = {0}; - irBlock *default_block = NULL; - + gb_local_persist i32 weird_count = 0; - isize case_count = body->stmts.count; for_array(i, body->stmts) { AstNode *clause = body->stmts.e[i]; ast_node(cc, CaseClause, clause); - - Entity *tag_var_entity = NULL; - Type *tag_var_type = NULL; - if (str_eq(tag_var_name, str_lit("_"))) { - Type *t = type_of_expr(proc->module->info, cc->list.e[0]); - if (match_type_kind == MatchType_Union) { - t = make_type_pointer(proc->module->allocator, t); - } - tag_var_type = t; - } else { - Scope *scope = *map_scope_get(&proc->module->info->scopes, hash_pointer(clause)); - tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); - GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name)); - tag_var_type = tag_var_entity->type; - } - GB_ASSERT(tag_var_type != NULL); - - irBlock *next_cond = NULL; - irValue *cond = NULL; - if (cc->list.count == 0) { - // default case - default_stmts = cc->stmts; - default_block = ir_new_block(proc, clause, "type-match.dflt.body"); - - - irValue *tag_var = NULL; - if (tag_var_entity != NULL) { - tag_var = ir_add_local(proc, tag_var_entity, NULL); - } else { - tag_var = ir_add_local_generated(proc, tag_var_type); - } - - if (!is_parent_ptr) { - ir_emit_store(proc, tag_var, ir_emit_load(proc, parent)); - } else { - ir_emit_store(proc, tag_var, parent); - } + default_ = clause; continue; } - GB_ASSERT(cc->list.count == 1); - irBlock *body = ir_new_block(proc, clause, "type-match.case.body"); - - - if (match_type_kind == MatchType_Union) { - Type *bt = type_deref(tag_var_type); - irValue *index = NULL; - Type *ut = base_type(type_deref(ir_type(parent))); - GB_ASSERT(ut->Record.kind == TypeRecord_Union); - for (isize variant_index = 1; variant_index < ut->Record.variant_count; variant_index++) { - Entity *f = ut->Record.variants[variant_index]; - if (are_types_identical(f->type, bt)) { - index = ir_const_int(allocator, variant_index); - break; + irBlock *body = ir_new_block(proc, clause, "typematch.body"); + irBlock *next = NULL; + Type *case_type = NULL; + for_array(type_index, cc->list) { + next = ir_new_block(proc, NULL, "typematch.next"); + case_type = type_of_expr(proc->module->info, cc->list.e[type_index]); + irValue *cond = NULL; + if (match_type_kind == MatchType_Union) { + Type *bt = type_deref(case_type); + irValue *index = NULL; + Type *ut = base_type(type_deref(parent_type)); + GB_ASSERT(ut->Record.kind == TypeRecord_Union); + for (isize variant_index = 1; variant_index < ut->Record.variant_count; variant_index++) { + Entity *f = ut->Record.variants[variant_index]; + if (are_types_identical(f->type, bt)) { + index = ir_const_int(allocator, variant_index); + break; + } } + GB_ASSERT(index != NULL); + cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index); + } else if (match_type_kind == MatchType_Any) { + irValue *any_ti = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 0)); + irValue *case_ti = ir_type_info(proc, case_type); + cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); } - GB_ASSERT(index != NULL); + GB_ASSERT(cond != NULL); - irValue *tag_var = NULL; - if (tag_var_entity != NULL) { - tag_var = ir_add_local(proc, tag_var_entity, NULL); - } else { - tag_var = ir_add_local_generated(proc, tag_var_type); - } - - Type *bt_ptr = make_type_pointer(proc->module->allocator, bt); - irValue *data_ptr = ir_emit_conv(proc, union_data, bt_ptr); - if (!is_type_pointer(type_deref(ir_type(tag_var)))) { - data_ptr = ir_emit_load(proc, data_ptr); - } - ir_emit_store(proc, tag_var, data_ptr); - - cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index); - } else if (match_type_kind == MatchType_Any) { - Type *type = tag_var_type; - irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 1)); - irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); - if (tag_var_entity != NULL) { - ir_module_add_value(proc->module, tag_var_entity, data); - } + ir_emit_if(proc, cond, body, next); + ir_start_block(proc, next); + } - irValue *any_ti = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 0)); - irValue *case_ti = ir_type_info(proc, type); - cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); - } else { - GB_PANIC("Invalid type for type match statement"); + Entity *case_entity = NULL; + { + Entity **found = map_entity_get(&proc->module->info->implicits, hash_pointer(clause)); + GB_ASSERT(found != NULL); + case_entity = *found; } - next_cond = ir_new_block(proc, clause, "type-match.case.next"); - ir_emit_if(proc, cond, body, next_cond); - ir_start_block(proc, next_cond); + + irValue *value = parent_value; ir_start_block(proc, body); - ir_push_target_list(proc, ms->label, done, NULL, NULL); - ir_open_scope(proc); - ir_build_stmt_list(proc, cc->stmts); - ir_close_scope(proc, irDeferExit_Default, body); - ir_pop_target_list(proc); + if (cc->list.count == 1) { + Type *ct = make_type_pointer(proc->module->allocator, case_entity->type); + irValue *data = NULL; + if (match_type_kind == MatchType_Union) { + data = union_data; + } else if (match_type_kind == MatchType_Any) { + irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 1)); + data = any_data; + } + value = ir_emit_load(proc, ir_emit_conv(proc, data, ct)); + } - ir_emit_jump(proc, done); - proc->curr_block = next_cond; - // ir_start_block(proc, next_cond); + ir_store_type_case_implicit(proc, clause, value); + ir_type_case_body(proc, ms->label, clause, body, done); + ir_start_block(proc, next); } - if (default_block != NULL) { - ir_emit_jump(proc, default_block); - ir_start_block(proc, default_block); - - ir_push_target_list(proc, ms->label, done, NULL, NULL); - ir_open_scope(proc); - ir_build_stmt_list(proc, default_stmts); - ir_close_scope(proc, irDeferExit_Default, default_block); - ir_pop_target_list(proc); + if (default_ != NULL) { + ir_store_type_case_implicit(proc, default_, parent_value); + ir_type_case_body(proc, ms->label, default_, proc->curr_block, done); + } else { + ir_emit_jump(proc, done); } - - ir_emit_jump(proc, done); ir_start_block(proc, done); case_end; @@ -5774,9 +5747,11 @@ void ir_number_proc_registers(irProcedure *proc) { GB_ASSERT(value->kind == irValue_Instr); irInstr *instr = &value->Instr; if (ir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + value->index = -1; continue; } value->index = reg_index; + value->index_set = true; reg_index++; } } @@ -5785,10 +5760,10 @@ void ir_number_proc_registers(irProcedure *proc) { void ir_begin_procedure_body(irProcedure *proc) { array_add(&proc->module->procs, proc); - array_init(&proc->blocks, heap_allocator()); - array_init(&proc->defer_stmts, heap_allocator()); - array_init(&proc->children, heap_allocator()); - array_init(&proc->branch_blocks, heap_allocator()); + array_init(&proc->blocks, heap_allocator()); + array_init(&proc->defer_stmts, heap_allocator()); + array_init(&proc->children, heap_allocator()); + array_init(&proc->branch_blocks, heap_allocator()); DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity)); if (found != NULL) { @@ -6949,6 +6924,7 @@ void ir_gen_tree(irGen *s) { } + // m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); } diff --git a/src/parser.c b/src/parser.c index 9fab3725e..a41fc5c06 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2162,7 +2162,7 @@ AstNode *parse_expr(AstFile *f, bool lhs) { AstNodeArray parse_expr_list(AstFile *f, bool lhs) { AstNodeArray list = make_ast_node_array(f); - do { + for (;;) { AstNode *e = parse_expr(f, lhs); array_add(&list, e); if (f->curr_token.kind != Token_Comma || @@ -2170,7 +2170,7 @@ AstNodeArray parse_expr_list(AstFile *f, bool lhs) { break; } next_token(f); - } while (true); + } return list; } @@ -3099,9 +3099,17 @@ AstNode *parse_case_clause(AstFile *f) { AstNode *parse_type_case_clause(AstFile *f) { Token token = f->curr_token; - AstNodeArray clause = make_ast_node_array(f); + AstNodeArray list = make_ast_node_array(f); if (allow_token(f, Token_case)) { - array_add(&clause, parse_type(f)); + for (;;) { + AstNode *t = parse_type(f); + array_add(&list, t); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } } else { expect_token(f, Token_default); } @@ -3109,7 +3117,7 @@ AstNode *parse_type_case_clause(AstFile *f) { // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? AstNodeArray stmts = parse_stmt_list(f); - return ast_case_clause(f, token, clause, stmts); + return ast_case_clause(f, token, list, stmts); } |