mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 10:13:35 +00:00
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:
@@ -689,7 +689,7 @@ gb_internal bool check_proc_params_assignable(CheckerContext *c, Type *x, Type *
|
||||
|
||||
#define MAXIMUM_TYPE_DISTANCE 10
|
||||
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming, bool allow_unions=true) {
|
||||
if (c == nullptr) {
|
||||
GB_ASSERT(operand->mode == Addressing_Value);
|
||||
GB_ASSERT(is_type_typed(operand->type));
|
||||
@@ -895,7 +895,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_union(dst)) {
|
||||
if (is_type_union(dst) && allow_unions) {
|
||||
for (Type *vt : dst->Union.variants) {
|
||||
if (are_types_identical(vt, s)) {
|
||||
return 1;
|
||||
@@ -917,7 +917,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
i64 prev_lowest_score = -1;
|
||||
i64 lowest_score = -1;
|
||||
for (Type *vt : dst->Union.variants) {
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming, /*allow_unions*/false);
|
||||
if (score >= 0) {
|
||||
if (lowest_score < 0) {
|
||||
lowest_score = score;
|
||||
@@ -1035,7 +1035,7 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true, bool allow_unions=true) {
|
||||
if (c == nullptr) {
|
||||
GB_ASSERT(operand->mode == Addressing_Value);
|
||||
GB_ASSERT(is_type_typed(operand->type));
|
||||
@@ -1045,7 +1045,7 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
|
||||
i64 score = check_distance_between_types(c, operand, type, allow_array_programming, allow_unions);
|
||||
if (score >= 0) {
|
||||
if (score_) *score_ = assign_score_function(score, is_variadic);
|
||||
return true;
|
||||
@@ -1159,7 +1159,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
|
||||
if (is_type_untyped(operand->type)) {
|
||||
Type *target_type = type;
|
||||
if (type == nullptr || is_type_any(type)) {
|
||||
Type *elem_type = core_broadcastable_elem_type(type);
|
||||
if (is_type_union(elem_type)) {
|
||||
target_type = elem_type;
|
||||
}
|
||||
if (type == nullptr || is_type_any(elem_type)) {
|
||||
if (type == nullptr && is_type_untyped_uninit(operand->type)) {
|
||||
String article = error_article(context_name); // Grab definite or indefinite article matching `context_name`, or "" if not found.
|
||||
|
||||
@@ -1252,6 +1256,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
}
|
||||
|
||||
if (check_is_assignable_to(c, operand, type)) {
|
||||
if (operand->mode == Addressing_Constant && type_conversion_is_variant(type, operand->type)) {
|
||||
Operand o = {};
|
||||
check_expr_with_type_hint(c, &o, operand->expr, type);
|
||||
operand->value = exact_value_variant(operand->expr);
|
||||
}
|
||||
if (operand->mode == Addressing_Type && is_type_typeid(type)) {
|
||||
add_type_info_type(c, operand->type);
|
||||
add_type_and_value(c, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
|
||||
@@ -3925,6 +3934,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
|
||||
x->mode = Addressing_Value;
|
||||
} else if (is_type_union(type)) {
|
||||
if (is_type_union_constantable(type)) {
|
||||
x->value = exact_value_variant(x->expr);
|
||||
return true;
|
||||
}
|
||||
x->mode = Addressing_Value;
|
||||
@@ -3991,8 +4001,9 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forb
|
||||
if (is_type_untyped(x->type)) {
|
||||
Type *final_type = type;
|
||||
if (is_const_expr && !is_type_constant_type(type)) {
|
||||
if (is_type_union(type)) {
|
||||
convert_to_typed(c, x, type);
|
||||
Type *elem_type = core_broadcastable_elem_type(type);
|
||||
if (is_type_union(elem_type)) {
|
||||
convert_to_typed(c, x, elem_type);
|
||||
}
|
||||
final_type = default_type(x->type);
|
||||
}
|
||||
@@ -5185,11 +5196,12 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
case Type_Array: {
|
||||
Type *elem = base_array_type(t);
|
||||
if (check_is_assignable_to(c, operand, elem)) {
|
||||
while (is_type_array(elem)) {
|
||||
elem = base_array_type(elem);
|
||||
}
|
||||
elem = core_broadcastable_elem_type(elem);
|
||||
operand->mode = Addressing_Value;
|
||||
convert_to_typed(c, operand, elem, /*no_final_update*/true);
|
||||
if (is_type_union(elem)) {
|
||||
target_type = operand->type;
|
||||
}
|
||||
} else {
|
||||
if (operand->value.kind == ExactValue_String) {
|
||||
String s = operand->value.value_string;
|
||||
@@ -5294,7 +5306,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
for_array(i, t->Union.variants) {
|
||||
Type *vt = t->Union.variants[i];
|
||||
i64 score = 0;
|
||||
if (check_is_assignable_to_with_score(c, operand, vt, &score)) {
|
||||
if (check_is_assignable_to_with_score(c, operand, vt, &score, /*is_variadic*/false, /*allow_array_programming*/true, /*allow_unions*/t->Union.variants.count == 1)) {
|
||||
valids[valid_count].index = i;
|
||||
valids[valid_count].score = score;
|
||||
valid_count += 1;
|
||||
@@ -5328,7 +5340,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
convert_to_typed(c, operand, new_type, /*no_final_update*/true);
|
||||
target_type = new_type;
|
||||
target_type = operand->type;
|
||||
break;
|
||||
} else if (valid_count > 1) {
|
||||
ERROR_BLOCK();
|
||||
@@ -12851,8 +12863,21 @@ gb_internal ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, T
|
||||
}
|
||||
gb_string_free(xs);
|
||||
}
|
||||
if (o->type != nullptr && is_type_untyped(o->type)) {
|
||||
add_untyped(c, node, o->mode, o->type, o->value);
|
||||
if (o->type != nullptr) {
|
||||
if (type_hint != nullptr) {
|
||||
Type *elem_type = core_broadcastable_elem_type(type_hint);
|
||||
if (is_type_untyped(o->type)) {
|
||||
if (is_type_union(elem_type)) {
|
||||
convert_to_typed(c, o, elem_type);
|
||||
}
|
||||
}
|
||||
if (type_conversion_is_variant(elem_type, o->type)) {
|
||||
o->value.variant_type = o->type;
|
||||
}
|
||||
}
|
||||
if (is_type_untyped(o->type)) {
|
||||
add_untyped(c, node, o->mode, o->type, o->value);
|
||||
}
|
||||
}
|
||||
check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed");
|
||||
|
||||
@@ -12986,6 +13011,14 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
|
||||
return v.value_procedure == nullptr;
|
||||
case ExactValue_Typeid:
|
||||
return v.value_typeid == nullptr;
|
||||
case ExactValue_Variant:
|
||||
if (v.value_variant == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (v.value_variant->tav.mode != Addressing_Constant) {
|
||||
return false;
|
||||
}
|
||||
return is_exact_value_zero(v.value_variant->tav.value);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -13013,8 +13046,26 @@ gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, E
|
||||
return test;
|
||||
}
|
||||
|
||||
gb_internal bool compare_exact_values_variant(TokenKind op, ExactValue x, ExactValue y) {
|
||||
Ast *lhs = x.value_variant;
|
||||
Ast *rhs = y.value_variant;
|
||||
|
||||
return compare_exact_values(op, lhs->tav.value, rhs->tav.value);
|
||||
}
|
||||
|
||||
gb_internal void match_exact_values_variant(ExactValue *x, ExactValue *y) {
|
||||
GB_ASSERT(x->kind == ExactValue_Variant);
|
||||
while (x->value_variant != nullptr &&
|
||||
x->value_variant->tav.mode == Addressing_Constant) {
|
||||
*x = x->value_variant->tav.value;
|
||||
}
|
||||
while (y->kind == ExactValue_Variant &&
|
||||
y->value_variant != nullptr &&
|
||||
y->value_variant->tav.mode == Addressing_Constant) {
|
||||
*y = y->value_variant->tav.value;
|
||||
}
|
||||
match_exact_values(x, y);
|
||||
}
|
||||
|
||||
gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user