From 8b5e3428a1e569abf763e63e859754e767e107e7 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 16:37:24 +0000 Subject: [PATCH] Optional ok for `union_cast` (similar to map indices) --- code/demo.odin | 8 +++++++- core/_preload.odin | 25 ++++++++++++++----------- core/fmt.odin | 6 +++--- src/check_decl.c | 11 ----------- src/check_expr.c | 33 ++++++++++++--------------------- src/check_stmt.c | 40 ++++++++++++++++++---------------------- src/checker.c | 25 +++++++++++++------------ src/ir.c | 41 +++++++++++++++++++++++++++++++++-------- 8 files changed, 100 insertions(+), 89 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index caee21a05..a2049654c 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -2,6 +2,12 @@ main :: proc() { + x := type_info(int); + t1, ok := union_cast(^Type_Info.Integer)x; + _, ok = union_cast(^Type_Info.Integer)x; + t2 := union_cast(^Type_Info.Integer)x; + + /* /* Version 0.1.1 @@ -17,7 +23,7 @@ main :: proc() { * Entities prefixes with an underscore do not get exported on imports * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps * enum types have an implict `names` field, a []string of all the names in that enum - * immutable variables are "completely immutable" + * immutable variables are "completely immutable" - rules need a full explanation * `slice_to_bytes` - convert any slice to a slice of bytes Removed: diff --git a/core/_preload.odin b/core/_preload.odin index 7ff3bc44f..5114e8ef2 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -315,7 +315,11 @@ __assert :: proc(file: string, line, column: int, msg: string) #inline { file, line, column, msg); __debug_trap(); } - +__panic :: proc(file: string, line, column: int, msg: string) #inline { + fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n", + file, line, column, msg); + __debug_trap(); +} __bounds_check_error :: proc(file: string, line, column: int, index, count: int) { if 0 <= index && index < count { return; @@ -341,6 +345,13 @@ __substring_expr_error :: proc(file: string, line, column: int, low, high: int) file, line, column, low, high); __debug_trap(); } +__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) { + if !ok { + fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n", + file, line, column, from, to); + __debug_trap(); + } +} __string_decode_rune :: proc(s: string) -> (rune, int) #inline { return utf8.decode_rune(s); @@ -449,6 +460,8 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in } +// Map stuff + __default_hash :: proc(data: []byte) -> u64 { return hash.fnv64a(data); } @@ -650,13 +663,3 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) { m.hashes[last.hash_index] = fr.entry_index; } } - - -__print_ti_ptr :: proc(ti: ^Type_Info) { - fmt.println(ti); - match e in ti { - case Type_Info.Enum: - fmt.println(e.names); - } -} - diff --git a/core/fmt.odin b/core/fmt.odin index 542f951e9..937d15c8a 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -790,10 +790,10 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { buffer_write_string(fi.buf, "map["); defer buffer_write_byte(fi.buf, ']'); entries := ^(cast(^Raw_Dynamic_Map)v.data).entries; - gs, gs_ok := union_cast(^Struct)type_info_base(info.generated_struct); assert(gs_ok); - ed, ed_ok := union_cast(^Dynamic_Array)type_info_base(gs.types[1]); assert(ed_ok); + gs := union_cast(^Struct)type_info_base(info.generated_struct); + ed := union_cast(^Dynamic_Array)type_info_base(gs.types[1]); - entry_type, et_ok := union_cast(^Struct)ed.elem; assert(et_ok); + entry_type := union_cast(^Struct)ed.elem; entry_size := ed.elem_size; for i in 0.. 0 { diff --git a/src/check_decl.c b/src/check_decl.c index ebee92b5e..bc49e36c1 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -72,25 +72,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra } } - isize max = gb_min(lhs_count, rhs_count); for (isize i = 0; i < max; i++) { check_init_variable(c, lhs[i], &operands.e[i], context_name); } - if (rhs_count > 0 && lhs_count != rhs_count) { error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); } -#if 0 - if (lhs[0]->kind == Entity_Variable && - lhs[0]->Variable.is_let) { - if (lhs_count != rhs_count) { - error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count); - } - } -#endif - gb_temp_arena_memory_end(tmp); } diff --git a/src/check_expr.c b/src/check_expr.c index 355f4016e..a93165574 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -265,8 +265,6 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n } - - if (type == NULL) { return; } @@ -1115,7 +1113,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { return 0; } -Type *make_map_tuple_type(gbAllocator a, Type *value) { +Type *make_optional_ok_type(gbAllocator a, Type *value) { Type *t = make_type_tuple(a); t->Tuple.variables = gb_alloc_array(a, Entity *, 2); t->Tuple.variable_count = 2; @@ -1221,7 +1219,7 @@ void check_map_type(Checker *c, Type *type, AstNode *node) { type->Map.generated_struct_type = generated_struct_type; } - type->Map.lookup_result_type = make_map_tuple_type(a, value); + type->Map.lookup_result_type = make_optional_ok_type(a, value); // error_node(node, "`map` types are not yet implemented"); } @@ -3821,17 +3819,15 @@ int valid_proc_and_score_cmp(void const *a, void const *b) { typedef Array(Operand) ArrayOperand; -void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray args, bool allow_map_ok) { - for_array(i, args) { +void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray rhs, bool allow_ok) { + for_array(i, rhs) { Operand o = {0}; - check_multi_expr(c, &o, args.e[i]); + check_multi_expr(c, &o, rhs.e[i]); if (o.type == NULL || o.type->kind != Type_Tuple) { - if (o.mode == Addressing_MapIndex && - allow_map_ok && - lhs_count == 2 && - args.count == 1) { - Type *tuple = make_map_tuple_type(c->allocator, o.type); + if (allow_ok && lhs_count == 2 && rhs.count == 1 && + (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { + Type *tuple = make_optional_ok_type(c->allocator, o.type); add_type_and_value(&c->info, o.expr, o.mode, tuple, o.value); Operand val = o; @@ -4922,16 +4918,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } - Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); - variables[0] = make_entity_param(c->allocator, NULL, empty_token, t, false, true); - variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false, true); + add_type_info_type(c, o->type); + add_type_info_type(c, t); - Type *tuple = make_type_tuple(c->allocator); - tuple->Tuple.variables = variables; - tuple->Tuple.variable_count = 2; - - o->type = tuple; - o->mode = Addressing_Value; + o->type = t; + o->mode = Addressing_OptionalOk; } break; case Token_down_cast: { if (o->mode == Addressing_Constant) { diff --git a/src/check_stmt.c b/src/check_stmt.c index 6769ddb40..138e1277e 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -260,6 +260,7 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { switch (lhs.mode) { case Addressing_Invalid: return NULL; + case Addressing_Variable: break; case Addressing_MapIndex: { @@ -278,6 +279,7 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { } } } break; + default: { if (lhs.expr->kind == AstNode_SelectorExpr) { // NOTE(bill): Extra error checks @@ -434,40 +436,34 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { switch (as->op.kind) { case Token_Eq: { // a, b, c = 1, 2, 3; // Multisided - if (as->lhs.count == 0) { + + isize lhs_count = as->lhs.count; + if (lhs_count == 0) { error(as->op, "Missing lhs in assignment statement"); return; } + // TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just + // leave it? + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation - Array(Operand) operands; - array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count); + ArrayOperand operands = {0}; + array_init_reserve(&operands, c->tmp_allocator, 2 * lhs_count); + check_unpack_arguments(c, lhs_count, &operands, as->rhs, true); - for_array(i, as->rhs) { - AstNode *rhs = as->rhs.e[i]; - Operand o = {0}; - check_multi_expr(c, &o, rhs); - if (o.type->kind != Type_Tuple) { - array_add(&operands, o); - } else { - TypeTuple *tuple = &o.type->Tuple; - for (isize j = 0; j < tuple->variable_count; j++) { - o.type = tuple->variables[j]->type; - array_add(&operands, o); - } + isize rhs_count = operands.count; + for_array(i, operands) { + if (operands.e[i].mode == Addressing_Invalid) { + rhs_count--; } } - isize lhs_count = as->lhs.count; - isize rhs_count = operands.count; - - isize operand_count = gb_min(as->lhs.count, operands.count); - for (isize i = 0; i < operand_count; i++) { - AstNode *lhs = as->lhs.e[i]; - check_assignment_variable(c, &operands.e[i], lhs); + isize max = gb_min(lhs_count, rhs_count); + for (isize i = 0; i < max; i++) { + check_assignment_variable(c, &operands.e[i], as->lhs.e[i]); } if (lhs_count != rhs_count) { error_node(as->lhs.e[0], "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); diff --git a/src/checker.c b/src/checker.c index c2fd9e672..9fbf3d36d 100644 --- a/src/checker.c +++ b/src/checker.c @@ -109,18 +109,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { #include "types.c" typedef enum AddressingMode { - Addressing_Invalid, // invalid addressing mode - Addressing_NoValue, // no value (void in C) - Addressing_Value, // computed value (rvalue) - Addressing_Immutable, // immutable computed value (const rvalue) - Addressing_Variable, // addressable variable (lvalue) - Addressing_Constant, // constant & type will be a of Type_Basic (stripping Type_Named) - Addressing_Type, // type - Addressing_Builtin, // built in procedure - Addressing_Overload, // overloaded procedure - Addressing_MapIndex, // map index expression - // lhs: acts like a Variable - // ths: acts like a value with an optional boolean part (for existence check) + Addressing_Invalid, // invalid addressing mode + Addressing_NoValue, // no value (void in C) + Addressing_Value, // computed value (rvalue) + Addressing_Immutable, // immutable computed value (const rvalue) + Addressing_Variable, // addressable variable (lvalue) + Addressing_Constant, // constant & type will be a of Type_Basic (stripping Type_Named) + Addressing_Type, // type + Addressing_Builtin, // built in procedure + Addressing_Overload, // overloaded procedure + Addressing_MapIndex, // map index expression - + // lhs: acts like a Variable + // rhs: acts like OptionalOk + Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) } AddressingMode; // Operand is used as an intermediate value whilst checking diff --git a/src/ir.c b/src/ir.c index f49f9200e..e6bf588de 100644 --- a/src/ir.c +++ b/src/ir.c @@ -2462,13 +2462,19 @@ irValue *ir_emit_down_cast(irProcedure *proc, irValue *value, Type *t) { return ir_emit_conv(proc, head, t); } -irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *tuple) { - GB_ASSERT(tuple->kind == Type_Tuple); +irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, TokenPos pos) { gbAllocator a = proc->module->allocator; Type *src_type = ir_type(value); bool is_ptr = is_type_pointer(src_type); + bool is_tuple = true; + Type *tuple = type; + if (type->kind != Type_Tuple) { + is_tuple = false; + tuple = make_optional_ok_type(a, type); + } + irValue *v = ir_add_local_generated(proc, tuple); if (is_ptr) { @@ -2541,6 +2547,25 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *tuple) { ir_start_block(proc, end_block); } + + if (!is_tuple) { + // NOTE(bill): Panic on invalid conversion + Type *dst_type = tuple->Tuple.variables[0]->type; + + irValue *ok = ir_emit_load(proc, ir_emit_struct_ep(proc, v, 1)); + irValue **args = gb_alloc_array(a, irValue *, 6); + args[0] = ok; + + args[1] = ir_make_const_string(a, pos.file); + args[2] = ir_make_const_int(a, pos.line); + args[3] = ir_make_const_int(a, pos.column); + + args[4] = ir_type_info(proc, src_type); + args[5] = ir_type_info(proc, dst_type); + ir_emit_global_call(proc, "__union_cast_check", args, 6); + + return ir_emit_load(proc, ir_emit_struct_ep(proc, v, 0)); + } return ir_emit_load(proc, v); } @@ -2930,23 +2955,23 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv case_ast_node(ce, CastExpr, expr); Type *type = tv->type; - irValue *expr = ir_build_expr(proc, ce->expr); + irValue *e = ir_build_expr(proc, ce->expr); switch (ce->token.kind) { case Token_cast: ir_emit_comment(proc, str_lit("cast - cast")); - return ir_emit_conv(proc, expr, type); + return ir_emit_conv(proc, e, type); case Token_transmute: ir_emit_comment(proc, str_lit("cast - transmute")); - return ir_emit_transmute(proc, expr, type); + return ir_emit_transmute(proc, e, type); case Token_down_cast: ir_emit_comment(proc, str_lit("cast - down_cast")); - return ir_emit_down_cast(proc, expr, type); + return ir_emit_down_cast(proc, e, type); case Token_union_cast: ir_emit_comment(proc, str_lit("cast - union_cast")); - return ir_emit_union_cast(proc, expr, type); + return ir_emit_union_cast(proc, e, type, ast_node_token(expr).pos); default: GB_PANIC("Unknown cast expression"); @@ -3384,7 +3409,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv args[1] = ir_make_const_int(proc->module->allocator, pos.line); args[2] = ir_make_const_int(proc->module->allocator, pos.column); args[3] = msg; - ir_emit_global_call(proc, "__assert", args, 4); + ir_emit_global_call(proc, "__panic", args, 4); return NULL; } break;