diff --git a/src/types.cpp b/src/types.cpp index 412448cbc..5df062677 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1,5 +1,5 @@ -struct Scope; struct Ast; +struct Scope; struct Entity; enum BasicKind { @@ -161,10 +161,10 @@ struct TypeStruct { struct TypeUnion { Slice variants; - + Ast * node; Scope * scope; - + i64 variant_block_size; i64 custom_align; Type * polymorphic_params; // Type_Tuple @@ -1438,7 +1438,7 @@ gb_internal bool is_type_matrix(Type *t) { gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) { t = base_type(t); GB_ASSERT(t->kind == Type_Matrix); - + Type *elem = t->Matrix.elem; i64 row_count = gb_max(t->Matrix.row_count, 1); i64 column_count = gb_max(t->Matrix.column_count, 1); @@ -1450,15 +1450,15 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) { i64 elem_align = type_align_of_internal(elem, tp); if (pop) type_path_pop(tp); - + i64 elem_size = type_size_of(elem); - + // NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding // It would be better for performance to pad each column so that each column // could be maximally aligned but as a compromise, having no padding will be // beneficial to third libraries that assume no padding - + i64 total_expected_size = row_count*column_count*elem_size; // i64 min_alignment = prev_pow2(elem_align * row_count); i64 min_alignment = prev_pow2(total_expected_size); @@ -1466,7 +1466,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) { min_alignment >>= 1; } min_alignment = gb_max(min_alignment, elem_align); - + i64 align = gb_min(min_alignment, build_context.max_simd_align); return align; } @@ -1480,7 +1480,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) { } else if (t->Matrix.row_count == 0) { return 0; } - + i64 elem_size; if (tp != nullptr) { elem_size = type_size_of_internal(t->Matrix.elem, tp); @@ -1489,7 +1489,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) { } i64 stride_in_bytes = 0; - + // NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding // It would be better for performance to pad each column/row so that each column/row // could be maximally aligned but as a compromise, having no padding will be @@ -1545,7 +1545,7 @@ gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) { gb_internal i64 matrix_column_major_index_to_offset(Type *t, i64 index) { t = base_type(t); GB_ASSERT(t->kind == Type_Matrix); - + i64 row_index = index%t->Matrix.row_count; i64 column_index = index/t->Matrix.row_count; return matrix_indices_to_offset(t, row_index, column_index); @@ -1566,7 +1566,7 @@ gb_internal bool is_type_valid_for_matrix_elems(Type *t) { return true; } else if (is_type_complex(t)) { return true; - } + } if (t->kind == Type_Generic) { return true; } @@ -2119,6 +2119,23 @@ gb_internal bool is_type_sliceable(Type *t) { return false; } +gb_internal Entity *type_get_polymorphic_parent(Type *t, Type **params_) { + t = base_type(t); + Type *parent = nullptr; + if (t->kind == Type_Struct) { + parent = t->Struct.polymorphic_parent; + if (params_) *params_ = t->Struct.polymorphic_params; + } else if (t->kind == Type_Union) { + parent = t->Union.polymorphic_parent; + if (params_) *params_ = t->Union.polymorphic_params; + } + if (parent != nullptr) { + GB_ASSERT(parent->kind == Type_Named); + + return parent->Named.type_name; + } + return nullptr; +} gb_internal bool is_type_polymorphic_record(Type *t) { t = base_type(t); @@ -2485,7 +2502,7 @@ gb_internal bool is_type_simple_compare(Type *t) { case Type_Proc: case Type_BitSet: return true; - + case Type_Matrix: return is_type_simple_compare(t->Matrix.elem); @@ -2732,7 +2749,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Array: return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem); - + case Type_Matrix: return x->Matrix.row_count == y->Matrix.row_count && x->Matrix.column_count == y->Matrix.column_count && @@ -3592,7 +3609,7 @@ gb_internal bool are_struct_fields_reordered(Type *type) { return false; } GB_ASSERT(type->Struct.offsets != nullptr); - + i64 prev_offset = 0; for_array(i, type->Struct.fields) { i64 offset = type->Struct.offsets[i]; @@ -3613,9 +3630,9 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo return {}; } GB_ASSERT(type->Struct.offsets != nullptr); - + auto indices = slice_make(allocator, type->Struct.fields.count); - + i64 prev_offset = 0; bool is_ordered = true; for_array(i, indices) { @@ -3630,14 +3647,14 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo isize n = indices.count; for (isize i = 1; i < n; i++) { isize j = i; - + while (j > 0 && type->Struct.offsets[indices[j-1]] > type->Struct.offsets[indices[j]]) { gb_swap(i32, indices[j-1], indices[j]); j -= 1; - } + } } } - + return indices; } @@ -3887,8 +3904,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { // IMPORTANT TODO(bill): Figure out the alignment of vector types return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2); } - - case Type_Matrix: + + case Type_Matrix: return matrix_align_of(t, path); case Type_SoaPointer: @@ -4175,7 +4192,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { Type *elem = t->SimdVector.elem; return count * type_size_of_internal(elem, path); } - + case Type_Matrix: { i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path); if (t->Matrix.is_row_major) { @@ -4580,7 +4597,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha break; case Type_Array: - str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count)); + str = gb_string_appendc(str, gb_bprintf("[%lld]", cast(long long)type->Array.count)); str = write_type_to_string(str, type->Array.elem); break; @@ -4753,10 +4770,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha } break; case ProcCC_CDecl: - str = gb_string_appendc(str, " \"cdecl\" "); + str = gb_string_appendc(str, " \"c\" "); break; case ProcCC_StdCall: - str = gb_string_appendc(str, " \"stdcall\" "); + str = gb_string_appendc(str, " \"std\" "); break; case ProcCC_FastCall: str = gb_string_appendc(str, " \"fastcall\" "); @@ -4814,7 +4831,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count); str = write_type_to_string(str, type->SimdVector.elem); break; - + case Type_Matrix: if (type->Matrix.is_row_major) { str = gb_string_appendc(str, "#row_major "); @@ -4856,5 +4873,209 @@ gb_internal gbString type_to_string_shorthand(Type *type) { return type_to_string(type, true); } +gb_internal gbString write_type_to_canonical_string(gbString w, Type *type); +gb_internal gbString write_canonical_params(gbString w, Type *params) { + w = gb_string_appendc(w, "("); + if (params) { + GB_ASSERT(params->kind == Type_Tuple); + for_array(i, params->Tuple.variables) { + Entity *v = params->Tuple.variables[i]; + if (i > 0) { + w = gb_string_appendc(w, ","); + } + if (v->kind == Entity_Variable) { + if (v->flags&EntityFlag_CVarArg) { + w = gb_string_appendc(w, "#c_vararg"); + } + if (v->flags&EntityFlag_Ellipsis) { + Type *slice = base_type(v->type); + w = gb_string_appendc(w, ".."); + GB_ASSERT(v->type->kind == Type_Slice); + w = write_type_to_canonical_string(w, slice->Slice.elem); + } else { + w = write_type_to_canonical_string(w, v->type); + } + } else if (v->kind == Entity_TypeName) { + w = gb_string_appendc(w, "$"); + w = write_type_to_canonical_string(w, v->type); + } else if (v->kind == Entity_Constant) { + w = gb_string_appendc(w, "$$"); + w = write_exact_value_to_string(w, v->Constant.value); + } else { + GB_PANIC("TODO(bill): handle non type/const parapoly parameter values"); + } + } + } + return gb_string_appendc(w, ")"); +} +gb_internal u64 type_hash_canonical_type(Type *type) { + if (type == nullptr) { + return 0; + } + TEMPORARY_ALLOCATOR_GUARD(); + gbString w = write_type_to_canonical_string(gb_string_make(temporary_allocator(), ""), type); + u64 hash = fnv64a(w, gb_string_length(w)); + return hash; +} +// NOTE(bill): This exists so that we deterministically hash a type by serializing it to a canonical string +gb_internal gbString write_type_to_canonical_string(gbString w, Type *type) { + if (type == nullptr) { + return gb_string_appendc(w, "<>"); // none/void type + } + + type = default_type(type); + GB_ASSERT(!is_type_untyped(type)); + + switch (type->kind) { + case Type_Basic: + return gb_string_append_length(w, type->Basic.name.text, type->Basic.name.len); + case Type_Pointer: + w = gb_string_append_rune(w, '^'); + return write_type_to_canonical_string(w, type->Pointer.elem); + case Type_MultiPointer: + w = gb_string_appendc(w, "[^]"); + return write_type_to_canonical_string(w, type->Pointer.elem); + case Type_SoaPointer: + w = gb_string_appendc(w, "#soa^"); + return write_type_to_canonical_string(w, type->Pointer.elem); + case Type_EnumeratedArray: + if (type->EnumeratedArray.is_sparse) { + w = gb_string_appendc(w, "#sparse"); + } + w = gb_string_append_rune(w, '['); + w = write_type_to_canonical_string(w, type->EnumeratedArray.index); + w = gb_string_append_rune(w, ']'); + return write_type_to_canonical_string(w, type->EnumeratedArray.elem); + case Type_Array: + w = gb_string_appendc(w, gb_bprintf("[%lld]", cast(long long)type->Array.count)); + return write_type_to_canonical_string(w, type->Array.elem); + case Type_Slice: + w = gb_string_appendc(w, "[]"); + return write_type_to_canonical_string(w, type->Array.elem); + case Type_DynamicArray: + w = gb_string_appendc(w, "[dynamic]"); + return write_type_to_canonical_string(w, type->DynamicArray.elem); + case Type_SimdVector: + w = gb_string_appendc(w, gb_bprintf("#simd[%lld]", cast(long long)type->SimdVector.count)); + return write_type_to_canonical_string(w, type->SimdVector.elem); + case Type_Matrix: + if (type->Matrix.is_row_major) { + w = gb_string_appendc(w, "#row_major "); + } + w = gb_string_appendc(w, gb_bprintf("matrix[%lld, %lld]", cast(long long)type->Matrix.row_count, cast(long long)type->Matrix.column_count)); + return write_type_to_canonical_string(w, type->Matrix.elem); + case Type_Map: + w = gb_string_appendc(w, "map["); + w = write_type_to_canonical_string(w, type->Map.key); + w = gb_string_appendc(w, "]"); + return write_type_to_canonical_string(w, type->Map.value); + + case Type_Enum: + w = gb_string_appendc(w, "enum"); + if (type->Enum.base_type != nullptr) { + w = gb_string_append_rune(w, ' '); + w = write_type_to_canonical_string(w, type->Enum.base_type); + w = gb_string_append_rune(w, ' '); + } + w = gb_string_append_rune(w, '{'); + for_array(i, type->Enum.fields) { + Entity *f = type->Enum.fields[i]; + GB_ASSERT(f->kind == Entity_Constant); + if (i > 0) { + w = gb_string_appendc(w, ","); + } + w = gb_string_append_length(w, f->token.string.text, f->token.string.len); + w = gb_string_appendc(w, "="); + w = write_exact_value_to_string(w, f->Constant.value); + } + return gb_string_append_rune(w, '}'); + case Type_BitSet: + w = gb_string_appendc(w, "bit_set["); + if (type->BitSet.elem == nullptr) { + w = write_type_to_canonical_string(w, type->BitSet.elem); + } else if (is_type_enum(type->BitSet.elem)) { + w = write_type_to_canonical_string(w, type->BitSet.elem); + } else { + w = gb_string_append_fmt(w, "%lld", type->BitSet.lower); + w = gb_string_append_fmt(w, "..="); + w = gb_string_append_fmt(w, "%lld", type->BitSet.upper); + } + if (type->BitSet.underlying != nullptr) { + w = gb_string_appendc(w, ";"); + w = write_type_to_canonical_string(w, type->BitSet.underlying); + } + return gb_string_appendc(w, "]"); + + case Type_Union: + w = gb_string_appendc(w, "union"); + return w; + case Type_Struct: + w = gb_string_appendc(w, "struct"); + return w; + + case Type_BitField: + w = gb_string_appendc(w, "bit_field"); + w = write_type_to_canonical_string(w, type->BitField.backing_type); + w = gb_string_appendc(w, " {"); + for (isize i = 0; i < type->BitField.fields.count; i++) { + Entity *f = type->BitField.fields[i]; + if (i > 0) { + w = gb_string_appendc(w, ","); + } + w = gb_string_append_length(w, f->token.string.text, f->token.string.len); + w = gb_string_appendc(w, ":"); + w = write_type_to_canonical_string(w, f->type); + w = gb_string_appendc(w, "|"); + w = gb_string_appendc(w, gb_bprintf("%u", type->BitField.bit_sizes[i])); + } + return gb_string_appendc(w, " }"); + + case Type_Proc: + w = gb_string_appendc(w, "proc"); + if (default_calling_convention() != type->Proc.calling_convention) { + w = gb_string_appendc(w, "\""); + w = gb_string_appendc(w, proc_calling_convention_strings[type->Proc.calling_convention]); + w = gb_string_appendc(w, "\""); + } + + w = write_canonical_params(w, type->Proc.params); + if (type->Proc.result_count > 0) { + w = gb_string_appendc(w, "->"); + w = write_canonical_params(w, type->Proc.results); + } + return w; + + case Type_Generic: + GB_PANIC("Type_Generic should never be hit"); + return w; + + case Type_Named: + if (type->Named.type_name != nullptr) { + Entity *e = type->Named.type_name; + if (e->pkg != nullptr) { + w = gb_string_append_length(w, e->pkg->name.text, e->pkg->name.len); + w = gb_string_appendc(w, "."); + } + Type *params = nullptr; + Entity *parent = type_get_polymorphic_parent(type, ¶ms); + if (parent) { + w = gb_string_append_length(w, parent->token.string.text, parent->token.string.len); + w = write_canonical_params(w, params); + } else { + w = gb_string_append_length(w, e->token.string.text, e->token.string.len); + } + } else { + w = gb_string_append_length(w, type->Named.name.text, type->Named.name.len); + } + // Handle parapoly stuff here? + return w; + + default: + GB_PANIC("unknown type kind %d", type->kind); + break; + } + + return w; +} \ No newline at end of file