aboutsummaryrefslogtreecommitdiff
path: root/src/check_stmt.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2022-07-24 22:27:45 +0100
committerGitHub <noreply@github.com>2022-07-24 22:27:45 +0100
commit02a8bba02e6d5a499781dcb362803533c34ab1f1 (patch)
tree5aa0c61eed5fc1f6ed25d735641fb56246f6c806 /src/check_stmt.cpp
parent6ea68869c934807f1ecdc411e58bdce6b64ee7e2 (diff)
parenta3afe617c218736563723fd1ab343f403bdd33f0 (diff)
Merge branch 'master' into fix/freebsd-syscall
Diffstat (limited to 'src/check_stmt.cpp')
-rw-r--r--src/check_stmt.cpp215
1 files changed, 95 insertions, 120 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 24ad0eec1..a6f6f1a7d 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
ctx->state_flags = out;
}
@@ -607,7 +615,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
@@ -635,10 +643,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
bool is_ptr = is_type_pointer(e->type);
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
- Scope *found = scope_of_node(t->Struct.node);
- if (found == nullptr) {
- found = t->Struct.scope;
- }
+ Scope *found = t->Struct.scope;
GB_ASSERT(found != nullptr);
for_array(i, found->elements.entries) {
Entity *f = found->elements.entries[i].value;
@@ -692,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
-
-struct TypeAndToken {
- Type *type;
- Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
- if (operand.mode != Addressing_Constant) {
- return;
- }
- if (operand.value.kind == ExactValue_Invalid) {
- return;
- }
-
- uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
- isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(temporary_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)) {
- continue;
- }
-
- 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",
- expr_str,
- token_pos_to_string(pos));
- gb_string_free(expr_str);
- } else {
- error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
- }
- return;
- }
- }
-
- TypeAndToken tap = {operand.type, ast_token(operand.expr)};
- multi_map_insert(seen, key, tap);
-}
-
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -964,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
- PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1004,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
- case Token_Ellipsis: upper_op = Token_GtEq; break;
- case Token_RangeFull: upper_op = Token_GtEq; break;
- case Token_RangeHalf: upper_op = Token_Gt; break;
+ case Token_Ellipsis: upper_op = Token_LtEq; break;
+ case Token_RangeFull: upper_op = Token_LtEq; break;
+ case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1027,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
- if (is_type_enum(x.type)) {
- // TODO(bill): Fix this logic so it's fast!!!
-
- i64 v0 = exact_value_to_i64(lhs.value);
- i64 v1 = exact_value_to_i64(rhs.value);
- Operand v = {};
- v.mode = Addressing_Constant;
- v.type = x.type;
- v.expr = x.expr;
-
- Type *bt = base_type(x.type);
- GB_ASSERT(bt->kind == Type_Enum);
- for (i64 vi = v0; vi <= v1; vi++) {
- if (upper_op != Token_GtEq && vi == v1) {
- break;
- }
-
- bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
- GB_ASSERT(f->kind == Entity_Constant);
-
- i64 fv = exact_value_to_i64(f->Constant.value);
- if (fv == vi) {
- found = true;
- break;
- }
- }
- if (found) {
- v.value = exact_value_i64(vi);
- add_constant_switch_case(ctx, &seen, v);
- }
- }
- } else {
- add_constant_switch_case(ctx, &seen, lhs);
- if (upper_op == Token_GtEq) {
- add_constant_switch_case(ctx, &seen, rhs);
- }
- }
+ add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1110,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
- add_constant_switch_case(ctx, &seen, y);
+ add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1146,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
- error_no_newline(node, "Unhandled switch cases: ");
+ error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
@@ -1399,9 +1318,9 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) {
ast_node(bs, BlockStmt, body);
// NOTE(bill, 2020-09-23): This logic is prevent common erros with block statements
// e.g. if cond { x := 123; } // this is an error
- if (body->scope != nullptr && body->scope->elements.entries.count > 0) {
- if (body->scope->parent->node != nullptr) {
- switch (body->scope->parent->node->kind) {
+ if (bs->scope != nullptr && bs->scope->elements.entries.count > 0) {
+ if (bs->scope->parent->node != nullptr) {
+ switch (bs->scope->parent->node->kind) {
case Ast_IfStmt:
case Ast_ForStmt:
case Ast_RangeStmt:
@@ -1462,6 +1381,18 @@ bool all_operands_valid(Array<Operand> const &operands) {
return true;
}
+bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
+ BuiltinProcId id = BuiltinProc_Invalid;
+ Entity *e = entity_of_node(expr);
+ if (e != nullptr && e->kind == Entity_Builtin) {
+ if (e->Builtin.id && e->Builtin.id != BuiltinProc_DIRECTIVE) {
+ id = cast(BuiltinProcId)e->Builtin.id;
+ }
+ }
+ if (id_) *id_ = id;
+ return id != BuiltinProc_Invalid;
+}
+
void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -1486,29 +1417,43 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (kind == Expr_Stmt) {
return;
}
- Ast *expr = strip_or_return_expr(operand.expr);
+ Ast *expr = strip_or_return_expr(operand.expr);
if (expr->kind == Ast_CallExpr) {
+ BuiltinProcId builtin_id = BuiltinProc_Invalid;
+ bool do_require = false;
+
AstCallExpr *ce = &expr->CallExpr;
- Type *t = type_of_expr(ce->proc);
- if (is_type_proc(t)) {
- if (t->Proc.require_results) {
- gbString expr_str = expr_to_string(ce->proc);
- error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
- }
+ Type *t = base_type(type_of_expr(ce->proc));
+ if (t->kind == Type_Proc) {
+ do_require = t->Proc.require_results;
+ } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+ auto const &bp = builtin_procs[builtin_id];
+ do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+ }
+ if (do_require) {
+ gbString expr_str = expr_to_string(ce->proc);
+ error(node, "'%s' requires that its results must be handled", expr_str);
+ gb_string_free(expr_str);
}
return;
} else if (expr->kind == Ast_SelectorCallExpr) {
+ BuiltinProcId builtin_id = BuiltinProc_Invalid;
+ bool do_require = false;
+
AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
- Type *t = type_of_expr(ce->proc);
- if (is_type_proc(t)) {
- if (t->Proc.require_results) {
- gbString expr_str = expr_to_string(ce->proc);
- error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
- }
+ Type *t = base_type(type_of_expr(ce->proc));
+ if (t->kind == Type_Proc) {
+ do_require = t->Proc.require_results;
+ } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+ auto const &bp = builtin_procs[builtin_id];
+ do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+ }
+ if (do_require) {
+ gbString expr_str = expr_to_string(ce->proc);
+ error(node, "'%s' requires that its results must be handled", expr_str);
+ gb_string_free(expr_str);
}
return;
}
@@ -1616,7 +1561,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
Operand lhs = {Addressing_Invalid};
Operand rhs = {Addressing_Invalid};
- Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
+ Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
ast_node(be, BinaryExpr, binary_expr);
be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
@@ -2197,7 +2142,26 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
if (new_name_count == 0) {
- error(node, "No new declarations on the lhs");
+ begin_error_block();
+ error(node, "No new declarations on the left hand side");
+ bool all_underscore = true;
+ for_array(i, vd->names) {
+ Ast *name = vd->names[i];
+ if (name->kind == Ast_Ident) {
+ if (!is_blank_ident(name)) {
+ all_underscore = false;
+ break;
+ }
+ } else {
+ all_underscore = false;
+ break;
+ }
+ }
+ if (all_underscore) {
+ error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n");
+ }
+
+ end_error_block();
}
Type *init_type = nullptr;
@@ -2233,7 +2197,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
e->state = EntityState_Resolved;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
- e->Variable.thread_local_model = ac.thread_local_model;
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -2246,6 +2209,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'static' variables cannot be declared within a defer statement");
+ }
}
}
if (ac.thread_local_model != "") {
@@ -2254,10 +2220,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'thread_local' variables cannot be declared within a defer statement");
+ }
}
e->Variable.thread_local_model = ac.thread_local_model;
}
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
+
if (ac.is_static && ac.thread_local_model != "") {
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
}
@@ -2339,7 +2313,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else if (is_type_struct(t) || is_type_raw_union(t)) {
ERROR_BLOCK();
- Scope *scope = scope_of_node(t->Struct.node);
+ Scope *scope = t->Struct.scope;
+ GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {