diff options
| author | RilleP <rikkypikki@hotmail.com> | 2024-04-10 19:10:33 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-10 19:10:33 +0200 |
| commit | 95a38d5a96322cd9adbb03bd5b7425b93469e62f (patch) | |
| tree | 8cdc3b962506ddc4b7b1883f0e2ecb9dce54e2f9 /src/check_stmt.cpp | |
| parent | 239d4e10762a12e96280bd91003acbf2170cadf2 (diff) | |
| parent | 13e459980b0143b49762cdfd04dd5cf1bbf83daa (diff) | |
Merge branch 'master' into parsing-package-fixes
Diffstat (limited to 'src/check_stmt.cpp')
| -rw-r--r-- | src/check_stmt.cpp | 195 |
1 files changed, 129 insertions, 66 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d56e5e212..fc3b9aa43 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -221,6 +221,12 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return true; } break; + + case Ast_ExprStmt: + if (stmt->ExprStmt.expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + break; } return false; @@ -485,7 +491,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } + Entity *lhs_e = entity_of_node(lhs->expr); + u8 prev_bit_field_bit_size = ctx->bit_field_bit_size; + if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size; + } + check_assignment(ctx, rhs, assignment_type, str_lit("assignment")); + + ctx->bit_field_bit_size = prev_bit_field_bit_size; + if (rhs->mode == Addressing_Invalid) { return nullptr; } @@ -724,6 +740,25 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, return true; } +gb_internal void error_var_decl_identifier(Ast *name) { + GB_ASSERT(name != nullptr); + GB_ASSERT(name->kind != Ast_Ident); + + ERROR_BLOCK(); + gbString s = expr_to_string(name); + defer (gb_string_free(s)); + + error(name, "A variable declaration must be an identifier, got '%s'", s); + if (name->kind == Ast_Implicit) { + String imp = name->Implicit.string; + if (imp == "context") { + error_line("\tSuggestion: '%.*s' is a reserved keyword, would 'ctx' suffice?\n", LIT(imp)); + } else { + error_line("\tNote: '%.*s' is a reserved keyword\n", LIT(imp)); + } + } +} + gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(irs, UnrollRangeStmt, node); check_open_scope(ctx, node); @@ -835,7 +870,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod entity = found; } } else { - error(name, "A variable declaration must be an identifier"); + error_var_decl_identifier(name); } if (entity == nullptr) { @@ -867,6 +902,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod } if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + ERROR_BLOCK(); if (prev_inline_for_depth > 0) { error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); } else { @@ -1349,6 +1385,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } if (unhandled.count > 0) { + ERROR_BLOCK(); + if (unhandled.count == 1) { gbString s = type_to_string(unhandled[0]); error_no_newline(node, "Unhandled switch case: %s", s); @@ -1447,25 +1485,6 @@ gb_internal bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *i return id != BuiltinProc_Invalid; } -gb_internal bool check_expr_is_stack_variable(Ast *expr) { - /* - expr = unparen_expr(expr); - Entity *e = entity_of_node(expr); - if (e && e->kind == Entity_Variable) { - if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) { - // okay - } else if (e->Variable.thread_local_model.len != 0) { - // okay - } else if (e->scope) { - if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) { - return true; - } - } - } - */ - return false; -} - gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(rs, RangeStmt, node); @@ -1479,6 +1498,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) auto vals = array_make<Type *>(temporary_allocator(), 0, 2); auto entities = array_make<Entity *>(temporary_allocator(), 0, 2); bool is_map = false; + bool is_bit_set = false; bool use_by_reference_for_value = false; bool is_soa = false; bool is_reverse = rs->reverse; @@ -1524,11 +1544,25 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) array_add(&vals, operand.type); array_add(&vals, t_int); add_type_info_type(ctx, operand.type); + if (build_context.no_rtti) { + error(node, "Iteration over an enum type is not allowed runtime type information (RTTI) has been disallowed"); + } goto skip_expr_range_stmt; } } else if (operand.mode != Addressing_Invalid) { + if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) { + Ast *expr = unparen_expr(operand.expr); + if (expr->kind != Ast_TypeAssertion) { // Only for procedure calls + Type *end_type = nullptr; + check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false); + if (is_type_boolean(end_type)) { + check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true); + } + } + } bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); + switch (t->kind) { case Type_Basic: if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { @@ -1543,6 +1577,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } break; + case Type_BitSet: + array_add(&vals, t->BitSet.elem); + max_val_count = 1; + is_bit_set = true; + is_possibly_addressable = false; + add_type_info_type(ctx, operand.type); + if (build_context.no_rtti && is_type_enum(t->BitSet.elem)) { + error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed"); + } + break; + case Type_EnumeratedArray: if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->EnumeratedArray.elem); @@ -1582,6 +1627,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { isize count = t->Tuple.variables.count; if (count < 1 || count > 3) { + ERROR_BLOCK(); check_not_tuple(ctx, &operand); error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n"); break; @@ -1637,6 +1683,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) defer (gb_string_free(s)); defer (gb_string_free(t)); + ERROR_BLOCK(); + error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t); if (rs->vals.count == 1) { @@ -1695,7 +1743,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (is_possibly_addressable && i == addressable_index) { entity->flags &= ~EntityFlag_Value; } else { - char const *idx_name = is_map ? "key" : "index"; + char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index"; error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } } else if (i == addressable_index && use_by_reference_for_value) { @@ -1718,9 +1766,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) entity = found; } } else { - gbString s = expr_to_string(lhs[i]); - error(name, "A variable declaration must be an identifier, got %s", s); - gb_string_free(s); + error_var_decl_identifier(name); } if (entity == nullptr) { @@ -1772,7 +1818,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f for (Ast *name : vd->names) { Entity *entity = nullptr; if (name->kind != Ast_Ident) { - error(name, "A variable declaration must be an identifier"); + error_var_decl_identifier(name); } else { Token token = name->Ident.token; String str = token.string; @@ -1895,17 +1941,19 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f 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"); } } + // NOTE(bill): This is to improve error handling for things like `x: [?]T = {...}` + Ast *prev_type_hint_expr = ctx->type_hint_expr; + ctx->type_hint_expr = vd->type; + check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); + + ctx->type_hint_expr = prev_type_hint_expr; + check_arity_match(ctx, vd, false); for (isize i = 0; i < entity_count; i++) { @@ -1934,7 +1982,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f TokenPos pos = f->token.pos; Type *this_type = base_type(e->type); Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { + if (!signature_parameter_similar_enough(this_type, other_type)) { error(e->token, "Foreign entity '%.*s' previously declared elsewhere with a different type\n" "\tat %s", @@ -2027,13 +2075,13 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } Ast *expr = strip_or_return_expr(operand.expr); - if (expr->kind == Ast_CallExpr) { + if (expr && expr->kind == Ast_CallExpr) { BuiltinProcId builtin_id = BuiltinProc_Invalid; bool do_require = false; AstCallExpr *ce = &expr->CallExpr; Type *t = base_type(type_of_expr(ce->proc)); - if (t->kind == Type_Proc) { + if (t && 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]; @@ -2045,7 +2093,7 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { gb_string_free(expr_str); } return; - } else if (expr->kind == Ast_SelectorCallExpr) { + } else if (expr && expr->kind == Ast_SelectorCallExpr) { BuiltinProcId builtin_id = BuiltinProc_Invalid; bool do_require = false; @@ -2071,6 +2119,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } return; } + + ERROR_BLOCK(); + gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); gb_string_free(expr_str); @@ -2264,29 +2315,6 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { if (is_type_untyped(o->type)) { update_untyped_expr_type(ctx, o->expr, e->type, true); } - - - // NOTE(bill): This is very basic escape analysis - // This needs to be improved tremendously, and a lot of it done during the - // middle-end (or LLVM side) to improve checks and error messages - Ast *expr = unparen_expr(o->expr); - if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { - Ast *x = unparen_expr(expr->UnaryExpr.expr); - if (x->kind == Ast_CompoundLit) { - error(expr, "Cannot return the address to a stack value from a procedure"); - } else if (x->kind == Ast_IndexExpr) { - Ast *array = x->IndexExpr.expr; - if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) { - gbString t = type_to_string(type_of_expr(array)); - error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t); - gb_string_free(t); - } - } else { - if (check_expr_is_stack_variable(x)) { - error(expr, "Cannot return the address to a stack variable from a procedure"); - } - } - } } } @@ -2294,16 +2322,51 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { if (o.expr == nullptr) { continue; } - if (o.expr->kind != Ast_CompoundLit || !is_type_slice(o.type)) { - continue; - } - ast_node(cl, CompoundLit, o.expr); - if (cl->elems.count == 0) { - continue; + Ast *expr = unparen_expr(o.expr); + + auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { + gbString s = expr_to_string(o.expr); + if (extra_type) { + gbString t = type_to_string(extra_type); + error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t); + gb_string_free(t); + } else { + error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s); + } + gb_string_free(s); + }; + + + // NOTE(bill): This is very basic escape analysis + // This needs to be improved tremendously, and a lot of it done during the + // middle-end (or LLVM side) to improve checks and error messages + if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) { + ast_node(cl, CompoundLit, expr); + if (cl->elems.count == 0) { + continue; + } + unsafe_return_error(o, "a compound literal of a slice"); + } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { + Ast *x = unparen_expr(expr->UnaryExpr.expr); + Entity *e = entity_of_node(x); + if (is_entity_local_variable(e)) { + unsafe_return_error(o, "the address of a local variable"); + } else if(x->kind == Ast_CompoundLit) { + unsafe_return_error(o, "the address of a compound literal"); + } else if (x->kind == Ast_IndexExpr) { + Entity *f = entity_of_node(x->IndexExpr.expr); + if (is_type_array_like(f->type) || is_type_matrix(f->type)) { + if (is_entity_local_variable(f)) { + unsafe_return_error(o, "the address of an indexed variable", f->type); + } + } + } else if (x->kind == Ast_MatrixIndexExpr) { + Entity *f = entity_of_node(x->MatrixIndexExpr.expr); + if (is_type_matrix(f->type) && is_entity_local_variable(f)) { + unsafe_return_error(o, "the address of an indexed variable", f->type); + } + } } - gbString s = type_to_string(o.type); - error(o.expr, "It is unsafe to return a compound literal of a slice ('%s') with elements from a procedure, as the contents of the slice uses the current stack frame's memory", s); - gb_string_free(s); } } |