Browse Source

Fix #2160 (deep subtyping through `using` of `_`)

gingerBill 2 years ago
parent
commit
2b7ca2bdd6
2 changed files with 61 additions and 24 deletions
  1. 23 24
      src/llvm_backend_expr.cpp
  2. 38 0
      src/types.cpp

+ 23 - 24
src/llvm_backend_expr.cpp

@@ -1952,34 +1952,33 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		Type *dt = t;
 
 		GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
-		String field_name = lookup_subtype_polymorphic_field(t, src_type);
-		if (field_name.len > 0) {
-			// NOTE(bill): It can be casted
-			Selection sel = lookup_field(st, field_name, false, true);
-			if (sel.entity != nullptr) {
-				if (st_is_ptr) {
-					lbValue res = lb_emit_deep_field_gep(p, value, sel);
-					Type *rt = res.type;
+		Selection sel = {};
+		sel.index.allocator = heap_allocator();
+		defer (array_free(&sel.index));
+		if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
+			if (sel.entity == nullptr) {
+				GB_PANIC("invalid subtype cast  %s -> ", type_to_string(src_type), type_to_string(t));
+			}
+			if (st_is_ptr) {
+				lbValue res = lb_emit_deep_field_gep(p, value, sel);
+				Type *rt = res.type;
+				if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+					res = lb_emit_load(p, res);
+				}
+				return res;
+			} else {
+				if (is_type_pointer(value.type)) {
+					Type *rt = value.type;
 					if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
-						res = lb_emit_load(p, res);
-					}
-					return res;
-				} else {
-					if (is_type_pointer(value.type)) {
-						Type *rt = value.type;
-						if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
-							value = lb_emit_load(p, value);
-						} else {
-							value = lb_emit_deep_field_gep(p, value, sel);
-							return lb_emit_load(p, value);
-						}
+						value = lb_emit_load(p, value);
+					} else {
+						value = lb_emit_deep_field_gep(p, value, sel);
+						return lb_emit_load(p, value);
 					}
+				}
 
-					return lb_emit_deep_field_ev(p, value, sel);
+				return lb_emit_deep_field_ev(p, value, sel);
 
-				}
-			} else {
-				GB_PANIC("invalid subtype cast  %s.%.*s", type_to_string(src_type), LIT(field_name));
 			}
 		}
 	}

+ 38 - 0
src/types.cpp

@@ -2526,6 +2526,44 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
 	return str_lit("");
 }
 
+bool lookup_subtype_polymorphic_selection(Type *dst, Type *src, Selection *sel) {
+	Type *prev_src = src;
+	// Type *prev_dst = dst;
+	src = base_type(type_deref(src));
+	// dst = base_type(type_deref(dst));
+	bool src_is_ptr = src != prev_src;
+	// bool dst_is_ptr = dst != prev_dst;
+
+	GB_ASSERT(is_type_struct(src) || is_type_union(src));
+	for_array(i, src->Struct.fields) {
+		Entity *f = src->Struct.fields[i];
+		if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
+			if (are_types_identical(dst, f->type)) {
+				array_add(&sel->index, cast(i32)i);
+				sel->entity = f;
+				return true;
+			}
+			if (src_is_ptr && is_type_pointer(dst)) {
+				if (are_types_identical(type_deref(dst), f->type)) {
+					array_add(&sel->index, cast(i32)i);
+					sel->indirect = true;
+					sel->entity = f;
+					return true;
+				}
+			}
+			if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
+				String name = lookup_subtype_polymorphic_field(dst, f->type);
+				if (name.len > 0) {
+					array_add(&sel->index, cast(i32)i);
+					return lookup_subtype_polymorphic_selection(dst, f->type, sel);
+				}
+			}
+		}
+	}
+	return false;
+}
+
+
 
 
 Type *strip_type_aliasing(Type *x) {