diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index b36716971..eb0b312e8 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -409,6 +409,11 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { write_type(buf, info.underlying); } write_byte(buf, ']'); + + case runtime.Type_Info_Opaque: + write_string(buf, "opaque "); + write_type(buf, info.elem); + } } @@ -927,6 +932,47 @@ fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") { } } +fmt_opaque :: proc(fi: ^Fmt_Info, v: any) { + is_nil :: proc(data: rawptr, n: int) -> bool { + if data == nil do return true; + if n == 0 do return true; + + a := (^byte)(data); + for i in 0..n-1 do if mem.ptr_offset(a, i)^ != 0 { + return false; + } + return true; + } + + rt :: runtime; + + type_info := type_info_of(v.id); + + if is_nil(v.data, type_info.size) { + write_string(fi.buf, "nil"); + return; + } + + if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok { + elem := rt.type_info_base(ot.elem); + if elem == nil do return; + write_type(fi.buf, type_info); + write_byte(fi.buf, '{'); + defer write_byte(fi.buf, '}'); + + switch in elem.variant { + case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float: + fmt_value(fi, any{v.data, elem.id}, 'v'); + case: + // Okay + } + } else { + write_type(fi.buf, type_info); + write_byte(fi.buf, '{'); + defer write_byte(fi.buf, '}'); + } +} + fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { if v.data == nil || v.id == nil { write_string(fi.buf, ""); @@ -986,6 +1032,8 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { fmt_bit_set(fi, v); case runtime.Type_Info_Bit_Field: fmt_bit_field(fi, v); + case runtime.Type_Info_Opaque: + fmt_opaque(fi, v); case: fmt_value(fi, any{v.data, info.base.id}, verb); } @@ -1158,6 +1206,9 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v); + + case runtime.Type_Info_Opaque: + fmt_opaque(fi, v); } } diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 808de0a38..42cb68aee 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -107,6 +107,10 @@ Type_Info_Bit_Set :: struct { upper: i64, }; +Type_Info_Opaque :: struct { + elem: ^Type_Info, +} + Type_Info :: struct { size: int, align: int, @@ -134,6 +138,7 @@ Type_Info :: struct { Type_Info_Map, Type_Info_Bit_Field, Type_Info_Bit_Set, + Type_Info_Opaque, }, } @@ -160,6 +165,7 @@ Typeid_Kind :: enum u8 { Map, Bit_Field, Bit_Set, + Opaque, } Typeid_Bit_Field :: bit_field #align align_of(uintptr) { diff --git a/core/types/types.odin b/core/types/types.odin index 025e0d2c6..485530fea 100644 --- a/core/types/types.odin +++ b/core/types/types.odin @@ -154,12 +154,16 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool { y, ok := b.variant.(rt.Type_Info_Bit_Set); if !ok do return false; return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper; + + case rt.Type_Info_Opaque: + y, ok := b.variant.(rt.Type_Info_Opaque); + if !ok do return false; + return x.elem == y.elem; } return false; } - is_signed :: proc(info: ^rt.Type_Info) -> bool { if info == nil do return false; switch i in rt.type_info_base(info).variant { @@ -258,3 +262,8 @@ is_enum :: proc(info: ^rt.Type_Info) -> bool { _, ok := rt.type_info_base(info).variant.(rt.Type_Info_Enum); return ok; } +is_opaque :: proc(info: ^rt.Type_Info) -> bool { + if info == nil do return false; + _, ok := rt.type_info_base(info).variant.(rt.Type_Info_Opaque); + return ok; +} diff --git a/src/check_decl.cpp b/src/check_decl.cpp index de0aa572d..3fb53bb19 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -198,6 +198,9 @@ bool is_type_distinct(Ast *node) { case Ast_DynamicArrayType: case Ast_MapType: return false; + + case Ast_OpaqueType: + return true; } return false; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6b2a07a86..61bf9eeca 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1911,6 +1911,13 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_opaque(src)) { + return are_types_identical(dst, src->Opaque.elem); + } + if (is_type_opaque(dst)) { + return are_types_identical(dst->Opaque.elem, src); + } + return false; } @@ -6602,6 +6609,11 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = write_expr_to_string(str, ht->type); case_end; + case_ast_node(ht, OpaqueType, node); + str = gb_string_appendc(str, "opaque "); + str = write_expr_to_string(str, ht->type); + case_end; + case_ast_node(pt, PolyType, node); str = gb_string_append_rune(str, '$'); str = write_expr_to_string(str, pt->type); diff --git a/src/check_type.cpp b/src/check_type.cpp index f38826a25..5bb404ae8 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2341,6 +2341,13 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t return true; case_end; + case_ast_node(ot, OpaqueType, e); + Type *elem = strip_opaque_type(check_type(ctx, ot->type)); + *type = alloc_type_opaque(elem); + set_base_type(named_type, *type); + return true; + case_end; + case_ast_node(at, ArrayType, e); if (at->count != nullptr) { Operand o = {}; diff --git a/src/checker.cpp b/src/checker.cpp index 5daefc896..d289d236c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1064,6 +1064,10 @@ void add_type_info_type(CheckerContext *c, Type *t) { add_type_info_type(c, bt->BitSet.elem); break; + case Type_Opaque: + add_type_info_type(c, bt->Opaque.elem); + break; + case Type_Union: add_type_info_type(c, t_int); add_type_info_type(c, t_type_info_ptr); @@ -1626,6 +1630,7 @@ void init_core_type_info(Checker *c) { t_type_info_map = find_core_type(c, str_lit("Type_Info_Map")); t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); + t_type_info_opaque = find_core_type(c, str_lit("Type_Info_Opaque")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -1648,6 +1653,7 @@ void init_core_type_info(Checker *c) { t_type_info_map_ptr = alloc_type_pointer(t_type_info_map); t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); + t_type_info_opaque_ptr = alloc_type_pointer(t_type_info_opaque); } void init_mem_allocator(Checker *c) { diff --git a/src/ir.cpp b/src/ir.cpp index 6f1637b7a..39c7eb25c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2273,6 +2273,10 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD return di; } + if (is_type_opaque(type)) { + return ir_add_debug_info_type(module, strip_opaque_type(type), e, scope, file); + } + if (is_type_struct(type) || is_type_union(type) || is_type_enum(type) || is_type_tuple(type)) { if (type->kind == Type_Named) { @@ -9455,10 +9459,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), ir_const_i64(t->BitSet.upper)); break; - - + case Type_Opaque: + ir_emit_comment(proc, str_lit("Type_Opaque")); + tag = ir_emit_conv(proc, variant_ptr, t_type_info_opaque_ptr); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->Opaque.elem)); + break; } + if (tag != nullptr) { Type *tag_type = type_deref(ir_type(tag)); GB_ASSERT(is_type_named(tag_type)); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c8b255d2f..1879e6d78 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -512,6 +512,10 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { ir_print_type(f, m, bit_set_to_int(t)); return; } + + case Type_Opaque: + ir_print_type(f, m, strip_opaque_type(t)); + return; } } diff --git a/src/parser.cpp b/src/parser.cpp index 38678a6dc..e7181bfbb 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -80,6 +80,7 @@ Token ast_token(Ast *node) { case Ast_TypeidType: return node->TypeidType.token; case Ast_HelperType: return node->HelperType.token; case Ast_DistinctType: return node->DistinctType.token; + case Ast_OpaqueType: return node->OpaqueType.token; case Ast_PolyType: return node->PolyType.token; case Ast_ProcType: return node->ProcType.token; case Ast_PointerType: return node->PointerType.token; @@ -319,6 +320,9 @@ Ast *clone_ast(Ast *node) { case Ast_DistinctType: n->DistinctType.type = clone_ast(n->DistinctType.type); break; + case Ast_OpaqueType: + n->OpaqueType.type = clone_ast(n->OpaqueType.type); + break; case Ast_ProcType: n->ProcType.params = clone_ast(n->ProcType.params); n->ProcType.results = clone_ast(n->ProcType.results); @@ -849,6 +853,13 @@ Ast *ast_distinct_type(AstFile *f, Token token, Ast *type) { return result; } +Ast *ast_opaque_type(AstFile *f, Token token, Ast *type) { + Ast *result = alloc_ast_node(f, Ast_OpaqueType); + result->OpaqueType.token = token; + result->OpaqueType.type = type; + return result; +} + Ast *ast_poly_type(AstFile *f, Token token, Ast *type, Ast *specialization) { Ast *result = alloc_ast_node(f, Ast_PolyType); result->PolyType.token = token; @@ -1654,6 +1665,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { return ast_distinct_type(f, token, type); } + case Token_opaque: { + Token token = expect_token(f, Token_opaque); + Ast *type = parse_type(f); + return ast_opaque_type(f, token, type); + } + case Token_Hash: { Token token = expect_token(f, Token_Hash); Token name = expect_token(f, Token_Ident); diff --git a/src/parser.hpp b/src/parser.hpp index 825e26f32..6a59f05a6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -434,6 +434,10 @@ AST_KIND(_TypeBegin, "", bool) \ Token token; \ Ast *type; \ }) \ + AST_KIND(OpaqueType, "opaque type", struct { \ + Token token; \ + Ast *type; \ + }) \ AST_KIND(PolyType, "polymorphic type", struct { \ Token token; \ Ast * type; \ diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3ec491fa7..e2a5827ac 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -113,6 +113,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_cast, "cast"), \ TOKEN_KIND(Token_transmute, "transmute"), \ TOKEN_KIND(Token_distinct, "distinct"), \ + TOKEN_KIND(Token_opaque, "opaque"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_inline, "inline"), \ TOKEN_KIND(Token_no_inline, "no_inline"), \ diff --git a/src/types.cpp b/src/types.cpp index f5d1bb4a9..74cc264da 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -127,6 +127,7 @@ struct TypeUnion { Scope *scope; \ }) \ TYPE_KIND(Pointer, struct { Type *elem; }) \ + TYPE_KIND(Opaque, struct { Type *elem; }) \ TYPE_KIND(Array, struct { \ Type *elem; \ i64 count; \ @@ -400,6 +401,7 @@ gb_global Type *t_type_info_enum = nullptr; gb_global Type *t_type_info_map = nullptr; gb_global Type *t_type_info_bit_field = nullptr; gb_global Type *t_type_info_bit_set = nullptr; +gb_global Type *t_type_info_opaque = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -423,6 +425,7 @@ gb_global Type *t_type_info_enum_ptr = nullptr; gb_global Type *t_type_info_map_ptr = nullptr; gb_global Type *t_type_info_bit_field_ptr = nullptr; gb_global Type *t_type_info_bit_set_ptr = nullptr; +gb_global Type *t_type_info_opaque_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -461,6 +464,19 @@ Type *base_type(Type *t) { return t; } +Type *strip_opaque_type(Type *t) { + for (;;) { + if (t == nullptr) { + break; + } + if (t->kind != Type_Opaque) { + break; + } + t = t->Opaque.elem; + } + return t; +} + Type *base_enum_type(Type *t) { Type *bt = base_type(t); if (bt != nullptr && @@ -486,6 +502,9 @@ Type *core_type(Type *t) { case Type_Enum: t = t->Enum.base_type; continue; + case Type_Opaque: + t = t->Opaque.elem; + continue; } break; } @@ -519,6 +538,12 @@ Type *alloc_type_generic(Scope *scope, i64 id, String name, Type *specialized) { return t; } +Type *alloc_type_opaque(Type *elem) { + Type *t = alloc_type(Type_Opaque); + t->Opaque.elem = elem; + return t; +} + Type *alloc_type_pointer(Type *elem) { Type *t = alloc_type(Type_Pointer); t->Pointer.elem = elem; @@ -819,8 +844,10 @@ bool is_type_tuple(Type *t) { t = base_type(t); return t->kind == Type_Tuple; } - - +bool is_type_opaque(Type *t) { + t = base_type(t); + return t->kind == Type_Opaque; +} bool is_type_uintptr(Type *t) { if (t->kind == Type_Basic) { return (t->Basic.kind == Basic_uintptr); @@ -1212,6 +1239,8 @@ bool type_has_nil(Type *t) { return true; case Type_Struct: return false; + case Type_Opaque: + return true; } return false; } @@ -1257,6 +1286,9 @@ bool is_type_comparable(Type *t) { case Type_BitSet: return true; + + case Type_Opaque: + return is_type_comparable(t->Opaque.elem); } return false; } @@ -1294,6 +1326,12 @@ bool are_types_identical(Type *x, Type *y) { } break; + case Type_Opaque: + if (y->kind == Type_Opaque) { + return are_types_identical(x->Opaque.elem, y->Opaque.elem); + } + break; + case Type_Basic: if (y->kind == Type_Basic) { return x->Basic.kind == y->Basic.kind; @@ -2061,6 +2099,9 @@ i64 type_align_of_internal(Type *t, TypePath *path) { return align; } + case Type_Opaque: + return type_align_of_internal(t->Opaque.elem, path); + case Type_DynamicArray: // data, count, capacity, allocator return build_context.word_size; @@ -2265,6 +2306,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) { case Type_Pointer: return build_context.word_size; + case Type_Opaque: + return type_size_of_internal(t->Opaque.elem, path); + case Type_Array: { i64 count, align, size, alignment; count = t->Array.count;