Add @(raddbg_type_view=<optional-string>)

If no string parameter is provided, then one will be generated from the struct field tags.
The attribute must be applied if the automatic struct field tag approach is to be used.
This commit is contained in:
gingerBill
2025-08-21 17:14:33 +01:00
parent 0c9e1f5807
commit d5b1fc48fb
4 changed files with 210 additions and 0 deletions
+7
View File
@@ -602,6 +602,13 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
} else if (ac.objc_is_implementation) {
error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
}
if (ac.raddbg_type_view) {
RaddbgTypeView type_view = {};
type_view.type = e->type;
type_view.view = ac.raddbg_type_view_string;
mpsc_enqueue(&ctx->info->raddbg_type_views_queue, type_view);
}
}
+170
View File
@@ -1451,6 +1451,9 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
mpsc_init(&i->raddbg_type_views_queue, a);
array_init(&i->raddbg_type_views, a);
string_map_init(&i->load_directory_cache);
map_init(&i->load_directory_map);
}
@@ -1479,6 +1482,9 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
mpsc_destroy(&i->foreign_decls_to_check);
mpsc_destroy(&i->raddbg_type_views_queue);
array_free(&i->raddbg_type_views);
map_destroy(&i->objc_msgSend_types);
string_set_destroy(&i->obcj_class_name_set);
mpsc_destroy(&i->objc_class_implementations);
@@ -4066,6 +4072,21 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
return true;
}
} else if (name == "raddbg_type_view") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Invalid) {
ac->raddbg_type_view = true;
} else if (ev.kind == ExactValue_String) {
ac->raddbg_type_view = true;
ac->raddbg_type_view_string = ev.value_string;
if (ev.value_string.len == 0) {
error(elem, "Expected a non-empty string for '%.*s'", LIT(name));
}
} else {
error(elem, "Expected a string or no value for '%.*s'", LIT(name));
}
return true;
}
return false;
}
@@ -7037,6 +7058,155 @@ gb_internal void check_parsed_files(Checker *c) {
}
}
TIME_SECTION("collate type info stuff");
{
auto const struct_tag_lookup = [](String tag, char const *key_c, String *value_) -> bool {
String t = tag;
String key = make_string_c(key_c);
while (t.len != 0) {
isize i = 0;
while (i < t.len && t[i] == ' ') { // Skip whitespace
i += 1;
}
t.text += i;
t.len -= i;
if (t.len == 0) {
break;
}
i = 0;
while (i < t.len) {
u8 c = t[i];
if (c == ':' || c == '"') {
break;
} else if ((0 <= c && c < ' ') || (0x7f <= c && c <= 0x9f)) {
// break if control character is found
break;
}
i += 1;
}
if (i == 0) {
break;
}
if (i+1 >= t.len) {
break;
}
if (t[i] != ':' || t[i+1] != '"') {
break;
}
String name = {t.text, i};
t = {t.text+i+1, t.len-(i+1)};
i = 1;
while (i < t.len && t[i] != '"') { // find closing quote
if (t[i] == '\\') {
i += 1; // Skip escaped characters
}
i += 1;
}
if (i >= t.len) {
break;
}
String value = {t.text, i+1};
t = {t.text+i+1, t.len-(i+1)};
if (key == name) {
value = {value.text+1, i-1};
if (value_) *value_ = value;
return true;
}
}
return false;
};
for (RaddbgTypeView type_view; mpsc_dequeue(&c->info.raddbg_type_views_queue, &type_view); /**/) {
Type *type = type_view.type;
if (type == nullptr || type == t_invalid) {
continue;
}
String view = type_view.view;
if (view.len == 0) {
// NOTE(bill): Generate one automatically from the struct field tags if they exist
// If it cannot be generated, it'll be ignored/err
Type *bt = base_type(type);
if (is_type_struct(type)) {
GB_ASSERT(bt->kind == Type_Struct);
if (bt->Struct.tags != nullptr) {
bool found_any = false;
for (isize i = 0; i < bt->Struct.fields.count; i++) {
String tag = bt->Struct.tags[i];
String value = {};
if (struct_tag_lookup(tag, "raddbg", &value)) {
found_any = true;
} else if (struct_tag_lookup(tag, "fmt", &value)) {
found_any = true;
}
}
if (!found_any) {
goto raddbg_type_view_end;
}
gbString s = gb_string_make(heap_allocator(), "");
s = gb_string_appendc(s, "rows($");
for (isize i = 0; i < bt->Struct.fields.count; i++) {
Entity *field = bt->Struct.fields[i];
GB_ASSERT(field != nullptr);
String name = field->token.string;
s = gb_string_appendc(s, ", ");
bool custom_rule = false;
String tag = bt->Struct.tags[i];
String value = {};
if (struct_tag_lookup(tag, "raddbg", &value)) {
s = gb_string_append_length(s, value.text, value.len);
custom_rule = true;
} else if (struct_tag_lookup(tag, "fmt", &value)) {
auto p = string_partition(value, make_string_c(","));
String tail = p.tail;
if (tail.len != 0 && tail != "0") {
s = gb_string_appendc(s, "array(");
s = gb_string_append_length(s, name.text, name.len);
s = gb_string_appendc(s, ", ");
s = gb_string_append_length(s, tail.text, tail.len);
s = gb_string_appendc(s, ")");
custom_rule = true;
}
}
if (!custom_rule) {
s = gb_string_append_length(s, name.text, name.len);
}
}
s = gb_string_appendc(s, ")");
view = make_string((u8 const *)s, gb_string_length(s));
}
}
}
raddbg_type_view_end:;
if (view.len == 0) {
// Ignore the type, it didn't anything custom
continue;
}
array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
}
}
TIME_SECTION("type check finish");
}
+11
View File
@@ -161,6 +161,9 @@ struct AttributeContext {
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
bool raddbg_type_view;
String raddbg_type_view_string;
};
gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) {
@@ -427,6 +430,11 @@ struct Defineable {
String pos_str;
};
struct RaddbgTypeView {
Type * type;
String view;
};
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -487,6 +495,9 @@ struct CheckerInfo {
MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
MPSCQueue<Entity *> foreign_decls_to_check;
MPSCQueue<RaddbgTypeView> raddbg_type_views_queue;
Array<RaddbgTypeView> raddbg_type_views;
MPSCQueue<Ast *> intrinsics_entry_point_usage;
BlockingMutex objc_objc_msgSend_mutex;
+22
View File
@@ -3416,7 +3416,29 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
TEMPORARY_ALLOCATOR_GUARD();
for (RaddbgTypeView const &type_view : gen->info->raddbg_type_views) {
if (type_view.type == nullptr) {
continue;
}
if (type_view.view.len == 0) {
continue;
}
String t_str = type_to_canonical_string(temporary_allocator(), type_view.type);
gbString s = gb_string_make(temporary_allocator(), "");
s = gb_string_appendc(s, "type_view: {type: \"");
s = gb_string_append_length(s, t_str.text, t_str.len);
s = gb_string_appendc(s, "\", expr: \"");
s = gb_string_append_length(s, type_view.view.text, type_view.view.len);
s = gb_string_appendc(s, "\"}");
lb_add_raddbg_string(m, s);
}
TEMPORARY_ALLOCATOR_GUARD();
u32 global_name_index = 0;
for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) {
LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false);