From 9c632128245e9a51bb63273d0d4531ba28773629 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 9 Aug 2019 21:59:58 +0100 Subject: [PATCH] Struct field tags --- core/odin/ast/ast.odin | 20 +++++++++++-------- core/odin/parser/parser.odin | 24 ++++++++++++++++------- core/runtime/core.odin | 5 +++-- examples/demo/demo.odin | 25 ++++++++++++++++++++++++ src/check_type.cpp | 6 ++++-- src/ir.cpp | 37 ++++++++++++++++++++++++++++++++---- src/parser.cpp | 37 ++++++++++++++++++++++++++++-------- src/parser.hpp | 4 +++- src/types.cpp | 1 + 9 files changed, 127 insertions(+), 32 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index c0d2948ee..3655a601d 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -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, } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 0c011e07e..2d8e42720 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -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; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 02578da3b..28767cc2d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -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, diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 206fa4a2d..21f889746 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -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(); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 36d8cf0f8..a9c1d3fe3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -110,9 +110,10 @@ bool does_field_type_allow_using(Type *t) { return false; } -void check_struct_fields(CheckerContext *ctx, Ast *node, Array *fields, Array const ¶ms, +void check_struct_fields(CheckerContext *ctx, Ast *node, Array *fields, Array *tags, Array const ¶ms, isize init_field_capacity, Type *struct_type, String context) { *fields = array_make(heap_allocator(), 0, init_field_capacity); + *tags = array_make(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 *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) { diff --git a/src/ir.cpp b/src/ir.cpp index ee67c0ea8..2499b848f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -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; } diff --git a/src/parser.cpp b/src/parser.cpp index d1fd94c1d..9aebc78d6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -805,13 +805,14 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array names, Ast *type, Ast *default_value, u32 flags, - CommentGroup *docs, CommentGroup *comment) { +Ast *ast_field(AstFile *f, Array 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 empty_names = {}; auto list = array_make(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 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); } diff --git a/src/parser.hpp b/src/parser.hpp index 9f4f68a7b..3489f1a9b 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -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 names; \ Ast * type; \ Ast * default_value; \ + Token tag; \ u32 flags; \ CommentGroup * docs; \ CommentGroup * comment; \ diff --git a/src/types.cpp b/src/types.cpp index e5d2509e0..1197ba974 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -107,6 +107,7 @@ struct BasicType { struct TypeStruct { Array fields; + Array tags; Ast *node; Scope * scope;