mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-10 21:31:37 -07:00
Support any in match type
This commit is contained in:
+13
-3
@@ -1543,22 +1543,32 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
|
||||
if (check_is_assignable_to(c, x, y->type) ||
|
||||
check_is_assignable_to(c, y, x->type)) {
|
||||
Type *err_type = x->type;
|
||||
b32 defined = false;
|
||||
switch (op.kind) {
|
||||
case Token_CmpEq:
|
||||
case Token_NotEq:
|
||||
defined = is_type_comparable(get_enum_base_type(base_type(x->type)));
|
||||
defined = is_type_comparable(x->type);
|
||||
break;
|
||||
case Token_Lt:
|
||||
case Token_Gt:
|
||||
case Token_LtEq:
|
||||
case Token_GtEq: {
|
||||
defined = is_type_ordered(get_enum_base_type(base_type(x->type)));
|
||||
defined = is_type_ordered(x->type);
|
||||
} break;
|
||||
}
|
||||
|
||||
// CLEANUP(bill) NOTE(bill): there is an auto assignment to `any` which needs to be checked
|
||||
if (is_type_any(x->type) && !is_type_any(y->type)) {
|
||||
err_type = x->type;
|
||||
defined = false;
|
||||
} else if (is_type_any(y->type) && !is_type_any(x->type)) {
|
||||
err_type = y->type;
|
||||
defined = false;
|
||||
}
|
||||
|
||||
if (!defined) {
|
||||
gbString type_string = type_to_string(x->type);
|
||||
gbString type_string = type_to_string(err_type);
|
||||
defer (gb_string_free(type_string));
|
||||
err_str = gb_string_make(c->tmp_allocator,
|
||||
gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string));
|
||||
|
||||
+52
-22
@@ -302,6 +302,18 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
return op_a->type;
|
||||
}
|
||||
|
||||
b32 check_valid_type_match_type(Type *type, b32 *is_union_ptr, b32 *is_any) {
|
||||
if (is_type_pointer(type)) {
|
||||
*is_union_ptr = is_type_union(type_deref(type));
|
||||
return *is_union_ptr;
|
||||
}
|
||||
if (is_type_any(type)) {
|
||||
*is_any = true;
|
||||
return *is_any;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 prev_stmt_state_flags = c->context.stmt_state_flags;
|
||||
@@ -729,17 +741,18 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_open_scope(c, node);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
b32 is_union_ptr = false;
|
||||
b32 is_any = false;
|
||||
|
||||
check_expr(c, &x, ms->tag);
|
||||
check_assignment(c, &x, NULL, make_string("type match expression"));
|
||||
if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) {
|
||||
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
defer (gb_string_free(str));
|
||||
error(ast_node_token(x.expr),
|
||||
"Expected a pointer to a union for this type match expression, got `%s`", str);
|
||||
"Invalid type for this type match expression, got `%s`", str);
|
||||
break;
|
||||
}
|
||||
Type *base_union = base_type(type_deref(x.type));
|
||||
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
@@ -787,27 +800,39 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
ast_node(cc, CaseClause, stmt);
|
||||
|
||||
// TODO(bill): Make robust
|
||||
Type *bt = base_type(type_deref(x.type));
|
||||
|
||||
|
||||
AstNode *type_expr = cc->list.count > 0 ? cc->list[0] : NULL;
|
||||
Type *tag_type = NULL;
|
||||
Type *case_type = NULL;
|
||||
if (type_expr != NULL) { // Otherwise it's a default expression
|
||||
Operand y = {};
|
||||
check_expr_or_type(c, &y, type_expr);
|
||||
b32 tag_type_found = false;
|
||||
for (isize i = 0; i < base_union->Record.field_count; i++) {
|
||||
Entity *f = base_union->Record.fields[i];
|
||||
if (are_types_identical(f->type, y.type)) {
|
||||
tag_type_found = true;
|
||||
break;
|
||||
|
||||
if (is_union_ptr) {
|
||||
GB_ASSERT(is_type_union(bt));
|
||||
b32 tag_type_found = false;
|
||||
for (isize i = 0; i < bt->Record.field_count; i++) {
|
||||
Entity *f = bt->Record.fields[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);
|
||||
defer (gb_string_free(type_str));
|
||||
error(ast_node_token(y.expr),
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
continue;
|
||||
}
|
||||
case_type = y.type;
|
||||
} else if (is_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);
|
||||
defer (gb_string_free(type_str));
|
||||
error(ast_node_token(y.expr),
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
continue;
|
||||
}
|
||||
tag_type = y.type;
|
||||
|
||||
HashKey key = hash_pointer(y.type);
|
||||
auto *found = map_get(&seen, key);
|
||||
@@ -823,14 +848,19 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
break;
|
||||
}
|
||||
map_set(&seen, key, cast(b32)true);
|
||||
|
||||
}
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
if (tag_type != NULL) {
|
||||
if (case_type != NULL) {
|
||||
add_type_info_type(c, case_type);
|
||||
|
||||
// NOTE(bill): Dummy type
|
||||
Type *tag_ptr_type = make_type_pointer(c->allocator, tag_type);
|
||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type);
|
||||
Type *tt = case_type;
|
||||
if (is_union_ptr) {
|
||||
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, ms->var->Ident, tt);
|
||||
tag_var->flags |= EntityFlag_Used;
|
||||
add_entity(c, c->context.scope, ms->var, tag_var);
|
||||
add_entity_use(c, ms->var, tag_var);
|
||||
|
||||
+10
-11
@@ -396,7 +396,14 @@ gb_global Type *t_context = NULL;
|
||||
gb_global Type *t_context_ptr = NULL;
|
||||
|
||||
|
||||
|
||||
Type *get_enum_base_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (bt->kind == Type_Record && bt->Record.kind == TypeRecord_Enum) {
|
||||
GB_ASSERT(bt->Record.enum_base != NULL);
|
||||
return bt->Record.enum_base;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
b32 is_type_named(Type *t) {
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -457,7 +464,7 @@ b32 is_type_untyped(Type *t) {
|
||||
return false;
|
||||
}
|
||||
b32 is_type_ordered(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(get_enum_base_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||
}
|
||||
@@ -581,14 +588,6 @@ b32 is_type_raw_union(Type *t) {
|
||||
return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
|
||||
}
|
||||
|
||||
Type *get_enum_base_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (is_type_enum(bt)) {
|
||||
return bt->Record.enum_base;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
b32 is_type_any(Type *t) {
|
||||
t = base_type(t);
|
||||
return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
|
||||
@@ -626,7 +625,7 @@ b32 type_has_nil(Type *t) {
|
||||
|
||||
|
||||
b32 is_type_comparable(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(get_enum_base_type(t));
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return t->kind != Basic_UntypedNil;
|
||||
|
||||
Reference in New Issue
Block a user