From d7d23e65eae616d44cc070edff8171eb3160b5b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 13:47:00 +0000 Subject: Clean up error block usage --- src/check_stmt.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index b93be734e..d56e5e212 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1085,8 +1085,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); @@ -1813,7 +1812,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } if (new_name_count == 0) { - begin_error_block(); + ERROR_BLOCK(); error(node, "No new declarations on the left hand side"); bool all_underscore = true; for (Ast *name : vd->names) { @@ -1831,7 +1830,6 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); } - end_error_block(); } Type *init_type = nullptr; -- cgit v1.2.3 From 3c47503780bd99b547777b727baf502504244bbb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 23:35:01 +0000 Subject: Improve foreign variable fuzzy type checking --- base/runtime/os_specific_bsd.odin | 3 ++- base/runtime/os_specific_darwin.odin | 1 + src/check_decl.cpp | 4 ++-- src/check_stmt.cpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 93ed9b4e6..7f23b625e 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -4,9 +4,10 @@ package runtime foreign import libc "system:c" +@(default_calling_convention="c") foreign libc { @(link_name="write") - _unix_write :: proc(fd: uintptr, buf: rawptr, size: int) -> int --- + _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int --- __error :: proc() -> ^i32 --- } diff --git a/base/runtime/os_specific_darwin.odin b/base/runtime/os_specific_darwin.odin index 5630c733c..0cb46024c 100644 --- a/base/runtime/os_specific_darwin.odin +++ b/base/runtime/os_specific_darwin.odin @@ -4,6 +4,7 @@ package runtime foreign import libc "system:System.framework" +@(default_calling_convention="c") foreign libc { @(link_name="__stderrp") _stderr: rawptr diff --git a/src/check_decl.cpp b/src/check_decl.cpp index ed3a109c2..3ccf1b97a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1143,7 +1143,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { "\tat %s", LIT(name), token_pos_to_string(pos)); } - } else if (!are_types_identical(this_type, other_type)) { + } else if (!signature_parameter_similar_enough(this_type, other_type)) { error(d->proc_lit, "Foreign entity '%.*s' previously declared elsewhere with a different type\n" "\tat %s", @@ -1284,7 +1284,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast 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", diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d56e5e212..6897701d6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1934,7 +1934,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", -- cgit v1.2.3 From 5a84a0822596fac47dd35bf1c2f1d9bb60bbe5c1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 17:24:42 +0000 Subject: Add general support for `bit_field`s --- base/runtime/internal.odin | 22 ++++++++++++ core/fmt/fmt.odin | 10 ++++-- src/check_expr.cpp | 86 ++++++++++++++++++++++++++++++++++++++------ src/check_stmt.cpp | 10 ++++++ src/check_type.cpp | 21 +++++++++-- src/checker.hpp | 1 + src/entity.cpp | 2 ++ src/llvm_backend.hpp | 8 +++++ src/llvm_backend_expr.cpp | 16 +++++++++ src/llvm_backend_general.cpp | 51 ++++++++++++++++++++++++-- src/parser.hpp | 1 + src/types.cpp | 34 ++++++++++++++++++ 12 files changed, 245 insertions(+), 17 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 691f76ff1..62bee8620 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1034,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 { } } + + + +__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) { + for i in 0..value.kind == ExactValue_Integer) { gbString b = type_to_string(type); i64 sz = type_size_of(type); + i64 bit_size = 8*sz; + bool size_changed = false; + if (max_bit_size > 0) { + size_changed = (bit_size != max_bit_size); + bit_size = gb_min(bit_size, max_bit_size); + } BigInt *bi = &o->value.value_integer; if (is_type_unsigned(type)) { if (big_int_is_neg(bi)) { @@ -2083,25 +2089,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } else { BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz); + BigInt bits = big_int_make_i64(bit_size); big_int_shl_eq(&max_size, &bits); big_int_sub_eq(&max_size, &one); String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } } } else { BigInt zero = big_int_make_u64(0); BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz - 1); + BigInt bits = big_int_make_i64(bit_size - 1); big_int_shl_eq(&max_size, &bits); + + String max_size_str = {}; if (big_int_is_neg(bi)) { big_int_neg(&max_size, &max_size); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); } else { big_int_sub_eq(&max_size, &one); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); + } + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); } } @@ -2112,7 +2129,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } return false; } -gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { +gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); defer( @@ -2143,7 +2160,7 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { error_line("\tSuggestion: the expression may be casted to %s\n", b); - } else if (check_integer_exceed_suggestion(c, o, type)) { + } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; } } @@ -2217,13 +2234,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { + i64 max_bit_size = 0; + if (ctx->bit_field_bit_size) { + max_bit_size = ctx->bit_field_bit_size; + } + if (are_types_identical(o->type, type)) { error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b); } else { error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c); } - check_assignment_error_suggestion(ctx, o, type); + check_assignment_error_suggestion(ctx, o, type, max_bit_size); } } else { error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); @@ -2234,6 +2256,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ } gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { + if (o->expr && o->expr->kind == Ast_SelectorExpr) { + if (o->expr->SelectorExpr.is_bit_field) { + return true; + } + } if (o->mode == Addressing_OptionalOk) { Ast *expr = unselector_expr(o->expr); if (expr->kind != Ast_TypeAssertion) { @@ -2306,6 +2333,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); + } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) { + error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str); } else { switch (o->mode) { case Addressing_Constant: @@ -5067,6 +5096,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod operand->type = entity->type; operand->expr = node; + if (entity->flags & EntityFlag_BitFieldField) { + add_package_dependency(c, "runtime", "__write_bits"); + add_package_dependency(c, "runtime", "__read_bits"); + } + switch (entity->kind) { case Entity_Constant: operand->value = entity->Constant.value; @@ -5080,6 +5114,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } break; case Entity_Variable: + if (sel.is_bit_field) { + se->is_bit_field = true; + } if (sel.indirect) { operand->mode = Addressing_Variable; } else if (operand->mode == Addressing_Context) { @@ -11115,6 +11152,33 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; + case_ast_node(f, BitFieldField, node); + str = write_expr_to_string(str, f->name, shorthand); + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, f->type, shorthand); + str = gb_string_appendc(str, " | "); + str = write_expr_to_string(str, f->bit_size, shorthand); + case_end; + case_ast_node(bf, BitFieldType, node); + str = gb_string_appendc(str, "bit_field "); + if (!shorthand) { + str = write_expr_to_string(str, bf->backing_type, shorthand); + } + str = gb_string_appendc(str, " {"); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, bf->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, bf->fields[i], false); + } + return str; + } + str = gb_string_appendc(str, "}"); + case_end; + case_ast_node(ia, InlineAsmExpr, node); str = gb_string_appendc(str, "asm("); for_array(i, ia->param_types) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6897701d6..a7dd9743b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -485,7 +485,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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 8afac2fc5..8c746a2f7 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1035,11 +1035,19 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64); bit_size_i64 = 64; } + i64 sz = 8*type_size_of(type); + if (bit_size_i64 > sz) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz); + bit_size_i64 = sz; + } + bit_size_u8 = cast(u8)bit_size_i64; Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index); e->Variable.docs = docs; e->Variable.comment = comment; + e->Variable.bit_field_bit_size = bit_size_u8; + e->flags |= EntityFlag_BitFieldField; add_entity(ctx, ctx->scope, nullptr, e); array_add(&fields, e); @@ -1050,6 +1058,14 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, GB_ASSERT(fields.count <= bf->fields.count); + auto bit_offsets = slice_make(permanent_allocator(), fields.count); + i64 curr_offset = 0; + for_array(i, bit_sizes) { + bit_offsets[i] = curr_offset; + curr_offset += cast(i64)bit_sizes[i]; + } + + if (total_bit_size > maximum_bit_size) { gbString s = type_to_string(backing_type); error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu", @@ -1059,8 +1075,9 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, gb_string_free(s); } - bit_field_type->BitField.fields = slice_from_array(fields); - bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.bit_offsets = bit_offsets; } gb_internal bool is_type_valid_bit_set_range(Type *t) { diff --git a/src/checker.hpp b/src/checker.hpp index 9aee82257..066d6bb4a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -475,6 +475,7 @@ struct CheckerContext { bool hide_polymorphic_errors; bool in_polymorphic_specialization; bool allow_arrow_right_selector_expr; + u8 bit_field_bit_size; Scope * polymorphic_scope; Ast *assignment_lhs_hint; diff --git a/src/entity.cpp b/src/entity.cpp index e6c46d37e..916c2b2bd 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -43,6 +43,7 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, + EntityFlag_BitFieldField = 1ull<<12, @@ -212,6 +213,7 @@ struct Entity { Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; i32 field_group_index; + u8 bit_field_bit_size; ParameterValue param_value; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 026454c81..00d1b7a21 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -84,6 +84,8 @@ enum lbAddrKind { lbAddr_Swizzle, lbAddr_SwizzleLarge, + + lbAddr_BitField, }; struct lbAddr { @@ -118,6 +120,12 @@ struct lbAddr { Type *type; Slice indices; } swizzle_large; + struct { + Type *type; + i64 index; + i64 bit_offset; + i64 bit_size; + } bitfield; }; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0c06c8c1b..6bef21822 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4627,6 +4627,22 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); + if (sel.is_bit_field) { + lbAddr addr = lb_build_addr(p, se->expr); + Type *bf_type = base_type(type_deref(lb_addr_type(addr))); + GB_ASSERT(bf_type->kind == Type_BitField); + + lbValue a = lb_addr_get_ptr(p, addr); + Selection sub_sel = sel; + sub_sel.index.count -= 1; + i32 index = sel.index[sel.index.count-1]; + + Entity *f = bf_type->BitField.fields[index]; + u8 bit_size = bf_type->BitField.bit_sizes[index]; + i64 bit_offset = bf_type->BitField.bit_offsets[index]; + + return lb_addr_bit_field(a, f->type, index, bit_offset, bit_size); + } if (sel.pseudo_field) { GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup); Entity *e = entity_of_node(sel_node); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 2102420f8..4ff8482a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -451,6 +451,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice(temporary_allocator(), 4); + args[0] = dst; + args[1] = lb_address_from_load_or_generate_local(p, value); + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__write_bits", args); + return; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); GB_ASSERT(rel_ptr->kind == Type_RelativePointer || rel_ptr->kind == Type_RelativeMultiPointer); @@ -1074,8 +1098,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { GB_ASSERT(addr.addr.value != nullptr); + if (addr.kind == lbAddr_BitField) { + lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true); + lbValue src = addr.addr; - if (addr.kind == lbAddr_RelativePointer) { + auto args = array_make(temporary_allocator(), 4); + args[0] = dst.addr; + args[1] = src; + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__read_bits", args); + + lbValue r = lb_addr_load(p, dst); + + if (!is_type_unsigned(core_type(addr.bitfield.type))) { + // Sign extension + // m := 1<<(bit_size-1) + // r = (r XOR m) - m + Type *t = addr.bitfield.type; + lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1)); + r = lb_emit_arith(p, Token_Xor, r, m, t); + r = lb_emit_arith(p, Token_Sub, r, m, t); + } + + return r; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); Type *base_integer = nullptr; Type *pointer_type = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index ff77c88c7..1f4ec8726 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -429,6 +429,7 @@ AST_KIND(_ExprBegin, "", bool) \ Ast *expr, *selector; \ u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \ u8 swizzle_indices; /*2 bits per component*/ \ + bool is_bit_field; \ }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { \ diff --git a/src/types.cpp b/src/types.cpp index 1c28e6583..be4b8944b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -287,6 +287,7 @@ struct TypeProc { Type * backing_type; \ Slice fields; \ Slice bit_sizes; \ + Slice bit_offsets; \ Ast * node; \ }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -408,6 +409,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool is_bit_field; bool pseudo_field; }; gb_global Selection const empty_selection = {0}; @@ -3187,6 +3189,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name else if (field_name == "a") mapped_field_name = str_lit("w"); return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident); } + } else if (type->kind == Type_BitField) { + for_array(i, type->BitField.fields) { + Entity *f = type->BitField.fields[i]; + if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { + continue; + } + String str = f->token.string; + if (field_name == str) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + sel.is_bit_field = true; + return sel; + } + } + } else if (type->kind == Type_Basic) { switch (type->Basic.kind) { case Basic_any: { @@ -4551,6 +4568,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count)); str = write_type_to_string(str, type->Matrix.elem); break; + + case Type_BitField: + str = gb_string_appendc(str, "bit_field "); + str = write_type_to_string(str, type->BitField.backing_type); + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->BitField.fields.count; i++) { + Entity *f = type->BitField.fields[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]); + } + str = gb_string_appendc(str, " }"); + break; } return str; -- cgit v1.2.3 From 3875fb08e839d45d2c855115008857b3134f8337 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Mar 2024 22:12:17 +0000 Subject: Fix #3284 --- src/check_expr.cpp | 2 ++ src/check_stmt.cpp | 6 ++++++ src/parser.hpp | 2 ++ 3 files changed, 10 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1e4c7499b..3f46e2bdd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8405,6 +8405,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no switch (be->token.kind) { case Token_or_break: + node->viral_state_flags |= ViralStateFlag_ContainsOrBreak; if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) { error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name)); } @@ -10254,6 +10255,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast case_end; case_ast_node(re, OrReturnExpr, node); + node->viral_state_flags |= ViralStateFlag_ContainsOrReturn; return check_or_return_expr(c, o, node, type_hint); case_end; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a7dd9743b..1aa473fd6 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; diff --git a/src/parser.hpp b/src/parser.hpp index f410419d4..f5997c4bd 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -310,6 +310,8 @@ enum StateFlag : u8 { enum ViralStateFlag : u8 { ViralStateFlag_ContainsDeferredProcedure = 1<<0, + ViralStateFlag_ContainsOrBreak = 1<<1, + ViralStateFlag_ContainsOrReturn = 1<<2, }; -- cgit v1.2.3 From 19eb2a8890e41d088d96e4608737bbede5508f59 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Mar 2024 22:24:34 +0000 Subject: Fix #3282 --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1aa473fd6..d31af08da 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1543,8 +1543,12 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) goto skip_expr_range_stmt; } } else if (operand.mode != Addressing_Invalid) { + if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) { + check_promote_optional_ok(ctx, &operand, nullptr, nullptr); + } 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) { -- cgit v1.2.3 From 43d695a9900ff72064c93ada30a882720af8d489 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Mar 2024 11:21:06 +0000 Subject: Fix `for x in y` where `y` is an "optional ok" value, but ignores `#optional_allocator_error` values --- src/check_expr.cpp | 22 ++++++++++++++-------- src/check_stmt.cpp | 6 +++++- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f46e2bdd..0f74a8ccb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -108,7 +108,7 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint); -gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_); +gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand=true); gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type); gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_); @@ -7801,7 +7801,7 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, } -gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) { +gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand) { switch (x->mode) { case Addressing_MapIndex: case Addressing_OptionalOk: @@ -7819,22 +7819,28 @@ gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type * Type *pt = base_type(type_of_expr(expr->CallExpr.proc)); if (is_type_proc(pt)) { Type *tuple = pt->Proc.results; - add_type_and_value(c, x->expr, x->mode, tuple, x->value); if (pt->Proc.result_count >= 2) { if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; } - expr->CallExpr.optional_ok_one = false; - x->type = tuple; + if (change_operand) { + expr->CallExpr.optional_ok_one = false; + x->type = tuple; + add_type_and_value(c, x->expr, x->mode, tuple, x->value); + } return; } } Type *tuple = make_optional_ok_type(x->type); + if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type; - add_type_and_value(c, x->expr, x->mode, tuple, x->value); - x->type = tuple; - GB_ASSERT(is_type_tuple(type_of_expr(x->expr))); + + if (change_operand) { + add_type_and_value(c, x->expr, x->mode, tuple, x->value); + x->type = tuple; + GB_ASSERT(is_type_tuple(type_of_expr(x->expr))); + } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d31af08da..a6ca4b9dd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1544,7 +1544,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } else if (operand.mode != Addressing_Invalid) { if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) { - check_promote_optional_ok(ctx, &operand, nullptr, nullptr); + 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)); -- cgit v1.2.3 From 80ecf5b68aa02a2445aa4cbf3d1d46ed5a007d75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:32:37 +0000 Subject: On `x: [?]T = {...}`, minimize errors by using the `[?]T` expression as a kind of hint --- src/check_expr.cpp | 47 ++++++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 12 +++++++----- src/checker.hpp | 1 + 3 files changed, 46 insertions(+), 14 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb31a1646..ed6182928 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8672,6 +8672,24 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicekind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; + if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + return true; + } + } + return false; +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -8682,20 +8700,31 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } bool is_to_be_determined_array_count = false; bool is_constant = true; - if (cl->type != nullptr) { + + Ast *type_expr = cl->type; + + bool used_type_hint_expr = false; + if (type_expr == nullptr && c->type_hint_expr != nullptr) { + if (is_expr_inferred_fixed_array(c->type_hint_expr)) { + type_expr = clone_ast(c->type_hint_expr); + used_type_hint_expr = true; + } + } + + if (type_expr != nullptr) { type = nullptr; // [?]Type - if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { - Ast *count = cl->type->ArrayType.count; + if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; if (count->kind == Ast_UnaryExpr && count->UnaryExpr.op.kind == Token_Question) { - type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); + type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1); is_to_be_determined_array_count = true; } if (cl->elems.count > 0) { - if (cl->type->ArrayType.tag != nullptr) { - Ast *tag = cl->type->ArrayType.tag; + if (type_expr->ArrayType.tag != nullptr) { + Ast *tag = type_expr->ArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8705,9 +8734,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } } } - if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { + if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) { if (cl->elems.count > 0) { - Ast *tag = cl->type->DynamicArrayType.tag; + Ast *tag = type_expr->DynamicArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8718,7 +8747,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (type == nullptr) { - type = check_type(c, cl->type); + type = check_type(c, type_expr); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a6ca4b9dd..4280e7578 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1919,17 +1919,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++) { diff --git a/src/checker.hpp b/src/checker.hpp index 066d6bb4a..eea99578e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -451,6 +451,7 @@ struct CheckerContext { u32 state_flags; bool in_defer; Type * type_hint; + Ast * type_hint_expr; String proc_name; DeclInfo * curr_proc_decl; -- cgit v1.2.3 From 8e0806be2d499c1eee1e9b4c124c012794d97b6d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 20 Mar 2024 18:09:57 +0000 Subject: Fix #3301 --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 4280e7578..7cccab226 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1661,6 +1661,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) { -- cgit v1.2.3 From 65cb38213520c2310bccfc198cfcf8e8eb6f455a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 20 Mar 2024 22:44:35 +0000 Subject: Fix error handling for type switch statement --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 7cccab226..17502a6e2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1365,6 +1365,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); -- cgit v1.2.3 From c8cdb22f0b55855b259c3cceb6b2b3734a195271 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 14:42:48 +0000 Subject: Disallow `for x in y.(T)` and `for x in y.?` --- src/check_stmt.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 17502a6e2..8a876eb01 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1546,10 +1546,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } else if (operand.mode != Addressing_Invalid) { if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) { - 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); + 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); -- cgit v1.2.3 From 624b870f2827b330c0e5c8aa887c61cfe3c7b33f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 14:58:10 +0000 Subject: Add some basic escape analysis errors for `return &x` --- src/check_stmt.cpp | 95 +++++++++++++++++++++++++----------------------------- src/checker.cpp | 1 + src/entity.cpp | 23 +++++++++++++ 3 files changed, 68 insertions(+), 51 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 8a876eb01..04b7359a8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1465,25 +1465,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); @@ -2297,29 +2278,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"); - } - } - } } } @@ -2327,16 +2285,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_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); } } diff --git a/src/checker.cpp b/src/checker.cpp index 6a1bce573..bf6a84588 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4050,6 +4050,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr); e->identifier = name; e->file = c->file; + e->Variable.is_global = true; if (entity_visibility_kind != EntityVisiblity_Public) { e->flags |= EntityFlag_NotExported; diff --git a/src/entity.cpp b/src/entity.cpp index a160313b4..6cea0930f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -229,6 +229,7 @@ struct Entity { CommentGroup *comment; bool is_foreign; bool is_export; + bool is_global; } Variable; struct { Type * type_parameter_specialization; @@ -480,3 +481,25 @@ gb_internal Entity *strip_entity_wrapping(Ast *expr) { Entity *e = entity_from_expr(expr); return strip_entity_wrapping(e); } + + +gb_internal bool is_entity_local_variable(Entity *e) { + if (e == nullptr) { + return false; + } + if (e->kind != Entity_Variable) { + return false; + } + if (e->Variable.is_global) { + return false; + } + if (e->scope == nullptr) { + return true; + } + if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue)) { + return false; + } + + return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) || + (e->scope->flags & ScopeFlag_Proc) != 0; +} \ No newline at end of file -- cgit v1.2.3 From 194d3fe6bd5e48e2e4f9a1fb2fef0a12c81f0952 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 14:59:35 +0000 Subject: Ignore wrong types --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 04b7359a8..d34695a3a 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2325,7 +2325,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { } } else if (x->kind == Ast_MatrixIndexExpr) { Entity *f = entity_of_node(x->MatrixIndexExpr.expr); - if (is_entity_local_variable(f)) { + if (is_type_matrix(f->type) && is_entity_local_variable(f)) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } -- cgit v1.2.3 From 517d7ae0b0fd400ceb6a213e7d644c19b8088bfd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 17:51:56 +0000 Subject: Add error block around `error_line` calls --- src/check_builtin.cpp | 3 +++ src/check_decl.cpp | 1 + src/check_expr.cpp | 5 +++++ src/check_stmt.cpp | 5 +++++ src/checker.cpp | 12 +++++++++++- src/parser.cpp | 6 +++--- 6 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d3158961e..53e4acbd1 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) { + ERROR_BLOCK(); gbString t = type_to_string(x.type); error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t); if (is_type_union(type_deref(x.type))) { @@ -1565,6 +1566,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } if (!operand->value.value_bool) { + ERROR_BLOCK(); gbString arg1 = expr_to_string(ce->args[0]); gbString arg2 = {}; @@ -1590,6 +1592,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_untyped_bool; operand->mode = Addressing_Constant; } else if (name == "panic") { + ERROR_BLOCK(); if (ce->args.count != 1) { error(call, "'#panic' expects 1 argument, got %td", ce->args.count); return false; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2c0f7a7b8..952a877a4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1630,6 +1630,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de Entity *uvar = entry.uvar; Entity *prev = scope_insert_no_mutex(ctx->scope, uvar); if (prev != nullptr) { + ERROR_BLOCK(); error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string)); break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 80008d73a..ecc8a804c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5905,6 +5905,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { + ERROR_BLOCK(); check_assignment(c, o, param_type, str_lit("procedure argument")); Type *src = base_type(o->type); @@ -8459,6 +8460,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no // NOTE(bill): allow implicit conversion between boolean types // within 'or_return' to improve the experience using third-party code } else if (!check_is_assignable_to(c, &rhs, end_type)) { + ERROR_BLOCK(); // TODO(bill): better error message gbString a = type_to_string(right_type); gbString b = type_to_string(end_type); @@ -10030,6 +10032,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); if (is_const) { if (index < 0) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); if (!build_context.terse_errors) { @@ -10046,6 +10049,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool finish = false; o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); if (!success) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); if (!build_context.terse_errors) { @@ -10236,6 +10240,7 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, } } if (!all_constant) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); if (!build_context.terse_errors) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d34695a3a..1d7e7d4e9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -883,6 +883,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 { @@ -1592,6 +1593,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; @@ -2085,6 +2087,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); diff --git a/src/checker.cpp b/src/checker.cpp index bf6a84588..6456cab0c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3180,6 +3180,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3428,6 +3429,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } else if (mode == "speed") { ac->optimization_mode = ProcedureOptimizationMode_Speed; } else { + ERROR_BLOCK(); error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); error_line("\tnone\n"); error_line("\tminimal\n"); @@ -3558,6 +3560,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { model == "localexec") { ac->thread_local_model = model; } else { + ERROR_BLOCK(); error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); error_line("\tdefault\n"); error_line("\tlocaldynamic\n"); @@ -3608,6 +3611,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3762,6 +3766,7 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (!proc(c, elem, name, value, ac)) { if (!build_context.ignore_unknown_attributes) { + ERROR_BLOCK(); error(elem, "Unknown attribute element name '%.*s'", LIT(name)); error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n"); } @@ -3831,6 +3836,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_ gb_string_free(str); return false; } else if (is_global) { + ERROR_BLOCK(); + Ast *n = vd->values[rhs-1]; error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs); error_line("Note: Global declarations do not allow for multi-valued expressions"); @@ -6052,11 +6059,14 @@ gb_internal void check_unique_package_names(Checker *c) { continue; } + + begin_error_block(); error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n" "\tA package name is required for link name prefixing to have a consistent ABI\n"); - error(prev, "found at previous location"); + error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos)); + end_error_block(); } } diff --git a/src/parser.cpp b/src/parser.cpp index bb9a526fe..b4a2e060c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6295,7 +6295,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } else if (init_fullpath.len != 0) { @@ -6308,7 +6308,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { String short_path = filename_from_path(path); char *cpath = alloc_cstring(temporary_allocator(), short_path); if (gb_file_exists(cpath)) { - error_line("Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); + error({}, "Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); return ParseFile_DirectoryAlreadyExists; } } @@ -6344,7 +6344,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); return ParseFile_WrongExtension; } } -- cgit v1.2.3 From b862691d7524d5dc05d6c43872daa7a488c2411a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 13:08:07 +0100 Subject: Support `for in` with `bit_set` --- src/check_stmt.cpp | 13 ++++++ src/llvm_backend_expr.cpp | 102 +++++++++++++++++++++++----------------------- src/llvm_backend_stmt.cpp | 94 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 152 insertions(+), 57 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1d7e7d4e9..b25df01a6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1554,6 +1554,19 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } break; + case Type_BitSet: + array_add(&vals, t->BitSet.elem); + if (rs->vals.count > 1) { + error(rs->vals[1], "Expected 1 name when iterating over a bit_set, got %td", rs->vals.count); + } + if (rs->vals.count == 1 && + rs->vals[0]->kind == Ast_UnaryExpr && + rs->vals[0]->UnaryExpr.op.kind == Token_And) { + error(rs->vals[0], "When iteraing across a bit_set, you cannot modify the value with '&' as that does not make much sense"); + } + add_type_info_type(ctx, operand.type); + break; + case Type_EnumeratedArray: if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->EnumeratedArray.elem); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 12949f0ab..f6f36e861 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1373,6 +1373,57 @@ gb_internal bool lb_is_empty_string_constant(Ast *expr) { return false; } +gb_internal lbValue lb_build_binary_in(lbProcedure *p, lbValue left, lbValue right, TokenKind op) { + Type *rt = base_type(right.type); + if (is_type_pointer(rt)) { + right = lb_emit_load(p, right); + rt = base_type(type_deref(rt)); + } + + switch (rt->kind) { + case Type_Map: + { + lbValue map_ptr = lb_address_from_load_or_generate_local(p, right); + lbValue key = left; + lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key); + if (op == Token_in) { + return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool); + } else { + return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool); + } + } + break; + case Type_BitSet: + { + Type *key_type = rt->BitSet.elem; + GB_ASSERT(are_types_identical(left.type, key_type)); + + Type *it = bit_set_to_int(rt); + left = lb_emit_conv(p, left, it); + if (is_type_different_to_arch_endianness(it)) { + left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it)); + } + + lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower)); + lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type); + lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type); + bit = lb_emit_conv(p, bit, it); + + lbValue old_value = lb_emit_transmute(p, right, it); + lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it); + + if (op == Token_in) { + return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool); + } else { + return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool); + } + } + break; + } + GB_PANIC("Invalid 'in' type"); + return {}; +} + gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { ast_node(be, BinaryExpr, expr); @@ -1480,57 +1531,8 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { { lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); - Type *rt = base_type(right.type); - if (is_type_pointer(rt)) { - right = lb_emit_load(p, right); - rt = base_type(type_deref(rt)); - } - - switch (rt->kind) { - case Type_Map: - { - lbValue map_ptr = lb_address_from_load_or_generate_local(p, right); - lbValue key = left; - lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key); - if (be->op.kind == Token_in) { - return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool); - } else { - return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool); - } - } - break; - case Type_BitSet: - { - Type *key_type = rt->BitSet.elem; - GB_ASSERT(are_types_identical(left.type, key_type)); - - Type *it = bit_set_to_int(rt); - left = lb_emit_conv(p, left, it); - if (is_type_different_to_arch_endianness(it)) { - left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it)); - } - - lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower)); - lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type); - lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type); - bit = lb_emit_conv(p, bit, it); - - lbValue old_value = lb_emit_transmute(p, right, it); - lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it); - - if (be->op.kind == Token_in) { - return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool); - } else { - return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool); - } - } - break; - default: - GB_PANIC("Invalid 'in' type"); - } - break; + return lb_build_binary_in(p, left, right, be->op.kind); } - break; default: GB_PANIC("Invalid binary expression"); break; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 4ecf70ec4..24dd321f6 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -737,6 +737,22 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, lb_start_block(p, done); } +gb_internal lbValue lb_enum_values_slice(lbProcedure *p, Type *enum_type, i64 *enum_count_) { + Type *t = enum_type; + GB_ASSERT(is_type_enum(t)); + t = base_type(t); + GB_ASSERT(t->kind == Type_Enum); + i64 enum_count = t->Enum.fields.count; + + if (enum_count_) *enum_count_ = enum_count; + + lbValue ti = lb_type_info(p, t); + lbValue variant = lb_emit_struct_ep(p, ti, 4); + lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr); + lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2)); + return values; +} + gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { lbModule *m = p->module; @@ -744,15 +760,11 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_ GB_ASSERT(is_type_enum(t)); t = base_type(t); Type *core_elem = core_type(t); - GB_ASSERT(t->kind == Type_Enum); - i64 enum_count = t->Enum.fields.count; - lbValue max_count = lb_const_int(m, t_int, enum_count); + i64 enum_count = 0; - lbValue ti = lb_type_info(p, t); - lbValue variant = lb_emit_struct_ep(p, ti, 4); - lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr); - lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2)); + lbValue values = lb_enum_values_slice(p, enum_type, &enum_count); lbValue values_data = lb_slice_elem(p, values); + lbValue max_count = lb_const_int(m, t_int, enum_count); lbAddr offset_ = lb_add_local_generated(p, t_int, false); lb_addr_store(p, offset_, lb_const_int(m, t_int, 0)); @@ -1052,6 +1064,74 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc case Type_Tuple: lb_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done); break; + + case Type_BitSet: { + lbModule *m = p->module; + + lbValue the_set = lb_build_expr(p, expr); + if (is_type_pointer(type_deref(the_set.type))) { + the_set = lb_emit_load(p, the_set); + } + + Type *elem = et->BitSet.elem; + if (is_type_enum(elem)) { + i64 enum_count = 0; + lbValue values = lb_enum_values_slice(p, elem, &enum_count); + lbValue values_data = lb_slice_elem(p, values); + lbValue max_count = lb_const_int(m, t_int, enum_count); + + lbAddr offset_ = lb_add_local_generated(p, t_int, false); + lb_addr_store(p, offset_, lb_const_int(m, t_int, 0)); + + loop = lb_create_block(p, "for.bit_set.enum.loop"); + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + lbBlock *body_check = lb_create_block(p, "for.bit_set.enum.body-check"); + lbBlock *body = lb_create_block(p, "for.bit_set.enum.body"); + done = lb_create_block(p, "for.bit_set.enum.done"); + + lbValue offset = lb_addr_load(p, offset_); + lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count); + lb_emit_if(p, cond, body_check, done); + lb_start_block(p, body_check); + + lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset); + lb_emit_increment(p, offset_.addr); + val = lb_emit_load(p, val_ptr); + val = lb_emit_conv(p, val, elem); + + lbValue check = lb_build_binary_in(p, val, the_set, Token_in); + lb_emit_if(p, check, body, loop); + lb_start_block(p, body); + } else { + lbAddr offset_ = lb_add_local_generated(p, t_int, false); + lb_addr_store(p, offset_, lb_const_int(m, t_int, et->BitSet.lower)); + + lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper); + + loop = lb_create_block(p, "for.bit_set.range.loop"); + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + lbBlock *body_check = lb_create_block(p, "for.bit_set.range.body-check"); + lbBlock *body = lb_create_block(p, "for.bit_set.range.body"); + done = lb_create_block(p, "for.bit_set.range.done"); + + lbValue offset = lb_addr_load(p, offset_); + lbValue cond = lb_emit_comp(p, Token_LtEq, offset, max_count); + lb_emit_if(p, cond, body_check, done); + lb_start_block(p, body_check); + + val = lb_emit_conv(p, offset, elem); + lb_emit_increment(p, offset_.addr); + + lbValue check = lb_build_binary_in(p, val, the_set, Token_in); + lb_emit_if(p, check, body, loop); + lb_start_block(p, body); + } + break; + } default: GB_PANIC("Cannot range over %s", type_to_string(expr_type)); break; -- cgit v1.2.3 From 3fa02427b318e6e4e226de8b0435a47e01ceb415 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 13:12:09 +0100 Subject: Unify error message logic for ranges over `bit_set` --- src/check_stmt.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index b25df01a6..1df582e6c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1479,6 +1479,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) auto vals = array_make(temporary_allocator(), 0, 2); auto entities = array_make(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; @@ -1556,14 +1557,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_BitSet: array_add(&vals, t->BitSet.elem); - if (rs->vals.count > 1) { - error(rs->vals[1], "Expected 1 name when iterating over a bit_set, got %td", rs->vals.count); - } - if (rs->vals.count == 1 && - rs->vals[0]->kind == Ast_UnaryExpr && - rs->vals[0]->UnaryExpr.op.kind == Token_And) { - error(rs->vals[0], "When iteraing across a bit_set, you cannot modify the value with '&' as that does not make much sense"); - } + max_val_count = 1; + is_bit_set = true; + is_possibly_addressable = false; add_type_info_type(ctx, operand.type); break; @@ -1722,7 +1718,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) { -- cgit v1.2.3 From 84686c70c56d95e6d42e0d15090f10f49c532695 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 13:16:49 +0100 Subject: Error message when RTTI is disabled when iterating over an `enum` type or a `bit_set` of `enum` with `for in` --- src/check_stmt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1df582e6c..883a6d213 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1525,6 +1525,9 @@ 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) { @@ -1561,6 +1564,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) 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: -- cgit v1.2.3 From 2375ac22a7535981b5177c09ca16f36f42fc2cda Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Apr 2024 16:57:08 +0100 Subject: Improve error messages for `A variable declaration must be an identifier` --- src/check_stmt.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 883a6d213..a543ed9b0 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -740,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); @@ -851,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) { @@ -1747,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) { @@ -1801,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; -- cgit v1.2.3 From ca46484ae3ee548745992e6ad1435255e3ff604b Mon Sep 17 00:00:00 2001 From: oskarnp Date: Sat, 6 Apr 2024 11:02:43 +0200 Subject: Fix checker crash when `or_return`/`or_break`/`or_continue` used for non-existing proc --- src/check_stmt.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a543ed9b0..fc3b9aa43 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2075,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]; @@ -2093,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; -- cgit v1.2.3 From f36fb6d1ef6ad1f4c5dc56b9b761e843195546b6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:41:01 +0100 Subject: Add `nil` checks --- src/check_stmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fc3b9aa43..a6def5997 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2355,14 +2355,14 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { 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 (f && (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)) { + if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } -- cgit v1.2.3 From 46b9bd8c0e3987080f94ae42921b513a79708ef9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 13:35:14 +0100 Subject: Improve error messages for `switch` and `for` r-values with a suggestion --- src/check_stmt.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ src/error.cpp | 21 ++++++++++++++++----- src/tokenizer.cpp | 1 + 3 files changed, 60 insertions(+), 5 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a6def5997..f2b7f8661 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -474,16 +474,59 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } Entity *e = entity_of_node(lhs->expr); + Entity *original_e = e; + + Ast *name = unparen_expr(lhs->expr); + while (name->kind == Ast_SelectorExpr) { + name = name->SelectorExpr.expr; + e = entity_of_node(name); + } + if (e == nullptr) { + e = original_e; + } gbString str = expr_to_string(lhs->expr); if (e != nullptr && e->flags & EntityFlag_Param) { + ERROR_BLOCK(); if (e->flags & EntityFlag_Using) { error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str); } else { error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str); } + error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + show_error_on_line(e->token.pos, token_pos_end(e->token)); } else { + ERROR_BLOCK(); error(lhs->expr, "Cannot assign to '%s'", str); + + if (e) if (e->flags & EntityFlag_ForValue) { + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + if (offset < 0) { + if (is_type_map(e->type)) { + error_line("\tSuggestion: Did you mean? 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\tSuggestion: Did you mean? 'for &%.*s in ...'\n", LIT(e->token.string)); + } + } else { + error_line("\t"); + for (isize i = 0; i < offset-1; i++) { + error_line(" "); + } + error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); + } + + } else if (e->flags & EntityFlag_SwitchValue) { + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + if (offset < 0) { + error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\t"); + for (isize i = 0; i < offset-1; i++) { + error_line(" "); + } + error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); + } + } } gb_string_free(str); diff --git a/src/error.cpp b/src/error.cpp index eb167d4c3..8647f60b9 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -84,6 +84,7 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); if (index >= global_file_path_strings.count) { array_resize(&global_file_path_strings, index+1); @@ -94,6 +95,7 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { ok = true; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return ok; } @@ -102,6 +104,7 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { bool ok = false; GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); if (index >= global_files.count) { array_resize(&global_files, index+1); @@ -111,7 +114,7 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { global_files[index] = file; ok = true; } - + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return ok; } @@ -119,12 +122,14 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { gb_internal String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); String path = {}; if (index < global_file_path_strings.count) { path = global_file_path_strings[index]; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return path; } @@ -132,12 +137,14 @@ gb_internal String get_file_path_string(i32 index) { gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); AstFile *file = nullptr; if (index < global_files.count) { file = global_files[index]; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return file; } @@ -247,10 +254,10 @@ gb_internal void terminal_reset_colours(void) { } -gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { +gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char const *prefix=nullptr) { get_error_value()->end = end; if (!show_error_line()) { - return false; + return -1; } i32 offset = 0; @@ -270,6 +277,10 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING, }; + if (prefix) { + error_out("\t%s\n\n", prefix); + } + error_out("\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); @@ -328,9 +339,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { terminal_reset_colours(); error_out("\n"); - return true; + return offset; } - return false; + return -1; } gb_internal void error_out_empty(void) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3d5348074..fdff9224a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -193,6 +193,7 @@ gb_internal void init_keyword_hash_table(void) { gb_global Array global_file_path_strings; // index is file id gb_global Array global_files; // index is file id +gb_global BlockingMutex global_files_mutex; gb_internal String get_file_path_string(i32 index); gb_internal struct AstFile *thread_safe_get_ast_file_from_id(i32 index); -- cgit v1.2.3 From 5726b7d9541d62f39cb5b04412f283a02e13c077 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 14:51:22 +0100 Subject: Remove warning on clang --- src/check_stmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f2b7f8661..971841165 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -499,7 +499,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O ERROR_BLOCK(); error(lhs->expr, "Cannot assign to '%s'", str); - if (e) if (e->flags & EntityFlag_ForValue) { + if (e && e->flags & EntityFlag_ForValue) { isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); if (offset < 0) { if (is_type_map(e->type)) { @@ -515,7 +515,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); } - } else if (e->flags & EntityFlag_SwitchValue) { + } else if (e && e->flags & EntityFlag_SwitchValue) { isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); if (offset < 0) { error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); -- cgit v1.2.3 From 393e4a9db6d649387dd5c831134e75d98b780156 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Apr 2024 09:53:02 +0100 Subject: Generalize Odin call-based "iterators" to work with more than 2-values: `for x, y, z, w in iterate(&it)` It has an artificial limitation of 100 values because if you need for than that, you're doing something wrong. --- src/check_stmt.cpp | 35 ++++++++++++++++++------------- src/llvm_backend_stmt.cpp | 53 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 23 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 971841165..c31056b33 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1669,12 +1669,20 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Tuple: { isize count = t->Tuple.variables.count; - if (count < 1 || count > 3) { + if (count < 1) { 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"); + error_line("\tMultiple return valued parameters in a range statement are limited to a minimum of 1 usable values with a trailing boolean for the conditional, got %td\n", count); break; } + enum : isize {MAXIMUM_COUNT = 100}; + if (count > MAXIMUM_COUNT) { + ERROR_BLOCK(); + check_not_tuple(ctx, &operand); + error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of %td usable values with a trailing boolean for the conditional, got %td\n", MAXIMUM_COUNT, count); + break; + } + Type *cond_type = t->Tuple.variables[count-1]->type; if (!is_type_boolean(cond_type)) { gbString s = type_to_string(cond_type); @@ -1683,24 +1691,23 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; } + max_val_count = count; + for (Entity *e : t->Tuple.variables) { array_add(&vals, e->type); } is_possibly_addressable = false; - if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) { - gbString s = type_to_string(t); - error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s); - gb_string_free(s); - break; - } - - if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) { - gbString s = type_to_string(t); - error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s); - gb_string_free(s); - break; + bool do_break = false; + for (isize i = rs->vals.count-1; i >= 0; i--) { + if (rs->vals[i] != nullptr && count < i+2) { + gbString s = type_to_string(t); + error(operand.expr, "Expected a %td-valued expression on the rhs, got (%s)", i+2, s); + gb_string_free(s); + do_break = true; + break; + } } if (is_reverse) { diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 24dd321f6..851433415 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -802,8 +802,19 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_ if (done_) *done_ = done; } -gb_internal void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1_type, - lbValue *val0_, lbValue *val1_, lbBlock **loop_, lbBlock **done_) { +gb_internal void lb_build_range_tuple(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { + Ast *expr = unparen_expr(rs->expr); + + Type *expr_type = type_of_expr(expr); + Type *et = base_type(type_deref(expr_type)); + GB_ASSERT(et->kind == Type_Tuple); + + i32 value_count = cast(i32)et->Tuple.variables.count; + + lbValue *values = gb_alloc_array(permanent_allocator(), lbValue, value_count); + + lb_open_scope(p, scope); + lbBlock *loop = lb_create_block(p, "for.tuple.loop"); lb_emit_jump(p, loop); lb_start_block(p, loop); @@ -821,11 +832,26 @@ gb_internal void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type lb_emit_if(p, cond, body, done); lb_start_block(p, body); + for (i32 i = 0; i < value_count; i++) { + values[i] = lb_emit_tuple_ev(p, tuple_value, i); + } - if (val0_) *val0_ = lb_emit_tuple_ev(p, tuple_value, 0); - if (val1_) *val1_ = lb_emit_tuple_ev(p, tuple_value, 1); - if (loop_) *loop_ = loop; - if (done_) *done_ = done; + GB_ASSERT(rs->vals.count <= value_count); + for (isize i = 0; i < rs->vals.count; i++) { + Ast *val = rs->vals[i]; + if (val != nullptr) { + lb_store_range_stmt_val(p, val, values[i]); + } + } + + lb_push_target_list(p, rs->label, done, loop, nullptr); + + lb_build_stmt(p, rs->body); + + lb_close_scope(p, lbDeferExit_Default, nullptr); + lb_pop_target_list(p); + lb_emit_jump(p, loop); + lb_start_block(p, done); } gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { @@ -968,6 +994,17 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc } } + TypeAndValue tav = type_and_value_of_expr(expr); + if (tav.mode != Addressing_Type) { + Type *expr_type = type_of_expr(expr); + Type *et = base_type(type_deref(expr_type)); + if (et->kind == Type_Tuple) { + lb_build_range_tuple(p, rs, scope); + return; + } + } + + lb_open_scope(p, scope); Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr; @@ -986,7 +1023,6 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc lbBlock *loop = nullptr; lbBlock *done = nullptr; bool is_map = false; - TypeAndValue tav = type_and_value_of_expr(expr); if (tav.mode == Addressing_Type) { lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done); @@ -1062,8 +1098,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc break; } case Type_Tuple: - lb_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done); - break; + GB_PANIC("Should be handled already"); case Type_BitSet: { lbModule *m = p->module; -- cgit v1.2.3 From 5e1b376e22854ee4dd1d455d9db372d24f445ae4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Apr 2024 10:34:17 +0100 Subject: Disallow `for x in bitset_or_map` if `x` is a variable that matches the "key" --- src/check_stmt.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c31056b33..cccbab4f6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1629,6 +1629,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) 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"); } + if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { + String name = rs->vals[0]->Ident.token.string; + Entity *found = scope_lookup(ctx->scope, name); + if (found && are_types_identical(found->type, t->BitSet.elem)) { + ERROR_BLOCK(); + gbString s = expr_to_string(expr); + error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); + error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); + gb_string_free(s); + } + } break; case Type_EnumeratedArray: @@ -1664,6 +1675,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (is_reverse) { error(node, "#reverse for is not supported for map types, as maps are unordered"); } + if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { + String name = rs->vals[0]->Ident.token.string; + Entity *found = scope_lookup(ctx->scope, name); + if (found && are_types_identical(found->type, t->Map.key)) { + ERROR_BLOCK(); + gbString s = expr_to_string(expr); + error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); + error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); + gb_string_free(s); + } + } break; case Type_Tuple: -- cgit v1.2.3 From ad5c9469d82e85b0d1fe4dbeb5cce98420e5a0ab Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 10 May 2024 14:22:43 +0100 Subject: Fix #3522 --- src/check_stmt.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cccbab4f6..c018077f9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -161,8 +161,7 @@ gb_internal bool check_is_terminating_list(Slice const &stmts, String con } gb_internal bool check_has_break_list(Slice const &stmts, String const &label, bool implicit) { - for_array(i, stmts) { - Ast *stmt = stmts[i]; + for (Ast *stmt : stmts) { if (check_has_break(stmt, label, implicit)) { return true; } @@ -170,6 +169,14 @@ gb_internal bool check_has_break_list(Slice const &stmts, String const &l return false; } +gb_internal bool check_has_break_expr_list(Slice const &exprs, String const &label) { + for (Ast *expr : exprs) { + if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + } + return false; +} gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) { switch (stmt->kind) { @@ -227,6 +234,20 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return true; } break; + + case Ast_ValueDecl: + if (stmt->ValueDecl.is_mutable && check_has_break_expr_list(stmt->ValueDecl.values, label)) { + return true; + } + break; + case Ast_AssignStmt: + if (check_has_break_expr_list(stmt->AssignStmt.lhs, label)) { + return true; + } + if (check_has_break_expr_list(stmt->AssignStmt.rhs, label)) { + return true; + } + break; } return false; @@ -250,6 +271,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { return check_is_terminating(unparen_expr(es->expr), label); case_end; + case_ast_node(vd, ValueDecl, node); + return check_has_break_expr_list(vd->values, label); + case_end; + + case_ast_node(as, AssignStmt, node); + return check_has_break_expr_list(as->lhs, label) || + check_has_break_expr_list(as->rhs, label); + case_end; + case_ast_node(bs, BranchStmt, node); return bs->token.kind == Token_fallthrough; case_end; -- cgit v1.2.3 From 4eab735b137b5bdf7e5c5805ab19177af97d0423 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:24:50 +0100 Subject: Minor clean up of `is_terminating` code This does not fix all known issues with it --- src/check_stmt.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c018077f9..3b836aa3c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -254,6 +254,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) } +String label_from_node(Ast *node, String default_label) { + if (node != nullptr && node->kind == Ast_Ident) { + return node->Ident.token.string; + } + return default_label; +} + // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly @@ -264,7 +271,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label); + return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); case_end; case_ast_node(es, ExprStmt, node); @@ -285,9 +292,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); + String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, label) && - check_is_terminating(is->else_stmt, label)) { + if (check_is_terminating(is->body, new_label) && + check_is_terminating(is->else_stmt, new_label)) { return true; } } @@ -320,7 +328,8 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + String new_label = label_from_node(fs->label, label); + if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { return true; } case_end; @@ -335,14 +344,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } @@ -351,14 +361,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } -- cgit v1.2.3 From 34c8739b69e0778fe19f0593fecb7e7080363862 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:42:06 +0100 Subject: Fix #3578 --- src/check_stmt.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3b836aa3c..ad91838b9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -253,15 +253,17 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return false; } - -String label_from_node(Ast *node, String default_label) { - if (node != nullptr && node->kind == Ast_Ident) { +String label_string(Ast *node) { + GB_ASSERT(node != nullptr); + if (node->kind == Ast_Ident) { return node->Ident.token.string; + } else if (node->kind == Ast_Label) { + return label_string(node->Label.name); } - return default_label; + GB_ASSERT("INVALID LABEL"); + return {}; } - // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly gb_internal bool check_is_terminating(Ast *node, String const &label) { @@ -271,7 +273,12 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); + if (check_is_terminating_list(bs->stmts, label)) { + if (bs->label != nullptr) { + return check_is_terminating_list(bs->stmts, label_string(bs->label)); + } + return true; + } case_end; case_ast_node(es, ExprStmt, node); @@ -292,10 +299,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); - String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, new_label) && - check_is_terminating(is->else_stmt, new_label)) { + if (check_is_terminating(is->body, label) && + check_is_terminating(is->else_stmt, label)) { return true; } } @@ -328,8 +334,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - String new_label = label_from_node(fs->label, label); - if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { + if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + if (fs->label) { + return !check_has_break(fs->body, label_string(fs->label), false); + } return true; } case_end; @@ -344,15 +352,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } @@ -361,15 +368,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } -- cgit v1.2.3 From 8b4a8e4d806a9d8328c3aedf8175c681b42cb324 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:49:12 +0100 Subject: Fix #3569 --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ad91838b9..719a0da15 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -751,7 +751,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, for (auto const &entry : scope->elements) { String name = entry.key; Entity *decl = entry.value; - if (!is_entity_exported(decl)) continue; + if (!is_entity_exported(decl, true)) continue; Entity *found = scope_insert_with_name(ctx->scope, name, decl); if (found != nullptr) { -- cgit v1.2.3 From 20f8f9012d71fa9d95ff65a0badf844c9cbe4ddb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:11:57 +0100 Subject: Attempt at fixing #3588 --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 719a0da15..ee55ff0d7 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -776,6 +776,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, bool is_ptr = is_type_pointer(e->type); Type *t = base_type(type_deref(e->type)); if (t->kind == Type_Struct) { + wait_signal_until_available(&t->Struct.fields_wait_signal); + Scope *found = t->Struct.scope; GB_ASSERT(found != nullptr); for (auto const &entry : found->elements) { -- cgit v1.2.3 From 7734b12f9a494142c59487265e840bc1c8e605d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:16:32 +0100 Subject: Fix #3587 --- src/check_stmt.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee55ff0d7..875503874 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -169,9 +169,16 @@ gb_internal bool check_has_break_list(Slice const &stmts, String const &l return false; } +gb_internal bool check_has_break_expr(Ast * expr, String const &label) { + if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + return false; +} + gb_internal bool check_has_break_expr_list(Slice const &exprs, String const &label) { for (Ast *expr : exprs) { - if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + if (check_has_break_expr(expr, label)) { return true; } } @@ -196,6 +203,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->BlockStmt.stmts, label, implicit); case Ast_IfStmt: + if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) { + return true; + } + if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) { + return true; + } + if (check_has_break(stmt->IfStmt.body, label, implicit) || (stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) { return true; @@ -206,6 +220,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->CaseClause.stmts, label, implicit); case Ast_SwitchStmt: + if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) { + return true; + } if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) { return true; } @@ -218,6 +235,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) break; case Ast_ForStmt: + if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) { + return true; + } + if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) { + return true; + } + if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) { + return true; + } + if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) { return true; } -- cgit v1.2.3 From a344bc4c0e672d9740f5777dae862723fe269973 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 14:39:16 +0100 Subject: Remove the old switch/for semantics entirely and enforce `switch &x in y` --- src/check_expr.cpp | 46 +++++++++++++--------------------------------- src/check_stmt.cpp | 30 ++---------------------------- src/entity.cpp | 2 -- 3 files changed, 15 insertions(+), 63 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a293a97e..c44232dab 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2440,32 +2440,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } -gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { - Entity *e = entity_of_node(expr); - if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { - GB_ASSERT(e->kind == Entity_Variable); - - ERROR_BLOCK(); - - if ((e->flags & EntityFlag_ForValue) != 0) { - Type *parent_type = type_deref(e->Variable.for_loop_parent_type); - - error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed."); - - if (is_type_map(parent_type)) { - error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); - } else { - error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); - } - } else { - GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); - - error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed."); - error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); - } - } -} - gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2493,7 +2467,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * { ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); - if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { + if (e == nullptr) { + break; + } + if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); if (parent_type != nullptr && is_type_string(parent_type)) { @@ -2503,9 +2480,17 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * } else { error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); } + + if (is_type_map(parent_type)) { + error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); + } } - if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) { + if ((e->flags & EntityFlag_SwitchValue) != 0) { error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n"); + + error_line("\t Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); } } break; @@ -2527,11 +2512,6 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * o->type = alloc_type_pointer(o->type); } } else { - if (ast_node_expect(node, Ast_UnaryExpr)) { - ast_node(ue, UnaryExpr, node); - check_old_for_or_switch_value_usage(ue->expr); - } - o->type = alloc_type_pointer(o->type); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 875503874..0267bdf80 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,7 +501,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: - check_old_for_or_switch_value_usage(lhs->expr); break; case Addressing_MapIndex: { @@ -523,9 +522,8 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O break; } - case Addressing_Context: { + case Addressing_Context: break; - } case Addressing_SoaVariable: break; @@ -1328,7 +1326,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } } - bool is_ptr = is_type_pointer(x.type); // NOTE(bill): Check for multiple defaults Ast *first_default = nullptr; @@ -1447,15 +1444,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } bool is_reference = is_addressed; - bool old_style = false; - - if (!is_reference && - is_ptr && - cc->list.count == 1 && - case_type != nullptr) { - is_reference = true; - old_style = true; - } if (cc->list.count > 1 || saw_nil) { case_type = nullptr; @@ -1477,9 +1465,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ if (!is_reference) { tag_var->flags |= EntityFlag_Value; } - if (old_style) { - tag_var->flags |= EntityFlag_OldForOrSwitchValue; - } add_entity(ctx, ctx->scope, lhs, tag_var); add_entity_use(ctx, lhs, tag_var); add_implicit_entity(ctx, stmt, tag_var); @@ -1618,7 +1603,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) auto entities = array_make(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; @@ -1679,7 +1663,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1719,32 +1702,27 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_EnumeratedArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->EnumeratedArray.elem); array_add(&vals, t->EnumeratedArray.index); break; case Type_Array: - if (is_ptr) use_by_reference_for_value = true; - if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable; + is_possibly_addressable = operand.mode == Addressing_Variable; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; case Type_DynamicArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->DynamicArray.elem); array_add(&vals, t_int); break; case Type_Slice: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Slice.elem); array_add(&vals, t_int); break; case Type_Map: - if (is_ptr) use_by_reference_for_value = true; is_map = true; array_add(&vals, t->Map.key); array_add(&vals, t->Map.value); @@ -1817,7 +1795,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { is_soa = true; - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); } @@ -1894,9 +1871,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) 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) { - entity->flags |= EntityFlag_OldForOrSwitchValue; - entity->flags &= ~EntityFlag_Value; } if (is_soa) { if (i == 0) { diff --git a/src/entity.cpp b/src/entity.cpp index 60ca208ec..8a7417006 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -85,8 +85,6 @@ enum EntityFlag : u64 { EntityFlag_Require = 1ull<<50, EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer - EntityFlag_OldForOrSwitchValue = 1ull<<52, - EntityFlag_Overridden = 1ull<<63, }; -- cgit v1.2.3 From b2dc5cc81254b4ab194dac900ab143609ba07bac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 15:32:15 +0100 Subject: Fix error reporting for enforce new switch/for syntax --- src/check_stmt.cpp | 4 ++-- src/error.cpp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0267bdf80..23a97696d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -572,7 +572,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O error(lhs->expr, "Cannot assign to '%s'", str); if (e && e->flags & EntityFlag_ForValue) { - isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token)); if (offset < 0) { if (is_type_map(e->type)) { error_line("\tSuggestion: Did you mean? 'for key, &%.*s in ...'\n", LIT(e->token.string)); @@ -588,7 +588,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } else if (e && e->flags & EntityFlag_SwitchValue) { - isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token)); if (offset < 0) { error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); } else { diff --git a/src/error.cpp b/src/error.cpp index f5123b969..da444e998 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -266,7 +266,7 @@ gb_internal void terminal_reset_colours(void) { } -gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char const *prefix=nullptr) { +gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { get_error_value()->end = end; if (!show_error_line()) { return -1; @@ -289,17 +289,13 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char con MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING, }; - if (prefix) { - error_out("\t%s\n\n", prefix); - } + i32 error_length = gb_max(end.offset - pos.offset, 1); error_out("\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); - i32 error_length = gb_max(end.offset - pos.offset, 1); - isize squiggle_extra = 0; if (line_len > MAX_LINE_LENGTH_PADDED) { -- cgit v1.2.3 From 46b3e7b6fa9477b7feb4389a353f92c7cc4c3d20 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 20 May 2024 13:36:32 +0100 Subject: Fix `for &v in &fixed_array` --- src/check_stmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 23a97696d..2083dbf11 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1663,6 +1663,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } + bool is_ptr = type_deref(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1707,7 +1708,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_Array: - is_possibly_addressable = operand.mode == Addressing_Variable; + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; -- cgit v1.2.3 From 7dc1f114b96b9e25dfc2537ab153fe655e65affc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 May 2024 22:22:41 +0100 Subject: Add shadow suggestion --- src/check_stmt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2083dbf11..866cdb5a1 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -565,7 +565,11 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } else { error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str); } - error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + if (is_type_pointer(e->type)) { + error_line("\tSuggestion: Did you mean to shadow it? '%.*s := %.*s'?\n", LIT(e->token.string), LIT(e->token.string)); + } else { + error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + } show_error_on_line(e->token.pos, token_pos_end(e->token)); } else { ERROR_BLOCK(); -- cgit v1.2.3 From 66acbb7fed88ed9132dfc8107865e0ac27ed3ac8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 May 2024 21:48:23 +0100 Subject: Add `@(link_suffix=)` --- src/check_decl.cpp | 13 +++++++------ src/check_stmt.cpp | 4 ++-- src/checker.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/checker.hpp | 5 ++++- src/entity.cpp | 2 ++ 5 files changed, 61 insertions(+), 9 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1ec366ae7..44b06d712 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -724,15 +724,16 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) return nullptr; } -gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) { +gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) { if (link_prefix.len > 0) { if (link_name.len > 0) { error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { - isize len = link_prefix.len + token.string.len; + isize len = link_prefix.len + token.string.len + link_suffix.len; u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); gb_memmove(name, &link_prefix[0], link_prefix.len); gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); + gb_memmove(name+link_prefix.len+token.string.len, link_suffix.text, link_suffix.len); name[len] = 0; link_name = make_string(name, len); @@ -862,7 +863,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } TypeProc *pt = &proc_type->Proc; - AttributeContext ac = make_attribute_context(e->Procedure.link_prefix); + AttributeContext ac = make_attribute_context(e->Procedure.link_prefix, e->Procedure.link_suffix); if (d != nullptr) { check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac); @@ -1015,7 +1016,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix,ac.link_suffix); if (ac.has_disabled_proc) { if (ac.disabled_proc) { e->flags |= EntityFlag_Disabled; @@ -1223,7 +1224,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast } e->flags |= EntityFlag_Visited; - AttributeContext ac = make_attribute_context(e->Variable.link_prefix); + AttributeContext ac = make_attribute_context(e->Variable.link_prefix, e->Variable.link_suffix); ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); @@ -1244,7 +1245,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { e->Variable.thread_local_model.len = 0; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 866cdb5a1..2c37bced0 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2020,7 +2020,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f // TODO NOTE(bill): This technically checks things multple times - AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); + AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix, ctx->foreign_context.link_suffix); check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); for (isize i = 0; i < entity_count; i++) { @@ -2037,7 +2037,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->type = init_type; e->state = EntityState_Resolved; } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; diff --git a/src/checker.cpp b/src/checker.cpp index 1ded6ea6e..ec58b9d8e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3127,6 +3127,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + if (ev.kind == ExactValue_String) { + String link_suffix = ev.value_string; + if (!is_foreign_name_valid(link_suffix)) { + error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); + } else { + c->foreign_context.link_suffix = link_suffix; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "private") { EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage; if (ev.kind == ExactValue_Invalid) { @@ -3421,6 +3433,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "deprecated") { ExactValue ev = check_decl_attribute_value(c, value); @@ -3702,6 +3726,17 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "link_section") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3733,6 +3768,7 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) { name == "linkage" || name == "link_name" || name == "link_prefix" || + name == "link_suffix" || false) { error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name)); return true; @@ -3775,8 +3811,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (attributes.count == 0) return; String original_link_prefix = {}; + String original_link_suffix = {}; if (ac) { original_link_prefix = ac->link_prefix; + original_link_suffix = ac->link_suffix; } StringSet set = {}; @@ -3851,6 +3889,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at ac->link_prefix.len = 0; } } + if (ac->link_suffix.text == original_link_suffix.text) { + if (ac->link_name.len > 0) { + ac->link_suffix.text = nullptr; + ac->link_suffix.len = 0; + } + } } } @@ -4145,6 +4189,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; + e->Variable.link_suffix = c->foreign_context.link_suffix; } Ast *init_expr = value; @@ -4219,6 +4264,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } e->Procedure.link_prefix = c->foreign_context.link_prefix; + e->Procedure.link_suffix = c->foreign_context.link_suffix; GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; diff --git a/src/checker.hpp b/src/checker.hpp index 6ae7b90e2..539b72b2d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -112,6 +112,7 @@ enum InstrumentationFlag : i32 { struct AttributeContext { String link_name; String link_prefix; + String link_suffix; String link_section; String linkage; isize init_expr_list_count; @@ -146,9 +147,10 @@ struct AttributeContext { String enable_target_feature; // will be enabled for the procedure only }; -gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) { +gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) { AttributeContext ac = {}; ac.link_prefix = link_prefix; + ac.link_suffix = link_suffix; return ac; } @@ -302,6 +304,7 @@ struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; + String link_suffix; EntityVisiblityKind visibility_kind; }; diff --git a/src/entity.cpp b/src/entity.cpp index 1461b96d7..e4fc66dac 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -223,6 +223,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; String link_section; CommentGroup *docs; CommentGroup *comment; @@ -243,6 +244,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; DeferredProcedure deferred_procedure; struct GenProcsData *gen_procs; -- cgit v1.2.3 From 9ef43fc782159893b7af139f9d9be3aec3108ecd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:16:34 +0100 Subject: Add `@(rodata)` --- src/check_decl.cpp | 6 ++++++ src/check_stmt.cpp | 6 ++++++ src/checker.cpp | 6 ++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend.cpp | 20 ++++++++++++++++++-- 6 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f2afce59c..43947836b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1264,6 +1264,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } + if (ac.rodata) { + e->Variable.is_rodata = true; + } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { @@ -1350,6 +1353,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + if (e->Variable.is_rodata && o.mode != Addressing_Constant) { + error(o.expr, "Variables declared with @(rodata) must have constant initialization"); + } check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c37bced0..fc443a7b5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,6 +501,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: + if (e && e->kind == Entity_Variable && e->Variable.is_rodata) { + error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string)); + } break; case Addressing_MapIndex: { @@ -2055,6 +2058,9 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } } + if (ac.rodata) { + error(e->token, "Only global variables can have @(rodata) applied"); + } if (ac.thread_local_model != "") { String name = e->token.string; if (name == "_") { diff --git a/src/checker.cpp b/src/checker.cpp index 97e685d33..8a58bb425 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3628,6 +3628,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } ac->is_static = true; return true; + } else if (name == "rodata") { + if (value != nullptr) { + error(elem, "'rodata' does not have any parameters"); + } + ac->rodata = true; + return true; } else if (name == "thread_local") { ExactValue ev = check_decl_attribute_value(c, value); if (ac->init_expr_list_count > 0) { diff --git a/src/checker.hpp b/src/checker.hpp index e793540e3..2ac4c8e7a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -133,6 +133,7 @@ struct AttributeContext { bool entry_point_only : 1; bool instrumentation_enter : 1; bool instrumentation_exit : 1; + bool rodata : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; diff --git a/src/entity.cpp b/src/entity.cpp index e4fc66dac..7f484e308 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -230,6 +230,7 @@ struct Entity { bool is_foreign; bool is_export; bool is_global; + bool is_rodata; } Variable; struct { Type * type_parameter_specialization; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 03c17a8bb..5dc6d94d5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1160,6 +1160,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc if (is_type_untyped_nil(init.type)) { LLVMSetInitializer(var.var.value, LLVMConstNull(global_type)); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); @@ -1174,6 +1178,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc } LLVMSetInitializer(var.var.value, init.value); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } } else { @@ -1206,8 +1214,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc var.is_initialized = true; } + + } - CheckerInfo *info = main_module->gen->info; for (Entity *e : info->init_procedures) { @@ -3210,14 +3219,21 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbValue init = lb_const_value(m, tav.type, v); LLVMSetInitializer(g.value, init.value); var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } } } if (!var.is_initialized && is_type_untyped_nil(tav.type)) { var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } + } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); } - array_add(&global_variables, var); lb_add_entity(m, e, g); -- cgit v1.2.3 From bea47db4953559dbbcdce1da5dbaf38d0bb8d943 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:20:47 +0100 Subject: Allow `@(rodata)` on `@(static)` variables --- src/check_stmt.cpp | 6 +++++- src/llvm_backend_stmt.cpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fc443a7b5..a1698bbfe 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2059,7 +2059,11 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } if (ac.rodata) { - error(e->token, "Only global variables can have @(rodata) applied"); + if (ac.is_static) { + e->Variable.is_rodata = true; + } else { + error(e->token, "Only global or @(static) variables can have @(rodata) applied"); + } } if (ac.thread_local_model != "") { String name = e->token.string; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index b18db4e45..9f28e45e0 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1850,7 +1850,9 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); if (value.value != nullptr) { LLVMSetInitializer(global, value.value); - } else { + } + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(global, true); } if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(global, true); -- cgit v1.2.3 From 7e994b6d216b091b90c47eba1b834bc7c690535c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 15:42:19 +0100 Subject: Remove empty line preventing a suggestion from happening --- src/check_stmt.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a1698bbfe..65bac36be 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1255,8 +1255,6 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } -- cgit v1.2.3 From be0774acc8a67ac2f9e003764284bbda21d4ebcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 16:07:28 +0100 Subject: Add error message on return a constant slice value from a procedure --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 65bac36be..c37c58cd6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2501,6 +2501,10 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } + } else if (o.mode == Addressing_Constant && is_type_slice(o.type)) { + ERROR_BLOCK(); + unsafe_return_error(o, "a compound literal of a slice"); + error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); } } -- cgit v1.2.3 From fa3cae2bb04db76f52f1b2288a9c858f20332b8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:02:34 +0100 Subject: Add `intrinsics.procedure_of` ```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ``` --- base/intrinsics/intrinsics.odin | 4 ++++ src/check_builtin.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ src/check_decl.cpp | 17 ++++++++++----- src/check_expr.cpp | 1 + src/check_stmt.cpp | 10 ++++++++- src/checker.cpp | 4 ++++ src/checker.hpp | 6 ++++++ src/checker_builtin_procs.hpp | 4 ++++ src/parser.hpp | 1 + 9 files changed, 87 insertions(+), 6 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 8873f3bbc..4f6fa2713 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- // if all listed features are supported. has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- + +// Returns the value of the procedure where `x` must be a call expression +procedure_of :: proc(x: $T) -> T where type_is_proc(T) --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 98c695a2c..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 43947836b..13b14149a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e->state.load() != EntityState_Resolved) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 641f70566..01cba881e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c37c58cd6..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index 8f0cc1cd1..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } diff --git a/src/checker.hpp b/src/checker.hpp index 2ac4c8e7a..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/parser.hpp b/src/parser.hpp index 0e411d9ac..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From e8517e2694524a94ee88cf925a4a667fb6575f90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 18:56:45 +0100 Subject: `-strict-style`: enforce `case` to be in the same column as `switch` --- src/check_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f2e3b0242..edf2fae39 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1258,6 +1258,20 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } + + if (build_context.strict_style) { + Token stok = ss->token; + for_array(i, bs->stmts) { + Ast *stmt = bs->stmts[i]; + if (stmt->kind != Ast_CaseClause) { + continue; + } + Token ctok = stmt->CaseClause.token; + if (ctok.pos.column > stok.pos.column) { + error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token"); + } + } + } } -- cgit v1.2.3 From c54e3d3c4f3d32761c17e3528714598717ecebbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Jul 2024 13:45:21 +0100 Subject: Improve warning handling for possible `default:` typo --- src/check_stmt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index edf2fae39..f4d3bd6b8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2031,6 +2031,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f gb_string_free(str); init_type = t_invalid; } + if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) { + Entity *e = entities[0]; + if (e != nullptr && e->token.string == "default") { + warning(e->token, "Did you mean 'case:'?"); + } + } } -- cgit v1.2.3 From b38237e8f0872fd8979a729553338e01d04dc3e8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:59:00 -0400 Subject: Fix compiler crash when switching on no value --- src/check_stmt.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f4d3bd6b8..74397828d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1060,6 +1060,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags if (ss->tag != nullptr) { check_expr(ctx, &x, ss->tag); check_assignment(ctx, &x, nullptr, str_lit("switch expression")); + if (x.type == nullptr) { + return; + } } else { x.mode = Addressing_Constant; x.type = t_bool; -- cgit v1.2.3 From 431227d4c50e5b53e5c4c922a76c11ec8464baa3 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 21 Jul 2024 02:52:53 +0200 Subject: Add NULL check in check_range_stmt Fixes #3953 --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 74397828d..6c3570cc8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1837,7 +1837,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (rs->vals.count == 1) { Type *t = type_deref(operand.type); - if (is_type_map(t) || is_type_bit_set(t)) { + if (t != NULL && (is_type_map(t) || is_type_bit_set(t))) { gbString v = expr_to_string(rs->vals[0]); defer (gb_string_free(v)); error_line("\tSuggestion: place parentheses around the expression\n"); -- cgit v1.2.3 From 6eb28aeafc177ae91ae704c3e4f2c75b9f27b092 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 22 Jul 2024 22:52:10 +0100 Subject: Check to see if people are return a slice of a local fixed array from a procedure --- src/check_stmt.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6c3570cc8..76b6d3f40 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2517,7 +2517,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { 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) { + } 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); @@ -2532,6 +2532,14 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } + } else if (expr->kind == Ast_SliceExpr) { + Ast *x = unparen_expr(expr->SliceExpr.expr); + Entity *e = entity_of_node(x); + if (is_entity_local_variable(e) && is_type_array(e->type)) { + unsafe_return_error(o, "a slice of a local variable"); + } else if (x->kind == Ast_CompoundLit) { + unsafe_return_error(o, "a slice of a compound literal"); + } } else if (o.mode == Addressing_Constant && is_type_slice(o.type)) { ERROR_BLOCK(); unsafe_return_error(o, "a compound literal of a slice"); -- cgit v1.2.3 From 4bb51249d176fbdecbce423911914566bd120255 Mon Sep 17 00:00:00 2001 From: Davi Date: Tue, 13 Aug 2024 23:28:34 -0300 Subject: Error if assigning to `rodata` variable with index --- src/check_stmt.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 76b6d3f40..cc3671ad2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -474,8 +474,15 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O rhs->proc_group = nullptr; } } else { + Ast *ident_node = nullptr; + if (node->kind == Ast_Ident) { - ast_node(i, Ident, node); + ident_node = node; + } else if (node->kind == Ast_IndexExpr && node->IndexExpr.expr->kind == Ast_Ident) { + ident_node = node->IndexExpr.expr; + } + if (ident_node != nullptr) { + ast_node(i, Ident, ident_node); e = scope_lookup(ctx->scope, i->token.string); if (e != nullptr && e->kind == Entity_Variable) { used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case -- cgit v1.2.3 From 17740966e5dc1581e91f87c616aedc9df8838f1c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 18 Aug 2024 12:06:58 +0100 Subject: Fix #4040 --- src/check_stmt.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cc3671ad2..de127b79c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2496,6 +2496,16 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { continue; } Ast *expr = unparen_expr(o.expr); + while (expr->kind == Ast_CallExpr && expr->CallExpr.proc->tav.mode == Addressing_Type) { + if (expr->CallExpr.args.count != 1) { + break; + } + Ast *arg = expr->CallExpr.args[0]; + if (arg->kind == Ast_FieldValue || !are_types_identical(arg->tav.type, expr->tav.type)) { + break; + } + expr = unparen_expr(arg); + } auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { gbString s = expr_to_string(o.expr); -- cgit v1.2.3 From f82bf6cd42c6a16705883af1e948b9ac85406899 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 18 Aug 2024 12:13:52 +0100 Subject: Fix #4022 --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index de127b79c..fbcabf3d2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1478,7 +1478,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ case_type = nullptr; } if (case_type == nullptr) { - case_type = x.type; + case_type = type_deref(x.type); } if (switch_kind == TypeSwitch_Any) { if (!is_type_untyped(case_type)) { -- cgit v1.2.3 From c4e0cbcd878f5984f559bdee1c6f02dc5a6e8ddc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 18 Aug 2024 12:21:35 +0100 Subject: Fix #4005 --- src/check_stmt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fbcabf3d2..cd1793a87 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1637,6 +1637,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Ast *expr = unparen_expr(rs->expr); + bool is_range = false; bool is_possibly_addressable = true; isize max_val_count = 2; if (is_ast_range(expr)) { @@ -1645,6 +1646,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Operand y = {}; is_possibly_addressable = false; + is_range = true; bool ok = check_range(ctx, expr, true, &x, &y, nullptr); if (!ok) { @@ -1889,7 +1891,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } if (found == nullptr) { entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); - entity->flags |= EntityFlag_ForValue; + if (!is_range) { + entity->flags |= EntityFlag_ForValue; + } entity->flags |= EntityFlag_Value; entity->identifier = name; entity->Variable.for_loop_parent_type = type_of_expr(expr); -- cgit v1.2.3 From 0e82a46047b619a6d70ec7e7a3e23a66794584b1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 18 Aug 2024 12:35:25 +0100 Subject: Fix #3999 --- src/check_stmt.cpp | 3 ++- src/llvm_backend_expr.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cd1793a87..12156df01 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -824,7 +824,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, if (f->kind == Entity_Variable) { Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr); if (!is_ptr && e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value; - if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param; + if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param; + if (e->flags & EntityFlag_SoaPtrField) uvar->flags |= EntityFlag_SoaPtrField; Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { gbString expr_str = expr_to_string(expr); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 2bbd48c1a..60b6237bf 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3754,10 +3754,13 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { lbValue v = {}; + bool is_soa = false; if (pv == nullptr && parent->flags & EntityFlag_SoaPtrField) { + is_soa = true; // NOTE(bill): using SOA value (probably from for-in statement) lbAddr parent_addr = lb_get_soa_variable_addr(p, parent); - v = lb_addr_get_ptr(p, parent_addr); + Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(parent_addr)); + v = lb_address_from_load_or_generate_local(p, lb_make_soa_pointer(p, soa_ptr_type, parent_addr.addr, parent_addr.soa.index)); } else if (pv != nullptr) { v = *pv; } else { @@ -3765,7 +3768,7 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { v = lb_build_addr_ptr(p, e->using_expr); } GB_ASSERT(v.value != nullptr); - GB_ASSERT_MSG(parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type)); + GB_ASSERT_MSG(is_soa || parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type)); lbValue ptr = lb_emit_deep_field_gep(p, v, sel); if (parent->scope) { if ((parent->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { -- cgit v1.2.3 From 683dde1fa011056477423ded1bc6098ac2636675 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 24 Aug 2024 12:47:29 +0100 Subject: Disallow labelled branches in `defer` - fix #3960 --- src/check_stmt.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 12156df01..1ef51e585 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -199,6 +199,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) } break; + case Ast_DeferStmt: + return check_has_break(stmt->DeferStmt.stmt, label, implicit); + case Ast_BlockStmt: return check_has_break_list(stmt->BlockStmt.stmts, label, implicit); @@ -2706,6 +2709,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) error(bs->label, "A branch statement's label name must be an identifier"); return; } + Ast *ident = bs->label; String name = ident->Ident.token.string; Operand o = {}; @@ -2737,6 +2741,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) break; } + + if (ctx->in_defer) { + error(bs->label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(token.string)); + } } case_end; -- cgit v1.2.3 From aa659a637a70af21feb60ee08c11c46718defa30 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 24 Aug 2024 15:46:54 +0100 Subject: Fix #4132 --- src/check_stmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1ef51e585..c8717ba98 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1698,7 +1698,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = type_deref(operand.type); + bool is_ptr = is_type_pointer(type_deref(operand.type)); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1738,6 +1738,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_EnumeratedArray: + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; array_add(&vals, t->EnumeratedArray.elem); array_add(&vals, t->EnumeratedArray.index); break; -- cgit v1.2.3 From abf6ea7732b855dcb0ddb549a6454f99c40b7328 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2024 10:24:19 +0100 Subject: Fix minor bug with addressability --- src/check_stmt.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c8717ba98..74a9e8825 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1641,6 +1641,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Ast *expr = unparen_expr(rs->expr); + Operand rhs_operand = {}; + bool is_range = false; bool is_possibly_addressable = true; isize max_val_count = 2; @@ -1698,7 +1700,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = is_type_pointer(type_deref(operand.type)); + bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1750,16 +1752,19 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_DynamicArray: + is_possibly_addressable = true; array_add(&vals, t->DynamicArray.elem); array_add(&vals, t_int); break; case Type_Slice: + is_possibly_addressable = true; array_add(&vals, t->Slice.elem); array_add(&vals, t_int); break; case Type_Map: + is_possibly_addressable = true; is_map = true; array_add(&vals, t->Map.key); array_add(&vals, t->Map.value); @@ -1781,6 +1786,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Tuple: { + is_possibly_addressable = false; + isize count = t->Tuple.variables.count; if (count < 1) { ERROR_BLOCK(); @@ -1810,8 +1817,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) array_add(&vals, e->type); } - is_possibly_addressable = false; - bool do_break = false; for (isize i = rs->vals.count-1; i >= 0; i--) { if (rs->vals[i] != nullptr && count < i+2) { @@ -1831,6 +1836,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { + if (t->Struct.soa_kind == StructSoa_Fixed) { + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; + } else { + is_possibly_addressable = true; + } is_soa = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); @@ -1907,7 +1917,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" : is_bit_set ? "element" : "index"; + char const *idx_name = is_map ? "key" : (is_bit_set || i == 0) ? "element" : "index"; error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } } -- cgit v1.2.3 From 2392300ffbc86b09fde2a4d170b8c38f8ad0d382 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Oct 2024 14:12:49 +0000 Subject: Add warning for `unsigned >= 0` like conditions in a `for` loop --- src/check_stmt.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 74a9e8825..2418fcc5c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2600,6 +2600,23 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_expr(ctx, &o, fs->cond); if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { error(fs->cond, "Non-boolean condition in 'for' statement"); + } else { + Ast *cond = unparen_expr(o.expr); + if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_GtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) && + cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.right->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } else if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_LtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) && + cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.left->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } } } if (fs->post != nullptr) { -- cgit v1.2.3 From f5b16aa42aae8dd53f6138cc8593f7937733dc90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 5 Dec 2024 15:27:41 +0000 Subject: Fix #4561 --- src/check_stmt.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2418fcc5c..e2d6f68fa 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2612,6 +2612,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } else if (cond && cond->kind == Ast_BinaryExpr && cond->BinaryExpr.left && cond->BinaryExpr.right && cond->BinaryExpr.op.kind == Token_LtEq && + type_of_expr(cond->BinaryExpr.right) != nullptr && is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) && cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer && is_exact_value_zero(cond->BinaryExpr.left->tav.value)) { -- cgit v1.2.3 From e3b16464908f2d6e85b7569ad4da6a802e3283c3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 5 Dec 2024 15:46:35 +0000 Subject: Fix #4552 --- src/check_stmt.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e2d6f68fa..02ad72388 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2605,6 +2605,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (cond && cond->kind == Ast_BinaryExpr && cond->BinaryExpr.left && cond->BinaryExpr.right && cond->BinaryExpr.op.kind == Token_GtEq && + type_of_expr(cond->BinaryExpr.left) != nullptr && is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) && cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer && is_exact_value_zero(cond->BinaryExpr.right->tav.value)) { -- cgit v1.2.3