mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-27 07:21:31 +00:00
backend: a union constant in a ternary or builds an ill-typed phi
This commit is contained in:
@@ -4605,6 +4605,9 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
if (is_type_internally_pointer_like(type)) {
|
||||
incoming_values[0] = LLVMBuildBitCast(p->builder, incoming_values[0], llvm_type, "");
|
||||
}
|
||||
// A union constant is built as an anonymous packed struct, which a phi cannot accept
|
||||
// alongside the named type it results in, even though the two are laid out identically
|
||||
incoming_values[0] = OdinLLVMBuildTransmute(p, incoming_values[0], llvm_type);
|
||||
|
||||
lb_emit_jump(p, done);
|
||||
lb_start_block(p, else_);
|
||||
@@ -4614,6 +4617,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
if (is_type_internally_pointer_like(type)) {
|
||||
incoming_values[1] = LLVMBuildBitCast(p->builder, incoming_values[1], llvm_type, "");
|
||||
}
|
||||
incoming_values[1] = OdinLLVMBuildTransmute(p, incoming_values[1], llvm_type);
|
||||
|
||||
lb_emit_jump(p, done);
|
||||
lb_start_block(p, done);
|
||||
|
||||
@@ -532,18 +532,22 @@ gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, Ty
|
||||
lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
|
||||
lb_start_block(p, then);
|
||||
|
||||
incoming_values[0] = lb_emit_conv(p, lhs, type).value;
|
||||
LLVMTypeRef llvm_type = lb_type(p->module, type);
|
||||
|
||||
// A union constant is built as an anonymous packed struct, which a phi cannot accept
|
||||
// alongside the named type it results in, even though the two are laid out identically
|
||||
incoming_values[0] = OdinLLVMBuildTransmute(p, lb_emit_conv(p, lhs, type).value, llvm_type);
|
||||
|
||||
lb_emit_jump(p, done);
|
||||
lb_start_block(p, else_);
|
||||
|
||||
incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, else_expr), type).value;
|
||||
incoming_values[1] = OdinLLVMBuildTransmute(p, lb_emit_conv(p, lb_build_expr(p, else_expr), type).value, llvm_type);
|
||||
|
||||
lb_emit_jump(p, done);
|
||||
lb_start_block(p, done);
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
|
||||
res.value = LLVMBuildPhi(p->builder, llvm_type, "");
|
||||
res.type = type;
|
||||
|
||||
GB_ASSERT(p->curr_block->preds.count >= 2);
|
||||
|
||||
49
tests/internal/test_union_ternary.odin
Normal file
49
tests/internal/test_union_ternary.odin
Normal file
@@ -0,0 +1,49 @@
|
||||
package test_internal
|
||||
|
||||
import "core:testing"
|
||||
|
||||
// A union constant is built as an anonymous packed struct, which is fine everywhere that adopts the
|
||||
// value's own type -- a global takes it verbatim. A phi cannot: its incoming values must have the
|
||||
// phi's own named type, even though the two layouts are identical. Both the ternary and `or_else`
|
||||
// build one
|
||||
@(test)
|
||||
union_constant_in_a_ternary :: proc(t: ^testing.T) {
|
||||
E :: enum { None, Bad, Worse }
|
||||
EU :: union { E, string }
|
||||
|
||||
f := true
|
||||
g := false
|
||||
|
||||
a: EU = EU(E.Worse) if f else nil
|
||||
b: EU = EU("s") if f else nil
|
||||
c: EU = nil if g else EU(E.Worse)
|
||||
d: EU = EU(E.Worse) if f else EU(E.Bad)
|
||||
e: EU = EU(E.Worse) if g else EU(E.Bad)
|
||||
|
||||
if v, ok := a.(E); testing.expect(t, ok, "a lost its variant") { testing.expect(t, v == E.Worse, "a") }
|
||||
if v, ok := b.(string); testing.expect(t, ok, "b lost its variant") { testing.expect_value(t, v, "s") }
|
||||
if v, ok := c.(E); testing.expect(t, ok, "c lost its variant") { testing.expect(t, v == E.Worse, "c") }
|
||||
if v, ok := d.(E); testing.expect(t, ok, "d lost its variant") { testing.expect(t, v == E.Worse, "d") }
|
||||
if v, ok := e.(E); testing.expect(t, ok, "e lost its variant") { testing.expect(t, v == E.Bad, "e") }
|
||||
|
||||
// the arm that is not taken must not decide the result
|
||||
n: EU = EU(E.Bad) if g else nil
|
||||
_, is_enum := n.(E)
|
||||
testing.expect(t, !is_enum, "the untaken arm was selected")
|
||||
}
|
||||
|
||||
@(test)
|
||||
union_constant_in_or_else :: proc(t: ^testing.T) {
|
||||
E :: enum { None, Bad }
|
||||
EU :: union { E, string }
|
||||
|
||||
m: map[int]EU
|
||||
defer delete(m)
|
||||
|
||||
missing := m[0] or_else EU(E.Bad)
|
||||
if v, ok := missing.(E); testing.expect(t, ok, "or_else default lost its variant") { testing.expect(t, v == E.Bad, "default") }
|
||||
|
||||
m[1] = EU("here")
|
||||
present := m[1] or_else EU(E.Bad)
|
||||
if v, ok := present.(string); testing.expect(t, ok, "or_else present lost its variant") { testing.expect_value(t, v, "here") }
|
||||
}
|
||||
Reference in New Issue
Block a user