peel union constant to its variant

This commit is contained in:
kalsprite
2026-08-19 16:30:37 -07:00
parent 6defecf436
commit cf5f181228
4 changed files with 88 additions and 22 deletions

View File

@@ -3439,7 +3439,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
cc.link_section = e->Variable.link_section;
ExactValue v = tav.value;
lbValue init = lb_const_value(m, e->type, v, tav.type, cc);
lbValue init = lb_const_value(m, e->type, v, lb_build_expr_original_const_type(decl->init_expr), cc);
LLVMDeleteGlobal(g.value);

View File

@@ -620,6 +620,10 @@ gb_internal LLVMValueRef lb_fill_fixed_capacity_dynamic_array(lbModule *m, i64 e
return llvm_const_named_struct(m, original_type, svalues, svalue_count);
}
// Defined in llvm_backend_expr.cpp. `U(3)` records the union as the expression's type, so a constant
// built from one has to be peeled back to the variant the checker resolved it to
gb_internal Type *lb_build_expr_original_const_type(Ast *expr);
gb_internal lbValue lb_const_value_bit_field(lbModule *m, Type *type, Ast *value_compound) {
ast_node(cl, CompoundLit, value_compound);
@@ -1411,7 +1415,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
aos_values[value_index++] = val;
}
@@ -1426,7 +1430,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
aos_values[value_index++] = val;
found = true;
break;
@@ -1481,7 +1485,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
aos_values[i] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
aos_values[i] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
for (isize i = elem_count; i < type->Struct.soa_count; i++) {
aos_values[i] = nullptr;
@@ -1560,7 +1564,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1575,7 +1579,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
values[value_index++] = val;
found = true;
break;
@@ -1603,7 +1607,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (is_type_tuple(tav.type)) {
elem_index += tav.type->Tuple.variables.count;
} else {
values[elem_index++] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
values[elem_index++] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
}
for (isize i = 0; i < type->Array.count; i++) {
@@ -1652,7 +1656,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1667,7 +1671,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
values[value_index++] = val;
found = true;
break;
@@ -1695,7 +1699,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (is_type_tuple(tav.type)) {
elem_index += tav.type->Tuple.variables.count;
} else {
values[elem_index++] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
values[elem_index++] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
}
for (isize i = 0; i < type->EnumeratedArray.count; i++) {
@@ -1744,7 +1748,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1762,7 +1766,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
values[value_index++] = val;
found = true;
break;
@@ -1804,7 +1808,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (is_type_tuple(tav.type)) {
elem_index += tav.type->Tuple.variables.count;
} else {
values[elem_index++] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
values[elem_index++] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
}
for (isize i = 0; i < capacity; i++) {
@@ -1852,7 +1856,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1867,7 +1871,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
values[value_index++] = val;
found = true;
break;
@@ -1886,7 +1890,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
values[i] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
@@ -1971,7 +1975,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
if (sel.index.count == 1) {
lbValue value = lb_const_value(m, f->type, tav.value, tav.type, cc);
lbValue value = lb_const_value(m, f->type, tav.value, lb_build_expr_original_const_type(fv->value), cc);
LLVMTypeRef value_type = LLVMTypeOf(value.value);
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
values[index] = value.value;
@@ -2081,7 +2085,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (elem_type_can_be_constant(f->type)) {
lbValue value = lb_const_value(m, f->type, tav.value, tav.type, cc);
lbValue value = lb_const_value(m, f->type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc);
LLVMTypeRef value_type = LLVMTypeOf(value.value);
isize lb_sizeof_value_type = lb_sizeof(value_type);
isize type_size_of_f_type = type_size_of(f->type);
@@ -2222,7 +2226,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
for (i64 k = lo; k < hi; k++) {
i64 offset = matrix_row_major_index_to_offset(type, k);
GB_ASSERT(values[offset] == nullptr);
@@ -2234,7 +2238,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 index = exact_value_to_i64(index_tav.value);
GB_ASSERT(index < max_count);
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(fv->value), cc).value;
i64 offset = matrix_row_major_index_to_offset(type, index);
GB_ASSERT(values[offset] == nullptr);
values[offset] = val;
@@ -2258,7 +2262,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
GB_ASSERT(tav.mode != Addressing_Invalid);
i64 offset = 0;
offset = matrix_row_major_index_to_offset(type, i);
values[offset] = lb_const_value(m, elem_type, tav.value, tav.type, cc).value;
values[offset] = lb_const_value(m, elem_type, tav.value, lb_build_expr_original_const_type(cl->elems[i]), cc).value;
}
for (isize i = 0; i < total_count; i++) {
if (values[i] == nullptr) {

View File

@@ -4471,6 +4471,14 @@ gb_internal Type *lb_build_expr_original_const_type(Ast *expr) {
Type *res = lb_build_expr_original_const_type(expr->CallExpr.args[0]);
return res;
}
} else if (expr->kind == Ast_Ident || expr->kind == Ast_SelectorExpr) {
// a named constant carries the union as its type, so follow it to the declaration the
// checker resolved: `C :: U(3)` is the variant `int`, not `U`
Entity *e = entity_of_node(expr);
if (e != nullptr && e->kind == Entity_Constant &&
e->decl_info != nullptr && e->decl_info->init_expr != nullptr) {
return lb_build_expr_original_const_type(e->decl_info->init_expr);
}
}
}
return type_of_expr(expr);
@@ -4850,7 +4858,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
GB_ASSERT(e != nullptr);
if (e->kind == Entity_Constant) {
Type *t = default_type(type_of_expr(expr));
lbValue v = lb_const_value(p->module, t, e->Constant.value, e->type, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL);
lbValue v = lb_const_value(p->module, t, e->Constant.value, lb_build_expr_original_const_type(expr), LB_CONST_CONTEXT_DEFAULT_NO_LOCAL);
if (LLVMIsConstant(v.value)) {
lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
return g;

View File

@@ -0,0 +1,54 @@
package test_internal
import "core:testing"
// `U(3)` records the union as the constant's type, so the backend has to peel it back to the variant
// the checker resolved. It could not, and panicked in lb_const_value. The variant cannot be guessed
// from the value's kind either -- `W(i16(300))` below is an integer, and picking the first integer
// variant would silently choose i8
@(test)
union_constant_selects_the_declared_variant :: proc(t: ^testing.T) {
U :: union { string, int, f32 }
W :: union { i8, i16, i32 }
E :: enum { None, Bad }
EU :: union { E, string }
CI :: U(int(3))
CS :: U("s")
CF :: U(f32(1.5))
CW :: W(i16(300))
CE :: EU(E.Bad)
ci := CI
cs := CS
cf := CF
cw := CW
ce := CE
if v, ok := ci.(int); testing.expect(t, ok, "CI is not the int variant") { testing.expect_value(t, v, 3) }
if v, ok := cs.(string); testing.expect(t, ok, "CS is not the string variant") { testing.expect_value(t, v, "s") }
if v, ok := cf.(f32); testing.expect(t, ok, "CF is not the f32 variant") { testing.expect_value(t, v, f32(1.5)) }
// i16, not the first integer variant
if v, ok := cw.(i16); testing.expect(t, ok, "CW is not the i16 variant") { testing.expect_value(t, v, i16(300)) }
if v, ok := ce.(E); testing.expect(t, ok, "CE is not the enum variant") { testing.expect(t, v == E.Bad, "wrong enum value") }
// the same constant reached through an aggregate
S :: struct { u: U, n: int }
s := S{CI, 7}
if v, ok := s.u.(int); testing.expect(t, ok, "struct field lost its variant") { testing.expect_value(t, v, 3) }
testing.expect_value(t, s.n, 7)
arr := [2]U{U(int(1)), U("x")}
if v, ok := arr[0].(int); testing.expect(t, ok, "array element 0 lost its variant") { testing.expect_value(t, v, 1) }
if v, ok := arr[1].(string); testing.expect(t, ok, "array element 1 lost its variant") { testing.expect_value(t, v, "x") }
}
@(private="file") U_G :: union { string, int }
@(private="file") G := U_G(int(3))
@(private="file") GS := U_G("hello")
@(test)
union_constant_as_a_global_initializer :: proc(t: ^testing.T) {
if v, ok := G.(int); testing.expect(t, ok, "global lost its variant") { testing.expect_value(t, v, 3) }
if v, ok := GS.(string); testing.expect(t, ok, "string global lost its variant") { testing.expect_value(t, v, "hello") }
}