mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-25 15:05:00 -07:00
Work on making name mangling deterministic
This commit is contained in:
@@ -1784,6 +1784,10 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
ctx->curr_proc_sig = type;
|
||||
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
|
||||
|
||||
if (decl->parent && decl->entity && decl->parent->entity) {
|
||||
decl->entity->parent_proc_decl = decl->parent;
|
||||
}
|
||||
|
||||
if (ctx->pkg->name != "runtime") {
|
||||
switch (type->Proc.calling_convention) {
|
||||
case ProcCC_None:
|
||||
@@ -1873,6 +1877,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
|
||||
check_open_scope(ctx, body);
|
||||
{
|
||||
ctx->scope->decl_info = decl;
|
||||
|
||||
for (auto const &entry : using_entities) {
|
||||
Entity *uvar = entry.uvar;
|
||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
|
||||
check_collect_entities(c, nodes);
|
||||
|
||||
for (auto const &entry : s->elements) {
|
||||
Entity *e = entry.value;
|
||||
Entity *e = entry.value;\
|
||||
switch (e->kind) {
|
||||
case Entity_Constant:
|
||||
case Entity_TypeName:
|
||||
|
||||
@@ -3894,6 +3894,7 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
||||
#include "check_expr.cpp"
|
||||
#include "check_builtin.cpp"
|
||||
#include "check_type.cpp"
|
||||
#include "name_canonicalization.cpp"
|
||||
#include "check_decl.cpp"
|
||||
#include "check_stmt.cpp"
|
||||
|
||||
|
||||
@@ -276,6 +276,8 @@ struct Scope {
|
||||
StringMap<Entity *> elements;
|
||||
PtrSet<Scope *> imported;
|
||||
|
||||
DeclInfo *decl_info;
|
||||
|
||||
i32 flags; // ScopeFlag
|
||||
union {
|
||||
AstPackage *pkg;
|
||||
|
||||
@@ -257,6 +257,7 @@ struct Entity {
|
||||
bool has_instrumentation : 1;
|
||||
bool is_memcpy_like : 1;
|
||||
bool uses_branch_location : 1;
|
||||
bool is_anonymous : 1;
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
|
||||
+1
-1
@@ -5856,7 +5856,7 @@ gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
|
||||
|
||||
|
||||
gb_inline char *gb_bprintf_va(char const *fmt, va_list va) {
|
||||
gb_local_persist char buffer[4096];
|
||||
gb_thread_local gb_local_persist char buffer[4096];
|
||||
gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ struct lbProcedure {
|
||||
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
|
||||
|
||||
gb_internal String lb_mangle_name(Entity *e);
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e);
|
||||
|
||||
gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
|
||||
gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
|
||||
|
||||
@@ -1444,6 +1444,7 @@ gb_internal void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
|
||||
}
|
||||
|
||||
gb_internal String lb_mangle_name(Entity *e) {
|
||||
#if 1
|
||||
String name = e->token.string;
|
||||
|
||||
AstPackage *pkg = e->pkg;
|
||||
@@ -1483,9 +1484,18 @@ gb_internal String lb_mangle_name(Entity *e) {
|
||||
|
||||
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
|
||||
return mangled_name;
|
||||
#else
|
||||
gbString w = gb_string_make(gb_heap_allocator(), "");
|
||||
w = write_canonical_entity_name(w, e);
|
||||
gb_printf_err(">> %s\n", w);
|
||||
|
||||
String mangled_name = make_string(cast(u8 const *)w, gb_string_length(w));
|
||||
return mangled_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p, lbModule *module) {
|
||||
#if 0
|
||||
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
|
||||
// and as a result, the declaration does not have time to determine what it should be
|
||||
|
||||
@@ -1516,6 +1526,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
String ts_name = e->token.string;
|
||||
@@ -1528,6 +1539,12 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
|
||||
{
|
||||
String s = type_to_canonical_string(temporary_allocator(), e->type);
|
||||
gb_printf_err("1) %.*s\n", LIT(s));
|
||||
gb_printf_err("2) %.*s\n", LIT(name));
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
|
||||
@@ -1538,11 +1555,18 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
|
||||
{
|
||||
String s = type_to_canonical_string(temporary_allocator(), e->type);
|
||||
gb_printf_err("3) %.*s\n", LIT(s));
|
||||
gb_printf_err("4) %.*s\n", LIT(name));
|
||||
}
|
||||
return name;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e) {
|
||||
GB_ASSERT(m != nullptr);
|
||||
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
@@ -1553,6 +1577,13 @@ gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_nam
|
||||
return e->token.string;
|
||||
}
|
||||
|
||||
#if 1
|
||||
gbString w = gb_string_make(heap_allocator(), "");
|
||||
w = write_canonical_entity_name(w, e);
|
||||
defer (gb_string_free(w));
|
||||
|
||||
String name = copy_string(permanent_allocator(), make_string(cast(u8 const *)w, gb_string_length(w)));
|
||||
#else
|
||||
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
|
||||
return lb_set_nested_type_name_ir_mangled_name(e, nullptr, m);
|
||||
}
|
||||
@@ -1576,11 +1607,17 @@ gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_nam
|
||||
|
||||
if (!no_name_mangle) {
|
||||
name = lb_mangle_name(e);
|
||||
|
||||
gbString w = gb_string_make(gb_heap_allocator(), "");
|
||||
w = write_canonical_entity_name(w, e);
|
||||
if (w[0] == 0) {
|
||||
gb_printf_err(">> %s %.*s\n", w, LIT(name));
|
||||
}
|
||||
}
|
||||
if (name.len == 0) {
|
||||
name = e->token.string;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (e->kind == Entity_TypeName) {
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
@@ -2869,6 +2906,8 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
|
||||
pl->decl->code_gen_module = m;
|
||||
e->decl_info = pl->decl;
|
||||
pl->decl->entity = e;
|
||||
e->parent_proc_decl = pl->decl->parent;
|
||||
e->Procedure.is_anonymous = true;
|
||||
e->flags |= EntityFlag_ProcBodyChecked;
|
||||
|
||||
lbProcedure *p = lb_create_procedure(m, e);
|
||||
|
||||
@@ -32,7 +32,8 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
|
||||
continue;
|
||||
}
|
||||
|
||||
lb_set_nested_type_name_ir_mangled_name(e, p, p->module);
|
||||
String name = lb_get_entity_name(p->module, e);
|
||||
gb_unused(name);
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
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;
|
||||
}
|
||||
|
||||
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
|
||||
gbString w = gb_string_make(allocator, "");
|
||||
w = write_type_to_canonical_string(w, type);
|
||||
return make_string(cast(u8 const *)w, gb_string_length(w));
|
||||
}
|
||||
|
||||
gb_internal void print_scope_flags(Scope *s) {
|
||||
if (s->flags & ScopeFlag_Pkg) gb_printf_err("Pkg ");
|
||||
if (s->flags & ScopeFlag_Builtin) gb_printf_err("Builtin ");
|
||||
if (s->flags & ScopeFlag_Global) gb_printf_err("Global ");
|
||||
if (s->flags & ScopeFlag_File) gb_printf_err("File ");
|
||||
if (s->flags & ScopeFlag_Init) gb_printf_err("Init ");
|
||||
if (s->flags & ScopeFlag_Proc) gb_printf_err("Proc ");
|
||||
if (s->flags & ScopeFlag_Type) gb_printf_err("Type ");
|
||||
if (s->flags & ScopeFlag_HasBeenImported) gb_printf_err("HasBeenImported ");
|
||||
if (s->flags & ScopeFlag_ContextDefined) gb_printf_err("ContextDefined ");
|
||||
gb_printf_err("\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal gbString write_canonical_parent_prefix(gbString w, Entity *e, bool ignore_final_dot=false) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
// auto const &parent_entity = [](Scope *s) -> Entity* {
|
||||
// while ((s->flags & (ScopeFlag_Proc|ScopeFlag_File)) == 0 && s->decl_info == nullptr) {
|
||||
// s = s->parent;
|
||||
// }
|
||||
// if (s->decl_info && s->decl_info->entity) {
|
||||
// return s->decl_info->entity;
|
||||
// }
|
||||
// return nullptr;
|
||||
// };
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
if (e->Procedure.is_export || e->Procedure.is_foreign) {
|
||||
// no prefix
|
||||
return w;
|
||||
}
|
||||
if (e->parent_proc_decl) {
|
||||
Entity *p = e->parent_proc_decl->entity;
|
||||
w = write_canonical_parent_prefix(w, p);
|
||||
w = gb_string_append_length(w, p->token.string.text, p->token.string.len);
|
||||
if (is_type_polymorphic(p->type)) {
|
||||
w = gb_string_appendc(w, "::");
|
||||
w = write_type_to_canonical_string(w, p->type);
|
||||
}
|
||||
w = gb_string_appendc(w, ".");
|
||||
|
||||
} else if (e->pkg && (scope_lookup_current(e->pkg->scope, e->token.string) == e)) {
|
||||
w = gb_string_append_length(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
gb_string_appendc(w, "$");
|
||||
}
|
||||
w = gb_string_appendc(w, ".");
|
||||
} else {
|
||||
String file_name = filename_without_directory(e->file->fullpath);
|
||||
w = gb_string_append_length(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
gb_string_appendc(w, "$");
|
||||
}
|
||||
w = gb_string_appendc(w, gb_bprintf(".[%.*s].", LIT(file_name)));
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
if (e->Procedure.is_export || e->Procedure.is_foreign) {
|
||||
// no prefix
|
||||
return w;
|
||||
}
|
||||
GB_PANIC("TODO(bill): handle entity kind: %d", e->kind);
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Procedure && e->Procedure.is_anonymous) {
|
||||
w = gb_string_appendc(w, gb_bprintf("$anon%d", e->token.pos.offset));
|
||||
} else {
|
||||
w = gb_string_append_length(w, e->token.string.text, e->token.string.len);
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(e->type)) {
|
||||
w = gb_string_appendc(w, "::");
|
||||
w = write_type_to_canonical_string(w, e->type);
|
||||
}
|
||||
if (!ignore_final_dot) {
|
||||
w = gb_string_appendc(w, ".");
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
gb_internal gbString write_canonical_entity_name(gbString w, Entity *e) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
if (e->token.string == "_") {
|
||||
GB_PANIC("_ string");
|
||||
}
|
||||
if (e->token.string.len == 0) {
|
||||
GB_PANIC("empty string");
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
w = gb_string_append_length(w, e->Variable.link_name.text, e->Variable.link_name.len);
|
||||
return w;
|
||||
} else if (is_foreign || is_export) {
|
||||
w = gb_string_append_length(w, e->token.string.text, e->token.string.len);
|
||||
return w;
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
|
||||
w = gb_string_append_length(w, e->Procedure.link_name.text, e->Procedure.link_name.len);
|
||||
return w;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
|
||||
w = gb_string_append_length(w, e->token.string.text, e->token.string.len);
|
||||
return w;
|
||||
}
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0 ||
|
||||
e->flags & EntityFlag_NotExported) {
|
||||
|
||||
Scope *s = e->scope;
|
||||
while ((s->flags & (ScopeFlag_Proc|ScopeFlag_File)) == 0 && s->decl_info == nullptr) {
|
||||
s = s->parent;
|
||||
}
|
||||
|
||||
if (s->decl_info != nullptr && s->decl_info->entity) {
|
||||
w = write_canonical_parent_prefix(w, s->decl_info->entity);
|
||||
goto write_base_name;
|
||||
} else if ((s->flags & ScopeFlag_File) && s->file != nullptr) {
|
||||
String file_name = filename_without_directory(s->file->fullpath);
|
||||
w = gb_string_append_length(w, e->pkg->name.text, e->pkg->name.len);
|
||||
if (e->pkg->name == "llvm") {
|
||||
gb_string_appendc(w, "$");
|
||||
}
|
||||
w = gb_string_appendc(w, gb_bprintf(".[%.*s].", LIT(file_name)));
|
||||
goto write_base_name;
|
||||
}
|
||||
gb_printf_err("%s HERE %s %u %p\n", token_pos_to_string(e->token.pos), type_to_string(e->type), s->flags, s->decl_info);
|
||||
print_scope_flags(s);
|
||||
GB_PANIC("weird entity");
|
||||
}
|
||||
if (e->pkg != nullptr) {
|
||||
w = gb_string_append_length(w, e->pkg->name.text, e->pkg->name.len);
|
||||
w = gb_string_appendc(w, ".");
|
||||
}
|
||||
|
||||
write_base_name:
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_TypeName:
|
||||
{
|
||||
Type *params = nullptr;
|
||||
Entity *parent = type_get_polymorphic_parent(e->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);
|
||||
}
|
||||
}
|
||||
// Handle parapoly stuff here?
|
||||
return w;
|
||||
|
||||
case Entity_Procedure:
|
||||
case Entity_Variable:
|
||||
w = gb_string_append_length(w, e->token.string.text, e->token.string.len);
|
||||
if (is_type_polymorphic(e->type)) {
|
||||
w = gb_string_appendc(w, "::");
|
||||
w = write_type_to_canonical_string(w, e->type);
|
||||
}
|
||||
return w;
|
||||
|
||||
default:
|
||||
GB_PANIC("TODO(bill): entity kind %d", e->kind);
|
||||
break;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
switch (type->Union.kind) {
|
||||
case UnionType_no_nil: w = gb_string_appendc(w, "#no_nil"); break;
|
||||
case UnionType_shared_nil: w = gb_string_appendc(w, "#shared_nil"); break;
|
||||
}
|
||||
if (type->Union.custom_align != 0) {
|
||||
w = gb_string_append_fmt(w, "#align(%lld)", cast(long long)type->Union.custom_align);
|
||||
}
|
||||
w = gb_string_appendc(w, "{");
|
||||
for_array(i, type->Union.variants) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (i > 0) w = gb_string_appendc(w, ", ");
|
||||
w = write_type_to_canonical_string(w, t);
|
||||
}
|
||||
return gb_string_appendc(w, "}");
|
||||
case Type_Struct:
|
||||
if (type->Struct.soa_kind != StructSoa_None) {
|
||||
switch (type->Struct.soa_kind) {
|
||||
case StructSoa_Fixed: w = gb_string_append_fmt(w, "#soa[%lld]", cast(long long)type->Struct.soa_count); break;
|
||||
case StructSoa_Slice: w = gb_string_appendc(w, "#soa[]"); break;
|
||||
case StructSoa_Dynamic: w = gb_string_appendc(w, "#soa[dynamic]"); break;
|
||||
default: GB_PANIC("Unknown StructSoaKind"); break;
|
||||
}
|
||||
return write_type_to_canonical_string(w, type->Struct.soa_elem);
|
||||
}
|
||||
|
||||
w = gb_string_appendc(w, "struct");
|
||||
if (type->Struct.is_packed) w = gb_string_appendc(w, "#packed");
|
||||
if (type->Struct.is_raw_union) w = gb_string_appendc(w, "#raw_union");
|
||||
if (type->Struct.is_no_copy) w = gb_string_appendc(w, "#no_copy");
|
||||
if (type->Struct.custom_min_field_align != 0) w = gb_string_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
|
||||
if (type->Struct.custom_max_field_align != 0) w = gb_string_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
|
||||
if (type->Struct.custom_align != 0) w = gb_string_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
|
||||
w = gb_string_appendc(w, "{");
|
||||
for_array(i, type->Struct.fields) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
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);
|
||||
String tag = type->Struct.tags[i];
|
||||
if (tag.len != 0) {
|
||||
String s = quote_to_ascii(heap_allocator(), tag);
|
||||
w = gb_string_append_length(w, s.text, s.len);
|
||||
gb_free(heap_allocator(), s.text);
|
||||
}
|
||||
}
|
||||
return gb_string_appendc(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) {
|
||||
return write_canonical_entity_name(w, type->Named.type_name);
|
||||
} 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;
|
||||
}
|
||||
-269
@@ -4872,272 +4872,3 @@ gb_internal gbString type_to_string(Type *type, bool shorthand) {
|
||||
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;
|
||||
}
|
||||
|
||||
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
|
||||
gbString w = gb_string_make(allocator, "");
|
||||
w = write_type_to_canonical_string(w, type);
|
||||
return make_string(cast(u8 const *)w, gb_string_length(w));
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
switch (type->Union.kind) {
|
||||
case UnionType_no_nil: w = gb_string_appendc(w, "#no_nil"); break;
|
||||
case UnionType_shared_nil: w = gb_string_appendc(w, "#shared_nil"); break;
|
||||
}
|
||||
if (type->Union.custom_align != 0) {
|
||||
w = gb_string_append_fmt(w, "#align(%lld)", cast(long long)type->Union.custom_align);
|
||||
}
|
||||
w = gb_string_appendc(w, "{");
|
||||
for_array(i, type->Union.variants) {
|
||||
Type *t = type->Union.variants[i];
|
||||
if (i > 0) w = gb_string_appendc(w, ", ");
|
||||
w = write_type_to_canonical_string(w, t);
|
||||
}
|
||||
return gb_string_appendc(w, "}");
|
||||
case Type_Struct:
|
||||
if (type->Struct.soa_kind != StructSoa_None) {
|
||||
switch (type->Struct.soa_kind) {
|
||||
case StructSoa_Fixed: w = gb_string_append_fmt(w, "#soa[%lld]", cast(long long)type->Struct.soa_count); break;
|
||||
case StructSoa_Slice: w = gb_string_appendc(w, "#soa[]"); break;
|
||||
case StructSoa_Dynamic: w = gb_string_appendc(w, "#soa[dynamic]"); break;
|
||||
default: GB_PANIC("Unknown StructSoaKind"); break;
|
||||
}
|
||||
return write_type_to_canonical_string(w, type->Struct.soa_elem);
|
||||
}
|
||||
|
||||
w = gb_string_appendc(w, "struct");
|
||||
if (type->Struct.is_packed) w = gb_string_appendc(w, "#packed");
|
||||
if (type->Struct.is_raw_union) w = gb_string_appendc(w, "#raw_union");
|
||||
if (type->Struct.is_no_copy) w = gb_string_appendc(w, "#no_copy");
|
||||
if (type->Struct.custom_min_field_align != 0) w = gb_string_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
|
||||
if (type->Struct.custom_max_field_align != 0) w = gb_string_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
|
||||
if (type->Struct.custom_align != 0) w = gb_string_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
|
||||
w = gb_string_appendc(w, "{");
|
||||
for_array(i, type->Struct.fields) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
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);
|
||||
String tag = type->Struct.tags[i];
|
||||
if (tag.len != 0) {
|
||||
String s = quote_to_ascii(heap_allocator(), tag);
|
||||
w = gb_string_append_length(w, s.text, s.len);
|
||||
gb_free(heap_allocator(), s.text);
|
||||
}
|
||||
}
|
||||
return gb_string_appendc(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->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0 ||
|
||||
e->flags & EntityFlag_NotExported) {
|
||||
if (e->scope->flags & ScopeFlag_Proc) {
|
||||
GB_PANIC("NESTED IN PROC\n");
|
||||
} else if (e->scope->flags & ScopeFlag_File) {
|
||||
GB_PANIC("PRIVATE TO FILE\n");
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user