From 85c2aaf0a426158e96261ddace4d5287c646ba58 Mon Sep 17 00:00:00 2001 From: misomosi Date: Sun, 28 Jun 2026 19:25:02 -0400 Subject: [PATCH 01/20] Use libtommath API for counting bits --- src/big_int.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/big_int.cpp b/src/big_int.cpp index 4f20f2ad9..a8ea2079b 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -296,8 +296,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x) { - int bits_used = (x->used-1) * MP_DIGIT_BIT; - return bits_used <= 64; + return mp_count_bits(x) <= 64; } gb_internal u64 big_int_to_u64(BigInt const *x) { From 5be99bf1c2ff63633a1e28be79f7e5df84eb90a5 Mon Sep 17 00:00:00 2001 From: misomosi Date: Sun, 28 Jun 2026 19:25:22 -0400 Subject: [PATCH 02/20] Add missing check_is_expressible --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 79e1154a1..5a95a5dbb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3421,6 +3421,8 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod x->expr = node; x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value)); + check_is_expressible(c, x, x->type); + return; } From 6e65b5e4bccc54ad7131d0cfeb87f1e87dcfb157 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 00:55:07 +0100 Subject: [PATCH 03/20] Allow for more "constantable" `union` values --- src/types.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types.cpp b/src/types.cpp index 32f5a10a9..d85f01c41 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2699,8 +2699,6 @@ gb_internal bool is_type_union_constantable(Type *type) { if (bt->Union.variants.count == 0) { return true; - } else if (bt->Union.variants.count == 1) { - return is_type_constant_type(bt->Union.variants[0]); } for (Type *v : bt->Union.variants) { From e227c736e66859d5ebcdf3a787232832cdc14721 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 10:49:29 +0100 Subject: [PATCH 04/20] Fix #6786 --- src/llvm_backend_const.cpp | 12 +++++++----- src/llvm_backend_general.cpp | 10 ++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index a314791c9..be036f553 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -911,6 +911,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty bool is_local = cc.allow_local && m->curr_procedure != nullptr; + if (is_type_union(type) && is_type_union_constantable(type)) { Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Union); @@ -1001,16 +1002,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty LLVMValueRef values[4] = {}; unsigned value_count = 0; - #if LLVM_VERSION_MAJOR == 14 + #if LLVM_VERSION_MAJOR == 14 LLVMTypeRef block_type = lb_type_internal_union_block_type(m, bt); values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type); - #else + #else values[value_count++] = cv.value; - if (type_size_of(variant_type) != block_size) { + { LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1); values[value_count++] = LLVMConstNull(padding_type); } - #endif + #endif Type *tag_type = union_tag_type(bt); LLVMTypeRef llvm_tag_type = lb_type(m, tag_type); @@ -1026,6 +1027,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } res.value = LLVMConstStructInContext(m->ctx, values, value_count, true); + return res; } } @@ -2103,7 +2105,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } if (is_constant) { - LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, tav.type, cc).value; + LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value; if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); } else if (is_local) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index d0249172c..45380e7b6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2498,7 +2498,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); } - auto fields = array_make(temporary_allocator(), 0, 3); + auto fields = array_make(temporary_allocator(), 0, 4); if (is_type_union_maybe_pointer(type)) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); array_add(&fields, variant); @@ -2519,6 +2519,12 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); + #if LLVM_VERSION_MAJOR > 14 + { // NOTE(bill): always add a zero-byte pad to make inline constant unions work + LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1); + array_add(&fields, padding_type); + } + #endif array_add(&fields, tag_type); i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); i64 padding = size - used_size; @@ -2528,7 +2534,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } } - return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, false); + return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, /*packed*/true); } break; From feb9b6159ef0c9b41c0dcefe319fc68fe645a8e7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 11:06:34 +0100 Subject: [PATCH 05/20] Fix `pkg.Constant.field` union access --- src/exact_value.cpp | 16 ++++++++++++++++ src/llvm_backend_const.cpp | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/exact_value.cpp b/src/exact_value.cpp index f232f95e0..184a8f2e4 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -32,6 +32,22 @@ enum ExactValueKind { ExactValue_Count, }; +gb_global char const *exact_value_kind_string[ExactValue_Count] = { + "Invalid", + + "Bool", + "String", + "Integer", + "Float", + "Complex", + "Quaternion", + "Pointer", + "Compound", + "Procedure", + "Typeid", + "String16", +}; + struct ExactValue { ExactValueKind kind; union { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index be036f553..0973d49d5 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -972,6 +972,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty 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); } @@ -983,18 +984,31 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty i64 block_size = bt->Union.variant_block_size; - if (are_types_identical(value_type, original_type)) { + 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("%s vs %s", type_to_string(value_type), type_to_string(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); Type *variant_type = cv.type; From 2f3174fe1064e0d0b81735d59ed5021bfe51247d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 11:41:27 +0100 Subject: [PATCH 06/20] Fix `llvm_const_cast` with constant `union` literals --- src/llvm_backend_const.cpp | 68 ++++++++++++++++++++++++++++++++---- src/llvm_backend_general.cpp | 14 ++++++-- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 0973d49d5..fffc9f94c 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -99,23 +99,69 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType return LLVMConstNull(dst); } - GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "%s vs %s", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src)); LLVMTypeKind kind = LLVMGetTypeKind(dst); switch (kind) { case LLVMPointerTypeKind: { + GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src), + cast(long long)lb_sizeof(dst), + cast(long long)lb_sizeof(src)); + return LLVMConstPointerCast(val, dst); } case LLVMStructTypeKind: { + GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src), + cast(long long)lb_sizeof(dst), + cast(long long)lb_sizeof(src)); + unsigned src_n = LLVMCountStructElementTypes(src); unsigned dst_n = LLVMCountStructElementTypes(dst); if (src_n != dst_n) goto failure; + bool skip_cast = true; + for (unsigned i = 0; i < dst_n; i++) { + LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i)); + LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i)); + if (dt != st) { + skip_cast = false; + } + if (dt == LLVMIntegerTypeKind) { + continue; + } + if (dt != LLVMArrayTypeKind) { + skip_cast = false; + break; + } + + LLVMValueRef field_val = llvm_const_extract_value(m, val, i); + if (field_val == nullptr) goto failure; + + LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); + LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) { + skip_cast = true; + continue; + } + + } + if (skip_cast) { + return val; + } + LLVMValueRef *field_vals = temporary_alloc_array(dst_n); for (unsigned i = 0; i < dst_n; i++) { LLVMValueRef field_val = llvm_const_extract_value(m, val, i); if (field_val == nullptr) goto failure; LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); + LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + + GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty), + cast(long long)lb_sizeof(dst_elem_ty), + cast(long long)lb_sizeof(src_elem_ty), + LLVMPrintTypeToString(dst), + LLVMPrintTypeToString(src) + ); + field_vals[i] = llvm_const_cast(m, field_val, dst_elem_ty, failure_); if (failure_ && *failure_) goto failure; } @@ -126,6 +172,9 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType return LLVMConstStructInContext(m->ctx, field_vals, dst_n, LLVMIsPackedStruct(dst)); } } + case LLVMArrayTypeKind: { + goto failure; + } } failure: @@ -946,21 +995,26 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty res.type = original_type; return res; } else { + LLVMValueRef values[4] = {}; + isize value_count = 0; + + // Payload + values[value_count++] = cv.value; unsigned tag_value = 1; if (bt->Union.kind == UnionType_no_nil) { tag_value = 0; } - LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false); - LLVMValueRef padding = nullptr; - isize value_count = 2; + // Tag + values[value_count++] = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);; + if (LLVMCountStructElementTypes(llvm_type) > 2) { - value_count = 3; - padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2)); + GB_ASSERT(LLVMCountStructElementTypes(llvm_type) == 3); + // Padding + values[value_count++] = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2)); } - LLVMValueRef values[3] = {cv.value, tag, padding}; res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count); res.type = original_type; return res; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 45380e7b6..e91ce3fd7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1636,22 +1636,32 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) { GB_ASSERT_MSG(is_type_pointer(t) && is_type_union(type_deref(t)), "%s", type_to_string(t)); Type *ut = type_deref(t); + Type *bt = base_type(ut); GB_ASSERT(!is_type_union_maybe_pointer_original_alignment(ut)); GB_ASSERT(!is_type_union_maybe_pointer(ut)); GB_ASSERT(type_size_of(ut) > 0); + GB_ASSERT(bt->kind == Type_Union); Type *tag_type = union_tag_type(ut); LLVMTypeRef uvt = llvm_addr_type(p->module, u); unsigned element_count = LLVMCountStructElementTypes(uvt); - GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); + if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { + GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); + } else { + GB_ASSERT_MSG(element_count >= 3, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); + } LLVMValueRef ptr = u.value; ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), ""); lbValue tag_ptr = {}; - tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); + if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { + tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); + } else { + tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 2, ""); + } tag_ptr.type = alloc_type_pointer(tag_type); return tag_ptr; } From 42f681baff7edd93aa5eb62adf05cb0e64ad8720 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 12:18:40 +0100 Subject: [PATCH 07/20] Revert some of the union changes --- src/llvm_backend_const.cpp | 54 ++++++++++++++++++------------------ src/llvm_backend_general.cpp | 31 ++++++++------------- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index fffc9f94c..8cd069772 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -117,35 +117,35 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType unsigned dst_n = LLVMCountStructElementTypes(dst); if (src_n != dst_n) goto failure; - bool skip_cast = true; - for (unsigned i = 0; i < dst_n; i++) { - LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i)); - LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i)); - if (dt != st) { - skip_cast = false; - } - if (dt == LLVMIntegerTypeKind) { - continue; - } - if (dt != LLVMArrayTypeKind) { - skip_cast = false; - break; - } + // bool skip_cast = true; + // for (unsigned i = 0; i < dst_n; i++) { + // LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i)); + // LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i)); + // if (dt != st) { + // skip_cast = false; + // } + // if (dt == LLVMIntegerTypeKind) { + // continue; + // } + // if (dt != LLVMArrayTypeKind) { + // skip_cast = false; + // break; + // } - LLVMValueRef field_val = llvm_const_extract_value(m, val, i); - if (field_val == nullptr) goto failure; + // LLVMValueRef field_val = llvm_const_extract_value(m, val, i); + // if (field_val == nullptr) goto failure; - LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); - LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); - if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) { - skip_cast = true; - continue; - } + // LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); + // LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + // if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) { + // skip_cast = true; + // continue; + // } - } - if (skip_cast) { - return val; - } + // } + // if (skip_cast) { + // return val; + // } LLVMValueRef *field_vals = temporary_alloc_array(dst_n); for (unsigned i = 0; i < dst_n; i++) { @@ -1075,7 +1075,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type); #else values[value_count++] = cv.value; - { + if (block_size != type_size_of(variant_type)) { LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1); values[value_count++] = LLVMConstNull(padding_type); } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index e91ce3fd7..d79cf5ed7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1636,32 +1636,22 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) { GB_ASSERT_MSG(is_type_pointer(t) && is_type_union(type_deref(t)), "%s", type_to_string(t)); Type *ut = type_deref(t); - Type *bt = base_type(ut); GB_ASSERT(!is_type_union_maybe_pointer_original_alignment(ut)); GB_ASSERT(!is_type_union_maybe_pointer(ut)); GB_ASSERT(type_size_of(ut) > 0); - GB_ASSERT(bt->kind == Type_Union); Type *tag_type = union_tag_type(ut); LLVMTypeRef uvt = llvm_addr_type(p->module, u); unsigned element_count = LLVMCountStructElementTypes(uvt); - if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { - GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); - } else { - GB_ASSERT_MSG(element_count >= 3, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); - } + GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); LLVMValueRef ptr = u.value; ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), ""); lbValue tag_ptr = {}; - if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { - tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); - } else { - tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 2, ""); - } + tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); tag_ptr.type = alloc_type_pointer(tag_type); return tag_ptr; } @@ -2507,6 +2497,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef fields[] = {lb_type(m, type->Union.variants[0])}; return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); } + bool is_packed = false; auto fields = array_make(temporary_allocator(), 0, 4); if (is_type_union_maybe_pointer(type)) { @@ -2524,17 +2515,18 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); array_add(&fields, padding_type); } + is_packed = true; } else { LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type); LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); - #if LLVM_VERSION_MAJOR > 14 - { // NOTE(bill): always add a zero-byte pad to make inline constant unions work - LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1); - array_add(&fields, padding_type); - } - #endif + // #if LLVM_VERSION_MAJOR > 14 + // { // NOTE(bill): always add a zero-byte pad to make inline constant unions work + // LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1); + // array_add(&fields, padding_type); + // } + // #endif array_add(&fields, tag_type); i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); i64 padding = size - used_size; @@ -2542,9 +2534,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); array_add(&fields, padding_type); } + is_packed = true; } - return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, /*packed*/true); + return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, is_packed); } break; From b9a9846b7c3e73cd9b5c3d86a5249266e3070125 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 13:00:44 +0100 Subject: [PATCH 08/20] Fix constant union stuff against tests --- src/llvm_backend_const.cpp | 31 +++---------------------------- src/llvm_backend_general.cpp | 36 +++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index fffc9f94c..0a77ddc3d 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -117,33 +117,7 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType unsigned dst_n = LLVMCountStructElementTypes(dst); if (src_n != dst_n) goto failure; - bool skip_cast = true; - for (unsigned i = 0; i < dst_n; i++) { - LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i)); - LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i)); - if (dt != st) { - skip_cast = false; - } - if (dt == LLVMIntegerTypeKind) { - continue; - } - if (dt != LLVMArrayTypeKind) { - skip_cast = false; - break; - } - - LLVMValueRef field_val = llvm_const_extract_value(m, val, i); - if (field_val == nullptr) goto failure; - - LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); - LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); - if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) { - skip_cast = true; - continue; - } - - } - if (skip_cast) { + if (LLVM_VERSION_MAJOR > 14) { return val; } @@ -1075,7 +1049,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type); #else values[value_count++] = cv.value; - { + if (block_size != type_size_of(variant_type)) { LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1); values[value_count++] = LLVMConstNull(padding_type); } @@ -1095,6 +1069,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } res.value = LLVMConstStructInContext(m->ctx, values, value_count, true); + gb_printf_err("%s\n", LLVMPrintValueToString(res.value)); return res; } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index e91ce3fd7..bcc4e0ee2 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1647,22 +1647,23 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) { LLVMTypeRef uvt = llvm_addr_type(p->module, u); unsigned element_count = LLVMCountStructElementTypes(uvt); - if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { + // if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); - } else { - GB_ASSERT_MSG(element_count >= 3, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); - } + // } else { + // GB_ASSERT_MSG(element_count >= 3, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); + // } LLVMValueRef ptr = u.value; ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), ""); lbValue tag_ptr = {}; - if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { + // if (LLVM_VERSION_MAJOR == 14 || bt->Union.variants.count == 1) { tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); - } else { - tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 2, ""); - } + // } else { + // tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 2, ""); + // } tag_ptr.type = alloc_type_pointer(tag_type); + tag_ptr.value = LLVMBuildPointerCast(p->builder, tag_ptr.value, lb_type(p->module, tag_ptr.type), ""); return tag_ptr; } @@ -2412,8 +2413,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { i64 full_type_align = type_align_of(type); GB_ASSERT(full_type_size % full_type_align == 0); - if (type->Struct.is_raw_union) { + bool requires_packing = type->Struct.is_packed; + if (type->Struct.is_raw_union) { lbStructFieldRemapping field_remapping = {}; slice_init(&field_remapping, permanent_allocator(), 1); @@ -2421,7 +2423,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align); field_remapping[0] = 0; - LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); + LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), requires_packing); map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping); map_set(&m->struct_field_remapping, cast(void *)type, field_remapping); return struct_type; @@ -2441,7 +2443,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } i64 prev_offset = 0; - bool requires_packing = type->Struct.is_packed; for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) { Entity *field = type->Struct.fields[field_index]; i64 offset = type->Struct.offsets[field_index]; @@ -2529,12 +2530,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); - #if LLVM_VERSION_MAJOR > 14 - { // NOTE(bill): always add a zero-byte pad to make inline constant unions work - LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1); - array_add(&fields, padding_type); - } - #endif array_add(&fields, tag_type); i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); i64 padding = size - used_size; @@ -2655,6 +2650,13 @@ gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type) { m->internal_type_level += 1; llvm_type = lb_type_internal(m, type); m->internal_type_level -= 1; + + // { + // i64 tsz = type_size_of(type); + // i64 lsz = lb_sizeof(llvm_type); + // GB_ASSERT_MSG(tsz == lsz, "%s %lld vs %lld %s", type_to_string(type), cast(long long)tsz, cast(long long)lsz, LLVMPrintTypeToString(llvm_type)); + // } + if (m->internal_type_level == 0) { map_set(&m->types, type, llvm_type); } From fc6211039244e381ba240980b01e1c68517fcd9e Mon Sep 17 00:00:00 2001 From: misomosi Date: Mon, 29 Jun 2026 08:29:14 -0400 Subject: [PATCH 09/20] Report range type correctly on out-of-bounds --- src/check_expr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5a95a5dbb..4709691c5 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5363,7 +5363,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open TEMPORARY_ALLOCATOR_GUARD(); String idx_str = big_int_to_string(temporary_allocator(), &i); gbString expr_str = expr_to_string(operand.expr, temporary_allocator()); - error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str)); + char range_type = open_range ? '=' : '<'; + error(operand.expr, "Index '%s' is out of bounds range 0..%c%lld, got %.*s", expr_str, range_type, max_count, LIT(idx_str)); return false; } From 9e713e2940ad366c5c0a597c5a6725da71c44cb3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 13:44:03 +0100 Subject: [PATCH 10/20] Update `core:encoding/json` to use `proc` groups and `@(require_results)` --- core/encoding/json/marshal.odin | 1 + core/encoding/json/parser.odin | 32 ++++++++++++++++++++++++++++--- core/encoding/json/tokenizer.odin | 19 ++++++++++-------- core/encoding/json/types.odin | 1 + core/encoding/json/unmarshal.odin | 2 +- core/encoding/json/validator.odin | 6 ++++++ 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index a38c6b6d9..e6c8fba38 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -134,6 +134,7 @@ register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Regist return .None } +@(require_results) marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) { b := strings.builder_make(allocator, loc) defer if err != nil { diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin index 58a47d308..91bc42e4c 100644 --- a/core/encoding/json/parser.odin +++ b/core/encoding/json/parser.odin @@ -14,9 +14,16 @@ Parser :: struct { parse_integers: bool, } -make_parser :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { +make_parser :: proc{ + make_parser_from_bytes, + make_parser_from_string, +} + +@(require_results) +make_parser_from_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { return make_parser_from_string(string(data), spec, parse_integers, allocator) } +@(require_results) make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { p: Parser p.tok = make_tokenizer(data, spec, parse_integers) @@ -27,11 +34,18 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par return p } +parse :: proc{ + parse_bytes, + parse_string, +} -parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { + +@(require_results) +parse_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { return parse_string(string(data), spec, parse_integers, allocator, loc) } +@(require_results) parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { context.allocator = allocator p := make_parser_from_string(data, spec, parse_integers, allocator) @@ -51,6 +65,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers return parse_object(&p, loc) } +@(require_results) token_end_pos :: proc(tok: Token) -> Pos { end := tok.pos end.offset += len(tok.text) @@ -65,6 +80,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) { } +@(require_results) allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool { if p.curr_token.kind == kind { advance_token(p) @@ -73,6 +89,7 @@ allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool { return false } +@(require_results) expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error { prev := p.curr_token advance_token(p) @@ -83,6 +100,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error { } +@(require_results) parse_colon :: proc(p: ^Parser) -> (err: Error) { colon_err := expect_token(p, .Colon) if colon_err == nil { @@ -91,6 +109,7 @@ parse_colon :: proc(p: ^Parser) -> (err: Error) { return .Expected_Colon_After_Key } +@(require_results) parse_comma :: proc(p: ^Parser) -> (do_break: bool) { switch p.spec { case .JSON5, .MJSON: @@ -106,6 +125,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) { return false } +@(require_results) parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { err = .None token := p.curr_token @@ -176,6 +196,7 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: return } +@(require_results) parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { err = .None expect_token(p, .Open_Bracket) or_return @@ -203,7 +224,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: return } -@(private) +@(private, require_results) bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) { b, berr := mem.alloc_bytes(size, alignment, allocator, loc) if berr != nil { @@ -217,6 +238,7 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #calle return } +@(require_results) clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) { n := len(s) b := bytes_make(n+1, 1, allocator, loc) or_return @@ -228,6 +250,7 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio return } +@(require_results) parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) { tok := p.curr_token if p.spec != .JSON { @@ -242,6 +265,7 @@ parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #calle return unquote_string(tok, p.spec, key_allocator, loc) } +@(require_results) parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) { obj = make(Object, allocator=p.allocator, loc=loc) @@ -282,6 +306,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca return obj, .None } +@(require_results) parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { expect_token(p, .Open_Brace) or_return obj := parse_object_body(p, .Close_Brace, loc) or_return @@ -291,6 +316,7 @@ parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: // IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string +@(require_results) unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) { get_u2_rune :: proc(s: string) -> rune { if len(s) < 4 || s[0] != '\\' || s[1] != 'x' { diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin index ad928b7d9..153c41b29 100644 --- a/core/encoding/json/tokenizer.odin +++ b/core/encoding/json/tokenizer.odin @@ -49,11 +49,12 @@ Tokenizer :: struct { curr_line_offset: int, spec: Specification, parse_integers: bool, - insert_comma: bool, + insert_comma: bool, } +@(require_results) make_tokenizer :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> Tokenizer { t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers} next_rune(&t) @@ -78,6 +79,7 @@ next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check { } +@(require_results) get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { skip_digits :: proc(t: ^Tokenizer) { for t.offset < len(t.data) { @@ -101,6 +103,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { } } + @(require_results) scan_escape :: proc(t: ^Tokenizer) -> bool { switch t.r { case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f': @@ -125,7 +128,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { return false } - skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) -> rune { + skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) { loop: for t.offset < len(t.data) { switch t.r { case ' ', '\t', '\v', '\f', '\r': @@ -149,7 +152,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { break loop } } - return t.r + return } skip_to_next_line :: proc(t: ^Tokenizer) { @@ -310,7 +313,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { break } if r == '\\' { - scan_escape(t) + _ = scan_escape(t) } } @@ -347,10 +350,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { case '*': // None-nested multi-line comments for t.offset < len(t.data) { - next_rune(t) - if t.r == '*' { - next_rune(t) - if t.r == '/' { + if next_rune(t) == '*' { + if next_rune(t) == '/' { next_rune(t) return get_token(t) } @@ -385,6 +386,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { +@(require_results) is_valid_number :: proc(str: string, spec: Specification) -> bool { s := str if s == "" { @@ -473,6 +475,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool { return s == "" } +@(require_results) is_valid_string_literal :: proc(str: string, spec: Specification) -> bool { s := str if len(s) < 2 { diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 6b6d8aae9..66ff5c700 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -112,6 +112,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call } } +@(require_results) clone_value :: proc(value: Value, allocator := context.allocator) -> Value { value := value context.allocator = allocator diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index d3cb083d1..0f8c19b5d 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -635,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm defer p.allocator = allocator p.allocator = mem.nil_allocator() - parse_value(p) or_return + _ = parse_value(p) or_return if parse_comma(p) { break struct_loop } diff --git a/core/encoding/json/validator.odin b/core/encoding/json/validator.odin index e90270335..e8b5cc49d 100644 --- a/core/encoding/json/validator.odin +++ b/core/encoding/json/validator.odin @@ -3,6 +3,7 @@ package encoding_json import "core:mem" // NOTE(bill): is_valid will not check for duplicate keys +@(require_results) is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool { p := make_parser(data, spec, parse_integers, mem.nil_allocator()) @@ -21,6 +22,7 @@ is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := return validate_object(&p) } +@(require_results) validate_object_key :: proc(p: ^Parser) -> bool { if p.spec != .JSON { if allow_token(p, .Ident) { @@ -31,6 +33,7 @@ validate_object_key :: proc(p: ^Parser) -> bool { return err == .None } +@(require_results) validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool { for p.curr_token.kind != end_token { if !validate_object_key(p) { @@ -48,6 +51,7 @@ validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool { return true } +@(require_results) validate_object :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Brace); err != .None { return false @@ -61,6 +65,7 @@ validate_object :: proc(p: ^Parser) -> bool { return true } +@(require_results) validate_array :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Bracket); err != .None { return false @@ -83,6 +88,7 @@ validate_array :: proc(p: ^Parser) -> bool { return true } +@(require_results) validate_value :: proc(p: ^Parser) -> bool { token := p.curr_token From 7d32c733d320c1425aada8d9b217afd558ada280 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 13:44:45 +0100 Subject: [PATCH 11/20] `json.match` utility procedure --- core/encoding/json/match.odin | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 core/encoding/json/match.odin diff --git a/core/encoding/json/match.odin b/core/encoding/json/match.odin new file mode 100644 index 000000000..d9598c626 --- /dev/null +++ b/core/encoding/json/match.odin @@ -0,0 +1,68 @@ +package encoding_json + +Match_Key_Variant :: union #no_nil { + int, // Index + string, // Key +} + +Match_Error :: enum { + None, + Invalid_Argument, + Invalid_Type_For_Index, + Invalid_Type_For_Key, + Key_Not_Found, + Out_Of_Bounds_Index, +} + +Match_Flags :: distinct bit_set[Match_Flag] +Match_Flag :: enum { + Ignore_Key_Not_Found, + Allow_String_Indexing_By_Byte, +} + +@(require_results) +match :: proc(value: Value, args: ..Match_Key_Variant, flags: Match_Flags = nil) -> (found: Value, err: Match_Error) { + found = value + arg_loop: for arg in args { + switch k in arg { + case int: + #partial switch v in found { + case Array: + if 0 <= k && k < len(v) { + found = v[k] + continue arg_loop + } + err = .Out_Of_Bounds_Index + return + case String: + if .Allow_String_Indexing_By_Byte in flags { + if 0 <= k && k < len(v) { + found = Integer(v[k]) + continue arg_loop + } + err = .Out_Of_Bounds_Index + return + } + } + err = .Invalid_Type_For_Index + return + case string: + v, ok := found.(Object) + if !ok { + err = .Invalid_Type_For_Key + return + } + vfound, vok := v[k] + if vok || (.Ignore_Key_Not_Found in flags) { + found = vfound + continue arg_loop + } + err = .Key_Not_Found + return + case: + err = .Invalid_Argument + return + } + } + return +} \ No newline at end of file From 37c349e604f8c96a32037af55a1cd4b7231f815c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 13:47:46 +0100 Subject: [PATCH 12/20] Fix #6907 --- src/checker.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index dd61f6fc3..48863e706 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6894,7 +6894,10 @@ gb_internal void check_deferred_procedures(Checker *c) { continue; } - GB_ASSERT(is_type_proc(src->type)); + if (!is_type_proc(src->type)) { + error(src->token, "Invalid procedure type found during deferred procedure checking"); + continue; + } GB_ASSERT(is_type_proc(dst->type)); Type *src_params = base_type(src->type)->Proc.params; Type *src_results = base_type(src->type)->Proc.results; From 12e8ba8d66af4c04ec5f13d2817a68e61023f5a5 Mon Sep 17 00:00:00 2001 From: misomosi Date: Mon, 29 Jun 2026 10:01:50 -0400 Subject: [PATCH 13/20] Use lb_add_values_to_array for cvarargs --- src/llvm_backend_proc.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7994ab4ba..d48f190eb 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -4762,14 +4762,22 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_ return res; } -gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lbValue value) { +gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lbValue value, Type *c_vararg_type = nullptr) { if (is_type_tuple(value.type)) { for_array(i, value.type->Tuple.variables) { lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i); - array_add(args, sub_value); + if (c_vararg_type) { + array_add(args, lb_emit_c_vararg(p, sub_value, c_vararg_type)); + } else { + array_add(args, sub_value); + } } } else { - array_add(args, value); + if (c_vararg_type) { + array_add(args, lb_emit_c_vararg(p, value, c_vararg_type)); + } else { + array_add(args, value); + } } } @@ -4890,9 +4898,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal if (is_type_untyped_nil(arg.type)) { arg = lb_const_nil(p->module, t_rawptr); } - array_add(&args, lb_emit_c_vararg(p, arg, arg.type)); + lb_add_values_to_array(p, &args, arg, arg.type); } else { - array_add(&args, lb_emit_c_vararg(p, arg, elem_type)); + lb_add_values_to_array(p, &args, arg, elem_type); } } break; @@ -5018,15 +5026,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal if (is_type_untyped_nil(arg.type)) { arg = lb_const_nil(p->module, t_rawptr); } - array_add(&args, lb_emit_c_vararg(p, arg, arg.type)); + lb_add_values_to_array(p, &args, arg, arg.type); } else { - array_add(&args, lb_emit_c_vararg(p, arg, elem_type)); + lb_add_values_to_array(p, &args, arg, elem_type); } } } else { lbValue value = lb_build_expr(p, fv->value); GB_ASSERT(!is_type_tuple(value.type)); - array_add(&args, lb_emit_c_vararg(p, value, value.type)); + lb_add_values_to_array(p, &args, value, value.type); } } else { lbValue value = lb_build_expr(p, fv->value); From 1cdf3140150832935e80bf3b97d240e7afadf9e7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 16:04:24 +0100 Subject: [PATCH 14/20] Remove debug print string --- src/llvm_backend_const.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 0a77ddc3d..9a88f57a6 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1069,7 +1069,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } res.value = LLVMConstStructInContext(m->ctx, values, value_count, true); - gb_printf_err("%s\n", LLVMPrintValueToString(res.value)); return res; } From ac6a0f9a40cf2d186ba6dd38a73ee61137a4a466 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 16:36:21 +0100 Subject: [PATCH 15/20] Move around the ordering of `find_visual_studio_and_windows_sdk` --- src/microsoft_craziness.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index 933607a2a..7fcdebb80 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -761,17 +761,27 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) { gb_internal Find_Result find_visual_studio_and_windows_sdk() { Find_Result r = {}; find_windows_kit_paths(&r); - find_visual_studio_by_fighting_through_microsoft_craziness(&r); + + // Prefer the toolset exported into the environment (e.g. by vsdevcmd) so the + // linker uses the same MSVC toolset that compiled our C++ dependencies. The COM + // autodetection grabs the first installed Visual Studio, which may be an older + // toolset whose runtime libraries lack symbols that the newer-built libraries + // reference. Fall back to autodetection when the environment is bare. + find_visual_studio_paths_from_env_vars(&r); + + bool vs_found = + r.vs_exe_path.len && + r.vs_library_path.len ; + + if (!vs_found) { + find_visual_studio_by_fighting_through_microsoft_craziness(&r); + } bool sdk_found = r.windows_sdk_bin_path.len && r.windows_sdk_um_library_path.len && r.windows_sdk_ucrt_library_path.len ; - bool vs_found = - r.vs_exe_path.len && - r.vs_library_path.len ; - if (!sdk_found) { find_windows_kit_paths_from_env_vars(&r); } From 47fa8df598bcaef597cc2a7e1630c13e72eba277 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 17:01:44 +0100 Subject: [PATCH 16/20] Fix comment --- src/microsoft_craziness.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index 7fcdebb80..56af7252d 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -763,8 +763,8 @@ gb_internal Find_Result find_visual_studio_and_windows_sdk() { find_windows_kit_paths(&r); // Prefer the toolset exported into the environment (e.g. by vsdevcmd) so the - // linker uses the same MSVC toolset that compiled our C++ dependencies. The COM - // autodetection grabs the first installed Visual Studio, which may be an older + // linker uses the same MSVC toolset of other externally compiled C++ dependencies. + // The COM autodetection grabs the first installed Visual Studio, which may be an older // toolset whose runtime libraries lack symbols that the newer-built libraries // reference. Fall back to autodetection when the environment is bare. find_visual_studio_paths_from_env_vars(&r); From c8aa151a665e2c7664348cb8cd638820e990310a Mon Sep 17 00:00:00 2001 From: misomosi Date: Mon, 29 Jun 2026 22:46:17 -0400 Subject: [PATCH 17/20] Add more constant type cases --- src/types.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types.cpp b/src/types.cpp index d85f01c41..b5bb208de 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1471,6 +1471,10 @@ gb_internal bool is_type_constant_type(Type *t) { return is_type_constant_type(t->Array.elem); case Type_EnumeratedArray: return is_type_constant_type(t->EnumeratedArray.elem); + case Type_SimdVector: + return is_type_constant_type(t->SimdVector.elem); + case Type_Matrix: + return is_type_constant_type(t->Matrix.elem); } return false; } From 28a43fd4f749a95f88151e944b089d1d7905ab69 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 30 Jun 2026 11:49:35 +0100 Subject: [PATCH 18/20] Short-circuit further for constant unions in the LLVM constant literal syntax --- src/llvm_backend_const.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 9a88f57a6..4d982bd4f 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -118,7 +118,7 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType if (src_n != dst_n) goto failure; if (LLVM_VERSION_MAJOR > 14) { - return val; + goto failure; } LLVMValueRef *field_vals = temporary_alloc_array(dst_n); @@ -129,6 +129,10 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + if (lb_sizeof(dst_elem_ty) != lb_sizeof(src_elem_ty)) { + goto failure; + } + GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty), cast(long long)lb_sizeof(dst_elem_ty), cast(long long)lb_sizeof(src_elem_ty), From 62511914defbd44b21cfac46fc2173a8c4721324 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 30 Jun 2026 12:16:21 +0100 Subject: [PATCH 19/20] When inserting a constant union into a struct literal, rebuild the struct to be non-named with `llvm_const_insert_value_with_rebuild` --- src/llvm_backend.hpp | 2 +- src/llvm_backend_const.cpp | 26 +++++++++++++++----------- src/llvm_backend_general.cpp | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fb8d0f89a..d152fafa0 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -595,7 +595,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block); gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_); -gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_); +gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named=false); gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name); gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 4d982bd4f..82ef04dd0 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -239,10 +239,15 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count); } -gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) { +gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named) { unsigned value_count = cast(unsigned)value_count_; unsigned elem_count = LLVMCountStructElementTypes(t); 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); + } + bool failure = false; for (unsigned i = 0; i < elem_count; i++) { LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i); @@ -2079,6 +2084,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty LLVMTypeRef struct_type = lb_type(m, original_type); + bool force_non_named = false; + auto field_remapping = lb_get_struct_remapping(m, type); unsigned value_count = LLVMCountStructElementTypes(struct_type); @@ -2115,7 +2122,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty visited[index] = true; } - unsigned idx_list_len = cast(unsigned)sel.index.count-1; unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len); @@ -2153,7 +2159,12 @@ 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; if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { - values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); + if (is_type_union(cv_type) || is_type_raw_union(cv_type)) { + force_non_named = true; + values[index] = llvm_const_insert_value_with_rebuild(m, values[index], elem_value, idx_list, idx_list_len); + } else { + values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); + } } else if (is_local) { #if 1 lbProcedure *p = m->curr_procedure; @@ -2200,13 +2211,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty Entity *f = type->Struct.fields[i+multiple_return_offset]; TypeAndValue tav = cl->elems[i]->tav; - // INFO: dead code: - - // ExactValue val = {}; - // if (tav.mode != Addressing_Invalid) { - // val = tav.value; - // } - if (is_type_tuple(tav.type)){ multiple_return_offset += tav.type->Tuple.variables.count-1; } @@ -2246,7 +2250,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } if (is_constant) { - res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count); + res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count, force_non_named); LLVMTypeRef res_type = LLVMTypeOf(res.value); GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type)); return res; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5f00db5e0..b980ca7f9 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -461,6 +461,27 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg, return extracted_value; } +gb_internal LLVMValueRef llvm_const_insert_value_with_rebuild(lbModule *m, LLVMValueRef agg, LLVMValueRef val, unsigned *indices, isize count) { + GB_ASSERT(LLVMIsConstant(agg)); + GB_ASSERT(LLVMIsConstant(val)); + GB_ASSERT(count > 0); + + unsigned value_count = LLVMCountStructElementTypes(LLVMTypeOf(agg)); + LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, count); + defer (gb_free(heap_allocator(), values)); + + for (unsigned i = 0; i < value_count; i++) { + values[i] = llvm_const_extract_value(m, agg, i); + } + if (count == 1) { + values[indices[0]] = val; + } else { + values[indices[0]] = llvm_const_insert_value_with_rebuild(m, values[indices[0]], val, indices+1, count-1); + } + + return LLVMConstStructInContext(m->ctx, values, value_count, true); +} + From 4b9f396cb31f9cfc85fe2f0bf5607dd3eeb154aa Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 30 Jun 2026 13:23:18 +0200 Subject: [PATCH 20/20] Fix whitespace normalize --- core/encoding/entity/entity.odin | 1 + tests/core/encoding/xml/test_core_xml.odin | 26 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin index a756bdf39..3f3d41953 100644 --- a/core/encoding/entity/entity.odin +++ b/core/encoding/entity/entity.odin @@ -146,6 +146,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := for i in 0..A & B` + + opts := xml.Options{ + flags = {.Ignore_Unsupported, .Decode_SGML_Entities}, + } + + doc, err := xml.parse_bytes(transmute([]byte)s, opts) + defer xml.destroy(doc) + assert(err == .None) + + testing.expect_value(t, doc.elements[0].value[0], "A & B") + attr := doc.elements[0].attribs + testing.expect_value(t, attr[0].val, "A & B") +} + @(private) doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { /* @@ -298,4 +322,4 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { print(strings.to_writer(&buf), doc) return strings.clone(strings.to_string(buf)) -} +} \ No newline at end of file