diff --git a/examples/demo.odin b/examples/demo.odin index 53d85d268..94007d486 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -4,7 +4,7 @@ print_type_info_kind :: proc(info: ^Type_Info) { using Type_Info - match type i : info { + match type info -> i { case Named: print_string("Named\n") case Integer: print_string("Integer\n") case Float: print_string("Float\n") @@ -23,6 +23,37 @@ print_type_info_kind :: proc(info: ^Type_Info) { } } +println :: proc(args: ..any) { + for i := 0; i < len(args); i++ { + arg := args[i] + + if i > 0 { + print_string(" ") + } + + using Type_Info + match type arg.type_info -> i { + case Named: print_string("Named") + case Integer: print_string("Integer") + case Float: print_string("Float") + case String: print_string("String") + case Boolean: print_string("Boolean") + case Pointer: print_string("Pointer") + case Procedure: print_string("Procedure") + case Array: print_string("Array") + case Slice: print_string("Slice") + case Vector: print_string("Vector") + case Struct: print_string("Struct") + case Union: print_string("Union") + case Raw_Union: print_string("RawUnion") + case Enum: print_string("Enum") + default: print_string("void") + } + } + + print_nl() +} + main :: proc() { i: int s: struct { @@ -30,7 +61,11 @@ main :: proc() { } p := ^s - print_type_info_kind(type_info(i)) - print_type_info_kind(type_info(s)) - print_type_info_kind(type_info(p)) + a: any = i + + println(137, "Hello", 1.23) + + // print_type_info_kind(a.type_info) + // print_type_info_kind(type_info(s)) + // print_type_info_kind(type_info(p)) } diff --git a/examples/runtime.odin b/examples/runtime.odin index dd765211e..d94597880 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -18,11 +18,11 @@ Type_Info :: union { base: ^Type_Info } Integer: struct { - bits: int + size: int // in bytes signed: bool } Float: struct { - bits: int + size: int // in bytes } String: struct {} Boolean: struct {} @@ -49,14 +49,6 @@ Type_Info :: union { } } -Any :: struct { - type_info: ^Type_Info - // pointer to the data stored - data: rawptr -} - - - assume :: proc(cond: bool) #foreign "llvm.assume" diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 94917d7c7..877f6e461 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -389,23 +389,7 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu } -Type *t_type_info = NULL; -Type *t_type_info_ptr = NULL; -Type *t_type_info_named = NULL; -Type *t_type_info_integer = NULL; -Type *t_type_info_float = NULL; -Type *t_type_info_string = NULL; -Type *t_type_info_boolean = NULL; -Type *t_type_info_pointer = NULL; -Type *t_type_info_procedure = NULL; -Type *t_type_info_array = NULL; -Type *t_type_info_slice = NULL; -Type *t_type_info_vector = NULL; -Type *t_type_info_struct = NULL; -Type *t_type_info_union = NULL; -Type *t_type_info_raw_union = NULL; -Type *t_type_info_enum = NULL; void init_universal_scope(void) { @@ -857,6 +841,7 @@ void check_parsed_files(Checker *c) { } } + auto check_global_entity = [](Checker *c, EntityKind kind) { gb_for_array(i, c->info.entities.entries) { auto *entry = &c->info.entities.entries[i]; @@ -869,6 +854,33 @@ void check_parsed_files(Checker *c) { }; check_global_entity(c, Entity_TypeName); + + if (t_type_info == NULL) { + String type_info_str = make_string("Type_Info"); + Entity **found = map_get(&c->global_scope->elements, hash_string(type_info_str)); + GB_ASSERT_MSG(found != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`"); + Entity *e = *found; + t_type_info = e->type; + t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); + + auto *record = &get_base_type(e->type)->Record; + GB_ASSERT(record->field_count == 15); + t_type_info_named = record->fields[ 1]->type; + t_type_info_integer = record->fields[ 2]->type; + t_type_info_float = record->fields[ 3]->type; + t_type_info_string = record->fields[ 4]->type; + t_type_info_boolean = record->fields[ 5]->type; + t_type_info_pointer = record->fields[ 6]->type; + t_type_info_procedure = record->fields[ 7]->type; + t_type_info_array = record->fields[ 8]->type; + t_type_info_slice = record->fields[ 9]->type; + t_type_info_vector = record->fields[10]->type; + t_type_info_struct = record->fields[11]->type; + t_type_info_union = record->fields[12]->type; + t_type_info_raw_union = record->fields[13]->type; + t_type_info_enum = record->fields[14]->type; + } + check_global_entity(c, Entity_Constant); check_global_entity(c, Entity_Procedure); check_global_entity(c, Entity_Variable); diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index b8919c0bb..e55efd56d 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -45,6 +45,44 @@ b32 check_is_assignable_to_using_subtype(Type *dst, Type *src) { return false; } + +void add_type_info_type(Checker *c, Type *t) { + if (t == NULL) { + return; + } + t = default_type(t); + if (map_get(&c->info.type_info_types, hash_pointer(t)) != NULL) { + // Types have already been added + return; + } + + map_set(&c->info.type_info_types, hash_pointer(t), t); + Type *bt = get_base_type(t); + switch (bt->kind) { + case Type_Named: add_type_info_type(c, bt->Named.base); break; + case Type_Array: add_type_info_type(c, bt->Array.elem); break; + case Type_Slice: add_type_info_type(c, bt->Slice.elem); break; + case Type_Vector: add_type_info_type(c, bt->Vector.elem); break; + case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break; + case Type_Record: { + switch (bt->Record.kind) { + case TypeRecord_Enum: + add_type_info_type(c, bt->Record.enum_base); + break; + default: + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->Record.fields[i]; + add_type_info_type(c, f->type); + } + break; + } + } break; + } + // TODO(bill): Type info for procedures and tuples + // TODO(bill): Remove duplicate identical types efficiently +} + + b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argument = false) { if (operand->mode == Addressing_Invalid || type == t_invalid) { @@ -110,6 +148,13 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu } } + + if (dst == t_any) { + // NOTE(bill): Anything can cast to `Any` + add_type_info_type(c, s); + return true; + } + if (is_argument) { // NOTE(bill): Polymorphism for subtyping if (check_is_assignable_to_using_subtype(type, src)) { @@ -133,11 +178,13 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n if (is_type_untyped(operand->type)) { Type *target_type = type; - if (type == NULL) + if (type == NULL || is_type_any(type)) { target_type = default_type(operand->type); + } convert_to_typed(c, operand, target_type); - if (operand->mode == Addressing_Invalid) + if (operand->mode == Addressing_Invalid) { return; + } } @@ -1900,42 +1947,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { return NULL; } -void add_type_info_type(Checker *c, Type *t) { - if (t == NULL) { - return; - } - t = default_type(t); - if (map_get(&c->info.type_info_types, hash_pointer(t)) != NULL) { - // Types have already been added - return; - } - - map_set(&c->info.type_info_types, hash_pointer(t), t); - Type *bt = get_base_type(t); - switch (bt->kind) { - case Type_Named: add_type_info_type(c, bt->Named.base); break; - case Type_Array: add_type_info_type(c, bt->Array.elem); break; - case Type_Slice: add_type_info_type(c, bt->Slice.elem); break; - case Type_Vector: add_type_info_type(c, bt->Vector.elem); break; - case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break; - case Type_Record: { - switch (bt->Record.kind) { - case TypeRecord_Enum: - add_type_info_type(c, bt->Record.enum_base); - break; - default: - for (isize i = 0; i < bt->Record.field_count; i++) { - Entity *f = bt->Record.fields[i]; - add_type_info_type(c, f->type); - } - break; - } - } break; - } - // TODO(bill): Type info for procedures and tuples - // TODO(bill): Remove duplicate identical types efficiently -} - b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { GB_ASSERT(call->kind == AstNode_CallExpr); ast_node(ce, CallExpr, call); @@ -2690,32 +2701,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) case BuiltinProc_type_info: { - if (t_type_info == NULL) { - String type_info_str = make_string("Type_Info"); - Entity **found = map_get(&c->global_scope->elements, hash_string(type_info_str)); - GB_ASSERT_MSG(found != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`"); - Entity *e = *found; - t_type_info = e->type; - t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); - - auto *record = &get_base_type(e->type)->Record; - GB_ASSERT(record->field_count == 15); - t_type_info_named = record->fields[ 1]->type; - t_type_info_integer = record->fields[ 2]->type; - t_type_info_float = record->fields[ 3]->type; - t_type_info_string = record->fields[ 4]->type; - t_type_info_boolean = record->fields[ 5]->type; - t_type_info_pointer = record->fields[ 6]->type; - t_type_info_procedure = record->fields[ 7]->type; - t_type_info_array = record->fields[ 8]->type; - t_type_info_slice = record->fields[ 9]->type; - t_type_info_vector = record->fields[10]->type; - t_type_info_struct = record->fields[11]->type; - t_type_info_union = record->fields[12]->type; - t_type_info_raw_union = record->fields[13]->type; - t_type_info_enum = record->fields[14]->type; - } - add_type_info_type(c, operand->type); operand->mode = Addressing_Value; operand->type = t_type_info_ptr; diff --git a/src/checker/type.cpp b/src/checker/type.cpp index e2971ed9f..2ef56111c 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -19,6 +19,9 @@ enum BasicKind { Basic_uint, Basic_rawptr, Basic_string, + + Basic_any, + Basic_UntypedBool, Basic_UntypedInteger, Basic_UntypedFloat, @@ -26,6 +29,7 @@ enum BasicKind { Basic_UntypedString, Basic_UntypedRune, + Basic_Count, Basic_byte = Basic_u8, @@ -306,6 +310,7 @@ gb_global Type basic_types[] = { {0, Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}}, {0, Type_Basic, {Basic_rawptr, BasicFlag_Pointer, STR_LIT("rawptr")}}, {0, Type_Basic, {Basic_string, BasicFlag_String, STR_LIT("string")}}, + {0, Type_Basic, {Basic_any, 0, STR_LIT("any")}}, {0, Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, STR_LIT("untyped bool")}}, {0, Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, STR_LIT("untyped integer")}}, {0, Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, STR_LIT("untyped float")}}, @@ -337,6 +342,7 @@ gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; gb_global Type *t_string = &basic_types[Basic_string]; +gb_global Type *t_any = &basic_types[Basic_any]; gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; @@ -346,6 +352,25 @@ gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_byte = &basic_type_aliases[Basic_byte]; gb_global Type *t_rune = &basic_type_aliases[Basic_rune]; +gb_global Type *t_type_info = NULL; +gb_global Type *t_type_info_ptr = NULL; + +gb_global Type *t_type_info_named = NULL; +gb_global Type *t_type_info_integer = NULL; +gb_global Type *t_type_info_float = NULL; +gb_global Type *t_type_info_string = NULL; +gb_global Type *t_type_info_boolean = NULL; +gb_global Type *t_type_info_pointer = NULL; +gb_global Type *t_type_info_procedure = NULL; +gb_global Type *t_type_info_array = NULL; +gb_global Type *t_type_info_slice = NULL; +gb_global Type *t_type_info_vector = NULL; +gb_global Type *t_type_info_struct = NULL; +gb_global Type *t_type_info_union = NULL; +gb_global Type *t_type_info_raw_union = NULL; +gb_global Type *t_type_info_enum = NULL; + + b32 is_type_named(Type *t) { if (t->kind == Type_Basic) @@ -488,6 +513,11 @@ Type *get_enum_base_type(Type *t) { return t; } +b32 is_type_any(Type *t) { + t = get_base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_any); +} + b32 is_type_comparable(Type *t) { @@ -675,6 +705,9 @@ void selection_add_index(Selection *s, isize index) { gb_array_append(s->index, index); } +gb_global Entity *entity_any_type_info = NULL; +gb_global Entity *entity_any_data = NULL; + Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) { GB_ASSERT(type_ != NULL); @@ -686,11 +719,39 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se b32 is_ptr = type != type_; type = get_base_type(type); + if (type->kind == Type_Basic) { + if (type->Basic.kind == Basic_any) { + String type_info_str = make_string("type_info"); + String data_str = make_string("data_str"); + if (entity_any_type_info == NULL) { + Token token = {Token_Identifier}; + token.string = type_info_str; + entity_any_type_info = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false); + } + if (entity_any_data == NULL) { + Token token = {Token_Identifier}; + token.string = data_str; + entity_any_data = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false); + } + + if (are_strings_equal(field_name, type_info_str)) { + selection_add_index(&sel, 0); + sel.entity = entity_any_type_info; + return sel; + } else if (are_strings_equal(field_name, data_str)) { + selection_add_index(&sel, 1); + sel.entity = entity_any_data; + return sel; + } + } + + return sel; + } + if (type->kind != Type_Record) { return sel; } if (is_type) { - if (is_type_union(type)) { for (isize i = 0; i < type->Record.field_count; i++) { Entity *f = type->Record.fields[i]; diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index c8dff6d6b..ea30d910c 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -153,6 +153,13 @@ void ssa_print_type(ssaFileBuffer *f, BaseTypeSizes s, Type *t) { case Basic_string: ssa_fprintf(f, "%%..string"); break; case Basic_uint: ssa_fprintf(f, "i%lld", word_bits); break; case Basic_int: ssa_fprintf(f, "i%lld", word_bits); break; + case Basic_any: + ssa_fprintf(f, "{"); + ssa_print_type(f, s, t_type_info_ptr); + ssa_fprintf(f, ", "); + ssa_print_type(f, s, t_rawptr); + ssa_fprintf(f, "}"); + break; } break; case Type_Array: diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 5bff4a344..a5fe3c566 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1053,9 +1053,26 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S if (is_type_raw_union(type)) { type = type->Record.fields[index]->type; e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); - } else { + } else if (type->kind == Type_Record) { type = type->Record.fields[index]->type; e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, type)); + } else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, type)); + } break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else { + GB_PANIC("un-gep-able type"); } } @@ -1079,9 +1096,26 @@ ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Se if (is_type_raw_union(type)) { type = type->Record.fields[index]->type; e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); - } else { + } else if (type->kind == Type_Record) { type = type->Record.fields[index]->type; e = ssa_emit_struct_ev(proc, e, index, type); + } else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_struct_ev(proc, e, index, type); + } break; + + default: + GB_PANIC("un-ev-able type"); + break; + } + } else { + GB_PANIC("un-ev-able type"); } } @@ -1303,12 +1337,17 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg } if (value->kind == ssaValue_Constant) { - if (dst->kind == Type_Basic) { + if (is_type_any(dst)) { + Type *dt = default_type(src); + ssaValue *default_value = ssa_add_local_generated(proc, dt); + ssa_emit_store(proc, default_value, value); + return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any, is_argument); + } else if (dst->kind == Type_Basic) { ExactValue ev = value->Constant.value; if (is_type_float(dst)) { ev = exact_value_to_float(ev); } else if (is_type_string(dst)) { - // + // Handled elsewhere } else if (is_type_integer(dst)) { ev = exact_value_to_integer(ev); } else if (is_type_pointer(dst)) { @@ -1487,6 +1526,33 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg return v; } + if (is_type_any(dst)) { + ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data"))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + + ssaValue *result = ssa_add_local_generated(proc, t_any); + + // NOTE(bill): Make copy on stack so I can reference it later + ssaValue *data = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, data, value); + data = ssa_emit_conv(proc, data, t_rawptr); + + + MapFindResult fr = map__find(&info->type_info_types, hash_pointer(src_type)); + GB_ASSERT(fr.entry_index >= 0); + ssaValue *ti = ssa_emit_struct_gep(proc, type_info_data, fr.entry_index, t_type_info_ptr); + + ssaValue *gep0 = ssa_emit_struct_gep(proc, result, v_zero32, make_type_pointer(proc->module->allocator, t_type_info_ptr)); + ssaValue *gep1 = ssa_emit_struct_gep(proc, result, v_one32, make_type_pointer(proc->module->allocator, t_rawptr)); + ssa_emit_store(proc, gep0, ti); + ssa_emit_store(proc, gep1, data); + + return ssa_emit_load(proc, result); + } + gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); @@ -3194,10 +3260,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssaBlock *default_block = NULL; isize case_count = body->list_count; - isize i = 0; - for (AstNode *clause = body->list; - clause != NULL; - clause = clause->next, i++) { + for (AstNode *clause = body->list; clause != NULL; clause = clause->next) { ast_node(cc, CaseClause, clause); if (cc->list == NULL) { diff --git a/src/parser.cpp b/src/parser.cpp index 3efb0451d..ec95e2926 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2297,9 +2297,9 @@ AstNode *parse_match_stmt(AstFile *f) { Token open, close; if (allow_token(f, Token_type)) { + tag = parse_expr(f, true); + expect_token(f, Token_ArrowRight); AstNode *var = parse_identifier(f); - expect_token(f, Token_Colon); - tag = parse_simple_stmt(f); open = expect_token(f, Token_OpenBrace); AstNode *list = NULL; @@ -2315,7 +2315,6 @@ AstNode *parse_match_stmt(AstFile *f) { close = expect_token(f, Token_CloseBrace); body = make_block_stmt(f, list, list_count, open, close); - tag = convert_stmt_to_expr(f, tag, make_string("type match expression")); return make_type_match_stmt(f, token, tag, var, body); } else { if (f->cursor[0].kind != Token_OpenBrace) {