mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-26 06:51:34 +00:00
peel union constant to its variant
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
54
tests/internal/test_union_const.odin
Normal file
54
tests/internal/test_union_const.odin
Normal 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") }
|
||||
}
|
||||
Reference in New Issue
Block a user