mirror of
https://github.com/ziglang/zig.git
synced 2024-12-16 16:20:14 +00:00
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
This commit is contained in:
parent
2bb795dc45
commit
1066004b79
@ -2119,6 +2119,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdUnOp,
|
||||
IrInstructionIdBinOp,
|
||||
IrInstructionIdLoadPtr,
|
||||
IrInstructionIdLoadPtrGen,
|
||||
IrInstructionIdStorePtr,
|
||||
IrInstructionIdFieldPtr,
|
||||
IrInstructionIdStructFieldPtr,
|
||||
@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr {
|
||||
IrInstruction *ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionLoadPtrGen {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *ptr;
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionStorePtr {
|
||||
IrInstruction base;
|
||||
|
||||
|
103
src/analyze.cpp
103
src/analyze.cpp
@ -365,19 +365,19 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
|
||||
if (!type_has_bits(type_entry))
|
||||
return 0;
|
||||
|
||||
if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) {
|
||||
uint64_t result = 0;
|
||||
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
|
||||
result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry);
|
||||
if (type_entry->id == ZigTypeIdStruct) {
|
||||
if (type_entry->data.structure.layout == ContainerLayoutPacked) {
|
||||
uint64_t result = 0;
|
||||
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
|
||||
result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry);
|
||||
}
|
||||
return result;
|
||||
} else if (type_entry->data.structure.layout == ContainerLayoutExtern) {
|
||||
return type_size(g, type_entry) * 8;
|
||||
}
|
||||
return result;
|
||||
} else if (type_entry->id == ZigTypeIdArray) {
|
||||
ZigType *child_type = type_entry->data.array.child_type;
|
||||
if (child_type->id == ZigTypeIdStruct &&
|
||||
child_type->data.structure.layout == ContainerLayoutPacked)
|
||||
{
|
||||
return type_entry->data.array.len * type_size_bits(g, child_type);
|
||||
}
|
||||
return type_entry->data.array.len * type_size_bits(g, child_type);
|
||||
}
|
||||
|
||||
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 **
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool type_allowed_in_packed_struct(ZigType *type_entry) {
|
||||
static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry,
|
||||
AstNode *source_node)
|
||||
{
|
||||
Error err;
|
||||
switch (type_entry->id) {
|
||||
case ZigTypeIdInvalid:
|
||||
zig_unreachable();
|
||||
@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOpaque:
|
||||
case ZigTypeIdPromise:
|
||||
return false;
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
buf_ptr(&type_entry->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdArray:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdVector:
|
||||
return true;
|
||||
return ErrorNone;
|
||||
case ZigTypeIdArray: {
|
||||
ZigType *elem_type = type_entry->data.array.child_type;
|
||||
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, elem_type, source_node)))
|
||||
return err;
|
||||
if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry))
|
||||
return ErrorNone;
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("array of '%s' not allowed in packed struct due to padding bits",
|
||||
buf_ptr(&elem_type->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
case ZigTypeIdStruct:
|
||||
return type_entry->data.structure.layout == ContainerLayoutPacked;
|
||||
case ZigTypeIdUnion:
|
||||
return type_entry->data.unionation.layout == ContainerLayoutPacked;
|
||||
case ZigTypeIdOptional:
|
||||
{
|
||||
ZigType *child_type = type_entry->data.maybe.child_type;
|
||||
return type_is_codegen_pointer(child_type);
|
||||
switch (type_entry->data.structure.layout) {
|
||||
case ContainerLayoutPacked:
|
||||
case ContainerLayoutExtern:
|
||||
return ErrorNone;
|
||||
case ContainerLayoutAuto:
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
buf_ptr(&type_entry->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
case ZigTypeIdEnum:
|
||||
return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr;
|
||||
zig_unreachable();
|
||||
case ZigTypeIdUnion:
|
||||
switch (type_entry->data.unionation.layout) {
|
||||
case ContainerLayoutPacked:
|
||||
case ContainerLayoutExtern:
|
||||
return ErrorNone;
|
||||
case ContainerLayoutAuto:
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("non-packed, non-extern union '%s' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
buf_ptr(&type_entry->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
zig_unreachable();
|
||||
case ZigTypeIdOptional:
|
||||
if (get_codegen_ptr_type(type_entry) != nullptr) {
|
||||
return ErrorNone;
|
||||
} else {
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
buf_ptr(&type_entry->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
case ZigTypeIdEnum: {
|
||||
AstNode *decl_node = type_entry->data.enumeration.decl_node;
|
||||
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
|
||||
return ErrorNone;
|
||||
}
|
||||
ErrorMsg *msg = add_node_error(g, source_node,
|
||||
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
buf_ptr(&type_entry->name)));
|
||||
add_error_note(g, msg, decl_node,
|
||||
buf_sprintf("enum declaration does not specify an integer tag type"));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
|
||||
type_struct_field->gen_index = gen_field_index;
|
||||
|
||||
if (packed) {
|
||||
if (!type_allowed_in_packed_struct(field_type)) {
|
||||
AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
|
||||
add_node_error(g, field_source_node,
|
||||
buf_sprintf("packed structs cannot contain fields of type '%s'",
|
||||
buf_ptr(&field_type->name)));
|
||||
AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
|
||||
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field_type, field_source_node))) {
|
||||
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
|
||||
break;
|
||||
}
|
||||
|
@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) {
|
||||
static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) {
|
||||
ZigType *child_type = instruction->base.value.type;
|
||||
if (!type_has_bits(child_type))
|
||||
return nullptr;
|
||||
@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
|
||||
|
||||
bool big_endian = g->is_big_endian;
|
||||
|
||||
assert(!handle_is_ptr(child_type));
|
||||
LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
|
||||
uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
|
||||
assert(host_bit_count == host_int_bytes * 8);
|
||||
@ -3308,7 +3307,16 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
|
||||
LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
|
||||
LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
|
||||
|
||||
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
|
||||
if (!handle_is_ptr(child_type))
|
||||
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
|
||||
|
||||
assert(instruction->tmp_ptr != nullptr);
|
||||
LLVMTypeRef same_size_int = LLVMIntType(size_in_bits);
|
||||
LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, "");
|
||||
LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
|
||||
LLVMPointerType(same_size_int, 0), "");
|
||||
LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr);
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static 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,
|
||||
case IrInstructionIdDeclVarSrc:
|
||||
case IrInstructionIdPtrCastSrc:
|
||||
case IrInstructionIdCmpxchgSrc:
|
||||
case IrInstructionIdLoadPtr:
|
||||
zig_unreachable();
|
||||
|
||||
case IrInstructionIdDeclVarGen:
|
||||
@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_br(g, executable, (IrInstructionBr *)instruction);
|
||||
case IrInstructionIdUnOp:
|
||||
return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction);
|
||||
case IrInstructionIdLoadPtr:
|
||||
return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction);
|
||||
case IrInstructionIdLoadPtrGen:
|
||||
return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction);
|
||||
case IrInstructionIdStorePtr:
|
||||
return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction);
|
||||
case IrInstructionIdVarPtr:
|
||||
@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
|
||||
LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);
|
||||
return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);
|
||||
}
|
||||
case ZigTypeIdArray:
|
||||
zig_panic("TODO bit pack an array");
|
||||
case ZigTypeIdArray: {
|
||||
LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
|
||||
if (const_val->data.x_array.special == ConstArraySpecialUndef) {
|
||||
return val;
|
||||
}
|
||||
expand_undef_array(g, const_val);
|
||||
bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
|
||||
uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type);
|
||||
size_t used_bits = 0;
|
||||
for (size_t i = 0; i < type_entry->data.array.len; i += 1) {
|
||||
ConstExprValue *elem_val = &const_val->data.x_array.data.s_none.elements[i];
|
||||
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val);
|
||||
|
||||
if (is_big_endian) {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
} else {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
|
||||
LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val_shifted);
|
||||
used_bits += packed_bits_size;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
case ZigTypeIdVector:
|
||||
zig_panic("TODO bit pack a vector");
|
||||
case ZigTypeIdUnion:
|
||||
@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else if (instruction->id == IrInstructionIdResizeSlice) {
|
||||
IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction;
|
||||
slot = &resize_slice_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdLoadPtrGen) {
|
||||
IrInstructionLoadPtrGen *load_ptr_inst = (IrInstructionLoadPtrGen *)instruction;
|
||||
slot = &load_ptr_inst->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdVectorToArray) {
|
||||
IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
|
||||
alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
|
||||
|
32
src/ir.cpp
32
src/ir.cpp
@ -416,6 +416,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
|
||||
return IrInstructionIdLoadPtr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) {
|
||||
return IrInstructionIdLoadPtrGen;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
|
||||
return IrInstructionIdStorePtr;
|
||||
}
|
||||
@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
||||
IrInstruction *ptr, ZigType *ty)
|
||||
{
|
||||
IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
|
||||
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
||||
instruction->base.value.type = ty;
|
||||
instruction->ptr = ptr;
|
||||
|
||||
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *dest_type, IrInstruction *value)
|
||||
{
|
||||
@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
|
||||
IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr);
|
||||
return ref_inst->value;
|
||||
}
|
||||
IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope,
|
||||
source_instruction->source_node, ptr);
|
||||
load_ptr_instruction->value.type = child_type;
|
||||
return load_ptr_instruction;
|
||||
IrInstruction *result = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type);
|
||||
if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
|
||||
ir_add_alloca(ira, result, child_type);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
ir_add_error_node(ira, source_instruction->source_node,
|
||||
buf_sprintf("attempt to dereference non-pointer type '%s'",
|
||||
@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
|
||||
}
|
||||
|
||||
// TODO audit the various ways to use @export
|
||||
if (want_var_export && target->id == IrInstructionIdLoadPtr) {
|
||||
IrInstructionLoadPtr *load_ptr = reinterpret_cast<IrInstructionLoadPtr *>(target);
|
||||
if (want_var_export && target->id == IrInstructionIdLoadPtrGen) {
|
||||
IrInstructionLoadPtrGen *load_ptr = reinterpret_cast<IrInstructionLoadPtrGen *>(target);
|
||||
if (load_ptr->ptr->id == IrInstructionIdVarPtr) {
|
||||
IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr);
|
||||
ZigVar *var = var_ptr->var;
|
||||
@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
|
||||
case IrInstructionIdVectorToArray:
|
||||
case IrInstructionIdAssertZero:
|
||||
case IrInstructionIdResizeSlice:
|
||||
case IrInstructionIdLoadPtrGen:
|
||||
zig_unreachable();
|
||||
|
||||
case IrInstructionIdReturn:
|
||||
@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdUnOp:
|
||||
case IrInstructionIdBinOp:
|
||||
case IrInstructionIdLoadPtr:
|
||||
case IrInstructionIdLoadPtrGen:
|
||||
case IrInstructionIdConst:
|
||||
case IrInstructionIdCast:
|
||||
case IrInstructionIdContainerInitList:
|
||||
|
@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) {
|
||||
fprintf(irp->f, ".*");
|
||||
}
|
||||
|
||||
static void ir_print_load_ptr_gen(IrPrint *irp, IrInstructionLoadPtrGen *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ".*");
|
||||
}
|
||||
|
||||
static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) {
|
||||
fprintf(irp->f, "*");
|
||||
ir_print_var_instruction(irp, instruction->ptr);
|
||||
@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdLoadPtr:
|
||||
ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdLoadPtrGen:
|
||||
ir_print_load_ptr_gen(irp, (IrInstructionLoadPtrGen *)instruction);
|
||||
break;
|
||||
case IrInstructionIdStorePtr:
|
||||
ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction);
|
||||
break;
|
||||
|
@ -1,6 +1,60 @@
|
||||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.addTest(
|
||||
"packed struct with fields of not allowed types",
|
||||
\\const A = packed struct {
|
||||
\\ x: anyerror,
|
||||
\\};
|
||||
\\const B = packed struct {
|
||||
\\ x: [2]u24,
|
||||
\\};
|
||||
\\const C = packed struct {
|
||||
\\ x: [1]anyerror,
|
||||
\\};
|
||||
\\const D = packed struct {
|
||||
\\ x: [1]S,
|
||||
\\};
|
||||
\\const E = packed struct {
|
||||
\\ x: [1]U,
|
||||
\\};
|
||||
\\const F = packed struct {
|
||||
\\ x: ?anyerror,
|
||||
\\};
|
||||
\\const G = packed struct {
|
||||
\\ x: Enum,
|
||||
\\};
|
||||
\\export fn entry() void {
|
||||
\\ var a: A = undefined;
|
||||
\\ var b: B = undefined;
|
||||
\\ var r: C = undefined;
|
||||
\\ var d: D = undefined;
|
||||
\\ var e: E = undefined;
|
||||
\\ var f: F = undefined;
|
||||
\\ var g: G = undefined;
|
||||
\\}
|
||||
\\const S = struct {
|
||||
\\ x: i32,
|
||||
\\};
|
||||
\\const U = struct {
|
||||
\\ A: i32,
|
||||
\\ B: u32,
|
||||
\\};
|
||||
\\const Enum = enum {
|
||||
\\ A,
|
||||
\\ B,
|
||||
\\};
|
||||
,
|
||||
".tmp_source.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits",
|
||||
".tmp_source.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation",
|
||||
".tmp_source.zig:38:14: note: enum declaration does not specify an integer tag type",
|
||||
);
|
||||
|
||||
cases.addCase(x: {
|
||||
var tc = cases.create(
|
||||
"deduplicate undeclared identifier",
|
||||
|
@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const expectEqualSlices = std.testing.expectEqualSlices;
|
||||
const builtin = @import("builtin");
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
@ -103,19 +104,20 @@ fn structInitializer() void {
|
||||
}
|
||||
|
||||
test "fn call of struct field" {
|
||||
expect(callStructField(Foo{ .ptr = aFunc }) == 13);
|
||||
}
|
||||
const Foo = struct {
|
||||
ptr: fn () i32,
|
||||
};
|
||||
const S = struct {
|
||||
fn aFunc() i32 {
|
||||
return 13;
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
ptr: fn () i32,
|
||||
};
|
||||
fn callStructField(foo: Foo) i32 {
|
||||
return foo.ptr();
|
||||
}
|
||||
};
|
||||
|
||||
fn aFunc() i32 {
|
||||
return 13;
|
||||
}
|
||||
|
||||
fn callStructField(foo: Foo) i32 {
|
||||
return foo.ptr();
|
||||
expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);
|
||||
}
|
||||
|
||||
test "store member function in variable" {
|
||||
@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" {
|
||||
b_ptr.* = 2;
|
||||
expect(s.b == 2);
|
||||
}
|
||||
|
||||
test "non-byte-aligned array inside packed struct" {
|
||||
const Foo = packed struct {
|
||||
a: bool,
|
||||
b: [0x16]u8,
|
||||
};
|
||||
const S = struct {
|
||||
fn bar(slice: []const u8) void {
|
||||
expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu");
|
||||
}
|
||||
fn doTheTest() void {
|
||||
var foo = Foo{
|
||||
.a = true,
|
||||
.b = "abcdefghijklmnopqurstu",
|
||||
};
|
||||
bar(foo.b);
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user