| author | |
| committer | |
| log | 1066004b79d014b4c3d10da19c84c679e21b88e5 |
| tree | db8c1c83788a624d1fab720073255e8c556d59a0 |
| parent | 2bb795dc455823e76ef3e0c9b3fcee6bcb15fddb |
| signature |
* 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 #6777 files changed, 246 insertions(+), 50 deletions(-)
src/all_types.hpp+8| ... | ... | @@ -2119,6 +2119,7 @@ enum IrInstructionId { |
| 2119 | 2119 | IrInstructionIdUnOp, |
| 2120 | 2120 | IrInstructionIdBinOp, |
| 2121 | 2121 | IrInstructionIdLoadPtr, |
| 2122 | IrInstructionIdLoadPtrGen, | |
| 2122 | 2123 | IrInstructionIdStorePtr, |
| 2123 | 2124 | IrInstructionIdFieldPtr, |
| 2124 | 2125 | IrInstructionIdStructFieldPtr, |
| ... | ... | @@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr { |
| 2414 | 2415 | IrInstruction *ptr; |
| 2415 | 2416 | }; |
| 2416 | 2417 | |
| 2418 | struct IrInstructionLoadPtrGen { | |
| 2419 | IrInstruction base; | |
| 2420 | ||
| 2421 | IrInstruction *ptr; | |
| 2422 | LLVMValueRef tmp_ptr; | |
| 2423 | }; | |
| 2424 | ||
| 2417 | 2425 | struct IrInstructionStorePtr { |
| 2418 | 2426 | IrInstruction base; |
| 2419 | 2427 |
src/analyze.cpp+73-26| ... | ... | @@ -365,19 +365,19 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { |
| 365 | 365 | if (!type_has_bits(type_entry)) |
| 366 | 366 | return 0; |
| 367 | 367 | |
| 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; | |
| 372 | 377 | } |
| 373 | return result; | |
| 374 | 378 | } else if (type_entry->id == ZigTypeIdArray) { |
| 375 | 379 | 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); | |
| 381 | 381 | } |
| 382 | 382 | |
| 383 | 383 | 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 ** |
| 1444 | 1444 | return true; |
| 1445 | 1445 | } |
| 1446 | 1446 | |
| 1447 | static bool type_allowed_in_packed_struct(ZigType *type_entry) { | |
| 1447 | static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry, | |
| 1448 | AstNode *source_node) | |
| 1449 | { | |
| 1450 | Error err; | |
| 1448 | 1451 | switch (type_entry->id) { |
| 1449 | 1452 | case ZigTypeIdInvalid: |
| 1450 | 1453 | zig_unreachable(); |
| ... | ... | @@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { |
| 1461 | 1464 | case ZigTypeIdArgTuple: |
| 1462 | 1465 | case ZigTypeIdOpaque: |
| 1463 | 1466 | 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; | |
| 1465 | 1471 | case ZigTypeIdVoid: |
| 1466 | 1472 | case ZigTypeIdBool: |
| 1467 | 1473 | case ZigTypeIdInt: |
| 1468 | 1474 | case ZigTypeIdFloat: |
| 1469 | 1475 | case ZigTypeIdPointer: |
| 1470 | case ZigTypeIdArray: | |
| 1471 | 1476 | case ZigTypeIdFn: |
| 1472 | 1477 | 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 | } | |
| 1474 | 1490 | 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(); | |
| 1476 | 1502 | 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(); | |
| 1478 | 1514 | 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; | |
| 1482 | 1522 | } |
| 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 | } | |
| 1485 | 1535 | } |
| 1486 | 1536 | zig_unreachable(); |
| 1487 | 1537 | } |
| ... | ... | @@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2051 | 2101 | type_struct_field->gen_index = gen_field_index; |
| 2052 | 2102 | |
| 2053 | 2103 | 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))) { | |
| 2059 | 2106 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; |
| 2060 | 2107 | break; |
| 2061 | 2108 | } |
src/codegen.cpp+43-7| ... | ... | @@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, |
| 3281 | 3281 | return nullptr; |
| 3282 | 3282 | } |
| 3283 | 3283 | |
| 3284 | static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) { | |
| 3284 | static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) { | |
| 3285 | 3285 | ZigType *child_type = instruction->base.value.type; |
| 3286 | 3286 | if (!type_has_bits(child_type)) |
| 3287 | 3287 | return nullptr; |
| ... | ... | @@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3296 | 3296 | |
| 3297 | 3297 | bool big_endian = g->is_big_endian; |
| 3298 | 3298 | |
| 3299 | assert(!handle_is_ptr(child_type)); | |
| 3300 | 3299 | LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); |
| 3301 | 3300 | uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); |
| 3302 | 3301 | assert(host_bit_count == host_int_bytes * 8); |
| ... | ... | @@ -3308,7 +3307,16 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3308 | 3307 | LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); |
| 3309 | 3308 | LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); |
| 3310 | 3309 | |
| 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; | |
| 3312 | 3320 | } |
| 3313 | 3321 | |
| 3314 | 3322 | 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, |
| 5460 | 5468 | case IrInstructionIdDeclVarSrc: |
| 5461 | 5469 | case IrInstructionIdPtrCastSrc: |
| 5462 | 5470 | case IrInstructionIdCmpxchgSrc: |
| 5471 | case IrInstructionIdLoadPtr: | |
| 5463 | 5472 | zig_unreachable(); |
| 5464 | 5473 | |
| 5465 | 5474 | case IrInstructionIdDeclVarGen: |
| ... | ... | @@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5478 | 5487 | return ir_render_br(g, executable, (IrInstructionBr *)instruction); |
| 5479 | 5488 | case IrInstructionIdUnOp: |
| 5480 | 5489 | 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); | |
| 5483 | 5492 | case IrInstructionIdStorePtr: |
| 5484 | 5493 | return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction); |
| 5485 | 5494 | case IrInstructionIdVarPtr: |
| ... | ... | @@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con |
| 5836 | 5845 | LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref); |
| 5837 | 5846 | return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); |
| 5838 | 5847 | } |
| 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 | } | |
| 5841 | 5874 | case ZigTypeIdVector: |
| 5842 | 5875 | zig_panic("TODO bit pack a vector"); |
| 5843 | 5876 | case ZigTypeIdUnion: |
| ... | ... | @@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) { |
| 6728 | 6761 | } else if (instruction->id == IrInstructionIdResizeSlice) { |
| 6729 | 6762 | IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; |
| 6730 | 6763 | 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; | |
| 6731 | 6767 | } else if (instruction->id == IrInstructionIdVectorToArray) { |
| 6732 | 6768 | IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; |
| 6733 | 6769 | 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 *) { |
| 416 | 416 | return IrInstructionIdLoadPtr; |
| 417 | 417 | } |
| 418 | 418 | |
| 419 | static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) { | |
| 420 | return IrInstructionIdLoadPtrGen; | |
| 421 | } | |
| 422 | ||
| 419 | 423 | static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { |
| 420 | 424 | return IrInstructionIdStorePtr; |
| 421 | 425 | } |
| ... | ... | @@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc |
| 2292 | 2296 | return &instruction->base; |
| 2293 | 2297 | } |
| 2294 | 2298 | |
| 2299 | static 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 | ||
| 2295 | 2312 | static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2296 | 2313 | IrInstruction *dest_type, IrInstruction *value) |
| 2297 | 2314 | { |
| ... | ... | @@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc |
| 11534 | 11551 | IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr); |
| 11535 | 11552 | return ref_inst->value; |
| 11536 | 11553 | } |
| 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; | |
| 11541 | 11559 | } else { |
| 11542 | 11560 | ir_add_error_node(ira, source_instruction->source_node, |
| 11543 | 11561 | buf_sprintf("attempt to dereference non-pointer type '%s'", |
| ... | ... | @@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio |
| 13398 | 13416 | } |
| 13399 | 13417 | |
| 13400 | 13418 | // 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); | |
| 13403 | 13421 | if (load_ptr->ptr->id == IrInstructionIdVarPtr) { |
| 13404 | 13422 | IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr); |
| 13405 | 13423 | ZigVar *var = var_ptr->var; |
| ... | ... | @@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 22316 | 22334 | case IrInstructionIdVectorToArray: |
| 22317 | 22335 | case IrInstructionIdAssertZero: |
| 22318 | 22336 | case IrInstructionIdResizeSlice: |
| 22337 | case IrInstructionIdLoadPtrGen: | |
| 22319 | 22338 | zig_unreachable(); |
| 22320 | 22339 | |
| 22321 | 22340 | case IrInstructionIdReturn: |
| ... | ... | @@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 22722 | 22741 | case IrInstructionIdUnOp: |
| 22723 | 22742 | case IrInstructionIdBinOp: |
| 22724 | 22743 | case IrInstructionIdLoadPtr: |
| 22744 | case IrInstructionIdLoadPtrGen: | |
| 22725 | 22745 | case IrInstructionIdConst: |
| 22726 | 22746 | case IrInstructionIdCast: |
| 22727 | 22747 | case IrInstructionIdContainerInitList: |
src/ir_print.cpp+8| ... | ... | @@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { |
| 336 | 336 | fprintf(irp->f, ".*"); |
| 337 | 337 | } |
| 338 | 338 | |
| 339 | static void ir_print_load_ptr_gen(IrPrint *irp, IrInstructionLoadPtrGen *instruction) { | |
| 340 | ir_print_other_instruction(irp, instruction->ptr); | |
| 341 | fprintf(irp->f, ".*"); | |
| 342 | } | |
| 343 | ||
| 339 | 344 | static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { |
| 340 | 345 | fprintf(irp->f, "*"); |
| 341 | 346 | ir_print_var_instruction(irp, instruction->ptr); |
| ... | ... | @@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1468 | 1473 | case IrInstructionIdLoadPtr: |
| 1469 | 1474 | ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction); |
| 1470 | 1475 | break; |
| 1476 | case IrInstructionIdLoadPtrGen: | |
| 1477 | ir_print_load_ptr_gen(irp, (IrInstructionLoadPtrGen *)instruction); | |
| 1478 | break; | |
| 1471 | 1479 | case IrInstructionIdStorePtr: |
| 1472 | 1480 | ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); |
| 1473 | 1481 | break; |
test/compile_errors.zig+54| ... | ... | @@ -1,6 +1,60 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub 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 | ||
| 4 | 58 | cases.addCase(x: { |
| 5 | 59 | var tc = cases.create( |
| 6 | 60 | "deduplicate undeclared identifier", |
test/stage1/behavior/struct.zig+34-11| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const expect = std.testing.expect; |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 3 | 4 | const builtin = @import("builtin"); |
| 4 | 5 | const maxInt = std.math.maxInt; |
| 5 | 6 | |
| ... | ... | @@ -103,19 +104,20 @@ fn structInitializer() void { |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | test "fn call of struct field" { |
| 106 | expect(callStructField(Foo{ .ptr = aFunc }) == 13); | |
| 107 | } | |
| 108 | ||
| 109 | const 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 | } | |
| 112 | 114 | |
| 113 | fn aFunc() i32 { | |
| 114 | return 13; | |
| 115 | } | |
| 115 | fn callStructField(foo: Foo) i32 { | |
| 116 | return foo.ptr(); | |
| 117 | } | |
| 118 | }; | |
| 116 | 119 | |
| 117 | fn callStructField(foo: Foo) i32 { | |
| 118 | return foo.ptr(); | |
| 120 | expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); | |
| 119 | 121 | } |
| 120 | 122 | |
| 121 | 123 | test "store member function in variable" { |
| ... | ... | @@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" { |
| 468 | 470 | b_ptr.* = 2; |
| 469 | 471 | expect(s.b == 2); |
| 470 | 472 | } |
| 473 | ||
| 474 | test "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 | } |