diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cc99a20af..316fcc25e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -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); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index cb4c5a578..d768525ad 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -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; }; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9cd3d1c9a..07f118f20 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -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, lb_build_expr_original_const_type(decl->init_expr), cc); + lbValue init = lb_const_value(m, e->type, v, cc); LLVMDeleteGlobal(g.value); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 563f854d7..6e933d152 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -418,7 +418,7 @@ static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, { gb_internal lbValue lb_const_nil(lbModule *m, Type *type); gb_internal lbValue lb_const_undef(lbModule *m, Type *type); -gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Type *value_type=nullptr, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT); +gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT); gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value); gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index bfb2c3c34..7a72dd1f4 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -206,7 +206,7 @@ gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeR GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count); if (force_non_named) { - return LLVMConstStructInContext(m->ctx, values, value_count, true); + return LLVMConstStructInContext(m->ctx, values, value_count, LLVMIsPackedStruct(t)); } bool failure = false; @@ -216,7 +216,7 @@ gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeR } if (failure) { - return LLVMConstStructInContext(m->ctx, values, value_count, true); + return LLVMConstStructInContext(m->ctx, values, value_count, LLVMIsPackedStruct(t)); } return LLVMConstNamedStruct(t, values, value_count); } @@ -582,13 +582,13 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, return lb_is_elem_const(elem, ft); } -gb_internal void lb_const_array_spread(lbModule *m, lbConstContext cc, Type *array, ExactValue value, lbValue *res, Type *value_type) { +gb_internal void lb_const_array_spread(lbModule *m, lbConstContext cc, Type *array, ExactValue value, lbValue *res) { GB_ASSERT(array->kind == Type_Array); i64 count = array->Array.count; Type *elem = array->Array.elem; - lbValue single_elem = lb_const_value(m, elem, value, value_type, cc); + lbValue single_elem = lb_const_value(m, elem, value, cc); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count); for (i64 i = 0; i < count; i++) { @@ -661,7 +661,7 @@ gb_internal lbValue lb_const_value_bit_field(lbModule *m, Type *type, Ast *value if (fv->value->tav.mode != Addressing_Constant) { continue; } - lbValue field_expr = lb_const_value(m, field_type, fv->value->tav.value, field_type); + lbValue field_expr = lb_const_value(m, field_type, fv->value->tav.value); array_add(&values, field_expr); array_add(&fields, FieldData{field_type, cast(u64)bit_offset, cast(u64)bit_size}); } @@ -791,7 +791,7 @@ gb_internal lbValue lb_const_value_bit_field(lbModule *m, Type *type, Ast *value } -gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Type *value_type, lbConstContext cc) { +gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) { if (cc.allow_local) { cc.is_rodata = false; } @@ -803,6 +803,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty lbValue res = {}; res.type = original_type; + while (value.kind == ExactValue_Variant && + (value.variant_type == nullptr || + are_types_identical(value.variant_type, original_type))) { + value = value.value_variant->tav.value; + } if (!is_type_bit_field(original_type)) { type = core_type(type); value = convert_exact_value_for_type(value, type); @@ -816,25 +821,36 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty GB_ASSERT(bt->kind == Type_Union); if (bt->Union.variants.count == 0) { return lb_const_nil(m, original_type); - } else if (bt->Union.variants.count == 1) { - if (value.kind == ExactValue_Compound) { - ast_node(cl, CompoundLit, value.value_compound); - if (cl->elems.count == 0) { - if (cl->type == nullptr) { - return lb_const_nil(m, original_type); - } - if (are_types_identical(type_of_expr(cl->type), original_type)) { - return lb_const_nil(m, original_type); - } - } - } + } - if (value_type == t_untyped_nil) { + Type *value_type = value.variant_type; + switch (value.kind) { + case ExactValue_Invalid: + return lb_const_nil(m, original_type); + + case ExactValue_Compound: { + ast_node(cl, CompoundLit, value.value_compound); + if (value_type == nullptr || are_types_identical(value_type, original_type)) { + GB_ASSERT(cl->elems.count == 0); return lb_const_nil(m, original_type); } + break; + } + case ExactValue_Variant: + value = value.value_variant->tav.value; + break; + } + GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value)); + GB_ASSERT(!are_types_identical(value_type, original_type)); + if (value_type == t_untyped_nil) { + return lb_const_nil(m, original_type); + } + + if (bt->Union.variants.count == 1) { Type *t = bt->Union.variants[0]; - lbValue cv = lb_const_value(m, t, value, value_type, cc); + + lbValue cv = lb_const_value(m, t, value, cc); GB_ASSERT(LLVMIsConstant(cv.value)); LLVMTypeRef llvm_type = lb_type(m, original_type); @@ -870,51 +886,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty return res; } } else { - if (value_type == nullptr) { - if (value.kind == ExactValue_Compound) { - ast_node(cl, CompoundLit, value.value_compound); - if (cl->elems.count == 0) { - return lb_const_nil(m, original_type); - } - value_type = type_of_expr(value.value_compound); - } else if (value.kind == ExactValue_Invalid) { - return lb_const_nil(m, original_type); - } - } else if (value_type == t_untyped_nil) { - return lb_const_nil(m, original_type); - } - - GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value)); - + // NOTE(korvahkh): forces calculation of variant_block_size + type_size_of(bt); i64 block_size = bt->Union.variant_block_size; - while (are_types_identical(value_type, original_type)) { - if (value.kind == ExactValue_Compound) { - ast_node(cl, CompoundLit, value.value_compound); - if (cl->elems.count == 0) { - return lb_const_nil(m, original_type); - } - - value_type = type_of_expr(value.value_compound); - if (!are_types_identical(value_type, original_type)) { - break; - } - - GB_PANIC("%s --> %s vs %s", - expr_to_string(value.value_compound), - temp_canonical_string(value_type), temp_canonical_string(original_type)); - - } else if (value.kind == ExactValue_Invalid) { - return lb_const_nil(m, original_type); - } - - GB_PANIC("(value.kind=%s) %s vs %s", - exact_value_kind_string[value.kind], - temp_canonical_string(value_type), temp_canonical_string(original_type)); - } - // union_multiple_allow_compound:; - - lbValue cv = lb_const_value(m, value_type, value, value_type, cc); + lbValue cv = lb_const_value(m, value_type, value, cc); Type *variant_type = cv.type; LLVMValueRef values[4] = {}; @@ -1015,7 +991,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty count = gb_max(cast(isize)cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); - lbValue backing_array = lb_const_value(m, t, value, nullptr, cc); + lbValue backing_array = lb_const_value(m, t, value, cc); LLVMValueRef array_data = nullptr; @@ -1154,7 +1130,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty value.kind != ExactValue_Invalid && value.kind != ExactValue_Compound) { - lb_const_array_spread(m, cc, type, value, &res, value_type); + lb_const_array_spread(m, cc, type, value, &res); return res; } else if (is_type_matrix(type) && value.kind != ExactValue_Invalid && @@ -1165,7 +1141,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty Type *elem = type->Matrix.elem; - lbValue single_elem = lb_const_value(m, elem, value, value_type, cc); + lbValue single_elem = lb_const_value(m, elem, value, cc); single_elem.value = llvm_const_cast(m, single_elem.value, lb_type(m, elem), /*failure_*/nullptr); i64 total_elem_count = matrix_type_total_internal_elems(type); @@ -1187,7 +1163,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty i64 count = type->SimdVector.count; Type *elem = type->SimdVector.elem; - lbValue single_elem = lb_const_value(m, elem, value, value_type, cc); + lbValue single_elem = lb_const_value(m, elem, value, cc); single_elem.value = llvm_const_cast(m, single_elem.value, lb_type(m, elem), /*failure_*/nullptr); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); @@ -1374,7 +1350,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty if (is_type_bit_field(original_type)) { return lb_const_value_bit_field(m, original_type, value.value_compound); } else if (is_type_slice(type)) { - return lb_const_value(m, type, value, value_type, cc); + return lb_const_value(m, type, value, cc); } else if (is_type_soa_struct(type)) { GB_ASSERT(type->kind == Type_Struct); GB_ASSERT(type->Struct.soa_kind == StructSoa_Fixed); @@ -1415,7 +1391,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { aos_values[value_index++] = val; } @@ -1430,7 +1406,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; aos_values[value_index++] = val; found = true; break; @@ -1485,7 +1461,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + aos_values[i] = lb_const_value(m, elem_type, tav.value, cc).value; } for (isize i = elem_count; i < type->Struct.soa_count; i++) { aos_values[i] = nullptr; @@ -1533,7 +1509,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); for (isize i = 0; i < type->Array.count; i++) { - values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; + values[i] = lb_const_value(m, elem_type, value, cc).value; } res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); @@ -1564,7 +1540,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -1579,7 +1555,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -1607,7 +1583,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + values[elem_index++] = lb_const_value(m, elem_type, tav.value, cc).value; } } for (isize i = 0; i < type->Array.count; i++) { @@ -1656,7 +1632,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -1671,7 +1647,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -1699,7 +1675,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + values[elem_index++] = lb_const_value(m, elem_type, tav.value, cc).value; } } for (isize i = 0; i < type->EnumeratedArray.count; i++) { @@ -1748,7 +1724,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -1766,7 +1742,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -1790,7 +1766,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)capacity); for (isize i = 0; i < capacity; i++) { - values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; + values[i] = lb_const_value(m, elem_type, value, cc).value; } res.value = lb_fill_fixed_capacity_dynamic_array(m, capacity, original_type, values, cc); @@ -1808,7 +1784,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + values[elem_index++] = lb_const_value(m, elem_type, tav.value, cc).value; } } for (isize i = 0; i < capacity; i++) { @@ -1856,7 +1832,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -1871,7 +1847,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -1890,7 +1866,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + values[i] = lb_const_value(m, elem_type, tav.value, cc).value; } LLVMTypeRef et = lb_type(m, elem_type); @@ -1920,7 +1896,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty TypeAndValue tav = fv->value->tav; if (tav.value.kind != ExactValue_Invalid) { - lbValue value = lb_const_value(m, f->type, tav.value, f->type, cc); + lbValue value = lb_const_value(m, f->type, tav.value, cc); LLVMValueRef values[2]; unsigned value_count = 0; @@ -1975,7 +1951,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, lb_build_expr_original_const_type(fv->value), cc); + lbValue value = lb_const_value(m, f->type, tav.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; @@ -1984,7 +1960,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty if (!visited[index]) { auto new_cc = cc; new_cc.allow_local = false; - values[index] = lb_const_value(m, f->type, {}, nullptr, new_cc).value; + values[index] = lb_const_value(m, f->type, {}, new_cc).value; visited[index] = true; } @@ -2023,7 +1999,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } if (is_constant) { - LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value; + LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, cc).value; if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { if (is_type_union(cv_type) || is_type_raw_union(cv_type)) { force_non_named = true; @@ -2085,7 +2061,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, lb_build_expr_original_const_type(cl->elems[i]), cc); + lbValue value = lb_const_value(m, f->type, tav.value, 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); @@ -2226,7 +2202,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.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); @@ -2238,7 +2214,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, lb_build_expr_original_const_type(fv->value), cc).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; i64 offset = matrix_row_major_index_to_offset(type, index); GB_ASSERT(values[offset] == nullptr); values[offset] = val; @@ -2262,7 +2238,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, lb_build_expr_original_const_type(cl->elems[i]), cc).value; + values[offset] = lb_const_value(m, elem_type, tav.value, cc).value; } for (isize i = 0; i < total_count; i++) { if (values[i] == nullptr) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index ea4e52a3f..f12965197 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3207,7 +3207,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { Type *elem = base_array_type(dst); lbValue e = lb_emit_conv(p, value, elem); lbAddr v = lb_add_local_generated(p, t, false); - lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), elem, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); + lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); for (i64 j = 0; j < dst->Matrix.column_count; j++) { for (i64 i = 0; i < dst->Matrix.row_count; i++) { lbValue ptr = lb_emit_matrix_epi(p, v.addr, i, j); @@ -3244,7 +3244,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lb_emit_store(p, d, s); } else if (i == j) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); - lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), dst->Matrix.elem, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); + lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); lb_emit_store(p, d, s); } } @@ -3439,11 +3439,12 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left if (are_types_identical(a, b)) { // NOTE(bill): No need for a conversion - } else if ((lb_is_const(left) && !is_type_array(left.type)) || lb_is_const_nil(left)) { + } else if ((lb_is_const(left) && !is_type_array(left.type) && !is_type_union(left.type)) || lb_is_const_nil(left)) { // NOTE(karl): !is_type_array(left.type) is there to avoid lb_emit_conv // trying to convert a constant array into a non-array. In that case we // want the `else` branch to happen, so it can try to convert the // non-array into an array instead. + // NOTE(korvahkh): We also need !is_type_union(left.type). if (lb_is_const_nil(left)) { if (internal_check_is_assignable_to(right.type, left.type)) { @@ -3454,7 +3455,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left } } left = lb_emit_conv(p, left, right.type); - } else if ((lb_is_const(right) && !is_type_array(right.type)) || lb_is_const_nil(right)) { + } else if ((lb_is_const(right) && !is_type_array(right.type) && !is_type_union(right.type)) || lb_is_const_nil(right)) { if (lb_is_const_nil(right)) { if (internal_check_is_assignable_to(left.type, right.type)) { @@ -4462,28 +4463,6 @@ gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return res; } -gb_internal Type *lb_build_expr_original_const_type(Ast *expr) { - expr = unparen_expr(expr); - Type *type = type_of_expr(expr); - if (is_type_union(type)) { - if (expr->kind == Ast_CallExpr) { - if (expr->CallExpr.proc->tav.mode == Addressing_Type) { - 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); -} - gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -4496,9 +4475,8 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { - Type *original_type = lb_build_expr_original_const_type(expr); // NOTE(bill): Short on constant values - return lb_const_value(p->module, type, tv.value, original_type, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); + return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); } else if (tv.mode == Addressing_Type) { // NOTE(bill, 2023-01-16): is this correct? I hope so at least return lb_typeid(m, tv.type); @@ -4579,7 +4557,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { TypeAndValue tav = type_and_value_of_expr(expr); GB_ASSERT(tav.mode == Addressing_Constant); - return lb_const_value(p->module, type, tv.value, tv.type, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); + return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); case_end; case_ast_node(se, SelectorCallExpr, expr); @@ -4862,7 +4840,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, lb_build_expr_original_const_type(expr), LB_CONST_CONTEXT_DEFAULT_NO_LOCAL); + lbValue v = lb_const_value(p->module, t, e->Constant.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL); if (LLVMIsConstant(v.value)) { lbAddr g = lb_add_global_generated_from_procedure(p, t, v); return g; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 5cdfb21ce..2e0c63f98 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2434,7 +2434,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { if (e->Variable.is_rodata) { cc.is_rodata = true; } - value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, nullptr, cc); + value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, cc); } String mangled_name = {}; diff --git a/src/types.cpp b/src/types.cpp index 8e9b25a53..d12d8dbee 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1951,6 +1951,13 @@ gb_internal Type *core_array_type(Type *t) { } } +gb_internal Type *core_broadcastable_elem_type(Type *t) { + while (is_type_array(t)) { + t = base_array_type(t); + } + return t; +} + gb_internal i32 type_math_rank(Type *t) { i32 rank = 0; for (;;) { @@ -3609,6 +3616,23 @@ gb_internal Type *union_tag_type(Type *u) { return t_uint; } +gb_internal bool type_conversion_is_variant(Type *dst, Type *src) { + dst = base_type(core_broadcastable_elem_type(dst)); + if (dst == nullptr) { return false; } + + switch (dst->kind) { + case Type_Union: + if (union_is_variant_of(dst, src)) { + return true; + } + if (dst->Union.variants.count == 1) { + return type_conversion_is_variant(dst->Union.variants[0], src); + } + return false; + } + return false; +} + gb_internal int matched_target_features(TypeProc *t) { if (t->require_target_feature.len == 0) { return 0; diff --git a/tests/internal/test_union_const.odin b/tests/internal/test_union_const.odin index 3f3260993..7180a0ce3 100644 --- a/tests/internal/test_union_const.odin +++ b/tests/internal/test_union_const.odin @@ -52,3 +52,195 @@ 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") } } + +A :: union {B, bool} +B :: union {C, int } +C :: struct{} + +@(test) +union_const_access :: proc(t: ^testing.T) { + X :: struct{x: A}{B(C{})} + testing.expect_value(t, X.x, A(B(C{}))) + + E :: enum {y} + Y : [E]A : {.y = true} + testing.expect_value(t, Y[.y], A(true)) +} + +@(test) +nested_union_implicit_cast :: proc(t: ^testing.T) { + Av: A: Bv + Bv: B: Cv + Cv: C: {} + testing.expect_value(t, Av, A(B(C{}))) +} + +@(test) +union_ternary :: proc(t: ^testing.T) { + E0 : A = B(C{}) if true else B(C{}) + E1 : A = true if true else true + E2 := A(B(C{}) if true else B(C{})) + E3 := A(true if true else true) + + testing.expect_value(t, E0, A(B(C{}))) + testing.expect_value(t, E1, A(true)) + testing.expect_value(t, E2, A(B(C{}))) + testing.expect_value(t, E3, A(true)) +} + +@(test) +union_array :: proc(t: ^testing.T) { + C0: [2][2]A: B(C{}) + C1: struct {x: [2][2]A} : {B(C{})} + testing.expect_value(t, C0, [2][2]A{0..<2 = B(C{})}) +} + +@(test) +union_multi_level_cast :: proc(t: ^testing.T) { + UI :: union {int, bool} + foo: Maybe(UI): 1 + bar: union{UI}: 2 + baz: UI: 3 + qux: struct{ui: UI}: {4} + testing.expect_value(t, foo, 1) + testing.expect_value(t, bar, 2) + testing.expect_value(t, baz, 3) + testing.expect_value(t, qux.ui, 4) +} + +@(test) +union_args :: proc(t: ^testing.T) { + implicit_bool :: proc(a: A = true ) -> A { return a.(bool) } + explicit_bool :: proc(a: A = A(true)) -> A { return a.(bool) } + nil_union :: proc(a: A = B{} ) -> A { return a.(B ) } + struct_union :: proc(a: A = B(C{}) ) -> A { return a.(B ) } + + testing.expect_value(t, implicit_bool(), true) + testing.expect_value(t, explicit_bool(), true) + testing.expect_value(t, nil_union (), B{}) + testing.expect_value(t, struct_union (), B(C{})) + + testing.expect_value(t, implicit_bool(true ), true) + testing.expect_value(t, explicit_bool(A(true)), true) + testing.expect_value(t, nil_union (B{} ), B{}) + testing.expect_value(t, struct_union (B(C{}) ), B(C{})) + + testing.expect_value(t, implicit_bool(a=true ), true) + testing.expect_value(t, explicit_bool(a=A(true)), true) + testing.expect_value(t, nil_union (a=B{} ), B{}) + testing.expect_value(t, struct_union (a=B(C{}) ), B(C{})) +} + +@(test) +union_in_aggregates :: proc(t: ^testing.T) { + S :: struct { + x : union { int, string }, + y : u128, + } + U :: struct #packed {v: [2]S, n: i64} + V :: struct {x: [dynamic; 2]S} + + s := [dynamic; 2]S { {x=1} } + u := U { {{x=1}, {x=2}}, 2 } + v := V{x = { {x=1} }} + + testing.expect_value(t, s [0], S{x=1}) + testing.expect_value(t, u.v[1], S{x=2}) + testing.expect_value(t, v.x[0], S{x=1}) +} + +@(test) +union_named_constants :: proc(t: ^testing.T) { + Inner_Left :: enum { + a, + b, + } + + Inner_Right :: enum { + c, + d, + } + + Inner :: union { + Inner_Left, + Inner_Right, + } + + Outer :: union { + Inner, + int, + } + + Atom :: struct { + token: Outer, + } + + Promoted_Value :: union { + int, + f32, + string, + } + + Promoted_Inner :: struct { + value: Promoted_Value, + padding0: int, + padding1: int, + } + + Promoted_Outer :: struct { + using inner: Promoted_Inner, + } + + NAMED_INNER :: Inner(Inner_Left.a) + + DIRECT_ATOMS :: [?]Atom{{token = Inner(Inner_Left.a)}} + + NAMED_ATOMS :: [?]Atom{{token = NAMED_INNER}} + + INDEXED_OUTERS :: [1]Outer { + 0 = Inner(Inner_Left.a), + } + + RANGED_OUTERS :: [1]Outer { + 0..=0 = Inner(Inner_Left.a), + } + + Outer_Index :: enum {first} + + ENUMERATED_OUTERS :: [Outer_Index]Outer { + .first = Inner(Inner_Left.a), + } + + RANGED_ENUMERATED_OUTERS :: [Outer_Index]Outer { + .first..=.first = Inner(Inner_Left.a), + } + + FIXED_CAPACITY_OUTERS :: [dynamic; 1]Outer{ + 0 = Inner(Inner_Left.a), + } + + RANGED_FIXED_CAPACITY_OUTERS :: [dynamic; 1]Outer{ + 0..=0 = Inner(Inner_Left.a), + } + + POSITIONAL_FIXED_CAPACITY_OUTERS :: [dynamic; 1]Outer{ + Inner(Inner_Left.a), + } + + testing.expect_value(t, DIRECT_ATOMS[0], Atom{token = Inner(.a)}) + testing.expect_value(t, NAMED_ATOMS[0], Atom{token = NAMED_INNER}) + testing.expect_value(t, INDEXED_OUTERS[0], Inner(.a)) + testing.expect_value(t, RANGED_OUTERS[0], Inner(.a)) + testing.expect_value(t, ENUMERATED_OUTERS[.first], Inner(.a)) + testing.expect_value(t, RANGED_ENUMERATED_OUTERS[.first], Inner(.a)) + fco := FIXED_CAPACITY_OUTERS + rfco := RANGED_FIXED_CAPACITY_OUTERS + testing.expect_value(t, fco[0], Inner(.a)) + testing.expect_value(t, rfco[0], Inner(.a)) + testing.expect_value(t, POSITIONAL_FIXED_CAPACITY_OUTERS[0], Inner(.a)) + testing.expect_value(t, Promoted_Outer { + value = Promoted_Value(int(1)), + }, Promoted_Outer { + inner = {Promoted_Value(int(1)), 0, 0}, + }) +}