From 001baf4419da9c43d4ef68d7ec1eac638d6fb742 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Thu, 29 Jun 2017 15:13:41 +0100 Subject: [PATCH] Add `Type` -- Runtime type for comparing types (similar to TypeInfo but simpler) --- code/demo.odin | 24 +++++++++++++----- core/_preload.odin | 1 + src/check_expr.cpp | 24 ++++++++++++++++-- src/check_stmt.cpp | 6 ++++- src/checker.cpp | 59 +++++++++++++++++++++++++++------------------ src/exact_value.cpp | 17 +++++++++++++ src/ir.cpp | 54 ++++++++++++++++++++++++++++++++++++----- src/ir_print.cpp | 6 +++++ src/parser.cpp | 2 +- src/types.cpp | 14 +++++++++++ 10 files changed, 167 insertions(+), 40 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index b0af47f80..9e5b43739 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -8,7 +8,6 @@ import ( "mem.odin"; "opengl.odin"; "os.odin"; - "pool.odin"; "raw.odin"; "strconv.odin"; "strings.odin"; @@ -327,11 +326,11 @@ explicit_parametric_polymorphic_procedures :: proc() { - new_entity :: proc(manager: ^EntityManager, Type: type, x, y: int) -> ^Type { - result := new(Type); + new_entity :: proc(manager: ^EntityManager, T: type, x, y: int) -> ^T { + result := new(T); result.entity = gen_new_entity(manager); result.derived.data = result; - result.derived.type_info = type_info(Type); + result.derived.type_info = type_info(T); result.position.x = f32(x); result.position.y = f32(y); @@ -345,14 +344,14 @@ explicit_parametric_polymorphic_procedures :: proc() { rock := new_entity(&manager, Rock, 3, 5); // Named arguments work too! - door := new_entity(manager = &manager, Type = Door, x = 3, y = 6); + door := new_entity(manager = &manager, T = Door, x = 3, y = 6); // And named arguments can be any order monster := new_entity( y = 1, x = 2, manager = &manager, - Type = Monster, + T = Monster, ); append(entities, rock, door, monster); @@ -380,6 +379,19 @@ main :: proc() { // Command line argument(s)! // -opt=0,1,2,3 + a: Type = int; + b: Type = f32; + c: Type = int; + match a { + case int: fmt.println("a == int"); + case f32: fmt.println("a == f32"); + case: fmt.println("What type is a?"); + } + assert(a != b); + assert(b != c); + assert(a == c); + + program := "+ + * - /"; accumulator := 0; diff --git a/core/_preload.odin b/core/_preload.odin index 77475de87..9c08f7153 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -61,6 +61,7 @@ TypeInfo :: union { String{}, Boolean{}, Any{}, + Type{}, Pointer{ elem: ^TypeInfo, // nil -> rawptr }, diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 465fcb15c..396a62eef 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -328,6 +328,14 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n return; } + if (operand->mode == Addressing_Type) { + if (type != NULL && is_type_type(type)) { + add_type_info_type(c, type); + add_type_info_type(c, operand->type); + return; + } + } + if (is_type_untyped(operand->type)) { Type *target_type = type; if (type == NULL || is_type_any(type)) { @@ -2604,6 +2612,7 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) { o->mode = Addressing_Value; } + void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) { if (x->mode == Addressing_Type && y->mode == Addressing_Type) { bool comp = are_types_identical(x->type, y->type); @@ -2616,6 +2625,15 @@ void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) { x->value = exact_value_bool(comp); return; } + if (x->mode == Addressing_Type && is_operand_a_type_value(*y)) { + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } else if (y->mode == Addressing_Type && is_operand_a_type_value(*x)) { + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } gbString err_str = NULL; gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); @@ -3046,11 +3064,13 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { check_expr_or_type(c, y, be->right); bool xt = x->mode == Addressing_Type; bool yt = y->mode == Addressing_Type; + bool xvt = is_operand_a_type_value(*x); + bool yvt = is_operand_a_type_value(*y); // If only one is a type, this is an error if (xt ^ yt) { GB_ASSERT(xt != yt); - if (xt) error_operand_not_expression(x); - if (yt) error_operand_not_expression(y); + if (xt && !yvt) error_operand_not_expression(x); + if (yt && !xvt) error_operand_not_expression(y); } } break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1b94e8ade..8cbbf4c25 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1297,7 +1297,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_comparison(c, &a1, &b1, op); } else { Operand y = {}; - check_expr(c, &y, expr); + if (is_operand_a_type_value(x)) { + check_expr_or_type(c, &y, expr); + } else { + check_expr(c, &y, expr); + } if (x.mode == Addressing_Invalid || y.mode == Addressing_Invalid) { continue; diff --git a/src/checker.cpp b/src/checker.cpp index 03bab5a13..c12aad9de 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -170,6 +170,9 @@ bool is_operand_value(Operand o) { bool is_operand_nil(Operand o) { return o.mode == Addressing_Value && o.type == t_untyped_nil; } +bool is_operand_a_type_value(Operand o) { + return is_operand_value(o) && is_type_type(o.type); +} struct BlockLabel { @@ -325,7 +328,7 @@ DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident); DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e); AstFile * ast_file_of_filename (CheckerInfo *i, String filename); // IMPORTANT: Only to use once checking is done -isize type_info_index (CheckerInfo *i, Type * type); +isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true); Entity *current_scope_lookup_entity(Scope *s, String name); @@ -879,7 +882,7 @@ void check_remove_expr_info(CheckerInfo *i, AstNode *expr) { -isize type_info_index(CheckerInfo *info, Type *type) { +isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { type = default_type(type); isize entry_index = -1; @@ -903,7 +906,7 @@ isize type_info_index(CheckerInfo *info, Type *type) { } } - if (entry_index < 0) { + if (error_on_failure && entry_index < 0) { compiler_error("TypeInfo for `%s` could not be found", type_to_string(type)); } return entry_index; @@ -1086,6 +1089,9 @@ void add_type_info_type(Checker *c, Type *t) { add_type_info_type(c, t_type_info_ptr); add_type_info_type(c, t_rawptr); break; + case Basic_Type: + add_type_info_type(c, t_type_info_type); + break; case Basic_complex64: add_type_info_type(c, t_type_info_float); @@ -1248,9 +1254,12 @@ Map generate_minimum_dependency_map(CheckerInfo *info, Entity *start) for_array(i, info->definitions.entries) { Entity *e = info->definitions.entries[i].value; - if (e->scope->is_global && !is_type_polymorphic(e->type)) { // TODO(bill): is the check enough? - // NOTE(bill): Require runtime stuff - add_dependency_to_map(&map, info, e); + // if (e->scope->is_global && !is_type_poly_proc(e->type)) { // TODO(bill): is the check enough? + if (e->scope->is_global) { // TODO(bill): is the check enough? + if (!is_type_poly_proc(e->type)) { + // NOTE(bill): Require runtime stuff + add_dependency_to_map(&map, info, e); + } } else if (e->kind == Entity_Procedure) { if ((e->Procedure.tags & ProcTag_export) != 0) { add_dependency_to_map(&map, info, e); @@ -1299,7 +1308,7 @@ void init_preload(Checker *c) { - if (record->variant_count != 23) { + if (record->variant_count != 24) { compiler_error("Invalid `TypeInfo` layout"); } t_type_info_named = record->variants[ 1]->type; @@ -1310,20 +1319,21 @@ void init_preload(Checker *c) { t_type_info_string = record->variants[ 6]->type; t_type_info_boolean = record->variants[ 7]->type; t_type_info_any = record->variants[ 8]->type; - t_type_info_pointer = record->variants[ 9]->type; - t_type_info_atomic = record->variants[10]->type; - t_type_info_procedure = record->variants[11]->type; - t_type_info_array = record->variants[12]->type; - t_type_info_dynamic_array = record->variants[13]->type; - t_type_info_slice = record->variants[14]->type; - t_type_info_vector = record->variants[15]->type; - t_type_info_tuple = record->variants[16]->type; - t_type_info_struct = record->variants[17]->type; - t_type_info_raw_union = record->variants[18]->type; - t_type_info_union = record->variants[19]->type; - t_type_info_enum = record->variants[20]->type; - t_type_info_map = record->variants[21]->type; - t_type_info_bit_field = record->variants[22]->type; + t_type_info_type = record->variants[ 9]->type; + t_type_info_pointer = record->variants[10]->type; + t_type_info_atomic = record->variants[11]->type; + t_type_info_procedure = record->variants[12]->type; + t_type_info_array = record->variants[13]->type; + t_type_info_dynamic_array = record->variants[14]->type; + t_type_info_slice = record->variants[15]->type; + t_type_info_vector = record->variants[16]->type; + t_type_info_tuple = record->variants[17]->type; + t_type_info_struct = record->variants[18]->type; + t_type_info_raw_union = record->variants[19]->type; + t_type_info_union = record->variants[20]->type; + t_type_info_enum = record->variants[21]->type; + t_type_info_map = record->variants[22]->type; + t_type_info_bit_field = record->variants[23]->type; t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named); t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer); @@ -1333,6 +1343,7 @@ void init_preload(Checker *c) { t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string); t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean); t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any); + t_type_info_type_ptr = make_type_pointer(c->allocator, t_type_info_type); t_type_info_pointer_ptr = make_type_pointer(c->allocator, t_type_info_pointer); t_type_info_atomic_ptr = make_type_pointer(c->allocator, t_type_info_atomic); t_type_info_procedure_ptr = make_type_pointer(c->allocator, t_type_info_procedure); @@ -2197,6 +2208,9 @@ void check_import_entities(Checker *c, Map *file_scopes) { void check_parsed_files(Checker *c) { Map file_scopes; // Key: String (fullpath) map_init(&file_scopes, heap_allocator()); + defer (map_destroy(&file_scopes)); + + add_type_info_type(c, t_invalid); // Map full filepaths to Scopes for_array(i, c->parser->files) { @@ -2349,7 +2363,4 @@ void check_parsed_files(Checker *c) { } } } - - map_destroy(&file_scopes); - } diff --git a/src/exact_value.cpp b/src/exact_value.cpp index e84b5b794..3bee3cdb2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -5,6 +5,8 @@ struct AstNode; struct HashKey; +struct Type; +bool are_types_identical(Type *x, Type *y); struct Complex128 { f64 real, imag; @@ -20,6 +22,7 @@ enum ExactValueKind { ExactValue_Complex, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? + ExactValue_Type, ExactValue_Count, }; @@ -34,6 +37,7 @@ struct ExactValue { i64 value_pointer; Complex128 value_complex; AstNode * value_compound; + Type * value_type; }; }; @@ -99,6 +103,12 @@ ExactValue exact_value_pointer(i64 ptr) { return result; } +ExactValue exact_value_type(Type *type) { + ExactValue result = {ExactValue_Type}; + result.value_type = type; + return result; +} + ExactValue exact_value_integer_from_string(String string) { return exact_value_u128(u128_from_string(string)); @@ -639,6 +649,13 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { case Token_GtEq: return a >= b; } } break; + + case ExactValue_Type: + switch (op) { + case Token_CmpEq: return are_types_identical(x.value_type, y.value_type); + case Token_NotEq: return !are_types_identical(x.value_type, y.value_type); + } + break; } GB_PANIC("Invalid comparison"); diff --git a/src/ir.cpp b/src/ir.cpp index 4267b6434..64dc71684 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -749,6 +749,7 @@ void ir_build_defer_stmt (irProcedure *proc, irDefer d); irAddr ir_build_addr (irProcedure *proc, AstNode *expr); void ir_build_proc (irValue *value, irProcedure *parent); void ir_gen_global_type_name(irModule *m, Entity *e, String name); +irValue *ir_get_type_info_ptr (irProcedure *proc, Type *type); @@ -2771,6 +2772,23 @@ String ir_lookup_subtype_polymorphic_field(CheckerInfo *info, Type *dst, Type *s } +irValue *ir_emit_ptr_to_int(irProcedure *proc, irValue *value, Type *t, bool allow_type_type = false) { + Type *vt = core_type(ir_type(value)); + GB_ASSERT(is_type_pointer(vt)); + if (allow_type_type) { + GB_ASSERT(is_type_int_or_uint(core_type(t)) || is_type_type(t)); + } else { + GB_ASSERT(is_type_int_or_uint(core_type(t))); + } + return ir_emit(proc, ir_instr_conv(proc, irConv_ptrtoint, value, vt, t)); +} +irValue *ir_emit_int_to_ptr(irProcedure *proc, irValue *value, Type *t) { + Type *vt = core_type(ir_type(value)); + GB_ASSERT(is_type_int_or_uint(vt)); + GB_ASSERT(is_type_pointer(core_type(t))); + return ir_emit(proc, ir_instr_conv(proc, irConv_inttoptr, value, vt, t)); +} + irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { Type *src_type = ir_type(value); @@ -2925,10 +2943,10 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { // Pointer <-> int if (is_type_pointer(src) && is_type_int_or_uint(dst)) { - return ir_emit(proc, ir_instr_conv(proc, irConv_ptrtoint, value, src_type, t)); + return ir_emit_ptr_to_int(proc, value, t); } if (is_type_int_or_uint(src) && is_type_pointer(dst)) { - return ir_emit(proc, ir_instr_conv(proc, irConv_inttoptr, value, src_type, t)); + return ir_emit_int_to_ptr(proc, value, t); } if (is_type_union(dst)) { @@ -4388,7 +4406,14 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { if (tv.mode == Addressing_Type) { // TODO(bill): Handle this correctly - return ir_value_nil(proc->module->allocator, tv.type); + i32 entry_index = type_info_index(proc->module->info, tv.type, false); + if (entry_index >= 0) { + irValue *ptr = ir_get_type_info_ptr(proc, tv.type); + return ir_emit_ptr_to_int(proc, ptr, t_type, true); + // i32 id = entry_index+1; + // return ir_value_constant(proc->module->allocator, t_int, exact_value_i64(id)); + } + return v_raw_nil; } if (tv.value.kind != ExactValue_Invalid) { @@ -4766,7 +4791,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { GB_ASSERT(!vari_expand); isize i = 0; for (; i < type->param_count-1; i++) { - args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type); + Entity *e = pt->variables[i]; + if (e->kind == Entity_Variable) { + args[i] = ir_emit_conv(proc, args[i], e->type); + } } Type *variadic_type = pt->variables[i]->type; GB_ASSERT(is_type_slice(variadic_type)); @@ -4783,7 +4811,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } else if (variadic) { isize i = 0; for (; i < type->param_count-1; i++) { - args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type); + Entity *e = pt->variables[i]; + if (e->kind == Entity_Variable) { + args[i] = ir_emit_conv(proc, args[i], e->type); + } } if (!vari_expand) { Type *variadic_type = pt->variables[i]->type; @@ -4795,7 +4826,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } } else { for (isize i = 0; i < type->param_count; i++) { - args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type); + Entity *e = pt->variables[i]; + if (e->kind == Entity_Variable) { + args[i] = ir_emit_conv(proc, args[i], e->type); + } } } @@ -7698,6 +7732,10 @@ void ir_gen_tree(irGen *s) { auto *entry = &info->type_info_map.entries[type_info_map_index]; Type *t = cast(Type *)entry->key.ptr; t = default_type(t); + if (t == t_invalid) { + continue; + } + isize entry_index = type_info_index(info, t); irValue *tag = NULL; @@ -7771,6 +7809,10 @@ void ir_gen_tree(irGen *s) { case Basic_any: tag = ir_emit_conv(proc, ti_ptr, t_type_info_any_ptr); break; + + case Basic_Type: + tag = ir_emit_conv(proc, ti_ptr, t_type_info_type_ptr); + break; } break; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b9bccc91c..c5456edf4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -246,6 +246,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return; case Basic_int: ir_fprintf(f, "i%lld", word_bits); return; case Basic_any: ir_fprintf(f, "%%..any"); return; + case Basic_Type: ir_fprintf(f, "%%..Type"); return; } break; case Type_Pointer: @@ -1691,6 +1692,11 @@ void print_llvm_ir(irGen *ir) { ir_print_type(f, m, t_type_info_ptr); ir_fprintf(f, "} ; Basic_any\n"); + ir_print_encoded_local(f, str_lit("..Type")); + ir_fprintf(f, " = type "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " ; Basic_Type\n"); + ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n"); ir_fprintf(f, "\n"); diff --git a/src/parser.cpp b/src/parser.cpp index bfdd3452d..c4b9aa0e7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -138,7 +138,7 @@ Array make_ast_node_array(AstFile *f, isize init_capacity = 8) { // all the nodes and even memcpy in a different kind of node #define AST_NODE_KINDS \ AST_NODE_KIND(Ident, "identifier", struct { \ - Token token; \ + Token token; \ }) \ AST_NODE_KIND(Implicit, "implicit", Token) \ AST_NODE_KIND(Undef, "undef", Token) \ diff --git a/src/types.cpp b/src/types.cpp index db957a21f..ce20bfdc6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -30,6 +30,7 @@ enum BasicKind { Basic_rawptr, Basic_string, // ^u8 + int Basic_any, // ^Type_Info + rawptr + Basic_Type, // `int` id of the type Basic_UntypedBool, Basic_UntypedInteger, @@ -261,6 +262,7 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, {Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}}, + {Type_Basic, {Basic_Type, 0, -1, STR_LIT("Type")}}, {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, @@ -306,6 +308,7 @@ 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_type = &basic_types[Basic_Type]; gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; @@ -340,6 +343,7 @@ gb_global Type *t_type_info_rune = NULL; gb_global Type *t_type_info_float = NULL; gb_global Type *t_type_info_complex = NULL; gb_global Type *t_type_info_any = NULL; +gb_global Type *t_type_info_type = 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; @@ -364,6 +368,7 @@ gb_global Type *t_type_info_float_ptr = NULL; gb_global Type *t_type_info_complex_ptr = NULL; gb_global Type *t_type_info_quaternion_ptr = NULL; gb_global Type *t_type_info_any_ptr = NULL; +gb_global Type *t_type_info_type_ptr = NULL; gb_global Type *t_type_info_string_ptr = NULL; gb_global Type *t_type_info_boolean_ptr = NULL; gb_global Type *t_type_info_pointer_ptr = NULL; @@ -775,6 +780,12 @@ bool is_type_tuple(Type *t) { return t->kind == Type_Tuple; } +bool is_type_type(Type *t) { + t = base_type(t); + return t->kind == Type_Basic && t->Basic.kind == Basic_Type; +} + + bool is_type_int_or_uint(Type *t) { if (t->kind == Type_Basic) { @@ -1054,6 +1065,7 @@ bool is_type_comparable(Type *t) { case Basic_any: return false; case Basic_rune: + case Basic_Type: return true; } return true; @@ -1747,6 +1759,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) { switch (t->Basic.kind) { case Basic_string: return build_context.word_size; case Basic_any: return build_context.word_size; + case Basic_Type: return build_context.word_size; case Basic_int: case Basic_uint: case Basic_rawptr: return build_context.word_size; @@ -1972,6 +1985,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) { switch (kind) { case Basic_string: return 2*build_context.word_size; case Basic_any: return 2*build_context.word_size; + case Basic_Type: return 1*build_context.word_size; case Basic_int: case Basic_uint: case Basic_rawptr: return build_context.word_size;