#complete switch; Removal of dyncall

This commit is contained in:
gingerBill
2018-02-17 11:54:08 +00:00
parent 6a85546b76
commit c4d2d287fc
52 changed files with 330 additions and 2328 deletions
+186 -44
View File
@@ -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;
Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap
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, 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) {