diff options
| author | gingerBill <bill@gingerbill.org> | 2018-02-17 11:54:08 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2018-02-17 11:54:08 +0000 |
| commit | c4d2d287fc0eac7348951ce275a1f3d80f25ef3d (patch) | |
| tree | e082b0f1d744689ced0cdab73dd405981cacbf3a /src/check_stmt.cpp | |
| parent | 6a85546b761b67fa012a2cd49e32b2211bf02971 (diff) | |
#complete switch; Removal of dyncall
Diffstat (limited to 'src/check_stmt.cpp')
| -rw-r--r-- | src/check_stmt.cpp | 230 |
1 files changed, 186 insertions, 44 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1014b8c46..840df6ad9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -454,7 +454,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo case Entity_TypeName: { Type *t = base_type(e->type); if (t->kind == Type_Enum) { - for (isize i = 0; i < t->Enum.field_count; i++) { + for_array(i, t->Enum.fields) { Entity *f = t->Enum.fields[i]; if (!is_entity_exported(f)) continue; @@ -555,6 +555,52 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo return true; } + +struct TypeAndToken { + Type *type; + Token token; +}; + +void add_constant_switch_case(Checker *c, Map<TypeAndToken> *seen, Operand operand, bool use_expr = true) { + if (operand.value.kind == ExactValue_Invalid) { + return; + } + HashKey key = hash_exact_value(operand.value); + TypeAndToken *found = map_get(seen, key); + if (found != nullptr) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + isize count = multi_map_count(seen, key); + TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); + + multi_map_get_all(seen, key, taps); + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (are_types_identical(operand.type, tap.type)) { + TokenPos pos = tap.token.pos; + if (use_expr) { + gbString expr_str = expr_to_string(operand.expr); + error(operand.expr, + "Duplicate case '%s'\n" + "\tprevious case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + } else { + error(operand.expr, + "Duplicate case found with previous case at %.*s(%td:%td)", + LIT(pos.file), pos.line, pos.column); + } + return; + } + } + } + + TypeAndToken tap = {operand.type, ast_node_token(operand.expr)}; + multi_map_insert(seen, key, tap); +} + void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { ast_node(ss, SwitchStmt, node); @@ -611,12 +657,16 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { } } - struct TypeAndToken { - Type *type; - Token token; - }; + bool complete = ss->complete; + + if (complete) { + if (!is_type_enum(x.type)) { + error(x.expr, "#complete switch statement can be only used with an enum type"); + complete = false; + } + } - Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap + Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue map_init(&seen, heap_allocator()); defer (map_destroy(&seen)); @@ -680,9 +730,48 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { default: error(ie->op, "Invalid interval operator"); continue; } + + Operand a1 = lhs; Operand b1 = rhs; check_comparison(c, &a1, &b1, op); + if (complete) { + if (lhs.mode != Addressing_Constant) { + error(lhs.expr, "#complete switch statement only allows constant case clauses"); + } + if (rhs.mode != Addressing_Constant) { + error(rhs.expr, "#complete switch statement only allows constant case clauses"); + } + } + + if (a1.mode != Addressing_Invalid && + lhs.mode == Addressing_Constant && rhs.mode == Addressing_Constant) { + ExactValue start = lhs.value; + ExactValue end = rhs.value; + ExactValue one = exact_value_i64(1); + match_exact_values(&one, &start); + for (ExactValue i = start; + compare_exact_values(op, i, end); + i = exact_value_add(i, one)) { + bool use_expr = false; + Operand x = lhs; + x.value = i; + if (compare_exact_values(Token_CmpEq, i, start)) { + use_expr = true; + } else if (compare_exact_values(Token_CmpEq, i, end)) { + x = rhs; + use_expr = true; + } + add_constant_switch_case(c, &seen, x, use_expr); + } + } else { + if (lhs.mode == Addressing_Constant) { + add_constant_switch_case(c, &seen, lhs); + } + if (rhs.mode == Addressing_Constant && op == Token_LtEq) { + add_constant_switch_case(c, &seen, rhs); + } + } } else { Operand y = {}; check_expr(c, &y, expr); @@ -704,47 +793,13 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { continue; } if (y.mode != Addressing_Constant) { + if (complete) { + error(y.expr, "#complete switch statement only allows constant case clauses"); + } continue; } - - if (y.value.kind != ExactValue_Invalid) { - HashKey key = hash_exact_value(y.value); - TypeAndToken *found = map_get(&seen, key); - if (found != nullptr) { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); - defer (gb_temp_arena_memory_end(tmp)); - - isize count = multi_map_count(&seen, key); - TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); - - multi_map_get_all(&seen, key, taps); - bool continue_outer = false; - - for (isize i = 0; i < count; i++) { - TypeAndToken tap = taps[i]; - if (are_types_identical(y.type, tap.type)) { - TokenPos pos = tap.token.pos; - gbString expr_str = expr_to_string(y.expr); - error(y.expr, - "Duplicate case '%s'\n" - "\tprevious case at %.*s(%td:%td)", - expr_str, - LIT(pos.file), pos.line, pos.column); - gb_string_free(expr_str); - continue_outer = true; - break; - } - } - - - if (continue_outer) { - continue; - } - } - TypeAndToken tap = {y.type, ast_node_token(y.expr)}; - multi_map_insert(&seen, key, tap); - } + add_constant_switch_case(c, &seen, y); } } @@ -752,6 +807,47 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { check_stmt_list(c, cc->stmts, mod_flags); check_close_scope(c); } + + if (complete) { + Type *et = base_type(x.type); + GB_ASSERT(is_type_enum(et)); + auto fields = et->Enum.fields; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + Array<Entity *> unhandled = {}; + array_init(&unhandled, c->tmp_allocator, fields.count); + + for_array(i, fields) { + Entity *f = fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + ExactValue v = f->Constant.value; + HashKey key = hash_exact_value(v); + auto found = map_get(&seen, key); + if (!found) { + array_add(&unhandled, f); + } + } + + if (unhandled.count > 0) { + if (unhandled.count == 1) { + error_no_newline(node, "Unhandled switch case: "); + } else { + error_no_newline(node, "Unhandled switch cases: "); + } + for_array(i, unhandled) { + Entity *f = unhandled[i]; + if (i > 0) { + gb_printf_err(", "); + } + gb_printf_err("%.*s", LIT(f->token.string)); + } + gb_printf_err("\n"); + } + } } @@ -812,6 +908,14 @@ void check_type_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { return; } + bool complete = ss->complete; + if (complete) { + if (switch_kind != Switch_Union) { + error(node, "#complete switch statement may only be used with a union"); + complete = false; + } + } + bool is_ptr = is_type_pointer(x.type); // NOTE(bill): Check for multiple defaults @@ -935,6 +1039,44 @@ void check_type_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { check_stmt_list(c, cc->stmts, mod_flags); check_close_scope(c); } + + if (complete) { + Type *ut = base_type(x.type); + GB_ASSERT(is_type_union(ut)); + auto variants = ut->Union.variants; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + Array<Type *> unhandled = {}; + array_init(&unhandled, c->tmp_allocator, variants.count); + + for_array(i, variants) { + Type *t = variants[i]; + auto found = map_get(&seen, hash_type(t)); + if (!found) { + array_add(&unhandled, t); + } + } + + if (unhandled.count > 0) { + if (unhandled.count == 1) { + error_no_newline(node, "Unhandled switch case: "); + } else { + error_no_newline(node, "Unhandled switch cases: "); + } + for_array(i, unhandled) { + Type *t = unhandled[i]; + if (i > 0) { + gb_printf_err(", "); + } + gbString s = type_to_string(t); + gb_printf_err("%s", s); + gb_string_free(s); + } + gb_printf_err("\n"); + } + } } void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { |