Browse Source

Add type assertion for `any`

Ginger Bill 8 years ago
parent
commit
e63393e394
2 changed files with 101 additions and 27 deletions
  1. 31 25
      src/check_expr.c
  2. 70 2
      src/ir.c

+ 31 - 25
src/check_expr.c

@@ -5583,38 +5583,44 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 			return kind;
 			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->mode = Addressing_Invalid;
 			o->expr = node;
 			o->expr = node;
 			return kind;
 			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_end;
 
 
 	case_ast_node(ue, UnaryExpr, node);
 	case_ast_node(ue, UnaryExpr, node);

+ 70 - 2
src/ir.c

@@ -3112,6 +3112,64 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 	return ir_emit_load(proc, v);
 	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) {
 isize ir_type_info_index(CheckerInfo *info, Type *type) {
 	type = default_type(type);
 	type = default_type(type);
@@ -3607,12 +3665,16 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 #endif
 #endif
 
 
 	case_ast_node(ta, TypeAssertion, expr);
 	case_ast_node(ta, TypeAssertion, expr);
+		TokenPos pos = ast_node_token(expr).pos;
 		Type *type = tv->type;
 		Type *type = tv->type;
 		irValue *e = ir_build_expr(proc, ta->expr);
 		irValue *e = ir_build_expr(proc, ta->expr);
 		Type *t = type_deref(ir_type(e));
 		Type *t = type_deref(ir_type(e));
 		if (is_type_union(t)) {
 		if (is_type_union(t)) {
 			ir_emit_comment(proc, str_lit("cast - union_cast"));
 			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 {
 		} else {
 			GB_PANIC("TODO(bill): type assertion %s", type_to_string(ir_type(e)));
 			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_end;
 
 
 	case_ast_node(ta, TypeAssertion, expr);
 	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);
 		irValue *e = ir_build_expr(proc, ta->expr);
 		Type *t = type_deref(ir_type(e));
 		Type *t = type_deref(ir_type(e));
 		if (is_type_union(t)) {
 		if (is_type_union(t)) {
 			Type *type = type_of_expr(proc->module->info, expr);
 			Type *type = type_of_expr(proc->module->info, expr);
 			irValue *v = ir_add_local_generated(proc, type);
 			irValue *v = ir_add_local_generated(proc, type);
 			ir_emit_comment(proc, str_lit("cast - union_cast"));
 			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);
 			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 {
 		} else {
 			GB_PANIC("TODO(bill): type assertion %s", type_to_string(ir_type(e)));
 			GB_PANIC("TODO(bill): type assertion %s", type_to_string(ir_type(e)));
 		}
 		}