Browse Source

Rudimentary support for some constant `struct #raw_union`

gingerBill 2 weeks ago
parent
commit
10ba956d6a
3 changed files with 50 additions and 3 deletions
  1. 1 1
      src/check_expr.cpp
  2. 33 0
      src/llvm_backend_const.cpp
  3. 16 2
      src/types.cpp

+ 1 - 1
src/check_expr.cpp

@@ -9847,7 +9847,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 			if (t->Struct.is_raw_union) {
 				if (cl->elems.count > 0) {
 					// NOTE: unions cannot be constant
-					is_constant = false;
+					is_constant = elem_type_can_be_constant(t);
 
 					if (cl->elems[0]->kind != Ast_FieldValue) {
 						gbString type_str = type_to_string(type);

+ 33 - 0
src/llvm_backend_const.cpp

@@ -1475,6 +1475,39 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
 			}
 
 			if (is_type_raw_union(type)) {
+				if (is_type_raw_union_constantable(type)) {
+					GB_ASSERT(cl->elems.count == 1);
+					GB_ASSERT(cl->elems[0]->kind == Ast_FieldValue);
+					ast_node(fv, FieldValue, cl->elems[0]);
+					Entity *f = entity_of_node(fv->field);
+
+					TypeAndValue tav = fv->value->tav;
+					if (tav.value.kind != ExactValue_Invalid) {
+						lbValue value = lb_const_value(m, f->type, tav.value, cc, f->type);
+
+						LLVMValueRef values[2];
+						unsigned value_count = 0;
+
+						values[value_count++] = value.value;
+
+						i64 union_alignment = type_align_of(type);
+						i64 value_alignment = type_align_of(f->type);
+						i64 alignment = gb_max(gb_min(value_alignment, union_alignment), 1);
+
+						i64 union_size = type_size_of(type);
+						i64 value_size = lb_sizeof(LLVMTypeOf(value.value));
+						i64 padding = union_size-value_size;
+						if (padding > 0) {
+							LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, alignment);
+							values[value_count++] = LLVMConstNull(padding_type);
+						}
+
+						LLVMValueRef res = LLVMConstStructInContext(m->ctx, values, value_count, true);
+
+						return {res, original_type};
+					}
+
+				}
 				return lb_const_nil(m, original_type);
 			}
 			

+ 16 - 2
src/types.cpp

@@ -2531,6 +2531,20 @@ gb_internal bool is_type_union_constantable(Type *type) {
 	return true;
 }
 
+gb_internal bool is_type_raw_union_constantable(Type *type) {
+	Type *bt = base_type(type);
+	GB_ASSERT(bt->kind == Type_Struct);
+	GB_ASSERT(bt->Struct.is_raw_union);
+
+	for (Entity *f : bt->Struct.fields) {
+		if (!is_type_constant_type(f->type)) {
+			return false;
+		}
+	}
+	return true;
+}
+
+
 gb_internal bool elem_type_can_be_constant(Type *t) {
 	t = base_type(t);
 	if (t == t_invalid) {
@@ -2540,7 +2554,7 @@ gb_internal bool elem_type_can_be_constant(Type *t) {
 		return false;
 	}
 	if (is_type_raw_union(t)) {
-		return false;
+		return is_type_raw_union_constantable(t);
 	}
 	if (is_type_union(t)) {
 		return is_type_union_constantable(t);
@@ -2556,7 +2570,7 @@ gb_internal bool elem_cannot_be_constant(Type *t) {
 		return !is_type_union_constantable(t);
 	}
 	if (is_type_raw_union(t)) {
-		return true;
+		return !is_type_raw_union_constantable(t);
 	}
 	return false;
 }