Browse Source

Add `intrinsics.type_convert_variants_to_pointers` and `reflect.get_union_as_ptr_variants`

gingerBill 2 years ago
parent
commit
f77709e67e

+ 2 - 0
core/intrinsics/intrinsics.odin

@@ -188,6 +188,8 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
 type_equal_proc  :: proc($T: typeid) -> (equal:  proc "contextless" (rawptr, rawptr) -> bool)                 where type_is_comparable(T) ---
 type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
 
+type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
+
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
 
 // SIMD related

+ 10 - 0
core/reflect/reflect.odin

@@ -2,6 +2,7 @@ package reflect
 
 import "core:runtime"
 import "core:intrinsics"
+import "core:mem"
 _ :: intrinsics
 
 Type_Info :: runtime.Type_Info
@@ -739,6 +740,15 @@ get_union_variant :: proc(a: any) -> any {
 	return any{a.data, id}
 }
 
+get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
+	ptr := rawptr(val)
+	tag := get_union_variant_raw_tag(val^)
+	mem.copy(&res, &ptr, size_of(ptr))
+	set_union_variant_raw_tag(res, tag)
+	return
+}
+
+
 
 set_union_variant_raw_tag :: proc(a: any, tag: i64) {
 	if a == nil { return }

+ 40 - 0
src/check_builtin.cpp

@@ -4613,6 +4613,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		operand->mode = Addressing_Type;
 		break;
 
+	case BuiltinProc_type_convert_variants_to_pointers:
+		if (operand->mode != Addressing_Type) {
+			error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+		} else {
+			Type *bt = base_type(operand->type);
+			if (is_type_polymorphic(bt)) {
+				// IGNORE polymorphic types
+				return true;
+			} else if (bt->kind != Type_Union) {
+				gbString t = type_to_string(operand->type);
+				error(operand->expr, "Expected a union type for '%.*s', got %s", LIT(builtin_name), t);
+				gb_string_free(t);
+
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			} else if (bt->Union.is_polymorphic) {
+				gbString t = type_to_string(operand->type);
+				error(operand->expr, "Expected a non-polymorphic union type for '%.*s', got %s", LIT(builtin_name), t);
+				gb_string_free(t);
+
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			Type *new_type = alloc_type_union();
+			auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
+			for_array(i, bt->Union.variants) {
+				variants[i] = alloc_type_pointer(bt->Union.variants[i]);
+			}
+			new_type->Union.variants = variants;
+
+			// NOTE(bill): Is this even correct?
+			new_type->Union.scope = bt->Union.scope;
+
+			operand->type = new_type;
+		}
+		operand->mode = Addressing_Type;
+		break;
 
 	case BuiltinProc_type_is_boolean:
 	case BuiltinProc_type_is_integer:

+ 3 - 0
src/checker_builtin_procs.hpp

@@ -200,6 +200,8 @@ BuiltinProc__type_begin,
 	BuiltinProc_type_core_type,
 	BuiltinProc_type_elem_type,
 
+	BuiltinProc_type_convert_variants_to_pointers,
+
 BuiltinProc__type_simple_boolean_begin,
 	BuiltinProc_type_is_boolean,
 	BuiltinProc_type_is_integer,
@@ -492,6 +494,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_base_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_core_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_elem_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_boolean"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},