diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-04-30 15:29:46 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-04-30 15:29:46 +0100 |
| commit | e63393e3941f43aca0976367d36f22207758e4a1 (patch) | |
| tree | c8e69d0a0cf816cdc0ea625e77b6c68508abfdca | |
| parent | 784f3ecf7e427c1d948541f62253d6d2eab9e70d (diff) | |
Add type assertion for `any`
| -rw-r--r-- | src/check_expr.c | 56 | ||||
| -rw-r--r-- | src/ir.c | 72 |
2 files changed, 101 insertions, 27 deletions
diff --git a/src/check_expr.c b/src/check_expr.c index c700e7d25..3722652b6 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -5583,38 +5583,44 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t return kind; } - if (!is_type_union(src)) { - error_node(o->expr, "Type assertions can only operate on unions"); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } + if (is_type_union(src)) { + bool ok = false; + for (isize i = 1; i < bsrc->Record.variant_count; i++) { + Entity *f = bsrc->Record.variants[i]; + if (are_types_identical(f->type, dst)) { + ok = true; + break; + } + } - bool ok = false; - for (isize i = 1; i < bsrc->Record.variant_count; i++) { - Entity *f = bsrc->Record.variants[i]; - if (are_types_identical(f->type, dst)) { - ok = true; - break; + if (!ok) { + gbString expr_str = expr_to_string(o->expr); + gbString dst_type_str = type_to_string(t); + error_node(o->expr, "Cannot type assert `%s` to `%s`", expr_str, dst_type_str); + gb_string_free(dst_type_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; } - } - if (!ok) { - gbString expr_str = expr_to_string(o->expr); - gbString dst_type_str = type_to_string(t); - error_node(o->expr, "Cannot type assert `%s` to `%s`", expr_str, dst_type_str); - gb_string_free(dst_type_str); - gb_string_free(expr_str); + add_type_info_type(c, o->type); + add_type_info_type(c, t); + + o->type = t; + o->mode = Addressing_OptionalOk; + } else if (is_type_any(o->type)) { + o->type = t; + o->mode = Addressing_OptionalOk; + + add_type_info_type(c, o->type); + add_type_info_type(c, t); + } else { + error_node(o->expr, "Type assertions can only operate on unions"); o->mode = Addressing_Invalid; o->expr = node; return kind; } - - add_type_info_type(c, o->type); - add_type_info_type(c, t); - - o->type = t; - o->mode = Addressing_OptionalOk; case_end; case_ast_node(ue, UnaryExpr, node); @@ -3112,6 +3112,64 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token return ir_emit_load(proc, v); } +irAddr ir_emit_any_cast_addr(irProcedure *proc, irValue *value, Type *type, TokenPos pos) { + gbAllocator a = proc->module->allocator; + Type *src_type = ir_type(value); + bool is_tuple = true; + Type *tuple = type; + if (type->kind != Type_Tuple) { + is_tuple = false; + tuple = make_optional_ok_type(a, type); + } + Type *dst_type = tuple->Tuple.variables[0]->type; + + irValue *v = ir_add_local_generated(proc, tuple); + + irValue *ti_ptr = ir_type_info(proc, dst_type); + irValue *any_ti = ir_emit_struct_ev(proc, value, 1); + + + irBlock *ok_block = ir_new_block(proc, NULL, "any_cast.ok"); + irBlock *end_block = ir_new_block(proc, NULL, "any_cast.end"); + irValue *cond = ir_emit_comp(proc, Token_CmpEq, any_ti, ti_ptr); + ir_emit_if(proc, cond, ok_block, end_block); + ir_start_block(proc, ok_block); + + irValue *gep0 = ir_emit_struct_ep(proc, v, 0); + irValue *gep1 = ir_emit_struct_ep(proc, v, 1); + + irValue *any_data = ir_emit_struct_ev(proc, value, 0); + irValue *ptr = ir_emit_conv(proc, any_data, make_type_pointer(a, dst_type)); + ir_emit_store(proc, gep0, ir_emit_load(proc, ptr)); + ir_emit_store(proc, gep1, v_true); + + ir_emit_jump(proc, end_block); + ir_start_block(proc, end_block); + + if (!is_tuple) { + // NOTE(bill): Panic on invalid conversion + + 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_const_string(a, pos.file); + args[2] = ir_const_int(a, pos.line); + args[3] = ir_const_int(a, pos.column); + + args[4] = any_ti; + args[5] = ti_ptr; + ir_emit_global_call(proc, "__type_assertion_check", args, 6); + + return ir_addr(ir_emit_struct_ep(proc, v, 0)); + } + return ir_addr(v); +} +irValue *ir_emit_any_cast(irProcedure *proc, irValue *value, Type *type, TokenPos pos) { + return ir_addr_load(proc, ir_emit_any_cast_addr(proc, value, type, pos)); +} + + isize ir_type_info_index(CheckerInfo *info, Type *type) { type = default_type(type); @@ -3607,12 +3665,16 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { #endif case_ast_node(ta, TypeAssertion, expr); + TokenPos pos = ast_node_token(expr).pos; Type *type = tv->type; irValue *e = ir_build_expr(proc, ta->expr); Type *t = type_deref(ir_type(e)); if (is_type_union(t)) { ir_emit_comment(proc, str_lit("cast - union_cast")); - return ir_emit_union_cast(proc, e, type, ast_node_token(expr).pos); + return ir_emit_union_cast(proc, e, type, pos); + } else if (is_type_any(t)) { + ir_emit_comment(proc, str_lit("cast - any_cast")); + return ir_emit_any_cast(proc, e, type, pos); } else { GB_PANIC("TODO(bill): type assertion %s", type_to_string(ir_type(e))); } @@ -4727,14 +4789,20 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { case_end; case_ast_node(ta, TypeAssertion, expr); + gbAllocator a = proc->module->allocator; + TokenPos pos = ast_node_token(expr).pos; irValue *e = ir_build_expr(proc, ta->expr); Type *t = type_deref(ir_type(e)); if (is_type_union(t)) { Type *type = type_of_expr(proc->module->info, expr); irValue *v = ir_add_local_generated(proc, type); ir_emit_comment(proc, str_lit("cast - union_cast")); - ir_emit_store(proc, v, ir_emit_union_cast(proc, ir_build_expr(proc, ta->expr), type, ast_node_token(expr).pos)); + ir_emit_store(proc, v, ir_emit_union_cast(proc, ir_build_expr(proc, ta->expr), type, pos)); return ir_addr(v); + } else if (is_type_any(t)) { + ir_emit_comment(proc, str_lit("cast - any_cast")); + Type *type = type_of_expr(proc->module->info, expr); + return ir_emit_any_cast_addr(proc, ir_build_expr(proc, ta->expr), type, pos); } else { GB_PANIC("TODO(bill): type assertion %s", type_to_string(ir_type(e))); } |