From 68df35b378d59f9813f5af81e61080c5f1b20e23 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 17:33:29 +0000 Subject: Add `#field_align(N)` It sets the minimum alignment for the fields within a struct. This cannot be used with `#packed`, but can be used with `#align(N)`. If `#align(N)` is less than `#field_align(N)`, then a warning will be printed. --- src/check_type.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index a95026711..5cb1eb9cc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -219,13 +219,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice 1) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &v); - error(node, "#align too large, %.*s", LIT(str)); + error(node, "#%s too large, %.*s", msg, LIT(str)); gb_free(a, str.text); return false; } i64 align = big_int_to_i64(&v); if (align < 1 || !gb_is_power_of_two(cast(isize)align)) { - error(node, "#align must be a power of 2, got %lld", align); + error(node, "#%s must be a power of 2, got %lld", msg, align); return false; } *align_ = align; @@ -251,7 +251,7 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) } } - error(node, "#align must be an integer"); + error(node, "#%s must be an integer", msg); return false; } @@ -645,16 +645,26 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); } - if (st->align != nullptr) { - if (st->is_packed) { - syntax_error(st->align, "'#align' cannot be applied with '#packed'"); - return; - } - i64 custom_align = 1; - if (check_custom_align(ctx, st->align, &custom_align)) { - struct_type->Struct.custom_align = custom_align; - } +#define ST_ALIGN(_name) if (st->_name != nullptr) { \ + if (st->is_packed) { \ + syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \ + return; \ + } \ + i64 align = 1; \ + if (check_custom_align(ctx, st->_name, &align, #_name)) { \ + struct_type->Struct.custom_##_name = align; \ + } \ } + + ST_ALIGN(field_align); + ST_ALIGN(align); + if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) { + warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)", + cast(long long)struct_type->Struct.custom_align, + cast(long long)struct_type->Struct.custom_field_align); + } + +#undef ST_ALIGN } gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_union(union_type)); @@ -746,7 +756,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no if (ut->align != nullptr) { i64 custom_align = 1; - if (check_custom_align(ctx, ut->align, &custom_align)) { + if (check_custom_align(ctx, ut->align, &custom_align, "align")) { if (variants.count == 0) { error(ut->align, "An empty union cannot have a custom alignment"); } else { -- cgit v1.2.3 From aff345f57fed6af4414f3adf5704110f8c75a4cf Mon Sep 17 00:00:00 2001 From: jakubtomsu <66876057+jakubtomsu@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:38:05 +0100 Subject: Allow fixed SOA structs --- src/check_type.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 5cb1eb9cc..0a2113e56 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2390,7 +2390,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e bool is_polymorphic = is_type_polymorphic(elem); - if ((!is_polymorphic || soa_kind == StructSoa_Fixed) && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) { + if (!is_polymorphic && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) { gbString str = type_to_string(elem); error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str); gb_string_free(str); @@ -2407,7 +2407,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e case StructSoa_Slice: extra_field_count = 1; break; case StructSoa_Dynamic: extra_field_count = 3; break; } - if (is_polymorphic && soa_kind != StructSoa_Fixed) { + if (is_polymorphic) { field_count = 0; soa_struct = alloc_type_struct(); -- cgit v1.2.3 From 42aca72d9f615b51b284b98e787dd6df8e7ba2d1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Feb 2024 13:21:41 +0000 Subject: Correct the logic for parapoly records to match that of procedures (polymorphic non-typeid parameters) --- src/check_type.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 0a2113e56..4d0901605 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,4 +1,5 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); +gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand); gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -417,11 +418,13 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly auto entities = array_make(permanent_allocator(), 0, variable_count); + i32 field_group_index = -1; for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { continue; } + field_group_index += 1; ast_node(p, Field, param); Ast *type_expr = p->type; Ast *default_value = unparen_expr(p->default_value); @@ -481,12 +484,12 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly type = t_invalid; } - if (is_type_polymorphic_type) { - gbString str = type_to_string(type); - error(params[i], "Parameter types cannot be polymorphic, got %s", str); - gb_string_free(str); - type = t_invalid; - } + // if (is_type_polymorphic_type) { + // gbString str = type_to_string(type); + // error(params[i], "Parameter types cannot be polymorphic, got %s", str); + // gb_string_free(str); + // type = t_invalid; + // } if (!is_type_param && check_constant_parameter_value(type, params[i])) { // failed @@ -523,13 +526,15 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly e->TypeName.is_type_alias = true; e->flags |= EntityFlag_PolyConst; } else { - if (is_type_polymorphic(base_type(operand.type))) { + Type *t = determine_type_from_polymorphic(ctx, type, operand); + if (is_type_polymorphic(base_type(t))) { *is_polymorphic_ = true; can_check_fields = false; } if (e == nullptr) { - e = alloc_entity_constant(scope, token, operand.type, operand.value); + e = alloc_entity_const_param(scope, token, t, operand.value, is_type_polymorphic(t)); e->Constant.param_value = param_value; + e->Constant.field_group_index = field_group_index; } } } else { @@ -538,7 +543,8 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly e->TypeName.is_type_alias = true; e->flags |= EntityFlag_PolyConst; } else { - e = alloc_entity_constant(scope, token, type, param_value.value); + e = alloc_entity_const_param(scope, token, type, param_value.value, is_type_polymorphic(type)); + e->Constant.field_group_index = field_group_index; e->Constant.param_value = param_value; } } -- cgit v1.2.3 From 59933b244ded0ab2476535b18875de95cd9f47bc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Feb 2024 13:41:02 +0000 Subject: Allow polymorphic checking with `intrinsics.type_is_subtype_of(Derived_Type, Poly_Type)` --- src/check_builtin.cpp | 2 +- src/check_type.cpp | 14 ++++++++++++-- src/types.cpp | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1cb43ec1..4e374add6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5686,7 +5686,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->value = exact_value_bool(is_type_subtype_of_and_allow_polymorphic(op_src.type, op_dst.type)); operand->mode = Addressing_Constant; operand->type = t_untyped_bool; } break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 4d0901605..15bba5319 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,5 +1,6 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand); +gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array const *operands); gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -394,7 +395,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly bool *is_polymorphic_, Ast *node, Array *poly_operands) { Type *polymorphic_params_type = nullptr; - bool can_check_fields = true; GB_ASSERT(is_polymorphic_ != nullptr); if (polymorphic_params == nullptr) { @@ -404,6 +404,17 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly return polymorphic_params_type; } + + // bool is_variadic = false; + // isize variadic_index = 0; + // bool success = false; + // isize specialization_count = 0; + // polymorphic_params_type = check_get_params(ctx, ctx->scope, polymorphic_params, &is_variadic, &variadic_index, &success, &specialization_count, poly_operands); + // if (success) { + // return nullptr; + // } + + bool can_check_fields = true; ast_node(field_list, FieldList, polymorphic_params); Slice params = field_list->list; if (params.count != 0) { @@ -565,7 +576,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly if (!*is_polymorphic_) { *is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr; } - return polymorphic_params_type; } diff --git a/src/types.cpp b/src/types.cpp index b99d469e4..c4b03c967 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4093,7 +4093,7 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } -gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { +gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false, bool allow_polymorphic=false) { Type *prev_src = src; src = type_deref(src); if (!src_is_ptr) { @@ -4105,11 +4105,19 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return 0; } + bool dst_is_polymorphic = is_type_polymorphic(dst); + for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } + if (allow_polymorphic && dst_is_polymorphic) { + Type *fb = base_type(type_deref(f->type)); + if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) { + return true; + } + } if (are_types_identical(f->type, dst)) { return level+1; @@ -4119,7 +4127,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return level+1; } } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic); if (nested_level > 0) { return nested_level; } @@ -4135,6 +4143,13 @@ gb_internal bool is_type_subtype_of(Type *src, Type *dst) { return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); } +gb_internal bool is_type_subtype_of_and_allow_polymorphic(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src), true); +} gb_internal bool has_type_got_objc_class_attribute(Type *t) { -- cgit v1.2.3 From dc5cfacc0af6f0d155124f6aefd960e83d54ff23 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Feb 2024 13:45:18 +0000 Subject: Fix edge case for procedures --- src/check_type.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 15bba5319..856e31cee 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -495,12 +495,12 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly type = t_invalid; } - // if (is_type_polymorphic_type) { - // gbString str = type_to_string(type); - // error(params[i], "Parameter types cannot be polymorphic, got %s", str); - // gb_string_free(str); - // type = t_invalid; - // } + if (is_type_polymorphic_type && !is_type_proc(type)) { + gbString str = type_to_string(type); + error(params[i], "Parameter types cannot be polymorphic, got %s", str); + gb_string_free(str); + type = t_invalid; + } if (!is_type_param && check_constant_parameter_value(type, params[i])) { // failed -- cgit v1.2.3 From 8f4ab3c07dd8c9cc5c188203f718eed3942bbe8b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Feb 2024 13:49:27 +0000 Subject: Add check for a procedure --- src/check_type.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 856e31cee..d216ae3e6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -537,7 +537,10 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly e->TypeName.is_type_alias = true; e->flags |= EntityFlag_PolyConst; } else { - Type *t = determine_type_from_polymorphic(ctx, type, operand); + Type *t = operand.type; + if (is_type_proc(type)) { + t = determine_type_from_polymorphic(ctx, type, operand); + } if (is_type_polymorphic(base_type(t))) { *is_polymorphic_ = true; can_check_fields = false; -- cgit v1.2.3 From c291fffce1972fcfc631dd4c6f623e3f5055f74b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Feb 2024 12:29:37 +0000 Subject: Add metadata type to `map`s debug information to aid debuggers knowing what is in it it --- src/check_type.cpp | 29 +++++++++++++++++++++++++++++ src/llvm_backend_debug.cpp | 2 +- src/types.cpp | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index d216ae3e6..5c70500d1 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2265,6 +2265,35 @@ gb_internal void init_map_internal_types(Type *type) { GB_ASSERT(key != nullptr); GB_ASSERT(value != nullptr); + Scope *metadata_scope = create_scope(nullptr, nullptr); + + Type *metadata_type = alloc_type_struct(); + metadata_type->Struct.fields = slice_make(permanent_allocator(), 3); + metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); + metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); + metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); + metadata_type->Struct.scope = metadata_scope; + + gb_unused(type_size_of(metadata_type)); + + // NOTE(bill): [0]^struct{key: Key, value: Value, hash: uintptr} + // This is a zero array to a pointer to keep the alignment to that of a pointer, and not effective the size of the final struct + metadata_type = alloc_type_array(alloc_type_pointer(metadata_type), 0);; + + + Scope *scope = create_scope(nullptr, nullptr); + Type *debug_type = alloc_type_struct(); + debug_type->Struct.fields = slice_make(permanent_allocator(), 4); + debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), t_uintptr, false, 0, EntityState_Resolved); + debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); + debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); + debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); + debug_type->Struct.scope = scope; + + gb_unused(type_size_of(debug_type)); + + type->Map.debug_metadata_type = debug_type; + type->Map.lookup_result_type = make_optional_ok_type(value); } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index e053c5b40..894c60729 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -710,7 +710,7 @@ gb_internal void lb_debug_complete_types(lbModule *m) { case Type_Map: GB_ASSERT(t_raw_map != nullptr); - bt = base_type(t_raw_map); + bt = base_type(t->Map.debug_metadata_type); /*fallthrough*/ case Type_Struct: if (file == nullptr) { diff --git a/src/types.cpp b/src/types.cpp index 8275b87ba..04fb06582 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -231,6 +231,7 @@ struct TypeProc { Type *key; \ Type *value; \ Type *lookup_result_type; \ + Type *debug_metadata_type; \ }) \ TYPE_KIND(Struct, TypeStruct) \ TYPE_KIND(Union, TypeUnion) \ -- cgit v1.2.3 From f378367fe72c12fccb19d32540fe86962967556e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Feb 2024 15:17:06 +0000 Subject: Store cell data in map debug metadata --- src/check_type.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 5c70500d1..01bb36255 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2255,6 +2255,34 @@ gb_internal void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) { if (len_) *len_ = len; } +gb_internal Type *get_map_cell_type(Type *type) { + i64 size, len; + i64 elem_size = type_size_of(type); + map_cell_size_and_len(type, &size, &len); + + if (size == len*elem_size) { + return type; + } + + if (is_power_of_two(len)) { + return type; + } + + i64 padding = size - len*elem_size; + GB_ASSERT(padding > 0); + + // Padding exists + Type *s = alloc_type_struct(); + Scope *scope = create_scope(nullptr, nullptr); + s->Struct.fields = slice_make(permanent_allocator(), 2); + s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved); + s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved); + s->Struct.scope = scope; + gb_unused(type_size_of(s)); + + return s; +} + gb_internal void init_map_internal_types(Type *type) { GB_ASSERT(type->kind == Type_Map); GB_ASSERT(t_allocator != nullptr); @@ -2265,13 +2293,19 @@ gb_internal void init_map_internal_types(Type *type) { GB_ASSERT(key != nullptr); GB_ASSERT(value != nullptr); - Scope *metadata_scope = create_scope(nullptr, nullptr); + + + Type *key_cell = get_map_cell_type(key); + Type *value_cell = get_map_cell_type(value); Type *metadata_type = alloc_type_struct(); - metadata_type->Struct.fields = slice_make(permanent_allocator(), 3); + Scope *metadata_scope = create_scope(nullptr, nullptr); + metadata_type->Struct.fields = slice_make(permanent_allocator(), 5); metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); + metadata_type->Struct.fields[3] = alloc_entity_field(metadata_scope, make_token_ident("key_cell"), key_cell, false, 3, EntityState_Resolved); + metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved); metadata_type->Struct.scope = metadata_scope; gb_unused(type_size_of(metadata_type)); -- cgit v1.2.3 From 5cd57a3a7f96e4966a7a17f99363893911fbad0d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Feb 2024 15:50:07 +0000 Subject: Use `Raw_Map` as the debug information for a `map` --- src/check_type.cpp | 2 ++ src/llvm_backend_debug.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 01bb36255..03c7474fb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2307,6 +2307,7 @@ gb_internal void init_map_internal_types(Type *type) { metadata_type->Struct.fields[3] = alloc_entity_field(metadata_scope, make_token_ident("key_cell"), key_cell, false, 3, EntityState_Resolved); metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved); metadata_type->Struct.scope = metadata_scope; + metadata_type->Struct.node = nullptr; gb_unused(type_size_of(metadata_type)); @@ -2323,6 +2324,7 @@ gb_internal void init_map_internal_types(Type *type) { debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); debug_type->Struct.scope = scope; + debug_type->Struct.node = nullptr; gb_unused(type_size_of(debug_type)); diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 894c60729..6bcbac13f 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -710,7 +710,9 @@ gb_internal void lb_debug_complete_types(lbModule *m) { case Type_Map: GB_ASSERT(t_raw_map != nullptr); - bt = base_type(t->Map.debug_metadata_type); + // bt = base_type(t->Map.debug_metadata_type); + bt = base_type(t_raw_map); + GB_ASSERT(bt->kind == Type_Struct); /*fallthrough*/ case Type_Struct: if (file == nullptr) { -- cgit v1.2.3 From cbfb32c34c09fd13098f0127bc98c88b53587a97 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Feb 2024 16:21:41 +0000 Subject: Fix race condition with regards to #soa arrays by using the fields mutex --- src/check_type.cpp | 10 +++++----- src/threading.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 03c7474fb..66f8b1185 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2553,6 +2553,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e GB_ASSERT(is_type_struct(elem)); Type *old_struct = base_type(elem); + RW_MUTEX_GUARD(&old_struct->Struct.fields_mutex); + field_count = old_struct->Struct.fields.count; soa_struct = alloc_type_struct(); @@ -2593,21 +2595,19 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e } if (soa_kind != StructSoa_Fixed) { - Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+0); + Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); soa_struct->Struct.fields[field_count+0] = len_field; add_entity(ctx, scope, nullptr, len_field); add_entity_use(ctx, nullptr, len_field); if (soa_kind == StructSoa_Dynamic) { - Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+1); + Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1); soa_struct->Struct.fields[field_count+1] = cap_field; add_entity(ctx, scope, nullptr, cap_field); add_entity_use(ctx, nullptr, cap_field); - Token token = {}; - token.string = str_lit("allocator"); init_mem_allocator(ctx->checker); - Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count+2); + Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2); soa_struct->Struct.fields[field_count+2] = allocator_field; add_entity(ctx, scope, nullptr, allocator_field); add_entity_use(ctx, nullptr, allocator_field); diff --git a/src/threading.cpp b/src/threading.cpp index c283da425..b8bc9b118 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -119,17 +119,25 @@ struct MutexGuard { explicit MutexGuard(RecursiveMutex *rm) noexcept : rm{rm} { mutex_lock(this->rm); } + explicit MutexGuard(RwMutex *rm) noexcept : rwm{rwm} { + rw_mutex_lock(this->rwm); + } explicit MutexGuard(BlockingMutex &bm) noexcept : bm{&bm} { mutex_lock(this->bm); } explicit MutexGuard(RecursiveMutex &rm) noexcept : rm{&rm} { mutex_lock(this->rm); } + explicit MutexGuard(RwMutex &rwm) noexcept : rwm{&rwm} { + rw_mutex_lock(this->rwm); + } ~MutexGuard() noexcept { if (this->bm) { mutex_unlock(this->bm); } else if (this->rm) { mutex_unlock(this->rm); + } else if (this->rwm) { + rw_mutex_unlock(this->rwm); } } @@ -137,10 +145,12 @@ struct MutexGuard { BlockingMutex *bm; RecursiveMutex *rm; + RwMutex *rwm; }; #define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m}) #define MUTEX_GUARD(m) mutex_lock(m); defer (mutex_unlock(m)) +#define RW_MUTEX_GUARD(m) rw_mutex_lock(m); defer (rw_mutex_unlock(m)) struct RecursiveMutex { -- cgit v1.2.3 From d496dbf3a0ee05819ab6e802939b4219cfa9c7fe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Feb 2024 16:54:41 +0000 Subject: Fix race condition with #soa --- src/check_type.cpp | 6 ++---- src/threading.cpp | 16 ++++++++++++++++ src/types.cpp | 17 ++++++----------- 3 files changed, 24 insertions(+), 15 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 66f8b1185..8a140d95e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -632,9 +632,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * scope_reserve(ctx->scope, min_field_count); - rw_mutex_lock(&struct_type->Struct.fields_mutex); - defer (rw_mutex_unlock(&struct_type->Struct.fields_mutex)); - if (st->is_raw_union && min_field_count > 1) { struct_type->Struct.is_raw_union = true; context = str_lit("struct #raw_union"); @@ -662,6 +659,7 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * gb_unused(where_clause_ok); } check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); + wait_signal_set(&struct_type->Struct.fields_wait_signal); } #define ST_ALIGN(_name) if (st->_name != nullptr) { \ @@ -2553,8 +2551,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e GB_ASSERT(is_type_struct(elem)); Type *old_struct = base_type(elem); - RW_MUTEX_GUARD(&old_struct->Struct.fields_mutex); + wait_signal_until_available(&old_struct->Struct.fields_wait_signal); field_count = old_struct->Struct.fields.count; soa_struct = alloc_type_struct(); diff --git a/src/threading.cpp b/src/threading.cpp index b8bc9b118..731394126 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -107,6 +107,22 @@ gb_internal void thread_set_name (Thread *t, char const *name); gb_internal void yield_thread(void); gb_internal void yield_process(void); +struct Wait_Signal { + Futex futex; +}; + +gb_internal void wait_signal_until_available(Wait_Signal *ws) { + if (ws->futex.load() == 0) { + futex_wait(&ws->futex, 1); + } +} + +gb_internal void wait_signal_set(Wait_Signal *ws) { + ws->futex.store(1); + futex_broadcast(&ws->futex); +} + + struct MutexGuard { MutexGuard() = delete; diff --git a/src/types.cpp b/src/types.cpp index 04fb06582..2f1994574 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -144,7 +144,7 @@ struct TypeStruct { Type * soa_elem; i32 soa_count; StructSoaKind soa_kind; - RwMutex fields_mutex; + Wait_Signal fields_wait_signal; BlockingMutex offset_mutex; // for settings offsets bool is_polymorphic; @@ -2969,9 +2969,8 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { isize max_count = 0; switch (type->kind) { case Type_Struct: - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); max_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); break; case Type_Tuple: max_count = type->Tuple.variables.count; break; } @@ -2982,8 +2981,7 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { switch (type->kind) { case Type_Struct: { - rw_mutex_shared_lock(&type->Struct.fields_mutex); - defer (rw_mutex_shared_unlock(&type->Struct.fields_mutex)); + wait_signal_until_available(&type->Struct.fields_wait_signal); for (isize i = 0; i < max_count; i++) { Entity *f = type->Struct.fields[i]; if (f->kind == Entity_Variable) { @@ -3048,9 +3046,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } if (type->kind == Type_Struct) { - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); isize field_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->flags&EntityFlag_Using) { @@ -3079,9 +3076,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } if (type->kind == Type_Struct) { - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); Scope *s = type->Struct.scope; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (s != nullptr) { Entity *found = scope_lookup_current(s, field_name); if (found != nullptr && found->kind != Entity_Variable) { @@ -3129,9 +3125,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); isize field_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { -- cgit v1.2.3 From a4b8c1ea1779ce93349b203aaf56c5aeca316b61 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 15:55:20 +0000 Subject: Begin work adding `bit_field` --- base/runtime/core.odin | 9 +++ base/runtime/print.odin | 14 ++++ core/encoding/json/marshal.odin | 3 + core/fmt/fmt.odin | 65 +++++++++++++++++ core/reflect/reflect.odin | 10 +++ core/reflect/types.odin | 31 ++++++++ src/check_type.cpp | 152 ++++++++++++++++++++++++++++++++++++++++ src/checker.cpp | 18 +++++ src/llvm_backend.cpp | 8 ++- src/llvm_backend_debug.cpp | 36 ++++++++++ src/llvm_backend_general.cpp | 4 +- src/llvm_backend_type.cpp | 67 ++++++++++++++++++ src/parser.cpp | 78 +++++++++++++++++++++ src/parser.hpp | 15 ++++ src/parser_pos.cpp | 3 + src/types.cpp | 24 +++++++ 16 files changed, 535 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 85e64242d..dcc1e7476 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -181,6 +181,13 @@ Type_Info_Matrix :: struct { Type_Info_Soa_Pointer :: struct { elem: ^Type_Info, } +Type_Info_Bit_Field :: struct { + backing_type: ^Type_Info, + names: []string, + types: []^Type_Info, + bit_sizes: []uintptr, + bit_offsets: []uintptr, +} Type_Info_Flag :: enum u8 { Comparable = 0, @@ -223,6 +230,7 @@ Type_Info :: struct { Type_Info_Relative_Multi_Pointer, Type_Info_Matrix, Type_Info_Soa_Pointer, + Type_Info_Bit_Field, }, } @@ -256,6 +264,7 @@ Typeid_Kind :: enum u8 { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } #assert(len(Typeid_Kind) < 32) diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 41ff9e1bb..c93c2ab49 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) { } print_byte(']') + case Type_Info_Bit_Field: + print_string("bit_field ") + print_type(info.backing_type) + print_string(" {") + for name, i in info.names { + if i > 0 { print_string(", ") } + print_string(name) + print_string(": ") + print_type(info.types[i]) + print_string(" | ") + print_u64(u64(info.bit_sizes[i])) + } + print_byte('}') + case Type_Info_Simd_Vector: print_string("#simd[") diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index e9285364b..e237892c3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -228,6 +228,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Matrix: return .Unsupported_Type + case runtime.Type_Info_Bit_Field: + return .Unsupported_Type + case runtime.Type_Info_Array: opt_write_start(w, opt, '[') or_return for i in 0.. (res: u64) { + for i in 0.. 0 { + io.write_string(fi.writer, ", ") + } + if hash { + fmt_write_indent(fi) + } + + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) + + + bit_offset := info.bit_offsets[i] + bit_size := info.bit_sizes[i] + + value := read_bits(([^]byte)(v.data), bit_offset, bit_size) + + fmt_value(fi, any{&value, info.types[i].id}, verb) + if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } + + } +} + + + // Formats a value based on its type and formatting verb // // Inputs: @@ -2611,6 +2673,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Matrix: fmt_matrix(fi, v, verb, info) + + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v, verb, info) } } // Formats a complex number based on the given formatting verb diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 0af23b18e..de5dec2e3 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -35,6 +35,7 @@ Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer +Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value @@ -70,6 +71,7 @@ Type_Kind :: enum { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } @@ -106,6 +108,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer case Type_Info_Matrix: return .Matrix case Type_Info_Soa_Pointer: return .Soa_Pointer + case Type_Info_Bit_Field: return .Bit_Field } } @@ -1604,6 +1607,13 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ } } return true + + case Type_Info_Bit_Field: + x, y := a, b + x.id = v.backing_type.id + y.id = v.backing_type.id + return equal(x, y, including_indirect_array_recursion, recursion_level+0) + } runtime.print_typeid(a.id) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index cbe108d82..2b96dd4fb 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -174,6 +174,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { if x.row_count != y.row_count { return false } if x.column_count != y.column_count { return false } return are_types_identical(x.elem, y.elem) + + case Type_Info_Bit_Field: + y := b.variant.(Type_Info_Bit_Field) or_return + if !are_types_identical(x.backing_type, y.backing_type) { return false } + if len(x.names) != len(y.names) { return false } + for _, i in x.names { + if x.names[i] != y.names[i] { + return false + } + if !are_types_identical(x.types[i], y.types[i]) { + return false + } + if x.bit_sizes[i] != y.bit_sizes[i] { + return false + } + } + return true } return false @@ -639,6 +656,20 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - } io.write_byte(w, ']', &n) or_return + case Type_Info_Bit_Field: + io.write_string(w, "bit_field ", &n) or_return + write_type(w, info.backing_type, &n) or_return + io.write_string(w, " {", &n) or_return + for name, i in info.names { + if i > 0 { io.write_string(w, ", ", &n) or_return } + io.write_string(w, name, &n) or_return + io.write_string(w, ": ", &n) or_return + write_type(w, info.types[i], &n) or_return + io.write_string(w, " | ", &n) or_return + io.write_u64(w, u64(info.bit_sizes[i]), 10, &n) or_return + } + io.write_string(w, "}", &n) or_return + case Type_Info_Simd_Vector: io.write_string(w, "#simd[", &n) or_return io.write_i64(w, i64(info.count), 10, &n) or_return diff --git a/src/check_type.cpp b/src/check_type.cpp index 8a140d95e..8afac2fc5 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -925,6 +925,144 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam enum_type->Enum.max_value_index = max_value_index; } +gb_internal bool is_valid_bit_field_backing_type(Type *type) { + if (type == nullptr) { + return nullptr; + } + type = base_type(type); + if (is_type_untyped(type)) { + return false; + } + if (is_type_integer(type)) { + return true; + } + if (type->kind == Type_Array) { + return is_type_integer(type->Array.elem); + } + return false; +} + +gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) { + ast_node(bf, BitFieldType, node); + GB_ASSERT(is_type_bit_field(bit_field_type)); + + Type *backing_type = check_type(ctx, bf->backing_type); + if (backing_type == nullptr || !is_valid_bit_field_backing_type(backing_type)) { + error(node, "Backing type for a bit_field must be an integer or an array of an integer"); + return; + } + + bit_field_type->BitField.backing_type = backing_type; + bit_field_type->BitField.scope = ctx->scope; + + auto fields = array_make(permanent_allocator(), 0, bf->fields.count); + auto bit_sizes = array_make (permanent_allocator(), 0, bf->fields.count); + + u64 maximum_bit_size = 8 * type_size_of(backing_type); + u64 total_bit_size = 0; + + for_array(i, bf->fields) { + i32 field_src_index = cast(i32)i; + Ast *field = bf->fields[i]; + if (field->kind != Ast_BitFieldField) { + error(field, "Invalid AST for a bit_field"); + continue; + } + ast_node(f, BitFieldField, field); + if (f->name == nullptr || f->name->kind != Ast_Ident) { + error(field, "A bit_field's field name must be an identifier"); + continue; + } + CommentGroup *docs = f->docs; + CommentGroup *comment = f->comment; + + String name = f->name->Ident.token.string; + + if (f->type == nullptr) { + error(field, "A bit_field's field must have a type"); + continue; + } + + Type *type = check_type(ctx, f->type); + if (type_size_of(type) > 8) { + error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type)); + } + + if (is_type_untyped(type)) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s); + gb_string_free(s); + } else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s); + gb_string_free(s); + } + + if (f->bit_size == nullptr) { + error(field, "A bit_field's field must have a specified bit size"); + continue; + } + + + Operand o = {}; + check_expr(ctx, &o, f->bit_size); + if (o.mode != Addressing_Constant) { + error(f->bit_size, "A bit_field's specified bit size must be a constant"); + o.mode = Addressing_Invalid; + } + if (o.value.kind == ExactValue_Float) { + o.value = exact_value_to_integer(o.value); + } + + ExactValue bit_size = o.value; + + if (bit_size.kind != ExactValue_Integer) { + gbString s = expr_to_string(f->bit_size); + error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s); + gb_string_free(s); + } + + if (scope_lookup_current(ctx->scope, name) != nullptr) { + error(f->name, "'%.*s' is already declared in this bit_field", LIT(name)); + } else { + i64 bit_size_i64 = exact_value_to_i64(bit_size); + u8 bit_size_u8 = 0; + if (bit_size_i64 <= 0) { + error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64); + bit_size_i64 = 1; + } + if (bit_size_i64 > 64) { + 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; + } + 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; + + add_entity(ctx, ctx->scope, nullptr, e); + array_add(&fields, e); + array_add(&bit_sizes, bit_size_u8); + add_entity_use(ctx, field, e); + } + } + + GB_ASSERT(fields.count <= bf->fields.count); + + 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", + cast(unsigned long long)total_bit_size, + s, + cast(unsigned long long)maximum_bit_size); + gb_string_free(s); + } + + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); +} + gb_internal bool is_type_valid_bit_set_range(Type *t) { if (is_type_integer(t)) { return true; @@ -3051,6 +3189,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T return true; case_end; + case_ast_node(bf, BitFieldType, e); + bool ips = ctx->in_polymorphic_specialization; + defer (ctx->in_polymorphic_specialization = ips); + ctx->in_polymorphic_specialization = false; + + *type = alloc_type_bit_field(); + set_base_type(named_type, *type); + check_open_scope(ctx, e); + check_bit_field_type(ctx, *type, named_type, e); + check_close_scope(ctx); + (*type)->BitField.node = e; + return true; + case_end; + case_ast_node(pt, ProcType, e); bool ips = ctx->in_polymorphic_specialization; diff --git a/src/checker.cpp b/src/checker.cpp index 569a3c76f..5827fc695 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -313,6 +313,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) { case Ast_StructType: node->StructType.scope = scope; break; case Ast_UnionType: node->UnionType.scope = scope; break; case Ast_EnumType: node->EnumType.scope = scope; break; + case Ast_BitFieldType: node->BitFieldType.scope = scope; break; default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); } } @@ -334,6 +335,7 @@ gb_internal Scope *scope_of_node(Ast *node) { case Ast_StructType: return node->StructType.scope; case Ast_UnionType: return node->UnionType.scope; case Ast_EnumType: return node->EnumType.scope; + case Ast_BitFieldType: return node->BitFieldType.scope; } GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); return nullptr; @@ -355,6 +357,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) { case Ast_EnumType: case Ast_UnionType: case Ast_BitSetType: + case Ast_BitFieldType: scope->flags |= ScopeFlag_Type; break; } @@ -2060,6 +2063,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_type_info_type_internal(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_type_info_type_internal(c, f->type); + } + break; case Type_Generic: break; @@ -2309,6 +2318,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_min_dep_type_info(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_min_dep_type_info(c, f->type); + } + break; + default: GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind])); break; @@ -2907,6 +2923,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); + t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -2936,6 +2953,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); + t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); } gb_internal void init_mem_allocator(Checker *c) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fa76ac22f..45d903b43 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2719,6 +2719,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { { // Type info member buffer // NOTE(bill): Removes need for heap allocation by making it global memory isize count = 0; + isize offsets_extra = 0; for (Type *t : m->info->type_info_types) { isize index = lb_type_info_index(m->info, t, false); @@ -2736,6 +2737,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { case Type_Tuple: count += t->Tuple.variables.count; break; + case Type_BitField: + count += t->BitField.fields.count; + // Twice is needed for the bit_offsets + offsets_extra += t->BitField.fields.count; + break; } } @@ -2752,7 +2758,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count); lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count); - lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count); + lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count+offsets_extra); lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count); lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count); } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index f45cf0cbc..7d3692a53 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -461,6 +461,42 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { lb_debug_type(m, type->Matrix.elem), subscripts, gb_count_of(subscripts)); } + + case Type_BitField: { + LLVMMetadataRef parent_scope = nullptr; + LLVMMetadataRef scope = nullptr; + LLVMMetadataRef file = nullptr; + unsigned line = 0; + u64 size_in_bits = 8*cast(u64)type_size_of(type); + u32 align_in_bits = 8*cast(u32)type_align_of(type); + LLVMDIFlags flags = LLVMDIFlagZero; + + unsigned element_count = cast(unsigned)type->BitField.fields.count; + LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); + + u64 offset_in_bits = 0; + for (unsigned i = 0; i < element_count; i++) { + Entity *f = type->BitField.fields[i]; + u8 bit_size = type->BitField.bit_sizes[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + unsigned field_line = 0; + LLVMDIFlags field_flags = LLVMDIFlagZero; + elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line, + bit_size, offset_in_bits, offset_in_bits, + field_flags, lb_debug_type(m, f->type) + ); + + offset_in_bits += bit_size; + } + + + return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line, + size_in_bits, align_in_bits, flags, + nullptr, elements, element_count, 0, nullptr, + "", 0 + ); + } } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f0f5327c6..2102420f8 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2216,7 +2216,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } return LLVMStructTypeInContext(ctx, fields, field_count, false); } - + + case Type_BitField: + return lb_type_internal(m, type->BitField.backing_type); } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e291e40a5..3567a550b 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -1788,6 +1788,73 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lb_emit_store(p, tag, res); } break; + + case Type_BitField: + { + tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_field_ptr); + LLVMValueRef vals[5] = {}; + + vals[0] = lb_type_info(m, t->BitField.backing_type).value; + isize count = t->BitField.fields.count; + if (count > 0) { + i64 names_offset = 0; + i64 types_offset = 0; + i64 bit_sizes_offset = 0; + i64 bit_offsets_offset = 0; + lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset); + lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset); + lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset); + lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset); + + u64 bit_offset = 0; + for (isize source_index = 0; source_index < count; source_index++) { + Entity *f = t->BitField.fields[source_index]; + u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index]; + + lbValue index = lb_const_int(m, t_int, source_index); + if (f->token.string.len > 0) { + lbValue name = lb_emit_ptr_offset(p, memory_names, index); + lb_emit_store(p, name, lb_const_string(m, f->token.string)); + } + lbValue type_ptr = lb_emit_ptr_offset(p, memory_types, index); + lbValue bit_size_ptr = lb_emit_ptr_offset(p, memory_bit_sizes, index); + lbValue bit_offset_ptr = lb_emit_ptr_offset(p, memory_bit_offsets, index); + + lb_emit_store(p, type_ptr, lb_type_info(m, f->type)); + lb_emit_store(p, bit_size_ptr, lb_const_int(m, t_uintptr, bit_size)); + lb_emit_store(p, bit_offset_ptr, lb_const_int(m, t_uintptr, bit_offset)); + + // lb_global_type_info_member_types_values [types_offset +source_index] = get_type_info_ptr(m, f->type); + // lb_global_type_info_member_offsets_values[bit_sizes_offset +source_index] = lb_const_int(m, t_uintptr, bit_size).value; + // lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value; + // if (f->token.string.len > 0) { + // lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value; + // } + + bit_offset += bit_size; + } + + lbValue cv = lb_const_int(m, t_int, count); + vals[1] = llvm_const_slice(m, memory_names, cv); + vals[2] = llvm_const_slice(m, memory_types, cv); + vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); + vals[4] = llvm_const_slice(m, memory_bit_offsets, cv); + } + + for (isize i = 0; i < gb_count_of(vals); i++) { + if (vals[i] == nullptr) { + vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); + } + } + + lbValue res = {}; + res.type = type_deref(tag.type); + res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); + lb_emit_store(p, tag, res); + + break; + } + } diff --git a/src/parser.cpp b/src/parser.cpp index 78ac29dfd..70da9414d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -350,6 +350,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->Field.names = clone_ast_array(n->Field.names, f); n->Field.type = clone_ast(n->Field.type, f); break; + case Ast_BitFieldField: + n->BitFieldField.name = clone_ast(n->BitFieldField.name, f); + n->BitFieldField.type = clone_ast(n->BitFieldField.type, f); + n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f); + break; case Ast_FieldList: n->FieldList.list = clone_ast_array(n->FieldList.list, f); break; @@ -406,6 +411,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->BitSetType.elem = clone_ast(n->BitSetType.elem, f); n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f); break; + case Ast_BitFieldType: + n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f); + n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f); + break; case Ast_MapType: n->MapType.count = clone_ast(n->MapType.count, f); n->MapType.key = clone_ast(n->MapType.key, f); @@ -1045,6 +1054,17 @@ gb_internal Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast return result; } +gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, + CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_BitFieldField); + result->BitFieldField.name = name; + result->BitFieldField.type = type; + result->BitFieldField.bit_size = bit_size; + result->BitFieldField.docs = docs; + result->BitFieldField.comment = comment; + return result; +} + gb_internal Ast *ast_field_list(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_FieldList); result->FieldList.token = token; @@ -1178,6 +1198,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under return result; } +gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array const &fields, Token close) { + Ast *result = alloc_ast_node(f, Ast_BitFieldType); + result->BitFieldType.token = token; + result->BitFieldType.backing_type = backing_type; + result->BitFieldType.open = open; + result->BitFieldType.fields = slice_from_array(fields); + result->BitFieldType.close = close; + return result; +} + + gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *result = alloc_ast_node(f, Ast_MapType); result->MapType.token = token; @@ -2549,6 +2580,53 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_matrix_type(f, token, row_count, column_count, type); } break; + case Token_bit_field: { + Token token = expect_token(f, Token_bit_field); + isize prev_level; + + prev_level = f->expr_level; + f->expr_level = -1; + + Ast *backing_type = parse_type_or_ident(f); + if (backing_type == nullptr) { + Token token = advance_token(f); + syntax_error(token, "Expected a backing type for a 'bit_field'"); + backing_type = ast_bad_expr(f, token, f->curr_token); + } + + skip_possible_newline_for_literal(f); + Token open = expect_token_after(f, Token_OpenBrace, "bit_field"); + + + auto fields = array_make(ast_allocator(f), 0, 0); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = nullptr; + CommentGroup *comment = nullptr; + + Ast *name = parse_ident(f); + expect_token(f, Token_Colon); + Ast *type = parse_type(f); + expect_token(f, Token_Or); + Ast *bit_size = parse_expr(f, true); + + Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, docs, comment); + array_add(&fields, bf_field); + + if (!allow_field_separator(f)) { + break; + } + } + + Token close = expect_closing_brace_of_field_list(f); + + f->expr_level = prev_level; + + return ast_bit_field_type(f, token, backing_type, open, fields, close); + } + + case Token_struct: { Token token = expect_token(f, Token_struct); Ast *polymorphic_params = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index 1edb1f9dd..ff77c88c7 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -650,6 +650,13 @@ AST_KIND(_DeclEnd, "", bool) \ CommentGroup * docs; \ CommentGroup * comment; \ }) \ + AST_KIND(BitFieldField, "bit field field", struct { \ + Ast * name; \ + Ast * type; \ + Ast * bit_size; \ + CommentGroup *docs; \ + CommentGroup *comment; \ + }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ Slice list; \ @@ -742,6 +749,14 @@ AST_KIND(_TypeBegin, "", bool) \ Ast * elem; \ Ast * underlying; \ }) \ + AST_KIND(BitFieldType, "bit field type", struct { \ + Scope *scope; \ + Token token; \ + Ast * backing_type; \ + Token open; \ + Slice fields; /* BitFieldField */ \ + Token close; \ + }) \ AST_KIND(MapType, "map type", struct { \ Token token; \ Ast *count; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index f49c40f16..b2e12999b 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) { case Ast_UnionType: return node->UnionType.token; case Ast_EnumType: return node->EnumType.token; case Ast_BitSetType: return node->BitSetType.token; + case Ast_BitFieldType: return node->BitFieldType.token; case Ast_MapType: return node->MapType.token; case Ast_MatrixType: return node->MatrixType.token; } @@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) { return ast_end_token(node->BitSetType.underlying); } return ast_end_token(node->BitSetType.elem); + case Ast_BitFieldType: + return node->BitFieldType.close; case Ast_MapType: return ast_end_token(node->MapType.value); case Ast_MatrixType: return ast_end_token(node->MatrixType.elem); } diff --git a/src/types.cpp b/src/types.cpp index 78d281715..1c28e6583 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -282,6 +282,13 @@ struct TypeProc { Type *generic_column_count; \ i64 stride_in_bytes; \ }) \ + TYPE_KIND(BitField, struct { \ + Scope * scope; \ + Type * backing_type; \ + Slice fields; \ + Slice bit_sizes; \ + Ast * node; \ + }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -355,6 +362,7 @@ enum Typeid_Kind : u8 { Typeid_Relative_Multi_Pointer, Typeid_Matrix, Typeid_SoaPointer, + Typeid_Bit_Field, }; // IMPORTANT NOTE(bill): This must match the same as the in core.odin @@ -641,6 +649,7 @@ gb_global Type *t_type_info_relative_pointer = nullptr; gb_global Type *t_type_info_relative_multi_pointer = nullptr; gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr; +gb_global Type *t_type_info_bit_field = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -670,6 +679,7 @@ gb_global Type *t_type_info_relative_pointer_ptr = nullptr; gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr; +gb_global Type *t_type_info_bit_field_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -1040,6 +1050,11 @@ gb_internal Type *alloc_type_enum() { return t; } +gb_internal Type *alloc_type_bit_field() { + Type *t = alloc_type(Type_BitField); + return t; +} + gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) { GB_ASSERT(is_type_pointer(pointer_type)); GB_ASSERT(is_type_integer(base_integer)); @@ -1707,6 +1722,10 @@ gb_internal bool is_type_bit_set(Type *t) { t = base_type(t); return (t->kind == Type_BitSet); } +gb_internal bool is_type_bit_field(Type *t) { + t = base_type(t); + return (t->kind == Type_BitField); +} gb_internal bool is_type_map(Type *t) { t = base_type(t); return t->kind == Type_Map; @@ -3568,6 +3587,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Slice: return build_context.int_size; + case Type_BitField: + return type_align_of_internal(t->BitField.backing_type, path); case Type_Tuple: { i64 max = 1; @@ -3943,6 +3964,9 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { return stride_in_bytes * t->Matrix.column_count; } + case Type_BitField: + return type_size_of_internal(t->BitField.backing_type, path); + case Type_RelativePointer: return type_size_of_internal(t->RelativePointer.base_integer, path); case Type_RelativeMultiPointer: -- 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_type.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 59479b2ba622a0c8ee528eabd84c99c955bc0ea8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 17:26:32 +0000 Subject: Fix typo --- src/check_type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 8c746a2f7..41eae2178 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -927,7 +927,7 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam gb_internal bool is_valid_bit_field_backing_type(Type *type) { if (type == nullptr) { - return nullptr; + return false; } type = base_type(type); if (is_type_untyped(type)) { -- cgit v1.2.3 From c14b9d461a5c58d4b80957682f00205714063435 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 19:14:16 +0000 Subject: Support `using` of a `bit_field` within a `struct` --- src/check_type.cpp | 2 ++ src/llvm_backend_expr.cpp | 14 +++++++-- src/llvm_backend_utility.cpp | 2 +- src/types.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 6 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 41eae2178..74828f97f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -89,6 +89,8 @@ gb_internal bool does_field_type_allow_using(Type *t) { return true; } else if (is_type_array(t)) { return t->Array.count <= 4; + } else if (is_type_bit_field(t)) { + return true; } return false; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7e000c9e8..5bf2642e6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4679,12 +4679,20 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { 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; + + Type *bf_type = type_from_selection(type, sub_sel); + bf_type = base_type(type_deref(bf_type)); + GB_ASSERT(bf_type->kind == Type_BitField); + + lbValue a = lb_addr_get_ptr(p, addr); + if (sub_sel.index.count > 0) { + a = lb_emit_deep_field_gep(p, a, sub_sel); + } + i32 index = sel.index[sel.index.count-1]; Entity *f = bf_type->BitField.fields[index]; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index bc5106601..5bd3cd8e2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1332,7 +1332,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection if (index == 0) { type = t_rawptr; } else if (index == 1) { - type = t_type_info_ptr; + type = t_typeid; } e = lb_emit_struct_ep(p, e, index); break; diff --git a/src/types.cpp b/src/types.cpp index be4b8944b..3945c7111 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -385,6 +385,9 @@ enum : int { gb_internal bool is_type_comparable(Type *t); gb_internal bool is_type_simple_compare(Type *t); +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false); +gb_internal Type *base_type(Type *t); +gb_internal Type *alloc_type_multi_pointer(Type *elem); gb_internal u32 type_info_flags_of_type(Type *type) { if (type == nullptr) { @@ -762,7 +765,6 @@ gb_internal bool is_type_proc(Type *t); gb_internal bool is_type_slice(Type *t); gb_internal bool is_type_integer(Type *t); gb_internal bool type_set_offsets(Type *t); -gb_internal Type *base_type(Type *t); gb_internal i64 type_size_of_internal(Type *t, TypePath *path); gb_internal i64 type_align_of_internal(Type *t, TypePath *path); @@ -1157,7 +1159,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co //////////////////////////////////////////////////////////////// -gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) { +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) { if (t != nullptr) { Type *bt = base_type(t); if (bt == nullptr) { @@ -4261,6 +4263,71 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ } +gb_internal Type *type_from_selection(Type *type, Selection const &sel) { + for (i32 index : sel.index) { + Type *bt = base_type(type_deref(type)); + switch (bt->kind) { + case Type_Struct: + type = bt->Struct.fields[index]->type; + break; + case Type_Tuple: + type = bt->Tuple.variables[index]->type; + break; + case Type_BitField: + type = bt->BitField.fields[index]->type; + break; + case Type_Array: + type = bt->Array.elem; + break; + case Type_EnumeratedArray: + type = bt->Array.elem; + break; + case Type_Slice: + switch (index) { + case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break; + case 1: type = t_int; break; + } + break; + case Type_DynamicArray: + switch (index) { + case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break; + case 1: type = t_int; break; + case 2: type = t_int; break; + case 3: type = t_allocator; break; + } + break; + case Type_Map: + switch (index) { + case 0: type = t_uintptr; break; + case 1: type = t_int; break; + case 2: type = t_allocator; break; + } + break; + case Type_Basic: + if (is_type_complex_or_quaternion(bt)) { + type = base_complex_elem_type(bt); + } else { + switch (type->Basic.kind) { + case Basic_any: + switch (index) { + case 0: type = t_rawptr; break; + case 1: type = t_typeid; break; + } + break; + case Basic_string: + switch (index) { + case 0: type = t_u8_multi_ptr; break; + case 1: type = t_int; break; + } + break; + } + } + break; + } + } + return type; +} + gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { -- cgit v1.2.3 From 54515af8ccff67cae71982d1bbf5bd1c31628af3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 19:41:48 +0000 Subject: Add field tags to `bit_field` --- base/runtime/core.odin | 1 + core/fmt/fmt.odin | 25 +++++++++++++++++++++++-- src/check_type.cpp | 10 ++++++++++ src/llvm_backend_type.cpp | 34 +++++++++++++++++++--------------- src/parser.cpp | 10 ++++++++-- src/parser.hpp | 1 + src/types.cpp | 1 + 7 files changed, 63 insertions(+), 19 deletions(-) (limited to 'src/check_type.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index dcc1e7476..2f63a7ac2 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -187,6 +187,7 @@ Type_Info_Bit_Field :: struct { types: []^Type_Info, bit_sizes: []uintptr, bit_offsets: []uintptr, + tags: []string, } Type_Info_Flag :: enum u8 { diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 51f158cd8..38e125c30 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2297,6 +2297,23 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit return } + handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) { + tag := info.tags[idx] + if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok { + value := strings.trim_space(string(vt)) + switch value { + case "": return false + case "-": return true + } + r, w := utf8.decode_rune_in_string(value) + value = value[w:] + if value == "" || value[0] == ',' { + verb^ = r + } + } + return false + } + io.write_string(fi.writer, "bit_field{", &fi.n) hash := fi.hash; defer fi.hash = hash @@ -2318,7 +2335,11 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit field_count := -1 for name, i in info.names { - _ = i + field_verb := verb + if handle_bit_field_tag(v.data, info, i, &field_verb) { + continue + } + field_count += 1 if !do_trailing_comma && field_count > 0 { @@ -2343,7 +2364,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit value = (value ~ m) - m } - fmt_value(fi, any{&value, type.id}, verb) + fmt_value(fi, any{&value, type.id}, field_verb) if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 74828f97f..1bcae140f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -959,6 +959,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, auto fields = array_make(permanent_allocator(), 0, bf->fields.count); auto bit_sizes = array_make (permanent_allocator(), 0, bf->fields.count); + auto tags = array_make (permanent_allocator(), 0, bf->fields.count); u64 maximum_bit_size = 8 * type_size_of(backing_type); u64 total_bit_size = 0; @@ -1054,6 +1055,14 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, add_entity(ctx, ctx->scope, nullptr, e); array_add(&fields, e); array_add(&bit_sizes, bit_size_u8); + + String tag = f->tag.string; + if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) { + error(f->tag, "Invalid string literal"); + tag = {}; + } + array_add(&tags, tag); + add_entity_use(ctx, field, e); } } @@ -1080,6 +1089,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, 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; + bit_field_type->BitField.tags = tags.data; } gb_internal bool is_type_valid_bit_set_range(Type *t) { diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 3567a550b..4952d75de 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -1792,19 +1792,21 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup case Type_BitField: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_field_ptr); - LLVMValueRef vals[5] = {}; + LLVMValueRef vals[6] = {}; vals[0] = lb_type_info(m, t->BitField.backing_type).value; isize count = t->BitField.fields.count; if (count > 0) { - i64 names_offset = 0; - i64 types_offset = 0; - i64 bit_sizes_offset = 0; + i64 names_offset = 0; + i64 types_offset = 0; + i64 bit_sizes_offset = 0; i64 bit_offsets_offset = 0; + i64 tags_offset = 0; lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset); lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset); lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset); lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset); + lbValue memory_tags = lb_type_info_member_tags_offset (m, count, &tags_offset); u64 bit_offset = 0; for (isize source_index = 0; source_index < count; source_index++) { @@ -1813,8 +1815,8 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lbValue index = lb_const_int(m, t_int, source_index); if (f->token.string.len > 0) { - lbValue name = lb_emit_ptr_offset(p, memory_names, index); - lb_emit_store(p, name, lb_const_string(m, f->token.string)); + lbValue name_ptr = lb_emit_ptr_offset(p, memory_names, index); + lb_emit_store(p, name_ptr, lb_const_string(m, f->token.string)); } lbValue type_ptr = lb_emit_ptr_offset(p, memory_types, index); lbValue bit_size_ptr = lb_emit_ptr_offset(p, memory_bit_sizes, index); @@ -1824,21 +1826,23 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lb_emit_store(p, bit_size_ptr, lb_const_int(m, t_uintptr, bit_size)); lb_emit_store(p, bit_offset_ptr, lb_const_int(m, t_uintptr, bit_offset)); - // lb_global_type_info_member_types_values [types_offset +source_index] = get_type_info_ptr(m, f->type); - // lb_global_type_info_member_offsets_values[bit_sizes_offset +source_index] = lb_const_int(m, t_uintptr, bit_size).value; - // lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value; - // if (f->token.string.len > 0) { - // lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value; - // } + if (t->BitField.tags) { + String tag = t->BitField.tags[source_index]; + if (tag.len > 0) { + lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index); + lb_emit_store(p, tag_ptr, lb_const_string(m, tag)); + } + } bit_offset += bit_size; } lbValue cv = lb_const_int(m, t_int, count); - vals[1] = llvm_const_slice(m, memory_names, cv); - vals[2] = llvm_const_slice(m, memory_types, cv); - vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); + vals[1] = llvm_const_slice(m, memory_names, cv); + vals[2] = llvm_const_slice(m, memory_types, cv); + vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); vals[4] = llvm_const_slice(m, memory_bit_offsets, cv); + vals[5] = llvm_const_slice(m, memory_tags, cv); } for (isize i = 0; i < gb_count_of(vals); i++) { diff --git a/src/parser.cpp b/src/parser.cpp index 6a9481693..03d1e7aeb 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1054,12 +1054,13 @@ gb_internal Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast return result; } -gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, +gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_BitFieldField); result->BitFieldField.name = name; result->BitFieldField.type = type; result->BitFieldField.bit_size = bit_size; + result->BitFieldField.tag = tag; result->BitFieldField.docs = docs; result->BitFieldField.comment = comment; return result; @@ -2611,7 +2612,12 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { expect_token(f, Token_Or); Ast *bit_size = parse_expr(f, true); - Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, docs, comment); + Token tag = {}; + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + } + + Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment); array_add(&fields, bf_field); if (!allow_field_separator(f)) { diff --git a/src/parser.hpp b/src/parser.hpp index 1f4ec8726..f410419d4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -655,6 +655,7 @@ AST_KIND(_DeclEnd, "", bool) \ Ast * name; \ Ast * type; \ Ast * bit_size; \ + Token tag; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ diff --git a/src/types.cpp b/src/types.cpp index eac834f25..90cb130b6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -286,6 +286,7 @@ struct TypeProc { Scope * scope; \ Type * backing_type; \ Slice fields; \ + String * tags; /*count == fields.count*/ \ Slice bit_sizes; \ Slice bit_offsets; \ Ast * node; \ -- cgit v1.2.3 From 9ea11da00f65a7b69c2cdf55bc7625713e0bd374 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 20:10:56 +0000 Subject: Add warning when using `bit_field` when a `bit_set` would be a much better idea. --- src/check_type.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 1bcae140f..6e10798f3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1016,6 +1016,11 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, if (o.value.kind == ExactValue_Float) { o.value = exact_value_to_integer(o.value); } + if (f->bit_size->kind == Ast_BinaryExpr && f->bit_size->BinaryExpr.op.kind == Token_Or) { + gbString s = expr_to_string(f->bit_size); + error(f->bit_size, "Wrap the expression in parentheses, e.g. (%s)", s); + gb_string_free(s); + } ExactValue bit_size = o.value; @@ -1076,7 +1081,6 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, 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", @@ -1086,6 +1090,27 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, gb_string_free(s); } + if (bit_sizes.count > 0 && is_type_integer(backing_type)) { + bool all_booleans = is_type_boolean(fields[0]->type); + bool all_ones = bit_sizes[0] == 1; + if (all_ones && all_booleans) { + for_array(i, bit_sizes) { + all_ones = bit_sizes[i] == 1; + if (!all_ones) { + break; + } + all_booleans = is_type_boolean(fields[i]->type); + if (!all_booleans) { + break; + } + } + if (all_ones && all_booleans) { + warning(node, "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer"); + } + } + } + + 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; -- cgit v1.2.3 From 00fc4c4e1bfea6b61e8b869a61a4f0560b1931dd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 20:21:21 +0000 Subject: Make that warning an error with `-vet-style` --- src/check_type.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 6e10798f3..dd77031a3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1105,7 +1105,13 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, } } if (all_ones && all_booleans) { - warning(node, "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer"); + if (build_context.vet_flags & VetFlag_Style) { + char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)"; + error(node, msg); + } else { + char const *msg = "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer"; + warning(node, msg); + } } } } -- cgit v1.2.3 From 5cc936245c0ddc3863de89069757f5020dde2324 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 13 Mar 2024 19:50:41 +0000 Subject: Improve bit_set error message --- src/check_type.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index dd77031a3..e71b35809 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1272,7 +1272,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t } if (!is_valid) { if (actual_lower != lower) { - error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required); + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required); } else { error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required); } @@ -1342,7 +1342,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t if (upper - lower >= bits) { i64 bits_required = upper-lower+1; if (lower_changed) { - error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required); + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required); } else { error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required); } -- cgit v1.2.3 From 89315986d4e4fb0977d98c2cfb8003763f5b93cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 12:37:11 +0000 Subject: Add suggestion when mistyping an array backwards e.g. `T[]` --- src/check_type.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index e71b35809..da4479f6e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3344,8 +3344,25 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) if (!ok) { gbString err_str = expr_to_string(e); + defer (gb_string_free(err_str)); + + ERROR_BLOCK(); error(e, "'%s' is not a type", err_str); - gb_string_free(err_str); + + Ast *node = unparen_expr(e); + if (node && node->kind == Ast_IndexExpr) { + gbString index_str = nullptr; + if (node->IndexExpr.index) { + index_str = expr_to_string(node->IndexExpr.index); + } + defer (gb_string_free(index_str)); + + gbString type_str = expr_to_string(node->IndexExpr.expr); + defer (gb_string_free(type_str)); + + error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + } + type = t_invalid; } -- cgit v1.2.3 From d5daa9fda59e7b8ad8749f8a7630fb30f1f73a04 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:05:28 +0000 Subject: Minimize error propagation of bad array syntax by treating this like a type --- src/check_type.cpp | 220 ++++++++++++++++++++++++++++------------------------- 1 file changed, 116 insertions(+), 104 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index da4479f6e..5889cbcd0 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2834,6 +2834,111 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_ return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic); } +gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { + ast_node(at, ArrayType, e); + if (at->count != nullptr) { + Operand o = {}; + i64 count = check_array_count(ctx, &o, at->count); + Type *generic_type = nullptr; + + Type *elem = check_type_expr(ctx, at->elem, nullptr); + + if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { + generic_type = o.type; + } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { + Type *index = o.type; + Type *bt = base_type(index); + GB_ASSERT(bt->kind == Type_Enum); + + Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); + + bool is_sparse = false; + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "sparse") { + is_sparse = true; + } else { + error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); + } + } + + if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { + error(e, "Non-contiguous enumeration used as an index in an enumerated array"); + long long ea_count = cast(long long)t->EnumeratedArray.count; + long long enum_count = cast(long long)bt->Enum.fields.count; + error_line("\tenumerated array length: %lld\n", ea_count); + error_line("\tenum field count: %lld\n", enum_count); + error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); + if (2*enum_count < ea_count) { + error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); + error_line("\t this warning will be removed if #sparse is applied\n"); + } + } + t->EnumeratedArray.is_sparse = is_sparse; + + *type = t; + + return; + } + + if (count < 0) { + error(at->count, "? can only be used in conjuction with compound literals"); + count = 0; + } + + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); + } else if (name == "simd") { + if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { + gbString str = type_to_string(elem); + error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); + gb_string_free(str); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + if (generic_type != nullptr) { + // Ignore + } else if (count < 1 || !is_power_of_two(count)) { + error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + *type = alloc_type_simd_vector(count, elem, generic_type); + + if (count > SIMD_ELEMENT_COUNT_MAX) { + error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); + } + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_array(elem, count, generic_type); + } + } else { + *type = alloc_type_array(elem, count, generic_type); + } + } else { + Type *elem = check_type(ctx, at->elem); + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_slice(ctx, e, at->elem, elem); + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_slice(elem); + } + } else { + *type = alloc_type_slice(elem); + } + } +} gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { GB_ASSERT_NOT_NULL(type); if (e == nullptr) { @@ -3072,109 +3177,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_end; case_ast_node(at, ArrayType, e); - if (at->count != nullptr) { - Operand o = {}; - i64 count = check_array_count(ctx, &o, at->count); - Type *generic_type = nullptr; - - Type *elem = check_type_expr(ctx, at->elem, nullptr); - - if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { - generic_type = o.type; - } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { - Type *index = o.type; - Type *bt = base_type(index); - GB_ASSERT(bt->kind == Type_Enum); - - Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); - - bool is_sparse = false; - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "sparse") { - is_sparse = true; - } else { - error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); - } - } - - if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { - error(e, "Non-contiguous enumeration used as an index in an enumerated array"); - long long ea_count = cast(long long)t->EnumeratedArray.count; - long long enum_count = cast(long long)bt->Enum.fields.count; - error_line("\tenumerated array length: %lld\n", ea_count); - error_line("\tenum field count: %lld\n", enum_count); - error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); - if (2*enum_count < ea_count) { - error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); - error_line("\t this warning will be removed if #sparse is applied\n"); - } - } - t->EnumeratedArray.is_sparse = is_sparse; - - *type = t; - - goto array_end; - } - - if (count < 0) { - error(at->count, "? can only be used in conjuction with compound literals"); - count = 0; - } - - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); - } else if (name == "simd") { - if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { - gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); - gb_string_free(str); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - if (generic_type != nullptr) { - // Ignore - } else if (count < 1 || !is_power_of_two(count)) { - error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - *type = alloc_type_simd_vector(count, elem, generic_type); - - if (count > SIMD_ELEMENT_COUNT_MAX) { - error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); - } - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_array(elem, count, generic_type); - } - } else { - *type = alloc_type_array(elem, count, generic_type); - } - } else { - Type *elem = check_type(ctx, at->elem); - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_slice(ctx, e, at->elem, elem); - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_slice(elem); - } - } else { - *type = alloc_type_slice(elem); - } - } - array_end: + check_array_type_internal(ctx, e, type, named_type); set_base_type(named_type, *type); return true; case_end; @@ -3349,6 +3352,10 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) ERROR_BLOCK(); error(e, "'%s' is not a type", err_str); + type = t_invalid; + + + // NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N] Ast *node = unparen_expr(e); if (node && node->kind == Ast_IndexExpr) { gbString index_str = nullptr; @@ -3361,9 +3368,14 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) defer (gb_string_free(type_str)); error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + + // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type + if (node->IndexExpr.expr != nullptr) { + Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); + check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); + } } - type = t_invalid; } if (type == nullptr) { -- cgit v1.2.3 From 5159f30c9cec63e467e82ed76d54988f58e3bdcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:10:35 +0000 Subject: Fix error block handling --- src/check_type.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 5889cbcd0..d5cf187a4 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3349,7 +3349,7 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) gbString err_str = expr_to_string(e); defer (gb_string_free(err_str)); - ERROR_BLOCK(); + begin_error_block(); error(e, "'%s' is not a type", err_str); type = t_invalid; @@ -3368,14 +3368,16 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) defer (gb_string_free(type_str)); error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + end_error_block(); // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type if (node->IndexExpr.expr != nullptr) { Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); } + } else { + end_error_block(); } - } if (type == nullptr) { -- cgit v1.2.3 From a750fc0ba63c9f1461bba4cc0446b1b4c2d2b3a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 21:05:23 +0000 Subject: Add `#row_major matrix[R, C]T` As well as `#column_major matrix[R, C]T` as an alias for just `matrix[R, C]T`. This is because some libraries require a row_major internal layout but still want to be used with row or major oriented vectors. --- base/runtime/core.odin | 4 ++++ core/fmt/fmt.odin | 12 ++++++++++-- core/reflect/types.odin | 4 ++++ src/check_expr.cpp | 12 +++++++++--- src/check_type.cpp | 2 +- src/llvm_backend_const.cpp | 4 ++-- src/llvm_backend_expr.cpp | 39 +++++++++++++++++++++++++++++---------- src/llvm_backend_type.cpp | 3 ++- src/llvm_backend_utility.cpp | 22 +++++++++++++++------- src/parser.cpp | 13 +++++++++++++ src/parser.hpp | 1 + src/types.cpp | 19 +++++++++++++++---- 12 files changed, 105 insertions(+), 30 deletions(-) (limited to 'src/check_type.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 8f27ca674..7ad3ef1d6 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -177,6 +177,10 @@ Type_Info_Matrix :: struct { row_count: int, column_count: int, // Total element count = column_count * elem_stride + layout: enum u8 { + Column_Major, // array of column vectors + Row_Major, // array of row vectors + }, } Type_Info_Soa_Pointer :: struct { elem: ^Type_Info, diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 02803f882..6f9801bc8 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2396,7 +2396,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } - offset := (row + col*info.elem_stride)*info.elem_size + offset: int + switch info.layout { + case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size + case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size + } data := uintptr(v.data) + uintptr(offset) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) @@ -2410,7 +2414,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } - offset := (row + col*info.elem_stride)*info.elem_size + offset: int + switch info.layout { + case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size + case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size + } data := uintptr(v.data) + uintptr(offset) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2b96dd4fb..9cff46a00 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -173,6 +173,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { y := b.variant.(Type_Info_Matrix) or_return if x.row_count != y.row_count { return false } if x.column_count != y.column_count { return false } + if x.layout != y.layout { return false } return are_types_identical(x.elem, y.elem) case Type_Info_Bit_Field: @@ -689,6 +690,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - write_type(w, info.pointer, &n) or_return case Type_Info_Matrix: + if info.layout == .Row_Major { + io.write_string(w, "#row_major ", &n) or_return + } io.write_string(w, "matrix[", &n) or_return io.write_i64(w, i64(info.row_count), 10, &n) or_return io.write_string(w, ", ", &n) or_return diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f359d5a54..67c7f1a04 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3431,6 +3431,11 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand if (xt->Matrix.column_count != yt->Matrix.row_count) { goto matrix_error; } + + if (xt->Matrix.is_row_major != yt->Matrix.is_row_major) { + goto matrix_error; + } + x->mode = Addressing_Value; if (are_types_identical(xt, yt)) { if (!is_type_named(x->type) && is_type_named(y->type)) { @@ -3438,7 +3443,8 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand x->type = y->type; } } else { - x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + bool is_row_major = xt->Matrix.is_row_major && yt->Matrix.is_row_major; + x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count, nullptr, nullptr, is_row_major); } goto matrix_success; } else if (yt->kind == Type_Array) { @@ -3452,7 +3458,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand // Treat arrays as column vectors x->mode = Addressing_Value; - if (type_hint == nullptr && xt->Matrix.row_count == yt->Array.count) { + if (xt->Matrix.row_count == yt->Array.count) { x->type = y->type; } else { x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1); @@ -3483,7 +3489,7 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand // Treat arrays as row vectors x->mode = Addressing_Value; - if (type_hint == nullptr && yt->Matrix.column_count == xt->Array.count) { + if (yt->Matrix.column_count == xt->Array.count) { x->type = x->type; } else { x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count); diff --git a/src/check_type.cpp b/src/check_type.cpp index d5cf187a4..3fe633892 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2658,7 +2658,7 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) } type_assign:; - *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column); + *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column, mt->is_row_major); return; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 2291f24ac..bbb0b8387 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1302,11 +1302,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)total_count); - for_array(i, cl->elems) { TypeAndValue tav = cl->elems[i]->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - i64 offset = matrix_row_major_index_to_offset(type, i); + i64 offset = 0; + offset = matrix_row_major_index_to_offset(type, i); values[offset] = lb_const_value(m, elem_type, tav.value, allow_local).value; } for (isize i = 0; i < total_count; i++) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 98618798b..6eb8fdcc6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -684,12 +684,6 @@ gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type Type *mt = base_type(m.type); GB_ASSERT(mt->kind == Type_Matrix); - // TODO(bill): Determine why this fails on Windows sometimes - if (false && lb_is_matrix_simdable(mt)) { - LLVMValueRef vector = lb_matrix_to_trimmed_vector(p, m); - return lb_matrix_cast_vector_to_type(p, vector, type); - } - lbAddr res = lb_add_local_generated(p, type, true); i64 row_count = mt->Matrix.row_count; @@ -763,6 +757,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, GB_ASSERT(is_type_matrix(yt)); GB_ASSERT(xt->Matrix.column_count == yt->Matrix.row_count); GB_ASSERT(are_types_identical(xt->Matrix.elem, yt->Matrix.elem)); + GB_ASSERT(xt->Matrix.is_row_major == yt->Matrix.is_row_major); Type *elem = xt->Matrix.elem; @@ -770,7 +765,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, unsigned inner = cast(unsigned)xt->Matrix.column_count; unsigned outer_columns = cast(unsigned)yt->Matrix.column_count; - if (lb_is_matrix_simdable(xt)) { + if (!xt->Matrix.is_row_major && lb_is_matrix_simdable(xt)) { unsigned x_stride = cast(unsigned)matrix_type_stride_in_elems(xt); unsigned y_stride = cast(unsigned)matrix_type_stride_in_elems(yt); @@ -812,7 +807,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, return lb_addr_load(p, res); } - { + if (!xt->Matrix.is_row_major) { lbAddr res = lb_add_local_generated(p, type, true); auto inners = slice_make(permanent_allocator(), inner); @@ -835,6 +830,30 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, } } + return lb_addr_load(p, res); + } else { + lbAddr res = lb_add_local_generated(p, type, true); + + auto inners = slice_make(permanent_allocator(), inner); + + for (unsigned i = 0; i < outer_rows; i++) { + for (unsigned j = 0; j < outer_columns; j++) { + lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j); + for (unsigned k = 0; k < inner; k++) { + inners[k][0] = lb_emit_matrix_ev(p, lhs, i, k); + inners[k][1] = lb_emit_matrix_ev(p, rhs, k, j); + } + + lbValue sum = lb_const_nil(p->module, elem); + for (unsigned k = 0; k < inner; k++) { + lbValue a = inners[k][0]; + lbValue b = inners[k][1]; + sum = lb_emit_mul_add(p, a, b, sum, elem); + } + lb_emit_store(p, dst, sum); + } + } + return lb_addr_load(p, res); } } @@ -855,7 +874,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal Type *elem = mt->Matrix.elem; - if (lb_is_matrix_simdable(mt)) { + if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) { unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt); unsigned row_count = cast(unsigned)mt->Matrix.row_count; @@ -924,7 +943,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal Type *elem = mt->Matrix.elem; - if (lb_is_matrix_simdable(mt)) { + if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) { unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt); unsigned row_count = cast(unsigned)mt->Matrix.row_count; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index aec1fb201..de83f5058 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -979,12 +979,13 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ tag_type = t_type_info_matrix; i64 ez = type_size_of(t->Matrix.elem); - LLVMValueRef vals[5] = { + LLVMValueRef vals[6] = { get_type_info_ptr(m, t->Matrix.elem), lb_const_int(m, t_int, ez).value, lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value, lb_const_int(m, t_int, t->Matrix.row_count).value, lb_const_int(m, t_int, t->Matrix.column_count).value, + lb_const_int(m, t_u8, cast(u8)t->Matrix.is_row_major).value, }; variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index f18aa5521..fb61c41c3 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1464,14 +1464,16 @@ gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isi Type *t = s.type; GB_ASSERT(is_type_pointer(t)); Type *mt = base_type(type_deref(t)); - if (column == 0) { - GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt)); - return lb_emit_epi(p, s, row); - } else if (row == 0 && is_type_array_like(mt)) { - return lb_emit_epi(p, s, column); + + if (!mt->Matrix.is_row_major) { + if (column == 0) { + GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt)); + return lb_emit_epi(p, s, row); + } else if (row == 0 && is_type_array_like(mt)) { + return lb_emit_epi(p, s, column); + } } - GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt)); isize offset = matrix_indices_to_offset(mt, row, column); @@ -1491,7 +1493,13 @@ gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lb row = lb_emit_conv(p, row, t_int); column = lb_emit_conv(p, column, t_int); - LLVMValueRef index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), ""); + LLVMValueRef index = nullptr; + + if (mt->Matrix.is_row_major) { + index = LLVMBuildAdd(p->builder, column.value, LLVMBuildMul(p->builder, row.value, stride_elems, ""), ""); + } else { + index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), ""); + } LLVMValueRef indices[2] = { LLVMConstInt(lb_type(p->module, t_int), 0, false), diff --git a/src/parser.cpp b/src/parser.cpp index 1aa40ccbf..eb9e73342 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2329,6 +2329,19 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { break; } return original_type; + } else if (name.string == "row_major" || + name.string == "column_major") { + Ast *original_type = parse_type(f); + Ast *type = unparen_expr(original_type); + switch (type->kind) { + case Ast_MatrixType: + type->MatrixType.is_row_major = (name.string == "row_major"); + break; + default: + syntax_error(type, "Expected a matrix type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); + break; + } + return original_type; } else if (name.string == "partial") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_expr = parse_expr(f, lhs); diff --git a/src/parser.hpp b/src/parser.hpp index f5997c4bd..ff3c5eb34 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -772,6 +772,7 @@ AST_KIND(_TypeBegin, "", bool) \ Ast *row_count; \ Ast *column_count; \ Ast *elem; \ + bool is_row_major; \ }) \ AST_KIND(_TypeEnd, "", bool) diff --git a/src/types.cpp b/src/types.cpp index 5a3ad5d6b..ab5e4de03 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -281,6 +281,7 @@ struct TypeProc { Type *generic_row_count; \ Type *generic_column_count; \ i64 stride_in_bytes; \ + bool is_row_major; \ }) \ TYPE_KIND(BitField, struct { \ Scope * scope; \ @@ -1002,7 +1003,7 @@ gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = return t; } -gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr) { +gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr, bool is_row_major = false) { if (generic_row_count != nullptr || generic_column_count != nullptr) { Type *t = alloc_type(Type_Matrix); t->Matrix.elem = elem; @@ -1010,12 +1011,14 @@ gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, t->Matrix.column_count = column_count; t->Matrix.generic_row_count = generic_row_count; t->Matrix.generic_column_count = generic_column_count; + t->Matrix.is_row_major = is_row_major; return t; } Type *t = alloc_type(Type_Matrix); t->Matrix.elem = elem; t->Matrix.row_count = row_count; t->Matrix.column_count = column_count; + t->Matrix.is_row_major = is_row_major; return t; } @@ -1512,14 +1515,18 @@ gb_internal i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_inde GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count); GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count); i64 stride_elems = matrix_type_stride_in_elems(t); - // NOTE(bill): Column-major layout internally - return row_index + stride_elems*column_index; + if (t->Matrix.is_row_major) { + return column_index + stride_elems*row_index; + } else { + // NOTE(bill): Column-major layout internally + return row_index + stride_elems*column_index; + } } gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) { t = base_type(t); GB_ASSERT(t->kind == Type_Matrix); - + i64 row_index = index/t->Matrix.column_count; i64 column_index = index%t->Matrix.column_count; return matrix_indices_to_offset(t, row_index, column_index); @@ -2690,6 +2697,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Matrix: return x->Matrix.row_count == y->Matrix.row_count && x->Matrix.column_count == y->Matrix.column_count && + x->Matrix.is_row_major == y->Matrix.is_row_major && are_types_identical(x->Matrix.elem, y->Matrix.elem); case Type_DynamicArray: @@ -4735,6 +4743,9 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha break; case Type_Matrix: + if (type->Matrix.is_row_major) { + str = gb_string_appendc(str, "#row_major "); + } 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; -- cgit v1.2.3 From 29e5f94c2a0d666eed93a1013f895f3c86d6373f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 11:52:48 +0000 Subject: Add `#no_broadcast` procedure parameter to disallow automatic array programming broadcasting on procedure arguments --- base/runtime/core_builtin.odin | 12 +++---- core/image/common.odin | 36 ++++++++++---------- core/math/big/prime.odin | 2 +- core/odin/ast/ast.odin | 16 +++++---- core/thread/thread.odin | 2 +- src/check_expr.cpp | 76 ++++++++++++++++++++++++++---------------- src/check_type.cpp | 16 ++++++++- src/entity.cpp | 1 + src/parser.cpp | 17 +++++----- src/parser.hpp | 3 +- vendor/raylib/raymath.odin | 4 +-- 11 files changed, 113 insertions(+), 72 deletions(-) (limited to 'src/check_type.cpp') diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 401dcb857..fba2e1328 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -447,12 +447,12 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: } @builtin -append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elem(array, arg, true, loc=loc) } @builtin -non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elem(array, arg, false, loc=loc) } @@ -496,12 +496,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l } @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, true, loc, ..args) } @builtin -non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { return _append_elems(array, false, loc, ..args) } @@ -556,7 +556,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i @builtin -inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } @@ -574,7 +574,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle } @builtin -inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if array == nil { return } diff --git a/core/image/common.odin b/core/image/common.odin index c7507a85f..b576a9521 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -651,7 +651,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := // We have keyed alpha. o: GA_Pixel for p in inp { - if p == key.r { + if p.r == key.r { o = GA_Pixel{0, key.g} } else { o = GA_Pixel{p.r, 255} @@ -710,7 +710,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := // We have keyed alpha. o: GA_Pixel_16 for p in inp { - if p == key.r { + if p.r == key.r { o = GA_Pixel_16{0, key.g} } else { o = GA_Pixel_16{p.r, 65535} @@ -842,11 +842,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al bg := G_Pixel{} if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { // Background is RGB 16-bit, take just the red channel's topmost byte. - bg = u8(temp_bg.r >> 8) + bg.r = u8(temp_bg.r >> 8) } for p in inp { - out[0] = bg if p == key else p + out[0] = bg if p.r == key else p out = out[1:] } @@ -865,8 +865,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 255.0 c := ((1.0 - a) * bg + a * f32(p.r)) - out[0] = u8(c) - out = out[1:] + out[0].r = u8(c) + out = out[1:] } } else if .alpha_premultiply in options { @@ -874,14 +874,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 255.0 c := f32(p.r) * a - out[0] = u8(c) - out = out[1:] + out[0].r = u8(c) + out = out[1:] } } else { // Just drop alpha on the floor. for p in inp { - out[0] = p.r - out = out[1:] + out[0].r = p.r + out = out[1:] } } @@ -951,11 +951,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al bg := G_Pixel_16{} if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { // Background is RGB 16-bit, take just the red channel. - bg = temp_bg.r + bg.r = temp_bg.r } for p in inp { - out[0] = bg if p == key else p + out[0] = bg if p.r == key else p out = out[1:] } @@ -974,8 +974,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 65535.0 c := ((1.0 - a) * bg + a * f32(p.r)) - out[0] = u16(c) - out = out[1:] + out[0].r = u16(c) + out = out[1:] } } else if .alpha_premultiply in options { @@ -983,14 +983,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al for p in inp { a := f32(p.g) / 65535.0 c := f32(p.r) * a - out[0] = u16(c) - out = out[1:] + out[0].r = u16(c) + out = out[1:] } } else { // Just drop alpha on the floor. for p in inp { - out[0] = p.r - out = out[1:] + out[0].r = p.r + out = out[1:] } } diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index cb0b08dbb..b02b7cb4e 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -1112,7 +1112,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all Generate the restable. */ for x := 1; x < _PRIME_TAB_SIZE; x += 1 { - res_tab = internal_mod(a, _private_prime_table[x]) or_return + res_tab = cast(type_of(res_tab))(internal_mod(a, _private_prime_table[x]) or_return) } for { diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index f6bcbab4e..3e215e0f2 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -597,6 +597,7 @@ Field_Flag :: enum { Any_Int, Subtype, By_Ptr, + No_Broadcast, Results, Tags, @@ -616,6 +617,7 @@ field_flag_strings := [Field_Flag]string{ .Any_Int = "#any_int", .Subtype = "#subtype", .By_Ptr = "#by_ptr", + .No_Broadcast ="#no_broadcast", .Results = "results", .Tags = "field tag", @@ -624,12 +626,13 @@ field_flag_strings := [Field_Flag]string{ } field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{ - {"no_alias", .No_Alias}, - {"c_vararg", .C_Vararg}, - {"const", .Const}, - {"any_int", .Any_Int}, - {"subtype", .Subtype}, - {"by_ptr", .By_Ptr}, + {"no_alias", .No_Alias}, + {"c_vararg", .C_Vararg}, + {"const", .Const}, + {"any_int", .Any_Int}, + {"subtype", .Subtype}, + {"by_ptr", .By_Ptr}, + {"no_broadcast", .No_Broadcast}, } @@ -650,6 +653,7 @@ Field_Flags_Signature :: Field_Flags{ .Const, .Any_Int, .By_Ptr, + .No_Broadcast, .Default_Parameters, } diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 1c473bd1d..55f73d106 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -163,7 +163,7 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co t := create(thread_proc, priority) t.data = rawptr(fn) t.user_index = 1 - t.user_args = data + t.user_args[0] = data if self_cleanup { t.flags += {.Self_Cleanup} } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8fb2cf36b..51fe3fc8a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -623,7 +623,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type); #define MAXIMUM_TYPE_DISTANCE 10 -gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) { +gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) { if (c == nullptr) { GB_ASSERT(operand->mode == Addressing_Value); GB_ASSERT(is_type_typed(operand->type)); @@ -832,7 +832,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand if (dst->Union.variants.count == 1) { Type *vt = dst->Union.variants[0]; - i64 score = check_distance_between_types(c, operand, vt); + i64 score = check_distance_between_types(c, operand, vt, allow_array_programming); if (score >= 0) { return score+2; } @@ -840,7 +840,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand i64 prev_lowest_score = -1; i64 lowest_score = -1; for (Type *vt : dst->Union.variants) { - i64 score = check_distance_between_types(c, operand, vt); + i64 score = check_distance_between_types(c, operand, vt, allow_array_programming); if (score >= 0) { if (lowest_score < 0) { lowest_score = score; @@ -863,14 +863,14 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } if (is_type_relative_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type); + i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming); if (score >= 0) { return score+2; } } if (is_type_relative_multi_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type); + i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming); if (score >= 0) { return score+2; } @@ -896,19 +896,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } } - if (is_type_array(dst)) { - Type *elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, elem); - if (distance >= 0) { - return distance + 6; + if (allow_array_programming) { + if (is_type_array(dst)) { + Type *elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming); + if (distance >= 0) { + return distance + 6; + } } - } - if (is_type_simd_vector(dst)) { - Type *dst_elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, dst_elem); - if (distance >= 0) { - return distance + 6; + if (is_type_simd_vector(dst)) { + Type *dst_elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming); + if (distance >= 0) { + return distance + 6; + } } } @@ -918,7 +920,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } if (dst->Matrix.row_count == dst->Matrix.column_count) { Type *dst_elem = base_array_type(dst); - i64 distance = check_distance_between_types(c, operand, dst_elem); + i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming); if (distance >= 0) { return distance + 7; } @@ -966,9 +968,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) { } -gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) { +gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) { i64 score = 0; - i64 distance = check_distance_between_types(c, operand, type); + i64 distance = check_distance_between_types(c, operand, type, allow_array_programming); bool ok = distance >= 0; if (ok) { score = assign_score_function(distance, is_variadic); @@ -978,9 +980,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o } -gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { +gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) { i64 score = 0; - return check_is_assignable_to_with_score(c, operand, type, &score); + return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming); } gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) { @@ -3142,6 +3144,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return true; } + + if (is_type_array(dst)) { + Type *elem = base_array_type(dst); + if (check_is_castable_to(c, operand, elem)) { + return true; + } + } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { if (src->SimdVector.count != dst->SimdVector.count) { return false; @@ -5853,15 +5863,20 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } - auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error, bool allow_array_programming) -> i64 { i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) { + if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) { bool ok = false; - if (e && e->flags & EntityFlag_AnyInt) { + if (e && (e->flags & EntityFlag_AnyInt)) { if (is_type_integer(param_type)) { ok = check_is_castable_to(c, o, param_type); } } + if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) { + if (show_error) { + error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument"); + } + } if (ok) { s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { @@ -5878,7 +5893,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } err = CallArgumentError_WrongTypes; } - } else if (show_error) { check_assignment(c, o, param_type, str_lit("procedure argument")); } @@ -5963,12 +5977,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A if (param_is_variadic) { continue; } - score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); + bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast)); + score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error, allow_array_programming); } } if (variadic) { - Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index]; + Type *slice = var_entity->type; GB_ASSERT(is_type_slice(slice)); Type *elem = base_type(slice)->Slice.elem; Type *t = elem; @@ -5994,7 +6010,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A return CallArgumentError_MultipleVariadicExpand; } } - score += eval_param_and_score(c, o, t, err, true, nullptr, show_error); + bool allow_array_programming = !(var_entity && (var_entity->flags & EntityFlag_NoBroadcast)); + score += eval_param_and_score(c, o, t, err, true, nullptr, show_error, allow_array_programming); } } @@ -11148,6 +11165,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan if (f->flags&FieldFlag_any_int) { str = gb_string_appendc(str, "#any_int "); } + if (f->flags&FieldFlag_no_broadcast) { + str = gb_string_appendc(str, "#no_broadcast "); + } if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } diff --git a/src/check_type.cpp b/src/check_type.cpp index 3fe633892..96885bd27 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1869,6 +1869,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para error(name, "'#any_int' can only be applied to variable fields"); p->flags &= ~FieldFlag_any_int; } + if (p->flags&FieldFlag_no_broadcast) { + error(name, "'#no_broadcast' can only be applied to variable fields"); + p->flags &= ~FieldFlag_no_broadcast; + } if (p->flags&FieldFlag_by_ptr) { error(name, "'#by_ptr' can only be applied to variable fields"); p->flags &= ~FieldFlag_by_ptr; @@ -1926,7 +1930,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } } } - if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { + + bool allow_array_programming = true; + if (p->flags&FieldFlag_no_broadcast) { + allow_array_programming = false; + } + + if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) { bool ok = true; if (p->flags&FieldFlag_any_int) { if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { @@ -2002,6 +2012,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (p->flags&FieldFlag_no_alias) { param->flags |= EntityFlag_NoAlias; } + if (p->flags&FieldFlag_no_broadcast) { + param->flags |= EntityFlag_NoBroadcast; + } + if (p->flags&FieldFlag_any_int) { if (!is_type_integer(param->type) && !is_type_enum(param->type)) { gbString str = type_to_string(param->type); diff --git a/src/entity.cpp b/src/entity.cpp index 916c2b2bd..9161ea733 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -61,6 +61,7 @@ enum EntityFlag : u64 { EntityFlag_CVarArg = 1ull<<22, + EntityFlag_NoBroadcast = 1ull<<23, EntityFlag_AnyInt = 1ull<<24, EntityFlag_Disabled = 1ull<<25, diff --git a/src/parser.cpp b/src/parser.cpp index 6a7be8a7c..b6b62461f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3898,14 +3898,15 @@ struct ParseFieldPrefixMapping { FieldFlag flag; }; -gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { - {str_lit("using"), Token_using, FieldFlag_using}, - {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, - {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, - {str_lit("const"), Token_Hash, FieldFlag_const}, - {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, - {str_lit("subtype"), Token_Hash, FieldFlag_subtype}, - {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr}, +gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = { + {str_lit("using"), Token_using, FieldFlag_using}, + {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, + {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, + {str_lit("const"), Token_Hash, FieldFlag_const}, + {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldFlag_subtype}, + {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr}, + {str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast}, }; diff --git a/src/parser.hpp b/src/parser.hpp index ff3c5eb34..5820275c8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -326,6 +326,7 @@ enum FieldFlag : u32 { FieldFlag_any_int = 1<<6, FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, + FieldFlag_no_broadcast = 1<<9, // disallow array programming // Internal use by the parser only FieldFlag_Tags = 1<<10, @@ -336,7 +337,7 @@ enum FieldFlag : u32 { FieldFlag_Invalid = 1u<<31, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast, FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; diff --git a/vendor/raylib/raymath.odin b/vendor/raylib/raymath.odin index beeda7989..9682ffe4f 100644 --- a/vendor/raylib/raymath.odin +++ b/vendor/raylib/raymath.odin @@ -159,7 +159,7 @@ Vector2Transform :: proc "c" (v: Vector2, m: Matrix) -> Vector2 { // Calculate linear interpolation between two vectors @(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)") Vector2Lerp :: proc "c" (v1, v2: Vector2, amount: f32) -> Vector2 { - return linalg.lerp(v1, v2, amount) + return linalg.lerp(v1, v2, Vector2(amount)) } // Calculate reflected vector to normal @(require_results, deprecated="Prefer = linalg.reflect(v, normal)") @@ -405,7 +405,7 @@ Vector3Transform :: proc "c" (v: Vector3, m: Matrix) -> Vector3 { // Calculate linear interpolation between two vectors @(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)") Vector3Lerp :: proc "c" (v1, v2: Vector3, amount: f32) -> Vector3 { - return linalg.lerp(v1, v2, amount) + return linalg.lerp(v1, v2, Vector3(amount)) } // Calculate reflected vector to normal @(require_results, deprecated="Prefer = linalg.reflect(v, normal)") -- cgit v1.2.3 From 6e2efce670fa7dd18fbecb7440c6179ddc283218 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 21 Mar 2024 12:05:15 +0000 Subject: Fix missing `ERROR_BLOCK();` calls --- src/check_type.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 96885bd27..ee9a60059 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2396,6 +2396,8 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { return 0; } + ERROR_BLOCK(); + gbString s = expr_to_string(o->expr); error(e, "Array count must be a constant integer, got %s", s); gb_string_free(s); @@ -2878,6 +2880,8 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t } if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { + ERROR_BLOCK(); + error(e, "Non-contiguous enumeration used as an index in an enumerated array"); long long ea_count = cast(long long)t->EnumeratedArray.count; long long enum_count = cast(long long)bt->Enum.fields.count; -- cgit v1.2.3 From 61aa4558dc864019192072ff9a7f1f1ee23096bf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 16:41:50 +0000 Subject: Fix #3314 --- src/check_type.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index ee9a60059..38bfa56ef 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3112,20 +3112,22 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T Type *elem = t_invalid; Operand o = {}; + check_expr_or_type(&c, &o, pt->type); if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { - // NOTE(bill): call check_type_expr again to get a consistent error message - ERROR_BLOCK(); - elem = check_type_expr(&c, pt->type, nullptr); if (o.mode == Addressing_Variable) { gbString s = expr_to_string(pt->type); - error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s); + error(e, "^ is used for pointer types, did you mean '&%s'?", s); gb_string_free(s); + } else { + // NOTE(bill): call check_type_expr again to get a consistent error message + elem = check_type_expr(&c, pt->type, nullptr); } } else { elem = o.type; } + if (pt->tag != nullptr) { GB_ASSERT(pt->tag->kind == Ast_BasicDirective); String name = pt->tag->BasicDirective.name.string; @@ -3385,7 +3387,7 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) gbString type_str = expr_to_string(node->IndexExpr.expr); defer (gb_string_free(type_str)); - error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + error_line("\tSuggestion: Did you mean '[%s]%s'?\n", index_str ? index_str : "", type_str); end_error_block(); // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type -- cgit v1.2.3 From 1d46adb598328b4977d5b4cfad2a1dc679b05a21 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 16:51:04 +0000 Subject: Treat `*x` as an unary operator to improve error messages for common C-programmer mistakes --- src/check_expr.cpp | 13 +++++++++++++ src/check_type.cpp | 16 +++++++++++++++- src/parser.cpp | 7 ++++++- 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d1f393bef..f09c8fe3c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1800,6 +1800,19 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { } break; + case Token_Mul: + { + ERROR_BLOCK(); + error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string)); + if (is_type_pointer(o->type)) { + str = expr_to_string(o->expr); + error_line("\tSuggestion: Did you mean '%s^'?\n", str); + } else if (is_type_multi_pointer(o->type)) { + str = expr_to_string(o->expr); + error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str); + } + } + break; default: error(op, "Unknown operator '%.*s'", LIT(op.string)); return false; diff --git a/src/check_type.cpp b/src/check_type.cpp index 38bfa56ef..0b9042905 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3375,7 +3375,9 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) type = t_invalid; - // NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N] + // NOTE(bill): Check for common mistakes from C programmers + // e.g. T[] and T[N] + // e.g. *T Ast *node = unparen_expr(e); if (node && node->kind == Ast_IndexExpr) { gbString index_str = nullptr; @@ -3395,6 +3397,18 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); } + } else if (node && node->kind == Ast_UnaryExpr && node->UnaryExpr.op.kind == Token_Mul) { + gbString type_str = expr_to_string(node->UnaryExpr.expr); + defer (gb_string_free(type_str)); + + error_line("\tSuggestion: Did you mean '^%s'?\n", type_str); + end_error_block(); + + // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type + if (node->UnaryExpr.expr != nullptr) { + Ast *pseudo_pointer_expr = ast_pointer_type(e->file(), ast_token(node->UnaryExpr.expr), node->UnaryExpr.expr); + return check_type_expr(ctx, pseudo_pointer_expr, named_type); + } } else { end_error_block(); } diff --git a/src/parser.cpp b/src/parser.cpp index b6b62461f..bb9a526fe 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2541,6 +2541,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_pointer_type(f, token, elem); } break; + case Token_Mul: + return parse_unary_expr(f, true); + case Token_OpenBracket: { Token token = expect_token(f, Token_OpenBracket); Ast *count_expr = nullptr; @@ -3274,7 +3277,9 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) { case Token_Sub: case Token_Xor: case Token_And: - case Token_Not: { + case Token_Not: + case Token_Mul: // Used for error handling when people do C-like things + { Token token = advance_token(f); Ast *expr = parse_unary_expr(f, lhs); return ast_unary_expr(f, token, expr); -- cgit v1.2.3 From 6d4f30de1a85fe51159808d70a342c1c915d15de Mon Sep 17 00:00:00 2001 From: rick-masters Date: Sun, 24 Mar 2024 16:28:55 +0000 Subject: Fix fields_wait_signal futex. --- src/check_builtin.cpp | 2 ++ src/check_expr.cpp | 1 + src/check_type.cpp | 4 ++++ src/threading.cpp | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 53e4acbd1..f4aa9567d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3393,6 +3393,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count); elem->Struct.node = dummy_node_struct; type_set_offsets(elem); + wait_signal_set(&elem->Struct.fields_wait_signal); } Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem); @@ -3766,6 +3767,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; } } + wait_signal_set(&soa_struct->Struct.fields_wait_signal); Token token = {}; token.string = str_lit("Base_Type"); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fd10374c1..d19af4a62 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8873,6 +8873,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * break; } + wait_signal_until_available(&t->Struct.fields_wait_signal); isize field_count = t->Struct.fields.count; isize min_field_count = t->Struct.fields.count; for (isize i = min_field_count-1; i >= 0; i--) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 0b9042905..ae79d4edc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2490,6 +2490,7 @@ gb_internal Type *get_map_cell_type(Type *type) { s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved); s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved); s->Struct.scope = scope; + wait_signal_set(&s->Struct.fields_wait_signal); gb_unused(type_size_of(s)); return s; @@ -2520,6 +2521,7 @@ gb_internal void init_map_internal_types(Type *type) { metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved); metadata_type->Struct.scope = metadata_scope; metadata_type->Struct.node = nullptr; + wait_signal_set(&metadata_type->Struct.fields_wait_signal); gb_unused(type_size_of(metadata_type)); @@ -2537,6 +2539,7 @@ gb_internal void init_map_internal_types(Type *type) { debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); debug_type->Struct.scope = scope; debug_type->Struct.node = nullptr; + wait_signal_set(&debug_type->Struct.fields_wait_signal); gb_unused(type_size_of(debug_type)); @@ -2832,6 +2835,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e add_entity(ctx, scope, nullptr, base_type_entity); add_type_info_type(ctx, soa_struct); + wait_signal_set(&soa_struct->Struct.fields_wait_signal); return soa_struct; } diff --git a/src/threading.cpp b/src/threading.cpp index a469435d2..9e4a1607c 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -113,7 +113,7 @@ struct Wait_Signal { gb_internal void wait_signal_until_available(Wait_Signal *ws) { if (ws->futex.load() == 0) { - futex_wait(&ws->futex, 1); + futex_wait(&ws->futex, 0); } } -- cgit v1.2.3 From e5629dafd0be1be42a3bd183a09ff82492b6b386 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Mar 2024 13:23:43 +0000 Subject: Potentially fix a race condition with parapoly types (related to #3328) --- src/check_expr.cpp | 11 +++- src/check_type.cpp | 179 +++++++++++++++++++++++++++-------------------------- src/checker.hpp | 9 ++- src/threading.cpp | 2 - 4 files changed, 104 insertions(+), 97 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fd10374c1..44d65e376 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp gb_internal Type * make_optional_ok_type (Type *value, bool typed=true); gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); -gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure); gb_internal void check_not_tuple (CheckerContext *c, Operand *operand); gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gb_internal gbString expr_to_string (Ast *expression); @@ -121,6 +120,8 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); +gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -7171,8 +7172,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O } { - bool failure = false; - Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); + GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type); + + mutex_lock(&found_gen_types->mutex); + defer (mutex_unlock(&found_gen_types->mutex)); + Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands); + if (found_entity) { operand->mode = Addressing_Type; operand->type = found_entity->type; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0b9042905..e22d4b62b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -260,84 +260,21 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, } -gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure) { - rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global - - auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); - if (found_gen_types == nullptr) { - rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global - return nullptr; - } - - rw_mutex_shared_lock(&found_gen_types->mutex); // @@local - defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local - - rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global - - for (Entity *e : found_gen_types->types) { - Type *t = base_type(e->type); - TypeTuple *tuple = nullptr; - switch (t->kind) { - case Type_Struct: - if (t->Struct.polymorphic_params) { - tuple = &t->Struct.polymorphic_params->Tuple; - } - break; - case Type_Union: - if (t->Union.polymorphic_params) { - tuple = &t->Union.polymorphic_params->Tuple; - } - break; - } - GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); - GB_ASSERT(param_count == tuple->variables.count); - - bool skip = false; - - for (isize j = 0; j < param_count; j++) { - Entity *p = tuple->variables[j]; - Operand o = {}; - if (j < ordered_operands.count) { - o = ordered_operands[j]; - } - if (o.expr == nullptr) { - continue; - } - Entity *oe = entity_of_node(o.expr); - if (p == oe) { - // NOTE(bill): This is the same type, make sure that it will be be same thing and use that - // Saves on a lot of checking too below - continue; - } - - if (p->kind == Entity_TypeName) { - if (is_type_polymorphic(o.type)) { - // NOTE(bill): Do not add polymorphic version to the gen_types - skip = true; - break; - } - if (!are_types_identical(o.type, p->type)) { - skip = true; - break; - } - } else if (p->kind == Entity_Constant) { - if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { - skip = true; - break; - } - if (!are_types_identical(o.type, p->type)) { - skip = true; - break; - } - } else { - GB_PANIC("Unknown entity kind"); - } - } - if (!skip) { - return e; - } +gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) { + mutex_lock(&ctx->info->gen_types_mutex); // @@global + + GenTypesData *found_gen_types = nullptr; + auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); + if (found_gen_types_ptr == nullptr) { + GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData); + gen_types->types = array_make(heap_allocator()); + map_set(&ctx->info->gen_types, original_type, gen_types); + found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); } - return nullptr; + found_gen_types = *found_gen_types_ptr; + GB_ASSERT(found_gen_types != nullptr); + mutex_unlock(&ctx->info->gen_types_mutex); // @@global + return found_gen_types; } @@ -367,19 +304,16 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T // TODO(bill): Is this even correct? Or should the metadata be copied? e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata; - rw_mutex_lock(&ctx->info->gen_types_mutex); - auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); - if (found_gen_types) { - rw_mutex_lock(&found_gen_types->mutex); - array_add(&found_gen_types->types, e); - rw_mutex_unlock(&found_gen_types->mutex); - } else { - GenTypesData gen_types = {}; - gen_types.types = array_make(heap_allocator()); - array_add(&gen_types.types, e); - map_set(&ctx->info->gen_types, original_type, gen_types); + auto *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(ctx, original_type); + mutex_lock(&found_gen_types->mutex); + defer (mutex_unlock(&found_gen_types->mutex)); + + for (Entity *prev : found_gen_types->types) { + if (prev == e) { + return; + } } - rw_mutex_unlock(&ctx->info->gen_types_mutex); + array_add(&found_gen_types->types, e); } @@ -612,6 +546,73 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T return true; } +gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands) { + for (Entity *e : found_gen_types->types) { + Type *t = base_type(e->type); + TypeTuple *tuple = nullptr; + switch (t->kind) { + case Type_Struct: + if (t->Struct.polymorphic_params) { + tuple = &t->Struct.polymorphic_params->Tuple; + } + break; + case Type_Union: + if (t->Union.polymorphic_params) { + tuple = &t->Union.polymorphic_params->Tuple; + } + break; + } + GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); + GB_ASSERT(param_count == tuple->variables.count); + + bool skip = false; + + for (isize j = 0; j < param_count; j++) { + Entity *p = tuple->variables[j]; + Operand o = {}; + if (j < ordered_operands.count) { + o = ordered_operands[j]; + } + if (o.expr == nullptr) { + continue; + } + Entity *oe = entity_of_node(o.expr); + if (p == oe) { + // NOTE(bill): This is the same type, make sure that it will be be same thing and use that + // Saves on a lot of checking too below + continue; + } + + if (p->kind == Entity_TypeName) { + if (is_type_polymorphic(o.type)) { + // NOTE(bill): Do not add polymorphic version to the gen_types + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; + } + } else if (p->kind == Entity_Constant) { + if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; + } + } else { + GB_PANIC("Unknown entity kind"); + } + } + if (!skip) { + return e; + } + } + return nullptr; +}; + gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_struct(struct_type)); diff --git a/src/checker.hpp b/src/checker.hpp index eea99578e..e0dc54a87 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -360,7 +360,7 @@ struct GenProcsData { struct GenTypesData { Array types; - RwMutex mutex; + RecursiveMutex mutex; }; // CheckerInfo stores all the symbol information for a type-checked program @@ -400,8 +400,8 @@ struct CheckerInfo { RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files - RwMutex gen_types_mutex; - PtrMap gen_types; + BlockingMutex gen_types_mutex; + PtrMap gen_types; BlockingMutex type_info_mutex; // NOT recursive Array type_info_types; @@ -560,3 +560,6 @@ gb_internal void init_core_context(Checker *c); gb_internal void init_mem_allocator(Checker *c); gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped); + + +gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type); \ No newline at end of file diff --git a/src/threading.cpp b/src/threading.cpp index a469435d2..d9538f66e 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -122,8 +122,6 @@ gb_internal void wait_signal_set(Wait_Signal *ws) { futex_broadcast(&ws->futex); } - - struct MutexGuard { MutexGuard() = delete; MutexGuard(MutexGuard const &) = delete; -- cgit v1.2.3 From 533ba63c82d21dbd21551970200ebc5b81d237f1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 26 Mar 2024 13:06:39 +0000 Subject: Fix #3327 #3204 #3200 --- src/check_type.cpp | 215 +++++++++++++++++++++++++++++++++++++++-------------- src/types.cpp | 1 + 2 files changed, 160 insertions(+), 56 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 0f63d8bcb..d26d9b660 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2683,6 +2683,107 @@ type_assign:; return; } +struct SoaTypeWorkerData { + CheckerContext ctx; + Type * type; + bool wait_to_finish; +}; + + +gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_finish) { + Type *original_type = t; + t = base_type(t); + if (t == nullptr || !is_type_soa_struct(t)) { + return; + } + + MUTEX_GUARD(&t->Struct.soa_mutex); + + if (t->Struct.fields_wait_signal.futex.load()) { + return; + } + + isize field_count = 0; + i32 extra_field_count = 0; + switch (t->Struct.soa_kind) { + case StructSoa_Fixed: extra_field_count = 0; break; + case StructSoa_Slice: extra_field_count = 1; break; + case StructSoa_Dynamic: extra_field_count = 3; break; + } + + + Ast *node = t->Struct.node; + Scope *scope = t->Struct.scope; + i64 soa_count = t->Struct.soa_count; + Type *elem = t->Struct.soa_elem; + Type *old_struct = base_type(elem); + GB_ASSERT(old_struct->kind == Type_Struct); + + if (wait_to_finish) { + wait_signal_until_available(&old_struct->Struct.fields_wait_signal); + } + + + field_count = old_struct->Struct.fields.count; + + t->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); + if (soa_count > I32_MAX) { + soa_count = I32_MAX; + error(node, "Array count too large for an #soa struct, got %lld", cast(long long)soa_count); + } + t->Struct.soa_count = cast(i32)soa_count; + + for_array(i, old_struct->Struct.fields) { + Entity *old_field = old_struct->Struct.fields[i]; + if (old_field->kind == Entity_Variable) { + Type *field_type = nullptr; + if (t->Struct.soa_kind == StructSoa_Fixed) { + GB_ASSERT(soa_count >= 0); + field_type = alloc_type_array(old_field->type, soa_count); + } else { + field_type = alloc_type_pointer(old_field->type); + } + Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index); + t->Struct.fields[i] = new_field; + add_entity(ctx, scope, nullptr, new_field); + add_entity_use(ctx, nullptr, new_field); + } else { + t->Struct.fields[i] = old_field; + } + + t->Struct.tags[i] = old_struct->Struct.tags[i]; + } + + if (t->Struct.soa_kind != StructSoa_Fixed) { + Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); + t->Struct.fields[field_count+0] = len_field; + add_entity(ctx, scope, nullptr, len_field); + add_entity_use(ctx, nullptr, len_field); + + if (t->Struct.soa_kind == StructSoa_Dynamic) { + Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1); + t->Struct.fields[field_count+1] = cap_field; + add_entity(ctx, scope, nullptr, cap_field); + add_entity_use(ctx, nullptr, cap_field); + + init_mem_allocator(ctx->checker); + Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2); + t->Struct.fields[field_count+2] = allocator_field; + add_entity(ctx, scope, nullptr, allocator_field); + add_entity_use(ctx, nullptr, allocator_field); + } + } + + add_type_info_type(ctx, original_type); +} + +gb_internal WORKER_TASK_PROC(complete_soa_type_worker) { + SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data; + complete_soa_type(&wd->ctx, wd->type, wd->wait_to_finish); + return 0; +} + gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) { @@ -2697,8 +2798,9 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e return alloc_type_array(elem, count, generic_type); } - Type *soa_struct = nullptr; - Scope *scope = nullptr; + Type * soa_struct = nullptr; + Scope *scope = nullptr; + bool is_complete = false; isize field_count = 0; i32 extra_field_count = 0; @@ -2707,39 +2809,40 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e case StructSoa_Slice: extra_field_count = 1; break; case StructSoa_Dynamic: extra_field_count = 3; break; } + + soa_struct = alloc_type_struct(); + soa_struct->Struct.soa_kind = soa_kind; + soa_struct->Struct.soa_elem = elem; + soa_struct->Struct.is_polymorphic = is_polymorphic; + soa_struct->Struct.node = array_typ_expr; + + if (count > I32_MAX) { + count = I32_MAX; + error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count); + } + soa_struct->Struct.soa_count = cast(i32)count; + + scope = create_scope(ctx->info, ctx->scope); + soa_struct->Struct.scope = scope; + + if (is_polymorphic) { field_count = 0; - soa_struct = alloc_type_struct(); soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - soa_struct->Struct.node = array_typ_expr; - soa_struct->Struct.soa_kind = soa_kind; - soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_count = 0; - soa_struct->Struct.is_polymorphic = true; - scope = create_scope(ctx->info, ctx->scope); - soa_struct->Struct.scope = scope; + is_complete = true; + } else if (is_type_array(elem)) { Type *old_array = base_type(elem); field_count = cast(isize)old_array->Array.count; - soa_struct = alloc_type_struct(); soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - soa_struct->Struct.node = array_typ_expr; - soa_struct->Struct.soa_kind = soa_kind; - soa_struct->Struct.soa_elem = elem; - if (count > I32_MAX) { - count = I32_MAX; - error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count); - } - soa_struct->Struct.soa_count = cast(i32)count; - scope = create_scope(ctx->info, ctx->scope); string_map_init(&scope->elements, 8); - soa_struct->Struct.scope = scope; String params_xyzw[4] = { str_lit("x"), @@ -2765,52 +2868,44 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e add_entity_use(ctx, nullptr, new_field); } + is_complete = true; + } else { GB_ASSERT(is_type_struct(elem)); Type *old_struct = base_type(elem); - wait_signal_until_available(&old_struct->Struct.fields_wait_signal); - field_count = old_struct->Struct.fields.count; + if (old_struct->Struct.fields_wait_signal.futex.load()) { + field_count = old_struct->Struct.fields.count; - soa_struct = alloc_type_struct(); - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - soa_struct->Struct.node = array_typ_expr; - soa_struct->Struct.soa_kind = soa_kind; - soa_struct->Struct.soa_elem = elem; - if (count > I32_MAX) { - count = I32_MAX; - error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count); - } - soa_struct->Struct.soa_count = cast(i32)count; - - scope = create_scope(ctx->info, old_struct->Struct.scope->parent); - soa_struct->Struct.scope = scope; - - for_array(i, old_struct->Struct.fields) { - Entity *old_field = old_struct->Struct.fields[i]; - if (old_field->kind == Entity_Variable) { - Type *field_type = nullptr; - if (soa_kind == StructSoa_Fixed) { - GB_ASSERT(count >= 0); - field_type = alloc_type_array(old_field->type, count); + soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); + + for_array(i, old_struct->Struct.fields) { + Entity *old_field = old_struct->Struct.fields[i]; + if (old_field->kind == Entity_Variable) { + Type *field_type = nullptr; + if (soa_kind == StructSoa_Fixed) { + GB_ASSERT(count >= 0); + field_type = alloc_type_array(old_field->type, count); + } else { + field_type = alloc_type_pointer(old_field->type); + } + Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index); + soa_struct->Struct.fields[i] = new_field; + add_entity(ctx, scope, nullptr, new_field); + add_entity_use(ctx, nullptr, new_field); } else { - field_type = alloc_type_pointer(old_field->type); + soa_struct->Struct.fields[i] = old_field; } - Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index); - soa_struct->Struct.fields[i] = new_field; - add_entity(ctx, scope, nullptr, new_field); - add_entity_use(ctx, nullptr, new_field); - } else { - soa_struct->Struct.fields[i] = old_field; - } - soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; + soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; + } + is_complete = true; } } - if (soa_kind != StructSoa_Fixed) { + if (is_complete && soa_kind != StructSoa_Fixed) { Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); soa_struct->Struct.fields[field_count+0] = len_field; add_entity(ctx, scope, nullptr, len_field); @@ -2835,8 +2930,16 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved); add_entity(ctx, scope, nullptr, base_type_entity); - add_type_info_type(ctx, soa_struct); - wait_signal_set(&soa_struct->Struct.fields_wait_signal); + if (is_complete) { + add_type_info_type(ctx, soa_struct); + wait_signal_set(&soa_struct->Struct.fields_wait_signal); + } else { + SoaTypeWorkerData *wd = gb_alloc_item(permanent_allocator(), SoaTypeWorkerData); + wd->ctx = *ctx; + wd->type = soa_struct; + wd->wait_to_finish = true; + thread_pool_add_task(complete_soa_type_worker, wd); + } return soa_struct; } diff --git a/src/types.cpp b/src/types.cpp index c2358056b..6e63f56ed 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -145,6 +145,7 @@ struct TypeStruct { i32 soa_count; StructSoaKind soa_kind; Wait_Signal fields_wait_signal; + BlockingMutex soa_mutex; BlockingMutex offset_mutex; // for settings offsets bool is_polymorphic; -- cgit v1.2.3 From 1009182f7b35e38e0fba375ad830fc609a7be831 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 26 Mar 2024 14:13:55 +0000 Subject: Fix another #soa race condition bug --- src/check_expr.cpp | 5 ++++ src/check_type.cpp | 60 ++++++++++++++++++++++++++++++----------------- src/checker.cpp | 31 ++++++++++++++++++++++++ src/checker.hpp | 1 + src/llvm_backend_type.cpp | 2 +- src/types.cpp | 2 ++ 6 files changed, 78 insertions(+), 23 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a34b425c2..3a275729f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -122,6 +122,8 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands); +gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -5031,6 +5033,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } } + if (operand->type && is_type_soa_struct(type_deref(operand->type))) { + complete_soa_type(c->checker, type_deref(operand->type), false); + } if (entity == nullptr && selector->kind == Ast_Ident) { String field_name = selector->Ident.token.string; diff --git a/src/check_type.cpp b/src/check_type.cpp index d26d9b660..40a7ec947 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2690,17 +2690,19 @@ struct SoaTypeWorkerData { }; -gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_finish) { +gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish) { Type *original_type = t; + gb_unused(original_type); + t = base_type(t); if (t == nullptr || !is_type_soa_struct(t)) { - return; + return true; } MUTEX_GUARD(&t->Struct.soa_mutex); if (t->Struct.fields_wait_signal.futex.load()) { - return; + return true; } isize field_count = 0; @@ -2711,8 +2713,6 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi case StructSoa_Dynamic: extra_field_count = 3; break; } - - Ast *node = t->Struct.node; Scope *scope = t->Struct.scope; i64 soa_count = t->Struct.soa_count; Type *elem = t->Struct.soa_elem; @@ -2721,18 +2721,26 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi if (wait_to_finish) { wait_signal_until_available(&old_struct->Struct.fields_wait_signal); + } else { + GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load()); } - field_count = old_struct->Struct.fields.count; t->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - if (soa_count > I32_MAX) { - soa_count = I32_MAX; - error(node, "Array count too large for an #soa struct, got %lld", cast(long long)soa_count); - } - t->Struct.soa_count = cast(i32)soa_count; + + + auto const &add_entity = [](Scope *scope, Entity *entity) { + String name = entity->token.string; + if (!is_blank_ident(name)) { + Entity *ie = scope_insert(scope, entity); + if (ie != nullptr) { + redeclaration_error(name, entity, ie); + } + } + }; + for_array(i, old_struct->Struct.fields) { Entity *old_field = old_struct->Struct.fields[i]; @@ -2746,8 +2754,8 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi } Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index); t->Struct.fields[i] = new_field; - add_entity(ctx, scope, nullptr, new_field); - add_entity_use(ctx, nullptr, new_field); + add_entity(scope, new_field); + new_field->flags |= EntityFlag_Used; } else { t->Struct.fields[i] = old_field; } @@ -2758,29 +2766,32 @@ gb_internal void complete_soa_type(CheckerContext *ctx, Type *t, bool wait_to_fi if (t->Struct.soa_kind != StructSoa_Fixed) { Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); t->Struct.fields[field_count+0] = len_field; - add_entity(ctx, scope, nullptr, len_field); - add_entity_use(ctx, nullptr, len_field); + add_entity(scope, len_field); + len_field->flags |= EntityFlag_Used; if (t->Struct.soa_kind == StructSoa_Dynamic) { Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1); t->Struct.fields[field_count+1] = cap_field; - add_entity(ctx, scope, nullptr, cap_field); - add_entity_use(ctx, nullptr, cap_field); + add_entity(scope, cap_field); + cap_field->flags |= EntityFlag_Used; - init_mem_allocator(ctx->checker); + init_mem_allocator(checker); Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2); t->Struct.fields[field_count+2] = allocator_field; - add_entity(ctx, scope, nullptr, allocator_field); - add_entity_use(ctx, nullptr, allocator_field); + add_entity(scope, allocator_field); + allocator_field->flags |= EntityFlag_Used; } } - add_type_info_type(ctx, original_type); + // add_type_info_type(ctx, original_type); + + wait_signal_set(&t->Struct.fields_wait_signal); + return true; } gb_internal WORKER_TASK_PROC(complete_soa_type_worker) { SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data; - complete_soa_type(&wd->ctx, wd->type, wd->wait_to_finish); + complete_soa_type(wd->ctx.checker, wd->type, wd->wait_to_finish); return 0; } @@ -2825,6 +2836,9 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e scope = create_scope(ctx->info, ctx->scope); soa_struct->Struct.scope = scope; + if (elem && elem->kind == Type_Named) { + add_declaration_dependency(ctx, elem->Named.type_name); + } if (is_polymorphic) { field_count = 0; @@ -2938,6 +2952,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e wd->ctx = *ctx; wd->type = soa_struct; wd->wait_to_finish = true; + + mpsc_enqueue(&ctx->checker->soa_types_to_complete, soa_struct); thread_pool_add_task(complete_soa_type_worker, wd); } diff --git a/src/checker.cpp b/src/checker.cpp index e7d0ad9cb..ccda31a4f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1371,6 +1371,7 @@ gb_internal void init_checker(Checker *c) { array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20); mpsc_init(&c->global_untyped_queue, a); // , 1<<20); + mpsc_init(&c->soa_types_to_complete, a); // , 1<<20); c->builtin_ctx = make_checker_context(c); } @@ -1383,6 +1384,7 @@ gb_internal void destroy_checker(Checker *c) { array_free(&c->nested_proc_lits); array_free(&c->procs_to_check); mpsc_destroy(&c->global_untyped_queue); + mpsc_destroy(&c->soa_types_to_complete); } @@ -1682,6 +1684,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden } return true; } + +gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) { + if (scope == nullptr) { + return false; + } + + + if (!is_blank_ident(name)) { + Entity *ie = scope_insert(scope, entity); + if (ie != nullptr) { + return redeclaration_error(name, entity, ie); + } + } + if (identifier != nullptr) { + GB_ASSERT(entity->file != nullptr); + add_entity_definition(info, identifier, entity); + } + return true; +} + gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) { return add_entity_with_name(c, scope, identifier, entity, entity->token.string); } @@ -4440,6 +4462,10 @@ gb_internal void check_all_global_entities(Checker *c) { DeclInfo *d = e->decl_info; check_single_global_entity(c, e, d); if (e->type != nullptr && is_type_typed(e->type)) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } + (void)type_size_of(e->type); (void)type_align_of(e->type); } @@ -6108,6 +6134,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) { } gb_internal void check_merge_queues_into_arrays(Checker *c) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } check_add_entities_from_queues(c); check_add_definitions_from_queues(c); } @@ -6318,6 +6347,8 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check bodies have all been checked"); check_unchecked_bodies(c); + TIME_SECTION("check #soa types"); + check_merge_queues_into_arrays(c); thread_pool_wait(); diff --git a/src/checker.hpp b/src/checker.hpp index e0dc54a87..1701da58d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -500,6 +500,7 @@ struct Checker { MPSCQueue global_untyped_queue; + MPSCQueue soa_types_to_complete; }; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index de83f5058..20e4991e7 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -860,7 +860,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ Entity *f = t->Struct.fields[source_index]; i64 foffset = 0; if (!t->Struct.is_raw_union) { - GB_ASSERT(t->Struct.offsets != nullptr); + GB_ASSERT_MSG(t->Struct.offsets != nullptr, "%s", type_to_string(t)); GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count); foffset = t->Struct.offsets[source_index]; } diff --git a/src/types.cpp b/src/types.cpp index 6e63f56ed..ebe6271f2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3772,6 +3772,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { return 1; } + type_set_offsets(t); + i64 max = 1; for_array(i, t->Struct.fields) { Type *field_type = t->Struct.fields[i]->type; -- cgit v1.2.3 From 4edcaa6124eac5e73359ff98239be6f447a42f47 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 30 Mar 2024 10:29:20 +0000 Subject: Try storing a pointer to a fake metadata type in the debug info for a `map` --- src/check_type.cpp | 10 ++++------ src/llvm_backend_debug.cpp | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 40a7ec947..2846aae86 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2526,18 +2526,16 @@ gb_internal void init_map_internal_types(Type *type) { gb_unused(type_size_of(metadata_type)); - // NOTE(bill): [0]^struct{key: Key, value: Value, hash: uintptr} - // This is a zero array to a pointer to keep the alignment to that of a pointer, and not effective the size of the final struct - metadata_type = alloc_type_array(alloc_type_pointer(metadata_type), 0);; + // NOTE(bill): ^struct{key: Key, value: Value, hash: uintptr} + metadata_type = alloc_type_pointer(metadata_type); Scope *scope = create_scope(nullptr, nullptr); Type *debug_type = alloc_type_struct(); - debug_type->Struct.fields = slice_make(permanent_allocator(), 4); - debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), t_uintptr, false, 0, EntityState_Resolved); + debug_type->Struct.fields = slice_make(permanent_allocator(), 3); + debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved); debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); - debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); debug_type->Struct.scope = scope; debug_type->Struct.node = nullptr; wait_signal_set(&debug_type->Struct.fields_wait_signal); diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 9ecacb4f4..048f5f933 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -748,8 +748,8 @@ gb_internal void lb_debug_complete_types(lbModule *m) { case Type_Map: GB_ASSERT(t_raw_map != nullptr); - // bt = base_type(bt->Map.debug_metadata_type); - bt = base_type(t_raw_map); + bt = base_type(bt->Map.debug_metadata_type); + // bt = base_type(t_raw_map); GB_ASSERT(bt->kind == Type_Struct); /*fallthrough*/ case Type_Struct: -- cgit v1.2.3 From 8e1a2094a73205f0494295699ccb31f6610728b7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 30 Mar 2024 13:46:23 +0000 Subject: Fix debug info for `map` --- src/check_type.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 2846aae86..609b73229 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2477,10 +2477,6 @@ gb_internal Type *get_map_cell_type(Type *type) { return type; } - if (is_power_of_two(len)) { - return type; - } - i64 padding = size - len*elem_size; GB_ASSERT(padding > 0); -- cgit v1.2.3 From b47d73c651b02b94389fa205e154f4be905148b3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 1 Apr 2024 13:34:30 +0100 Subject: Fix type checking for invalid enum backing type --- src/check_type.cpp | 4 +++- src/types.cpp | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 609b73229..81e67f261 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -790,6 +790,9 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam ast_node(et, EnumType, node); GB_ASSERT(is_type_enum(enum_type)); + enum_type->Enum.base_type = t_int; + enum_type->Enum.scope = ctx->scope; + Type *base_type = t_int; if (et->base_type != nullptr) { base_type = check_type(ctx, et->base_type); @@ -811,7 +814,6 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam // NOTE(bill): Must be up here for the 'check_init_constant' system enum_type->Enum.base_type = base_type; - enum_type->Enum.scope = ctx->scope; auto fields = array_make(permanent_allocator(), 0, et->fields.count); diff --git a/src/types.cpp b/src/types.cpp index 256c654ac..0bf28a28c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -768,6 +768,7 @@ gb_internal i64 type_offset_of (Type *t, i64 index, Type **field_type_=null gb_internal gbString type_to_string (Type *type, bool shorthand=true); gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true); gb_internal i64 type_size_of_internal(Type *t, TypePath *path); +gb_internal i64 type_align_of_internal(Type *t, TypePath *path); gb_internal void init_map_internal_types(Type *type); gb_internal Type * bit_set_to_int(Type *t); gb_internal bool are_types_identical(Type *x, Type *y); @@ -780,9 +781,6 @@ gb_internal bool is_type_slice(Type *t); gb_internal bool is_type_integer(Type *t); gb_internal bool type_set_offsets(Type *t); -gb_internal i64 type_size_of_internal(Type *t, TypePath *path); -gb_internal i64 type_align_of_internal(Type *t, TypePath *path); - // IMPORTANT TODO(bill): SHould this TypePath code be removed since type cycle checking is handled much earlier on? @@ -3576,8 +3574,6 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo -gb_internal i64 type_size_of_internal (Type *t, TypePath *path); -gb_internal i64 type_align_of_internal(Type *t, TypePath *path); gb_internal i64 type_size_of(Type *t); gb_internal i64 type_align_of(Type *t); -- cgit v1.2.3 From 219eb58c08bea94dda151100c00600187f74bd6f Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 9 Apr 2024 12:03:41 +0100 Subject: Fix typo in using parameter error message --- src/check_type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 81e67f261..f1d991acb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1689,7 +1689,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para bool is_using = (p->flags&FieldFlag_using) != 0; if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) { ERROR_BLOCK(); - error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied"); + error(param, "'using' on a procedure parameter is not allowed when '-vet' or '-vet-using-param' is applied"); error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n"); } -- cgit v1.2.3