Merge pull request #7400 from kalsprite/union_slice_assert

backend: only reuse a compound literal's storage for a variable of its type
This commit is contained in:
Jeroen van Rijn
2026-08-20 03:38:09 +02:00
committed by GitHub
2 changed files with 49 additions and 1 deletions

View File

@@ -3390,7 +3390,10 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
// NOTE(bill, 2023-02-17): lb_const_value might produce a stack local variable for the
// compound literal, so reusing that variable should minimize the stack wastage
lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs);
if (comp_lit_addr) {
// Only when the variable IS the literal. `x: U = []int{0}` declares a union and
// the literal is one of its variants, so reusing that storage would bind the
// variable to a bare `[]int` and never build the union at all
if (comp_lit_addr && are_types_identical(lb_addr_type(*comp_lit_addr), type_of_expr(vd->names[lval_index]))) {
if (Entity *e = entity_of_node(vd->names[lval_index])) {
lbValue val = comp_lit_addr->addr;
lb_add_entity(p->module, e, val);

View File

@@ -0,0 +1,45 @@
package test_internal
import "core:testing"
// `x := []int{0}` lets the variable reuse the compound literal's storage, which is a stack saving.
// `x: U = []int{0}` is not the same thing: the variable is a union and the literal is one of its
// variants, so reusing that storage bound the variable to a bare `[]int` and never built the union.
// A later type assertion then reached the backend with the wrong operand type
@(test)
union_declared_from_a_variant_compound_literal :: proc(t: ^testing.T) {
U :: union { []int, int }
S :: struct { a: int }
V :: union { S, int }
A :: union { [2]int, int }
x: U = []int{7}
if v, ok := x.([]int); testing.expect(t, ok, "slice variant lost") {
testing.expect_value(t, len(v), 1)
testing.expect_value(t, v[0], 7)
}
// the same shape through a switch, which is how the union is usually consumed
got := 0
switch v in x {
case []int: got = v[0]
case int: got = -1
}
testing.expect_value(t, got, 7)
s: V = S{5}
if v, ok := s.(S); testing.expect(t, ok, "struct variant lost") {
testing.expect_value(t, v.a, 5)
}
a: A = [2]int{3, 4}
if v, ok := a.([2]int); testing.expect(t, ok, "array variant lost") {
testing.expect_value(t, v[1], 4)
}
// the reuse is still correct when the variable IS the literal
plain := []int{1, 2}
testing.expect_value(t, plain[1], 2)
ps := S{9}
testing.expect_value(t, ps.a, 9)
}