mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-10 21:31:37 -07:00
Struct field tags
This commit is contained in:
+12
-8
@@ -435,7 +435,9 @@ Field_Flag :: enum {
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
In,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
Default_Parameters,
|
||||
Typeid_Token,
|
||||
}
|
||||
@@ -443,18 +445,19 @@ Field_Flag :: enum {
|
||||
Field_Flags :: distinct bit_set[Field_Flag];
|
||||
|
||||
Field_Flags_Struct :: Field_Flags{
|
||||
Field_Flag.Using,
|
||||
.Using,
|
||||
.Tags,
|
||||
};
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
Field_Flag.Typeid_Token,
|
||||
.Typeid_Token,
|
||||
};
|
||||
Field_Flags_Signature :: Field_Flags{
|
||||
Field_Flag.Ellipsis,
|
||||
Field_Flag.Using,
|
||||
Field_Flag.No_Alias,
|
||||
Field_Flag.C_Vararg,
|
||||
Field_Flag.Auto_Cast,
|
||||
Field_Flag.Default_Parameters,
|
||||
.Ellipsis,
|
||||
.Using,
|
||||
.No_Alias,
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Default_Parameters,
|
||||
};
|
||||
|
||||
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
|
||||
@@ -483,6 +486,7 @@ Field :: struct {
|
||||
names: []^Expr, // Could be polymorphic
|
||||
type: ^Expr,
|
||||
default_value: ^Expr,
|
||||
tag: token.Token,
|
||||
flags: Field_Flags,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
@@ -1388,19 +1388,18 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
|
||||
|
||||
for flag in ast.Field_Flag {
|
||||
if flag notin allowed_flags && flag in flags {
|
||||
using ast.Field_Flag;
|
||||
#complete switch flag {
|
||||
case Using:
|
||||
case .Using:
|
||||
error(p, p.curr_tok.pos, "'using' is not allowed within this field list");
|
||||
case No_Alias:
|
||||
case .No_Alias:
|
||||
error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list");
|
||||
case C_Vararg:
|
||||
case .C_Vararg:
|
||||
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list");
|
||||
case Auto_Cast:
|
||||
case .Auto_Cast:
|
||||
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list");
|
||||
case In:
|
||||
case .In:
|
||||
error(p, p.curr_tok.pos, "'in' is not allowed within this field list");
|
||||
case Ellipsis, Results, Default_Parameters, Typeid_Token:
|
||||
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
|
||||
panic("Impossible prefixes");
|
||||
}
|
||||
flags &~= {flag};
|
||||
@@ -1540,6 +1539,7 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel
|
||||
|
||||
type: ^ast.Expr;
|
||||
default_value: ^ast.Expr;
|
||||
tag: token.Token;
|
||||
|
||||
expect_token_after(p, token.Colon, "field list");
|
||||
if p.curr_tok.kind != token.Eq {
|
||||
@@ -1579,9 +1579,19 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel
|
||||
error(p, p.curr_tok.pos, "extra parameter after ellipsis without a default value");
|
||||
}
|
||||
|
||||
if type != nil && default_value == nil {
|
||||
if p.curr_tok.kind == token.String {
|
||||
tag = expect_token(p, token.String);
|
||||
if .Tags notin allowed_flags {
|
||||
error(p, tag.pos, "Field tags are only allowed within structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ok := expect_field_separator(p, type);
|
||||
|
||||
field := new_ast_field(names, type, default_value);
|
||||
field.tag = tag;
|
||||
field.docs = docs;
|
||||
field.flags = flags;
|
||||
field.comment = p.line_comment;
|
||||
|
||||
@@ -79,8 +79,9 @@ Type_Info_Tuple :: struct { // Only really used for procedures
|
||||
Type_Info_Struct :: struct {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []uintptr, // offsets may not be used in tuples
|
||||
usings: []bool, // usings may not be used in tuples
|
||||
offsets: []uintptr,
|
||||
usings: []bool,
|
||||
tags: []string,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
custom_align: bool,
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:runtime"
|
||||
|
||||
when os.OS == "windows" {
|
||||
import "core:thread"
|
||||
@@ -945,6 +946,29 @@ deferred_procedure_associations :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
struct_field_tags :: proc() {
|
||||
fmt.println("# struct_field_tags");
|
||||
|
||||
Foo :: struct {
|
||||
x: int `tag1`,
|
||||
y: string `tag2`,
|
||||
z: bool, // no tag
|
||||
}
|
||||
|
||||
f: Foo;
|
||||
ti := runtime.type_info_base(type_info_of(Foo));
|
||||
s := ti.variant.(runtime.Type_Info_Struct);
|
||||
fmt.println("Foo :: struct {");
|
||||
for _, i in s.names {
|
||||
if tag := s.tags[i]; tag != "" {
|
||||
fmt.printf("\t%s: %T `%s`,\n", s.names[i], s.types[i], tag);
|
||||
} else {
|
||||
fmt.printf("\t%s: %T,\n", s.names[i], s.types[i]);
|
||||
}
|
||||
}
|
||||
fmt.println("}");
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
general_stuff();
|
||||
@@ -963,5 +987,6 @@ main :: proc() {
|
||||
bit_set_type();
|
||||
diverging_procedures();
|
||||
deferred_procedure_associations();
|
||||
struct_field_tags();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -110,9 +110,10 @@ bool does_field_type_allow_using(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<Ast *> const ¶ms,
|
||||
void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const ¶ms,
|
||||
isize init_field_capacity, Type *struct_type, String context) {
|
||||
*fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
|
||||
*tags = array_make<String>(heap_allocator(), 0, init_field_capacity);
|
||||
|
||||
GB_ASSERT(node->kind == Ast_StructType);
|
||||
GB_ASSERT(struct_type->kind == Type_Struct);
|
||||
@@ -171,6 +172,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields
|
||||
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
|
||||
add_entity(ctx->checker, ctx->scope, name, field);
|
||||
array_add(fields, field);
|
||||
array_add(tags, p->tag.string);
|
||||
|
||||
field_src_index += 1;
|
||||
}
|
||||
@@ -504,7 +506,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
||||
|
||||
|
||||
if (!is_polymorphic) {
|
||||
check_struct_fields(ctx, node, &struct_type->Struct.fields, st->fields, min_field_count, struct_type, context);
|
||||
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) {
|
||||
|
||||
+33
-4
@@ -174,6 +174,7 @@ gbAllocator ir_allocator(void) {
|
||||
#define IR_TYPE_INFO_NAMES_NAME "__$type_info_names_data"
|
||||
#define IR_TYPE_INFO_OFFSETS_NAME "__$type_info_offsets_data"
|
||||
#define IR_TYPE_INFO_USINGS_NAME "__$type_info_usings_data"
|
||||
#define IR_TYPE_INFO_TAGS_NAME "__$type_info_tags_data"
|
||||
|
||||
|
||||
#define IR_INSTR_KINDS \
|
||||
@@ -5271,12 +5272,14 @@ gb_global irValue *ir_global_type_info_member_types = nullptr;
|
||||
gb_global irValue *ir_global_type_info_member_names = nullptr;
|
||||
gb_global irValue *ir_global_type_info_member_offsets = nullptr;
|
||||
gb_global irValue *ir_global_type_info_member_usings = nullptr;
|
||||
gb_global irValue *ir_global_type_info_member_tags = nullptr;
|
||||
|
||||
gb_global i32 ir_global_type_info_data_index = 0;
|
||||
gb_global i32 ir_global_type_info_member_types_index = 0;
|
||||
gb_global i32 ir_global_type_info_member_names_index = 0;
|
||||
gb_global i32 ir_global_type_info_member_offsets_index = 0;
|
||||
gb_global i32 ir_global_type_info_member_usings_index = 0;
|
||||
gb_global i32 ir_global_type_info_member_tags_index = 0;
|
||||
|
||||
isize ir_type_info_count(CheckerInfo *info) {
|
||||
return info->minimum_dependency_type_info_set.entries.count+1;
|
||||
@@ -9587,6 +9590,16 @@ void ir_init_module(irModule *m, Checker *c) {
|
||||
map_set(&m->members, hash_string(name), g);
|
||||
ir_global_type_info_member_usings = g;
|
||||
}
|
||||
|
||||
{
|
||||
String name = str_lit(IR_TYPE_INFO_TAGS_NAME);
|
||||
Entity *e = alloc_entity_variable(nullptr, make_token_ident(name),
|
||||
alloc_type_array(t_string, count), false);
|
||||
irValue *g = ir_value_global(e, nullptr);
|
||||
ir_module_add_value(m, e, g);
|
||||
map_set(&m->members, hash_string(name), g);
|
||||
ir_global_type_info_member_tags = g;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9720,6 +9733,11 @@ irValue *ir_type_info_member_usings_offset(irProcedure *proc, isize count) {
|
||||
ir_global_type_info_member_usings_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
irValue *ir_type_info_member_tags_offset(irProcedure *proc, isize count) {
|
||||
irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_tags, ir_global_type_info_member_tags_index);
|
||||
ir_global_type_info_member_tags_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10065,9 +10083,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
irValue *is_packed = ir_const_bool(t->Struct.is_packed);
|
||||
irValue *is_raw_union = ir_const_bool(t->Struct.is_raw_union);
|
||||
irValue *is_custom_align = ir_const_bool(t->Struct.custom_align != 0);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), is_packed);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_raw_union);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_custom_align);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_packed);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union);
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align);
|
||||
}
|
||||
|
||||
isize count = t->Struct.fields.count;
|
||||
@@ -10076,6 +10094,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
irValue *memory_names = ir_type_info_member_names_offset (proc, count);
|
||||
irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, count);
|
||||
irValue *memory_usings = ir_type_info_member_usings_offset (proc, count);
|
||||
irValue *memory_tags = ir_type_info_member_tags_offset (proc, count);
|
||||
|
||||
type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
|
||||
for (isize source_index = 0; source_index < count; source_index++) {
|
||||
@@ -10091,7 +10110,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
irValue *index = ir_const_int(source_index);
|
||||
irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index);
|
||||
irValue *offset = ir_emit_ptr_offset(proc, memory_offsets, index);
|
||||
irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index);
|
||||
irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index);
|
||||
|
||||
ir_emit_store(proc, type_info, ir_type_info(proc, f->type));
|
||||
if (f->token.string.len > 0) {
|
||||
@@ -10100,6 +10119,15 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
}
|
||||
ir_emit_store(proc, offset, ir_const_uintptr(foffset));
|
||||
ir_emit_store(proc, is_using, ir_const_bool((f->flags&EntityFlag_Using) != 0));
|
||||
|
||||
if (t->Struct.tags.count > 0) {
|
||||
String tag_string = t->Struct.tags[source_index];
|
||||
if (tag_string.len > 0) {
|
||||
irValue *tag_ptr = ir_emit_ptr_offset(proc, memory_tags, index);
|
||||
ir_emit_store(proc, tag_ptr, ir_const_string(tag_string));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
irValue *cv = ir_const_int(count);
|
||||
@@ -10107,6 +10135,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, cv);
|
||||
ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, cv);
|
||||
ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 3), memory_usings, cv);
|
||||
ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 4), memory_tags, cv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+29
-8
@@ -805,13 +805,14 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags,
|
||||
CommentGroup *docs, CommentGroup *comment) {
|
||||
Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag,
|
||||
CommentGroup *docs, CommentGroup *comment) {
|
||||
Ast *result = alloc_ast_node(f, Ast_Field);
|
||||
result->Field.names = names;
|
||||
result->Field.type = type;
|
||||
result->Field.default_value = default_value;
|
||||
result->Field.flags = flags;
|
||||
result->Field.tag = tag;
|
||||
result->Field.docs = docs;
|
||||
result->Field.comment = comment;
|
||||
return result;
|
||||
@@ -2740,7 +2741,8 @@ Ast *parse_results(AstFile *f, bool *diverging) {
|
||||
Array<Ast *> empty_names = {};
|
||||
auto list = array_make<Ast *>(heap_allocator(), 0, 1);
|
||||
Ast *type = parse_type(f);
|
||||
array_add(&list, ast_field(f, empty_names, type, nullptr, 0, nullptr, nullptr));
|
||||
Token tag = {};
|
||||
array_add(&list, ast_field(f, empty_names, type, nullptr, 0, tag, nullptr, nullptr));
|
||||
return ast_field_list(f, begin_token, list);
|
||||
}
|
||||
|
||||
@@ -3095,6 +3097,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
|
||||
|
||||
Ast *type = nullptr;
|
||||
Ast *default_value = nullptr;
|
||||
Token tag = {};
|
||||
|
||||
expect_token_after(f, Token_Colon, "field list");
|
||||
if (f->curr_token.kind != Token_Eq) {
|
||||
@@ -3132,16 +3135,25 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
|
||||
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
|
||||
}
|
||||
|
||||
if (type != nullptr && default_value == nullptr) {
|
||||
if (f->curr_token.kind == Token_String) {
|
||||
tag = expect_token(f, Token_String);
|
||||
if ((allowed_flags & FieldFlag_Tags) == 0) {
|
||||
syntax_error(tag, "Field tags are only allowed within structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse_expect_field_separator(f, type);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
CommentGroup *docs = f->lead_comment;
|
||||
|
||||
u32 set_flags = parse_field_prefixes(f);
|
||||
Token tag = {};
|
||||
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
|
||||
if (names.count == 0) {
|
||||
syntax_error(f->curr_token, "Empty field declaration");
|
||||
@@ -3184,9 +3196,18 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
|
||||
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
|
||||
}
|
||||
|
||||
if (type != nullptr && default_value == nullptr) {
|
||||
if (f->curr_token.kind == Token_String) {
|
||||
tag = expect_token(f, Token_String);
|
||||
if ((allowed_flags & FieldFlag_Tags) == 0) {
|
||||
syntax_error(tag, "Field tags are only allowed within structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ok = parse_expect_field_separator(f, param);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (!ok) {
|
||||
@@ -3210,8 +3231,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
|
||||
token.pos = ast_token(type).pos;
|
||||
names[0] = ast_ident(f, token);
|
||||
u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags);
|
||||
|
||||
Ast *param = ast_field(f, names, list[i].node, nullptr, flags, docs, f->line_comment);
|
||||
Token tag = {};
|
||||
Ast *param = ast_field(f, names, list[i].node, nullptr, flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -186,11 +186,12 @@ enum FieldFlag {
|
||||
FieldFlag_c_vararg = 1<<3,
|
||||
FieldFlag_auto_cast = 1<<4,
|
||||
|
||||
FieldFlag_Tags = 1<<10,
|
||||
|
||||
FieldFlag_Results = 1<<16,
|
||||
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast,
|
||||
FieldFlag_Struct = FieldFlag_using,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
enum StmtAllowFlag {
|
||||
@@ -412,6 +413,7 @@ AST_KIND(_DeclEnd, "", bool) \
|
||||
Array<Ast *> names; \
|
||||
Ast * type; \
|
||||
Ast * default_value; \
|
||||
Token tag; \
|
||||
u32 flags; \
|
||||
CommentGroup * docs; \
|
||||
CommentGroup * comment; \
|
||||
|
||||
@@ -107,6 +107,7 @@ struct BasicType {
|
||||
|
||||
struct TypeStruct {
|
||||
Array<Entity *> fields;
|
||||
Array<String> tags;
|
||||
Ast *node;
|
||||
Scope * scope;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user