mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Fix issue #156
This commit is contained in:
+169
-164
@@ -540,7 +540,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error(us->token, "'using' can only be applied to variables of type struct or raw_union");
|
error(us->token, "'using' can only be applied to variables of type `struct`");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,6 +776,173 @@ void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_type_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) {
|
||||||
|
ast_node(ss, TypeSwitchStmt, node);
|
||||||
|
Operand x = {};
|
||||||
|
|
||||||
|
mod_flags |= Stmt_BreakAllowed;
|
||||||
|
check_open_scope(c, node);
|
||||||
|
check_label(c, ss->label); // TODO(bill): What should the label's "scope" be?
|
||||||
|
|
||||||
|
SwitchKind switch_kind = Switch_Invalid;
|
||||||
|
|
||||||
|
if (ss->tag->kind != AstNode_AssignStmt) {
|
||||||
|
error(ss->tag, "Expected an 'in' assignment for this type switch statement");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_node(as, AssignStmt, ss->tag);
|
||||||
|
Token as_token = ast_node_token(ss->tag);
|
||||||
|
if (as->lhs.count != 1) {
|
||||||
|
syntax_error(as_token, "Expected 1 name before 'in'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (as->rhs.count != 1) {
|
||||||
|
syntax_error(as_token, "Expected 1 expression after 'in'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstNode *lhs = as->lhs[0];
|
||||||
|
AstNode *rhs = as->rhs[0];
|
||||||
|
|
||||||
|
check_expr(c, &x, rhs);
|
||||||
|
check_assignment(c, &x, nullptr, str_lit("type switch expression"));
|
||||||
|
switch_kind = check_valid_type_switch_type(x.type);
|
||||||
|
if (check_valid_type_switch_type(x.type) == Switch_Invalid) {
|
||||||
|
gbString str = type_to_string(x.type);
|
||||||
|
error(x.expr, "Invalid type for this type switch expression, got '%s'", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_ptr = is_type_pointer(x.type);
|
||||||
|
|
||||||
|
// NOTE(bill): Check for multiple defaults
|
||||||
|
AstNode *first_default = nullptr;
|
||||||
|
ast_node(bs, BlockStmt, ss->body);
|
||||||
|
for_array(i, bs->stmts) {
|
||||||
|
AstNode *stmt = bs->stmts[i];
|
||||||
|
AstNode *default_stmt = nullptr;
|
||||||
|
if (stmt->kind == AstNode_CaseClause) {
|
||||||
|
ast_node(cc, CaseClause, stmt);
|
||||||
|
if (cc->list.count == 0) {
|
||||||
|
default_stmt = stmt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(stmt, "Invalid AST - expected case clause");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_stmt != nullptr) {
|
||||||
|
if (first_default != nullptr) {
|
||||||
|
TokenPos pos = ast_node_token(first_default).pos;
|
||||||
|
error(stmt,
|
||||||
|
"Multiple 'default' clauses\n"
|
||||||
|
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||||
|
} else {
|
||||||
|
first_default = default_stmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (lhs->kind != AstNode_Ident) {
|
||||||
|
error(rhs, "Expected an identifier, got '%.*s'", LIT(ast_node_strings[rhs->kind]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Map<bool> seen = {}; // Multimap
|
||||||
|
map_init(&seen, heap_allocator());
|
||||||
|
|
||||||
|
for_array(i, bs->stmts) {
|
||||||
|
AstNode *stmt = bs->stmts[i];
|
||||||
|
if (stmt->kind != AstNode_CaseClause) {
|
||||||
|
// NOTE(bill): error handled by above multiple default checker
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ast_node(cc, CaseClause, stmt);
|
||||||
|
|
||||||
|
// TODO(bill): Make robust
|
||||||
|
Type *bt = base_type(type_deref(x.type));
|
||||||
|
|
||||||
|
Type *case_type = nullptr;
|
||||||
|
for_array(type_index, cc->list) {
|
||||||
|
AstNode *type_expr = cc->list[type_index];
|
||||||
|
if (type_expr != nullptr) { // Otherwise it's a default expression
|
||||||
|
Operand y = {};
|
||||||
|
check_expr_or_type(c, &y, type_expr);
|
||||||
|
|
||||||
|
if (switch_kind == Switch_Union) {
|
||||||
|
GB_ASSERT(is_type_union(bt));
|
||||||
|
bool tag_type_found = false;
|
||||||
|
for_array(i, bt->Union.variants) {
|
||||||
|
Type *vt = bt->Union.variants[i];
|
||||||
|
if (are_types_identical(vt, y.type)) {
|
||||||
|
tag_type_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tag_type_found) {
|
||||||
|
gbString type_str = type_to_string(y.type);
|
||||||
|
error(y.expr, "Unknown tag type, got '%s'", type_str);
|
||||||
|
gb_string_free(type_str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case_type = y.type;
|
||||||
|
} else if (switch_kind == Switch_Any) {
|
||||||
|
case_type = y.type;
|
||||||
|
} else {
|
||||||
|
GB_PANIC("Unknown type to type switch statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
HashKey key = hash_type(y.type);
|
||||||
|
bool *found = map_get(&seen, key);
|
||||||
|
if (found) {
|
||||||
|
TokenPos pos = cc->token.pos;
|
||||||
|
gbString expr_str = expr_to_string(y.expr);
|
||||||
|
error(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_set(&seen, key, cast(bool)true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_ptr &&
|
||||||
|
!is_type_any(type_deref(x.type)) &&
|
||||||
|
cc->list.count == 1 &&
|
||||||
|
case_type != nullptr) {
|
||||||
|
case_type = make_type_pointer(c->allocator, case_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc->list.count > 1) {
|
||||||
|
case_type = nullptr;
|
||||||
|
}
|
||||||
|
if (case_type == nullptr) {
|
||||||
|
case_type = x.type;
|
||||||
|
}
|
||||||
|
add_type_info_type(c, case_type);
|
||||||
|
|
||||||
|
check_open_scope(c, stmt);
|
||||||
|
{
|
||||||
|
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident.token, case_type, false);
|
||||||
|
tag_var->flags |= EntityFlag_Used;
|
||||||
|
tag_var->flags |= EntityFlag_Value;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
map_destroy(&seen);
|
||||||
|
|
||||||
|
check_close_scope(c);
|
||||||
|
}
|
||||||
|
|
||||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
@@ -1396,169 +1563,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ss, TypeSwitchStmt, node);
|
case_ast_node(ss, TypeSwitchStmt, node);
|
||||||
Operand x = {};
|
check_type_switch_stmt(c, node, mod_flags);
|
||||||
|
|
||||||
mod_flags |= Stmt_BreakAllowed;
|
|
||||||
check_open_scope(c, node);
|
|
||||||
check_label(c, ss->label); // TODO(bill): What should the label's "scope" be?
|
|
||||||
|
|
||||||
SwitchKind switch_kind = Switch_Invalid;
|
|
||||||
|
|
||||||
if (ss->tag->kind != AstNode_AssignStmt) {
|
|
||||||
error(ss->tag, "Expected an 'in' assignment for this type switch statement");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_node(as, AssignStmt, ss->tag);
|
|
||||||
Token as_token = ast_node_token(ss->tag);
|
|
||||||
if (as->lhs.count != 1) {
|
|
||||||
syntax_error(as_token, "Expected 1 name before 'in'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (as->rhs.count != 1) {
|
|
||||||
syntax_error(as_token, "Expected 1 expression after 'in'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
AstNode *lhs = as->lhs[0];
|
|
||||||
AstNode *rhs = as->rhs[0];
|
|
||||||
|
|
||||||
check_expr(c, &x, rhs);
|
|
||||||
check_assignment(c, &x, nullptr, str_lit("type switch expression"));
|
|
||||||
switch_kind = check_valid_type_switch_type(x.type);
|
|
||||||
if (check_valid_type_switch_type(x.type) == Switch_Invalid) {
|
|
||||||
gbString str = type_to_string(x.type);
|
|
||||||
error(x.expr, "Invalid type for this type switch expression, got '%s'", str);
|
|
||||||
gb_string_free(str);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_ptr = is_type_pointer(x.type);
|
|
||||||
|
|
||||||
// NOTE(bill): Check for multiple defaults
|
|
||||||
AstNode *first_default = nullptr;
|
|
||||||
ast_node(bs, BlockStmt, ss->body);
|
|
||||||
for_array(i, bs->stmts) {
|
|
||||||
AstNode *stmt = bs->stmts[i];
|
|
||||||
AstNode *default_stmt = nullptr;
|
|
||||||
if (stmt->kind == AstNode_CaseClause) {
|
|
||||||
ast_node(cc, CaseClause, stmt);
|
|
||||||
if (cc->list.count == 0) {
|
|
||||||
default_stmt = stmt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error(stmt, "Invalid AST - expected case clause");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (default_stmt != nullptr) {
|
|
||||||
if (first_default != nullptr) {
|
|
||||||
TokenPos pos = ast_node_token(first_default).pos;
|
|
||||||
error(stmt,
|
|
||||||
"Multiple 'default' clauses\n"
|
|
||||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
|
||||||
} else {
|
|
||||||
first_default = default_stmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (lhs->kind != AstNode_Ident) {
|
|
||||||
error(rhs, "Expected an identifier, got '%.*s'", LIT(ast_node_strings[rhs->kind]));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Map<bool> seen = {}; // Multimap
|
|
||||||
map_init(&seen, heap_allocator());
|
|
||||||
|
|
||||||
for_array(i, bs->stmts) {
|
|
||||||
AstNode *stmt = bs->stmts[i];
|
|
||||||
if (stmt->kind != AstNode_CaseClause) {
|
|
||||||
// NOTE(bill): error handled by above multiple default checker
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ast_node(cc, CaseClause, stmt);
|
|
||||||
|
|
||||||
// TODO(bill): Make robust
|
|
||||||
Type *bt = base_type(type_deref(x.type));
|
|
||||||
|
|
||||||
Type *case_type = nullptr;
|
|
||||||
for_array(type_index, cc->list) {
|
|
||||||
AstNode *type_expr = cc->list[type_index];
|
|
||||||
if (type_expr != nullptr) { // Otherwise it's a default expression
|
|
||||||
Operand y = {};
|
|
||||||
check_expr_or_type(c, &y, type_expr);
|
|
||||||
|
|
||||||
if (switch_kind == Switch_Union) {
|
|
||||||
GB_ASSERT(is_type_union(bt));
|
|
||||||
bool tag_type_found = false;
|
|
||||||
for_array(i, bt->Union.variants) {
|
|
||||||
Type *vt = bt->Union.variants[i];
|
|
||||||
if (are_types_identical(vt, y.type)) {
|
|
||||||
tag_type_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tag_type_found) {
|
|
||||||
gbString type_str = type_to_string(y.type);
|
|
||||||
error(y.expr, "Unknown tag type, got '%s'", type_str);
|
|
||||||
gb_string_free(type_str);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case_type = y.type;
|
|
||||||
} else if (switch_kind == Switch_Any) {
|
|
||||||
case_type = y.type;
|
|
||||||
} else {
|
|
||||||
GB_PANIC("Unknown type to type switch statement");
|
|
||||||
}
|
|
||||||
|
|
||||||
HashKey key = hash_type(y.type);
|
|
||||||
bool *found = map_get(&seen, key);
|
|
||||||
if (found) {
|
|
||||||
TokenPos pos = cc->token.pos;
|
|
||||||
gbString expr_str = expr_to_string(y.expr);
|
|
||||||
error(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_set(&seen, key, cast(bool)true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_ptr &&
|
|
||||||
!is_type_any(type_deref(x.type)) &&
|
|
||||||
cc->list.count == 1 &&
|
|
||||||
case_type != nullptr) {
|
|
||||||
case_type = make_type_pointer(c->allocator, case_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cc->list.count > 1) {
|
|
||||||
case_type = nullptr;
|
|
||||||
}
|
|
||||||
if (case_type == nullptr) {
|
|
||||||
case_type = x.type;
|
|
||||||
}
|
|
||||||
add_type_info_type(c, case_type);
|
|
||||||
|
|
||||||
check_open_scope(c, stmt);
|
|
||||||
{
|
|
||||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident.token, case_type, false);
|
|
||||||
tag_var->flags |= EntityFlag_Used;
|
|
||||||
tag_var->flags |= EntityFlag_Value;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
map_destroy(&seen);
|
|
||||||
|
|
||||||
check_close_scope(c);
|
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -1574,8 +1574,6 @@ irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) {
|
|||||||
irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
|
irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
|
||||||
|
|
||||||
|
|
||||||
irValue *ir_emit_global_call(irProcedure *proc, char *name_, irValue **args, isize arg_count);
|
|
||||||
|
|
||||||
irValue *ir_find_or_generate_context_ptr(irProcedure *proc) {
|
irValue *ir_find_or_generate_context_ptr(irProcedure *proc) {
|
||||||
if (proc->context_stack.count > 0) {
|
if (proc->context_stack.count > 0) {
|
||||||
return proc->context_stack[proc->context_stack.count-1];
|
return proc->context_stack[proc->context_stack.count-1];
|
||||||
@@ -1635,8 +1633,8 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
irValue *ir_emit_global_call(irProcedure *proc, char *name_, irValue **args, isize arg_count) {
|
irValue *ir_emit_global_call(irProcedure *proc, char const *name_, irValue **args, isize arg_count) {
|
||||||
String name = make_string_c(name_);
|
String name = make_string_c(cast(char *)name_);
|
||||||
irValue **found = map_get(&proc->module->members, hash_string(name));
|
irValue **found = map_get(&proc->module->members, hash_string(name));
|
||||||
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
|
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
|
||||||
irValue *gp = *found;
|
irValue *gp = *found;
|
||||||
@@ -3652,7 +3650,7 @@ void ir_emit_slice_bounds_check(irProcedure *proc, Token token, irValue *low, ir
|
|||||||
args[3] = low;
|
args[3] = low;
|
||||||
args[4] = high;
|
args[4] = high;
|
||||||
|
|
||||||
char *func = is_substring ? "__substring_expr_error" : "__slice_expr_error";
|
char const *func = is_substring ? "__substring_expr_error" : "__slice_expr_error";
|
||||||
ir_emit_global_call(proc, func, args, 5);
|
ir_emit_global_call(proc, func, args, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user