Browse Source

Fix subtype polymorphism implicit conversion

Ginger Bill 8 years ago
parent
commit
1430ca30a3
4 changed files with 72 additions and 45 deletions
  1. 29 21
      src/check_expr.c
  2. 4 4
      src/entity.c
  3. 39 17
      src/ir.c
  4. 0 3
      src/tokenizer.c

+ 29 - 21
src/check_expr.c

@@ -79,32 +79,37 @@ void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) {
 }
 
 
-bool check_is_assignable_to_using_subtype(Type *dst, Type *src) {
-	bool src_is_ptr;
+bool check_is_assignable_to_using_subtype(Type *src, Type *dst) {
+	bool src_is_ptr = false;
 	Type *prev_src = src;
 	src = type_deref(src);
 	src_is_ptr = src != prev_src;
 	src = base_type(src);
 
-	if (is_type_struct(src) || is_type_union(src)) {
-		for (isize i = 0; i < src->Record.field_count; i++) {
-			Entity *f = src->Record.fields[i];
-			if (f->kind == Entity_Variable && (f->flags & EntityFlag_Using) != 0) {
-				if (are_types_identical(dst, f->type)) {
-					return true;
-				}
-				if (src_is_ptr && is_type_pointer(dst)) {
-					if (are_types_identical(type_deref(dst), f->type)) {
-						return true;
-					}
-				}
-				bool ok = check_is_assignable_to_using_subtype(dst, f->type);
-				if (ok) {
-					return true;
-				}
+	if (!is_type_struct(src) && !is_type_union(src)) {
+		return false;
+	}
+
+	for (isize i = 0; i < src->Record.field_count; i++) {
+		Entity *f = src->Record.fields[i];
+		if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
+			continue;
+		}
+
+		if (are_types_identical(f->type, dst)) {
+			return true;
+		}
+		if (src_is_ptr && is_type_pointer(dst)) {
+			if (are_types_identical(f->type, type_deref(dst))) {
+				return true;
 			}
 		}
+		bool ok = check_is_assignable_to_using_subtype(f->type, dst);
+		if (ok) {
+			return true;
+		}
 	}
+
 	return false;
 }
 
@@ -113,6 +118,7 @@ bool check_is_assignable_to_using_subtype(Type *dst, Type *src) {
 // -1 is not convertable
 // 0 is exact
 // >0 is convertable
+
 i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 	if (operand->mode == Addressing_Invalid ||
 	    type == t_invalid) {
@@ -383,10 +389,12 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 		ast_node(f, Field, decl);
 
 		Type *type = check_type(c, f->type);
+		bool is_using = (f->flags&FieldFlag_using) != 0;
 
-		if (f->flags&FieldFlag_using) {
+		if (is_using) {
 			if (f->names.count > 1) {
 				error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type");
+				is_using = false;
 			}
 		}
 
@@ -398,7 +406,7 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 
 			Token name_token = name->Ident;
 
-			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index);
+			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)field_index);
 			e->identifier = name;
 			if (str_eq(name_token.string, str_lit("_"))) {
 				fields[field_index++] = e;
@@ -421,7 +429,7 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 		}
 
 
-		if (f->flags&FieldFlag_using) {
+		if (is_using) {
 			Type *t = base_type(type_deref(type));
 			if (!is_type_struct(t) && !is_type_raw_union(t) &&
 			    f->names.count >= 1 &&

+ 4 - 4
src/entity.c

@@ -185,20 +185,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
 	return entity;
 }
 
-Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
+Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
 	Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
 	entity->flags |= EntityFlag_Used;
-	if (anonymous) entity->flags |= EntityFlag_Using;
+	if (is_using) entity->flags |= EntityFlag_Using;
 	entity->flags |= EntityFlag_Param;
 	return entity;
 }
 
-Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
+Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
 	Entity *entity = make_entity_variable(a, scope, token, type, false);
 	entity->Variable.field_src_index = field_src_index;
 	entity->Variable.field_index = field_src_index;
+	if (is_using) entity->flags |= EntityFlag_Using;
 	entity->flags |= EntityFlag_Field;
-	entity->flags |= EntityFlag_Using*(anonymous != 0);
 	return entity;
 }
 

+ 39 - 17
src/ir.c

@@ -2613,7 +2613,7 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base,
 
 
 
-String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
+String ir_lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	Type *prev_src = src;
 	// Type *prev_dst = dst;
 	src = base_type(type_deref(src));
@@ -2621,7 +2621,7 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	bool src_is_ptr = src != prev_src;
 	// bool dst_is_ptr = dst != prev_dst;
 
-	GB_ASSERT(is_type_struct(src));
+	GB_ASSERT(is_type_struct(src) || is_type_union(src));
 	for (isize i = 0; i < src->Record.field_count; i++) {
 		Entity *f = src->Record.fields[i];
 		if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
@@ -2634,7 +2634,7 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 				}
 			}
 			if (is_type_struct(f->type)) {
-				String name = lookup_polymorphic_field(info, dst, f->type);
+				String name = ir_lookup_polymorphic_field(info, dst, f->type);
 				if (name.len > 0) {
 					return name;
 				}
@@ -2805,23 +2805,45 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 		}
 	}
 
-	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
+	// NOTE(bill): This has to be done before `Pointer <-> Pointer` as it's
 	// subtype polymorphism casting
-	{
-		Type *sb = base_type(type_deref(src));
-		bool src_is_ptr = src != sb;
-		if (is_type_struct(sb)) {
-			String field_name = lookup_polymorphic_field(proc->module->info, t, src);
-			// gb_printf("field_name: %.*s\n", LIT(field_name));
-			if (field_name.len > 0) {
-				// NOTE(bill): It can be casted
-				Selection sel = lookup_field(proc->module->allocator, sb, field_name, false);
-				if (sel.entity != NULL) {
-					ir_emit_comment(proc, str_lit("cast - polymorphism"));
-					if (src_is_ptr) {
-						value = ir_emit_load(proc, value);
+	if (check_is_assignable_to_using_subtype(src_type, t)) {
+		Type *st = type_deref(src_type);
+		Type *pst = st;
+		st = type_deref(st);
+
+		bool st_is_ptr = st != pst;
+		st = base_type(st);
+
+		Type *dt = t;
+		bool dt_is_ptr = is_type_pointer(dt);
+
+		GB_ASSERT(is_type_struct(st) || is_type_union(st));
+		String field_name = ir_lookup_polymorphic_field(proc->module->info, t, st);
+		// gb_printf("field_name: %.*s\n", LIT(field_name));
+		if (field_name.len > 0) {
+			// NOTE(bill): It can be casted
+			Selection sel = lookup_field(proc->module->allocator, st, field_name, false);
+			if (sel.entity != NULL) {
+				ir_emit_comment(proc, str_lit("cast - polymorphism"));
+				if (st_is_ptr) {
+					irValue *res = ir_emit_deep_field_gep(proc, value, sel);
+					if (!dt_is_ptr) {
+						res = ir_emit_load(proc, res);
 					}
+					return res;
+				} else {
+					if (is_type_pointer(ir_type(value))) {
+						if (!dt_is_ptr) {
+							value = ir_emit_load(proc, value);
+						} else {
+							value = ir_emit_deep_field_gep(proc, value, sel);
+							return ir_emit_load(proc, value);
+						}
+					}
+
 					return ir_emit_deep_field_ev(proc, value, sel);
+
 				}
 			}
 		}

+ 0 - 3
src/tokenizer.c

@@ -107,9 +107,6 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_using,          "using"),               \
 	TOKEN_KIND(Token_no_alias,       "no_alias"),            \
 	TOKEN_KIND(Token_immutable,      "immutable"),           \
-	/* TOKEN_KIND(Token_cast,           "cast"), */                \
-	/* TOKEN_KIND(Token_transmute,      "transmute"), */           \
-	/* TOKEN_KIND(Token_union_cast,     "union_cast"), */          \
 	TOKEN_KIND(Token_context,        "context"),             \
 	TOKEN_KIND(Token_push_context,   "push_context"),        \
 	TOKEN_KIND(Token_push_allocator, "push_allocator"),      \