Merge remote-tracking branch 'origin/master'

This commit is contained in:
2024-01-29 09:27:03 -05:00
17 changed files with 107 additions and 158 deletions
+2 -16
View File
@@ -954,24 +954,10 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
start := 0
flags: strconv.Int_Flags
if fi.hash && !fi.zero { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
if fi.hash { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
c: byte = 0
switch base {
case 2: c = 'b'
case 8: c = 'o'
case 12: c = 'z'
case 16: c = 'x'
}
if c != 0 {
io.write_byte(fi.writer, '0', &fi.n)
io.write_byte(fi.writer, c, &fi.n)
}
}
prev_zero := fi.zero
defer fi.zero = prev_zero
fi.zero = false
+1
View File
@@ -768,6 +768,7 @@ Struct_Type :: struct {
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
field_align: ^Expr,
where_token: tokenizer.Token,
where_clauses: []^Expr,
is_packed: bool,
+1
View File
@@ -314,6 +314,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
case ^Struct_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.field_align = clone(r.field_align)
r.fields = auto_cast clone(r.fields)
case ^Union_Type:
r.poly_params = auto_cast clone(r.poly_params)
+7
View File
@@ -2547,6 +2547,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
poly_params: ^ast.Field_List
align: ^ast.Expr
field_align: ^ast.Expr
is_packed: bool
is_raw_union: bool
is_no_copy: bool
@@ -2578,6 +2579,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
}
align = parse_expr(p, true)
case "field_align":
if field_align != nil {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
}
field_align = parse_expr(p, true)
case "raw_union":
if is_raw_union {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
@@ -2620,6 +2626,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
st.poly_params = poly_params
st.align = align
st.field_align = field_align
st.is_packed = is_packed
st.is_raw_union = is_raw_union
st.is_no_copy = is_no_copy
+3 -3
View File
@@ -349,7 +349,7 @@ exists :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
return attribs != win32.INVALID_FILE_ATTRIBUTES
}
is_file :: proc(path: string) -> bool {
@@ -357,7 +357,7 @@ is_file :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
if attribs != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
}
return false
@@ -368,7 +368,7 @@ is_dir :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
if attribs != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
}
return false
+4 -4
View File
@@ -454,7 +454,7 @@ _remove :: proc(name: string) -> Error {
if err != err1 {
a := win32.GetFileAttributesW(p)
if a == ~u32(0) {
if a == win32.INVALID_FILE_ATTRIBUTES {
err = _get_platform_error()
} else {
if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
@@ -704,13 +704,13 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
_exists :: proc(path: string) -> bool {
wpath := _fix_long_path(path)
attribs := win32.GetFileAttributesW(wpath)
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
return attribs != win32.INVALID_FILE_ATTRIBUTES
}
_is_file :: proc(path: string) -> bool {
wpath := _fix_long_path(path)
attribs := win32.GetFileAttributesW(wpath)
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
if attribs != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
}
return false
@@ -719,7 +719,7 @@ _is_file :: proc(path: string) -> bool {
_is_dir :: proc(path: string) -> bool {
wpath := _fix_long_path(path)
attribs := win32.GetFileAttributesW(wpath)
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
if attribs != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
}
return false
+6 -8
View File
@@ -2,8 +2,6 @@ package reflect
import "core:runtime"
import "core:intrinsics"
import "core:mem"
_ :: mem
_ :: intrinsics
Type_Info :: runtime.Type_Info
@@ -513,13 +511,13 @@ struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
@(require_results)
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
value, _ = struct_tag_lookup(tag, key)
return
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
v, _ := struct_tag_lookup(tag, key)
return string(v)
}
@(require_results)
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
for t := tag; t != ""; /**/ {
i := 0
for i < len(t) && t[i] == ' ' { // Skip whitespace
@@ -570,7 +568,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, o
t = t[i+1:]
if key == name {
return Struct_Tag(val[1:i]), true
return val[1:i], true
}
}
return
@@ -761,7 +759,7 @@ get_union_variant :: proc(a: any) -> any {
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
ptr := rawptr(val)
tag := get_union_variant_raw_tag(val^)
mem.copy(&res, &ptr, size_of(ptr))
intrinsics.mem_copy(&res, &ptr, size_of(ptr))
set_union_variant_raw_tag(res, tag)
return
}
-74
View File
@@ -22,50 +22,6 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
return ([^]byte)(data)[:max(len, 0)]
}
bswap_16 :: proc "contextless" (x: u16) -> u16 {
return x>>8 | x<<8
}
bswap_32 :: proc "contextless" (x: u32) -> u32 {
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
}
bswap_64 :: proc "contextless" (x: u64) -> u64 {
z := x
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
z = (z & 0x00ff00ff00ff00ff) << 8 | (z & 0xff00ff00ff00ff00) >> 8
return z
}
bswap_128 :: proc "contextless" (x: u128) -> u128 {
z := transmute([4]u32)x
z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
return transmute(u128)z
}
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
x := transmute(u16)f
z := bswap_16(x)
return transmute(f16)z
}
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
x := transmute(u32)f
z := bswap_32(x)
return transmute(f32)z
}
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
x := transmute(u64)f
z := bswap_64(x)
return transmute(f64)z
}
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
if x <= 0 {
return false
@@ -608,36 +564,6 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
return r, size
}
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
return -x if x < 0 else x
}
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
return -x if x < 0 else x
}
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
return -x if x < 0 else x
}
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
return a if a < b else b
}
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
return a if a < b else b
}
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
return a if a < b else b
}
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
return a if a > b else b
}
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
return a if a > b else b
}
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
return a if a > b else b
}
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
p, q := abs(real(x)), abs(imag(x))
if p < q {
+1 -1
View File
@@ -2176,7 +2176,7 @@ WC_ERR_INVALID_CHARS :: 128
MAX_PATH :: 0x00000104
MAX_PATH_WIDE :: 0x8000
INVALID_FILE_ATTRIBUTES :: -1
INVALID_FILE_ATTRIBUTES :: DWORD(0xffff_ffff)
FILE_TYPE_DISK :: 0x0001
FILE_TYPE_CHAR :: 0x0002
+7 -1
View File
@@ -2988,6 +2988,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
if (is_type_polymorphic(dst)) {
if (is_type_polymorphic(src) &&
operand->mode == Addressing_Variable) {
return true;
}
return false;
}
return true;
}
@@ -3067,7 +3074,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
bool can_convert = check_cast_internal(c, x, type);
if (!can_convert) {
TEMPORARY_ALLOCATOR_GUARD();
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
+25 -15
View File
@@ -219,13 +219,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
}
gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, char const *msg) {
GB_ASSERT(align_ != nullptr);
Operand o = {};
check_expr(ctx, &o, node);
if (o.mode != Addressing_Constant) {
if (o.mode != Addressing_Invalid) {
error(node, "#align must be a constant");
error(node, "#%s must be a constant", msg);
}
return false;
}
@@ -237,13 +237,13 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
if (v.used > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
error(node, "#align too large, %.*s", LIT(str));
error(node, "#%s too large, %.*s", msg, LIT(str));
gb_free(a, str.text);
return false;
}
i64 align = big_int_to_i64(&v);
if (align < 1 || !gb_is_power_of_two(cast(isize)align)) {
error(node, "#align must be a power of 2, got %lld", align);
error(node, "#%s must be a power of 2, got %lld", msg, align);
return false;
}
*align_ = align;
@@ -251,7 +251,7 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
}
}
error(node, "#align must be an integer");
error(node, "#%s must be an integer", msg);
return false;
}
@@ -645,16 +645,26 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
}
if (st->align != nullptr) {
if (st->is_packed) {
syntax_error(st->align, "'#align' cannot be applied with '#packed'");
return;
}
i64 custom_align = 1;
if (check_custom_align(ctx, st->align, &custom_align)) {
struct_type->Struct.custom_align = custom_align;
}
#define ST_ALIGN(_name) if (st->_name != nullptr) { \
if (st->is_packed) { \
syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \
return; \
} \
i64 align = 1; \
if (check_custom_align(ctx, st->_name, &align, #_name)) { \
struct_type->Struct.custom_##_name = align; \
} \
}
ST_ALIGN(field_align);
ST_ALIGN(align);
if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) {
warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)",
cast(long long)struct_type->Struct.custom_align,
cast(long long)struct_type->Struct.custom_field_align);
}
#undef ST_ALIGN
}
gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
GB_ASSERT(is_type_union(union_type));
@@ -746,7 +756,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
if (ut->align != nullptr) {
i64 custom_align = 1;
if (check_custom_align(ctx, ut->align, &custom_align)) {
if (check_custom_align(ctx, ut->align, &custom_align, "align")) {
if (variants.count == 0) {
error(ut->align, "An empty union cannot have a custom alignment");
} else {
+7 -3
View File
@@ -2517,13 +2517,11 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
// Odin internal procedures
str_lit("__init_context"),
str_lit("cstring_to_string"),
// str_lit("cstring_to_string"),
str_lit("_cleanup_runtime"),
// Pseudo-CRT required procedures
str_lit("memset"),
str_lit("memcpy"),
str_lit("memmove"),
// Utility procedures
str_lit("memory_equal"),
@@ -2531,6 +2529,12 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("memory_compare_zero"),
);
// Only required if no CRT is present
FORCE_ADD_RUNTIME_ENTITIES(build_context.no_crt,
str_lit("memcpy"),
str_lit("memmove"),
);
FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
// Extended data type internal procedures
str_lit("umodti3"),
+3 -3
View File
@@ -2033,9 +2033,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_clamp:
return lb_emit_clamp(p, type_of_expr(expr),
lb_build_expr(p, ce->args[0]),
lb_build_expr(p, ce->args[1]),
lb_build_expr(p, ce->args[2]));
lb_build_expr(p, ce->args[0]),
lb_build_expr(p, ce->args[1]),
lb_build_expr(p, ce->args[2]));
case BuiltinProc_soa_zip:
+6 -20
View File
@@ -83,27 +83,13 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
if (true || is_inlinable) {
LLVMValueRef args[4] = {};
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
LLVMValueRef args[4] = {};
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
} else {
lbValue pr = lb_lookup_runtime_procedure(p->module, str_lit("memset"));
LLVMValueRef args[3] = {};
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false);
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
// We always get the function pointer type rather than the function and there is apparently no way around that?
LLVMTypeRef type = lb_type_internal_for_procedures_raw(p->module, pr.type);
return LLVMBuildCall2(p->builder, type, pr.value, args, gb_count_of(args), "");
}
return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
}
+20 -5
View File
@@ -383,10 +383,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f);
break;
case Ast_StructType:
n->StructType.fields = clone_ast_array(n->StructType.fields, f);
n->StructType.fields = clone_ast_array(n->StructType.fields, f);
n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f);
n->StructType.align = clone_ast(n->StructType.align, f);
n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
n->StructType.align = clone_ast(n->StructType.align, f);
n->StructType.field_align = clone_ast(n->StructType.field_align, f);
n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
break;
case Ast_UnionType:
n->UnionType.variants = clone_ast_array(n->UnionType.variants, f);
@@ -1125,7 +1126,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy,
Ast *align,
Ast *align, Ast *field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
result->StructType.token = token;
@@ -1136,6 +1137,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_no_copy = is_no_copy;
result->StructType.align = align;
result->StructType.field_align = field_align;
result->StructType.where_token = where_token;
result->StructType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -2507,6 +2509,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
bool is_raw_union = false;
bool no_copy = false;
Ast *align = nullptr;
Ast *field_align = nullptr;
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
@@ -2543,6 +2546,18 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
error_line("\tSuggestion: #align(%s)", s);
gb_string_free(s);
}
} else if (tag.string == "field_align") {
if (field_align) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
field_align = parse_expr(f, true);
if (field_align && field_align->kind != Ast_ParenExpr) {
ERROR_BLOCK();
gbString s = expr_to_string(field_align);
syntax_warning(tag, "#field_align requires parentheses around the expression");
error_line("\tSuggestion: #field_align(%s)", s);
gb_string_free(s);
}
} else if (tag.string == "raw_union") {
if (is_raw_union) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
@@ -2591,7 +2606,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
decls = fields->FieldList.list;
}
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses);
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses);
} break;
case Token_union: {
+1
View File
@@ -713,6 +713,7 @@ AST_KIND(_TypeBegin, "", bool) \
isize field_count; \
Ast *polymorphic_params; \
Ast *align; \
Ast *field_align; \
Token where_token; \
Slice<Ast *> where_clauses; \
bool is_packed; \
+13 -5
View File
@@ -137,6 +137,7 @@ struct TypeStruct {
Scope * scope;
i64 custom_align;
i64 custom_field_align;
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
@@ -825,11 +826,13 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_SIZE 0
#define FAILURE_ALIGNMENT 0
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
if (ptr_set_exists(s, t)) {
if (type_ptr_set_exists(s, t)) {
return true;
}
ptr_set_add(s, t);
@@ -3666,10 +3669,15 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
}
gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {
gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) {
gbAllocator a = permanent_allocator();
auto offsets = gb_alloc_array(a, i64, fields.count);
i64 curr_offset = 0;
if (min_field_align == 0) {
min_field_align = 1;
}
if (is_raw_union) {
for_array(i, fields) {
offsets[i] = 0;
@@ -3690,7 +3698,7 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
offsets[i] = -1;
} else {
Type *t = fields[i]->type;
i64 align = gb_max(type_align_of(t), 1);
i64 align = gb_max(type_align_of(t), min_field_align);
i64 size = gb_max(type_size_of( t), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
@@ -3707,7 +3715,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true;
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align);
t->Struct.are_offsets_being_processed = false;
t->Struct.are_offsets_set = true;
return true;
@@ -3716,7 +3724,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true;
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1);
t->Tuple.are_offsets_being_processed = false;
t->Tuple.are_offsets_set = true;
return true;