authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-21 14:44:14-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-21 14:44:14-05:00
log1066004b79d014b4c3d10da19c84c679e21b88e5
treedb8c1c83788a624d1fab720073255e8c556d59a0
parent2bb795dc455823e76ef3e0c9b3fcee6bcb15fddb
signaturelock-open Commit is signed but in an unrecognized format.

better handling of arrays in packed structs

* Separate LoadPtr IR instructions into pass1 and pass2 variants. * Define `type_size_bits` for extern structs to be the same as their `@sizeOf(T) * 8` and allow them in packed structs. * More helpful error messages when trying to use types in packed structs that are not allowed. * Support arrays in packed structs even when they are not byte-aligned. * Add compile error for using arrays in packed structs when the padding bits would be problematic. This is necessary since we do not have packed arrays. closes #677

7 files changed, 246 insertions(+), 50 deletions(-)

src/all_types.hpp+8
......@@ -2119,6 +2119,7 @@ enum IrInstructionId {
21192119 IrInstructionIdUnOp,
21202120 IrInstructionIdBinOp,
21212121 IrInstructionIdLoadPtr,
2122 IrInstructionIdLoadPtrGen,
21222123 IrInstructionIdStorePtr,
21232124 IrInstructionIdFieldPtr,
21242125 IrInstructionIdStructFieldPtr,
......@@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr {
24142415 IrInstruction *ptr;
24152416};
24162417
2418struct IrInstructionLoadPtrGen {
2419 IrInstruction base;
2420
2421 IrInstruction *ptr;
2422 LLVMValueRef tmp_ptr;
2423};
2424
24172425struct IrInstructionStorePtr {
24182426 IrInstruction base;
24192427
src/analyze.cpp+73-26
......@@ -365,19 +365,19 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
365365 if (!type_has_bits(type_entry))
366366 return 0;
367367
368 if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) {
369 uint64_t result = 0;
370 for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
371 result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry);
368 if (type_entry->id == ZigTypeIdStruct) {
369 if (type_entry->data.structure.layout == ContainerLayoutPacked) {
370 uint64_t result = 0;
371 for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
372 result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry);
373 }
374 return result;
375 } else if (type_entry->data.structure.layout == ContainerLayoutExtern) {
376 return type_size(g, type_entry) * 8;
372377 }
373 return result;
374378 } else if (type_entry->id == ZigTypeIdArray) {
375379 ZigType *child_type = type_entry->data.array.child_type;
376 if (child_type->id == ZigTypeIdStruct &&
377 child_type->data.structure.layout == ContainerLayoutPacked)
378 {
379 return type_entry->data.array.len * type_size_bits(g, child_type);
380 }
380 return type_entry->data.array.len * type_size_bits(g, child_type);
381381 }
382382
383383 return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref);
......@@ -1444,7 +1444,10 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **
14441444 return true;
14451445}
14461446
1447static bool type_allowed_in_packed_struct(ZigType *type_entry) {
1447static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry,
1448 AstNode *source_node)
1449{
1450 Error err;
14481451 switch (type_entry->id) {
14491452 case ZigTypeIdInvalid:
14501453 zig_unreachable();
......@@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
14611464 case ZigTypeIdArgTuple:
14621465 case ZigTypeIdOpaque:
14631466 case ZigTypeIdPromise:
1464 return false;
1467 add_node_error(g, source_node,
1468 buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
1469 buf_ptr(&type_entry->name)));
1470 return ErrorSemanticAnalyzeFail;
14651471 case ZigTypeIdVoid:
14661472 case ZigTypeIdBool:
14671473 case ZigTypeIdInt:
14681474 case ZigTypeIdFloat:
14691475 case ZigTypeIdPointer:
1470 case ZigTypeIdArray:
14711476 case ZigTypeIdFn:
14721477 case ZigTypeIdVector:
1473 return true;
1478 return ErrorNone;
1479 case ZigTypeIdArray: {
1480 ZigType *elem_type = type_entry->data.array.child_type;
1481 if ((err = emit_error_unless_type_allowed_in_packed_struct(g, elem_type, source_node)))
1482 return err;
1483 if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry))
1484 return ErrorNone;
1485 add_node_error(g, source_node,
1486 buf_sprintf("array of '%s' not allowed in packed struct due to padding bits",
1487 buf_ptr(&elem_type->name)));
1488 return ErrorSemanticAnalyzeFail;
1489 }
14741490 case ZigTypeIdStruct:
1475 return type_entry->data.structure.layout == ContainerLayoutPacked;
1491 switch (type_entry->data.structure.layout) {
1492 case ContainerLayoutPacked:
1493 case ContainerLayoutExtern:
1494 return ErrorNone;
1495 case ContainerLayoutAuto:
1496 add_node_error(g, source_node,
1497 buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed struct; no guaranteed in-memory representation",
1498 buf_ptr(&type_entry->name)));
1499 return ErrorSemanticAnalyzeFail;
1500 }
1501 zig_unreachable();
14761502 case ZigTypeIdUnion:
1477 return type_entry->data.unionation.layout == ContainerLayoutPacked;
1503 switch (type_entry->data.unionation.layout) {
1504 case ContainerLayoutPacked:
1505 case ContainerLayoutExtern:
1506 return ErrorNone;
1507 case ContainerLayoutAuto:
1508 add_node_error(g, source_node,
1509 buf_sprintf("non-packed, non-extern union '%s' not allowed in packed struct; no guaranteed in-memory representation",
1510 buf_ptr(&type_entry->name)));
1511 return ErrorSemanticAnalyzeFail;
1512 }
1513 zig_unreachable();
14781514 case ZigTypeIdOptional:
1479 {
1480 ZigType *child_type = type_entry->data.maybe.child_type;
1481 return type_is_codegen_pointer(child_type);
1515 if (get_codegen_ptr_type(type_entry) != nullptr) {
1516 return ErrorNone;
1517 } else {
1518 add_node_error(g, source_node,
1519 buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
1520 buf_ptr(&type_entry->name)));
1521 return ErrorSemanticAnalyzeFail;
14821522 }
1483 case ZigTypeIdEnum:
1484 return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr;
1523 case ZigTypeIdEnum: {
1524 AstNode *decl_node = type_entry->data.enumeration.decl_node;
1525 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
1526 return ErrorNone;
1527 }
1528 ErrorMsg *msg = add_node_error(g, source_node,
1529 buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
1530 buf_ptr(&type_entry->name)));
1531 add_error_note(g, msg, decl_node,
1532 buf_sprintf("enum declaration does not specify an integer tag type"));
1533 return ErrorSemanticAnalyzeFail;
1534 }
14851535 }
14861536 zig_unreachable();
14871537}
......@@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
20512101 type_struct_field->gen_index = gen_field_index;
20522102
20532103 if (packed) {
2054 if (!type_allowed_in_packed_struct(field_type)) {
2055 AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
2056 add_node_error(g, field_source_node,
2057 buf_sprintf("packed structs cannot contain fields of type '%s'",
2058 buf_ptr(&field_type->name)));
2104 AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
2105 if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field_type, field_source_node))) {
20592106 struct_type->data.structure.resolve_status = ResolveStatusInvalid;
20602107 break;
20612108 }
src/codegen.cpp+43-7
......@@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
32813281 return nullptr;
32823282}
32833283
3284static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) {
3284static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) {
32853285 ZigType *child_type = instruction->base.value.type;
32863286 if (!type_has_bits(child_type))
32873287 return nullptr;
......@@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
32963296
32973297 bool big_endian = g->is_big_endian;
32983298
3299 assert(!handle_is_ptr(child_type));
33003299 LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
33013300 uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
33023301 assert(host_bit_count == host_int_bytes * 8);
......@@ -3308,7 +3307,16 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
33083307 LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
33093308 LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
33103309
3311 return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
3310 if (!handle_is_ptr(child_type))
3311 return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
3312
3313 assert(instruction->tmp_ptr != nullptr);
3314 LLVMTypeRef same_size_int = LLVMIntType(size_in_bits);
3315 LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, "");
3316 LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
3317 LLVMPointerType(same_size_int, 0), "");
3318 LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr);
3319 return instruction->tmp_ptr;
33123320}
33133321
33143322static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
......@@ -5460,6 +5468,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
54605468 case IrInstructionIdDeclVarSrc:
54615469 case IrInstructionIdPtrCastSrc:
54625470 case IrInstructionIdCmpxchgSrc:
5471 case IrInstructionIdLoadPtr:
54635472 zig_unreachable();
54645473
54655474 case IrInstructionIdDeclVarGen:
......@@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
54785487 return ir_render_br(g, executable, (IrInstructionBr *)instruction);
54795488 case IrInstructionIdUnOp:
54805489 return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction);
5481 case IrInstructionIdLoadPtr:
5482 return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction);
5490 case IrInstructionIdLoadPtrGen:
5491 return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction);
54835492 case IrInstructionIdStorePtr:
54845493 return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction);
54855494 case IrInstructionIdVarPtr:
......@@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
58365845 LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);
58375846 return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);
58385847 }
5839 case ZigTypeIdArray:
5840 zig_panic("TODO bit pack an array");
5848 case ZigTypeIdArray: {
5849 LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
5850 if (const_val->data.x_array.special == ConstArraySpecialUndef) {
5851 return val;
5852 }
5853 expand_undef_array(g, const_val);
5854 bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
5855 uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type);
5856 size_t used_bits = 0;
5857 for (size_t i = 0; i < type_entry->data.array.len; i += 1) {
5858 ConstExprValue *elem_val = &const_val->data.x_array.data.s_none.elements[i];
5859 LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val);
5860
5861 if (is_big_endian) {
5862 LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false);
5863 val = LLVMConstShl(val, shift_amt);
5864 val = LLVMConstOr(val, child_val);
5865 } else {
5866 LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
5867 LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
5868 val = LLVMConstOr(val, child_val_shifted);
5869 used_bits += packed_bits_size;
5870 }
5871 }
5872 return val;
5873 }
58415874 case ZigTypeIdVector:
58425875 zig_panic("TODO bit pack a vector");
58435876 case ZigTypeIdUnion:
......@@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) {
67286761 } else if (instruction->id == IrInstructionIdResizeSlice) {
67296762 IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction;
67306763 slot = &resize_slice_instruction->tmp_ptr;
6764 } else if (instruction->id == IrInstructionIdLoadPtrGen) {
6765 IrInstructionLoadPtrGen *load_ptr_inst = (IrInstructionLoadPtrGen *)instruction;
6766 slot = &load_ptr_inst->tmp_ptr;
67316767 } else if (instruction->id == IrInstructionIdVectorToArray) {
67326768 IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
67336769 alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
src/ir.cpp+26-6
......@@ -416,6 +416,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
416416 return IrInstructionIdLoadPtr;
417417}
418418
419static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) {
420 return IrInstructionIdLoadPtrGen;
421}
422
419423static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
420424 return IrInstructionIdStorePtr;
421425}
......@@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc
22922296 return &instruction->base;
22932297}
22942298
2299static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *source_instruction,
2300 IrInstruction *ptr, ZigType *ty)
2301{
2302 IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
2303 &ira->new_irb, source_instruction->scope, source_instruction->source_node);
2304 instruction->base.value.type = ty;
2305 instruction->ptr = ptr;
2306
2307 ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
2308
2309 return &instruction->base;
2310}
2311
22952312static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
22962313 IrInstruction *dest_type, IrInstruction *value)
22972314{
......@@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
1153411551 IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr);
1153511552 return ref_inst->value;
1153611553 }
11537 IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope,
11538 source_instruction->source_node, ptr);
11539 load_ptr_instruction->value.type = child_type;
11540 return load_ptr_instruction;
11554 IrInstruction *result = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type);
11555 if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
11556 ir_add_alloca(ira, result, child_type);
11557 }
11558 return result;
1154111559 } else {
1154211560 ir_add_error_node(ira, source_instruction->source_node,
1154311561 buf_sprintf("attempt to dereference non-pointer type '%s'",
......@@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
1339813416 }
1339913417
1340013418 // TODO audit the various ways to use @export
13401 if (want_var_export && target->id == IrInstructionIdLoadPtr) {
13402 IrInstructionLoadPtr *load_ptr = reinterpret_cast<IrInstructionLoadPtr *>(target);
13419 if (want_var_export && target->id == IrInstructionIdLoadPtrGen) {
13420 IrInstructionLoadPtrGen *load_ptr = reinterpret_cast<IrInstructionLoadPtrGen *>(target);
1340313421 if (load_ptr->ptr->id == IrInstructionIdVarPtr) {
1340413422 IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr);
1340513423 ZigVar *var = var_ptr->var;
......@@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2231622334 case IrInstructionIdVectorToArray:
2231722335 case IrInstructionIdAssertZero:
2231822336 case IrInstructionIdResizeSlice:
22337 case IrInstructionIdLoadPtrGen:
2231922338 zig_unreachable();
2232022339
2232122340 case IrInstructionIdReturn:
......@@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2272222741 case IrInstructionIdUnOp:
2272322742 case IrInstructionIdBinOp:
2272422743 case IrInstructionIdLoadPtr:
22744 case IrInstructionIdLoadPtrGen:
2272522745 case IrInstructionIdConst:
2272622746 case IrInstructionIdCast:
2272722747 case IrInstructionIdContainerInitList:
src/ir_print.cpp+8
......@@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) {
336336 fprintf(irp->f, ".*");
337337}
338338
339static void ir_print_load_ptr_gen(IrPrint *irp, IrInstructionLoadPtrGen *instruction) {
340 ir_print_other_instruction(irp, instruction->ptr);
341 fprintf(irp->f, ".*");
342}
343
339344static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) {
340345 fprintf(irp->f, "*");
341346 ir_print_var_instruction(irp, instruction->ptr);
......@@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
14681473 case IrInstructionIdLoadPtr:
14691474 ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction);
14701475 break;
1476 case IrInstructionIdLoadPtrGen:
1477 ir_print_load_ptr_gen(irp, (IrInstructionLoadPtrGen *)instruction);
1478 break;
14711479 case IrInstructionIdStorePtr:
14721480 ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction);
14731481 break;
test/compile_errors.zig+54
......@@ -1,6 +1,60 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.addTest(
5 "packed struct with fields of not allowed types",
6 \\const A = packed struct {
7 \\ x: anyerror,
8 \\};
9 \\const B = packed struct {
10 \\ x: [2]u24,
11 \\};
12 \\const C = packed struct {
13 \\ x: [1]anyerror,
14 \\};
15 \\const D = packed struct {
16 \\ x: [1]S,
17 \\};
18 \\const E = packed struct {
19 \\ x: [1]U,
20 \\};
21 \\const F = packed struct {
22 \\ x: ?anyerror,
23 \\};
24 \\const G = packed struct {
25 \\ x: Enum,
26 \\};
27 \\export fn entry() void {
28 \\ var a: A = undefined;
29 \\ var b: B = undefined;
30 \\ var r: C = undefined;
31 \\ var d: D = undefined;
32 \\ var e: E = undefined;
33 \\ var f: F = undefined;
34 \\ var g: G = undefined;
35 \\}
36 \\const S = struct {
37 \\ x: i32,
38 \\};
39 \\const U = struct {
40 \\ A: i32,
41 \\ B: u32,
42 \\};
43 \\const Enum = enum {
44 \\ A,
45 \\ B,
46 \\};
47 ,
48 ".tmp_source.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation",
49 ".tmp_source.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits",
50 ".tmp_source.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation",
51 ".tmp_source.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation",
52 ".tmp_source.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation",
53 ".tmp_source.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation",
54 ".tmp_source.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation",
55 ".tmp_source.zig:38:14: note: enum declaration does not specify an integer tag type",
56 );
57
458 cases.addCase(x: {
559 var tc = cases.create(
660 "deduplicate undeclared identifier",
test/stage1/behavior/struct.zig+34-11
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const expect = std.testing.expect;
3const expectEqualSlices = std.testing.expectEqualSlices;
34const builtin = @import("builtin");
45const maxInt = std.math.maxInt;
56
......@@ -103,19 +104,20 @@ fn structInitializer() void {
103104}
104105
105106test "fn call of struct field" {
106 expect(callStructField(Foo{ .ptr = aFunc }) == 13);
107}
108
109const Foo = struct {
110 ptr: fn () i32,
111};
107 const Foo = struct {
108 ptr: fn () i32,
109 };
110 const S = struct {
111 fn aFunc() i32 {
112 return 13;
113 }
112114
113fn aFunc() i32 {
114 return 13;
115}
115 fn callStructField(foo: Foo) i32 {
116 return foo.ptr();
117 }
118 };
116119
117fn callStructField(foo: Foo) i32 {
118 return foo.ptr();
120 expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);
119121}
120122
121123test "store member function in variable" {
......@@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" {
468470 b_ptr.* = 2;
469471 expect(s.b == 2);
470472}
473
474test "non-byte-aligned array inside packed struct" {
475 const Foo = packed struct {
476 a: bool,
477 b: [0x16]u8,
478 };
479 const S = struct {
480 fn bar(slice: []const u8) void {
481 expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu");
482 }
483 fn doTheTest() void {
484 var foo = Foo{
485 .a = true,
486 .b = "abcdefghijklmnopqurstu",
487 };
488 bar(foo.b);
489 }
490 };
491 S.doTheTest();
492 comptime S.doTheTest();
493}