Implement fixes for constant unions

Note that with this commit, casting through
multiply-nested unions is forbidden, e.g.
```
U :: union {int, V}
V :: union {bool}
x: U = true
```
does not compile.
(Previously the compiler would simply crash)

This is to avoid situations where adding variants
can lead to unexpected changes in the value.

For example if `U` is changed to have a `bool`
variant of its own:
```
U :: union {int, bool, V}
```
Then `x: U = true` would equal
`U(true)` instead of `U(V(true))`.

Single-variant unions are exempt, primarily to
improve the ergonomics of `Maybe` in cases like:
```
x: Maybe(union{int, bool}) = 1
```

Fix #6100
Fix #6699
Fix #6895
Fix #6896
Fix #6897
Fix #7036
Fix #7083
Fix #7091
This commit is contained in:
korvahkh
2026-07-22 09:58:49 -05:00
parent 7f80c7093a
commit 02246ac6fd
8 changed files with 199 additions and 139 deletions

View File

@@ -29,6 +29,7 @@ enum ExactValueKind {
ExactValue_Typeid = 10,
ExactValue_String16 = 11,
ExactValue_AsmTemplate = 12,
ExactValue_Variant = 13,
ExactValue_Count,
};
@@ -47,6 +48,8 @@ gb_global char const *exact_value_kind_string[ExactValue_Count] = {
"Procedure",
"Typeid",
"String16",
"AsmTemplate",
"Variant",
};
struct ExactValue {
@@ -64,7 +67,9 @@ struct ExactValue {
Type * value_typeid;
String16 value_string16;
Ast * value_asm_template;
Ast * value_variant;
};
Type *variant_type;
};
gb_global ExactValue const empty_exact_value = {};
@@ -115,6 +120,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) {
case ExactValue_Typeid:
res = ptr_map_hash_key(v.value_typeid);
break;
case ExactValue_Variant:
res = ptr_map_hash_key(v.value_variant);
break;
default:
res = gb_fnv32a(&v, gb_size_of(ExactValue));
}
@@ -202,6 +210,11 @@ gb_internal ExactValue exact_value_typeid(Type *type) {
return result;
}
gb_internal ExactValue exact_value_variant(Ast *node) {
ExactValue result = {ExactValue_Variant};
result.value_variant = node;
return result;
}
gb_internal ExactValue exact_value_integer_from_string(String const &string) {
ExactValue result = {ExactValue_Integer};
@@ -688,6 +701,7 @@ gb_internal i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
case ExactValue_Compound:
case ExactValue_Variant:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -712,6 +726,8 @@ gb_internal i32 exact_value_order(ExactValue const &v) {
}
}
gb_internal void match_exact_values_variant(ExactValue *x, ExactValue *y);
gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
if (exact_value_order(*y) < exact_value_order(*x)) {
match_exact_values(y, x);
@@ -772,6 +788,10 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
return;
}
break;
case ExactValue_Variant:
match_exact_values_variant(x, y);
return;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
@@ -973,6 +993,7 @@ gb_internal gb_inline i32 cmp_f64(f64 a, f64 b) {
}
gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y);
gb_internal bool compare_exact_values_variant(TokenKind op, ExactValue x, ExactValue y);
gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
@@ -1119,6 +1140,16 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
return false;
}
return compare_exact_values_compound_lit(op, x, y);
case ExactValue_Variant:
if (op != Token_CmpEq && op != Token_NotEq) {
return false;
}
if (x.kind != y.kind) {
return op == Token_NotEq;
}
return compare_exact_values_variant(op, x, y);
}
GB_PANIC("Invalid comparison: %d", x.kind);
@@ -1184,6 +1215,8 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const
return write_expr_to_string(str, v.value_compound, false);
case ExactValue_Procedure:
return write_expr_to_string(str, v.value_procedure, false);
case ExactValue_Variant:
return write_expr_to_string(str, v.value_variant, false);
}
return str;
};