| author | |
| committer | |
| log | 1066004b79d014b4c3d10da19c84c679e21b88e5 |
| tree | db8c1c83788a624d1fab720073255e8c556d59a0 |
| parent | 2bb795dc455823e76ef3e0c9b3fcee6bcb15fddb |
| signature | Commit is signed but in an unrecognized format. |
* 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,6 +2119,7 @@ enum IrInstructionId { |
| 2119 | IrInstructionIdUnOp, | 2119 | IrInstructionIdUnOp, |
| 2120 | IrInstructionIdBinOp, | 2120 | IrInstructionIdBinOp, |
| 2121 | IrInstructionIdLoadPtr, | 2121 | IrInstructionIdLoadPtr, |
| 2122 | IrInstructionIdLoadPtrGen, | ||
| 2122 | IrInstructionIdStorePtr, | 2123 | IrInstructionIdStorePtr, |
| 2123 | IrInstructionIdFieldPtr, | 2124 | IrInstructionIdFieldPtr, |
| 2124 | IrInstructionIdStructFieldPtr, | 2125 | IrInstructionIdStructFieldPtr, |
| ... | @@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr { | ... | @@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr { |
| 2414 | IrInstruction *ptr; | 2415 | IrInstruction *ptr; |
| 2415 | }; | 2416 | }; |
| 2416 | 2417 | ||
| 2418 | struct IrInstructionLoadPtrGen { | ||
| 2419 | IrInstruction base; | ||
| 2420 | |||
| 2421 | IrInstruction *ptr; | ||
| 2422 | LLVMValueRef tmp_ptr; | ||
| 2423 | }; | ||
| 2424 | |||
| 2417 | struct IrInstructionStorePtr { | 2425 | struct IrInstructionStorePtr { |
| 2418 | IrInstruction base; | 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,19 +365,19 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { |
| 365 | if (!type_has_bits(type_entry)) | 365 | if (!type_has_bits(type_entry)) |
| 366 | return 0; | 366 | return 0; |
| 367 | 367 | ||
| 368 | if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { | 368 | if (type_entry->id == ZigTypeIdStruct) { |
| 369 | uint64_t result = 0; | 369 | if (type_entry->data.structure.layout == ContainerLayoutPacked) { |
| 370 | for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { | 370 | uint64_t result = 0; |
| 371 | result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry); | 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 | } else if (type_entry->id == ZigTypeIdArray) { | 378 | } else if (type_entry->id == ZigTypeIdArray) { |
| 375 | ZigType *child_type = type_entry->data.array.child_type; | 379 | ZigType *child_type = type_entry->data.array.child_type; |
| 376 | if (child_type->id == ZigTypeIdStruct && | 380 | return type_entry->data.array.len * type_size_bits(g, child_type); |
| 377 | child_type->data.structure.layout == ContainerLayoutPacked) | ||
| 378 | { | ||
| 379 | return type_entry->data.array.len * type_size_bits(g, child_type); | ||
| 380 | } | ||
| 381 | } | 381 | } |
| 382 | 382 | ||
| 383 | return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref); | 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,7 +1444,10 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** |
| 1444 | return true; | 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 | switch (type_entry->id) { | 1451 | switch (type_entry->id) { |
| 1449 | case ZigTypeIdInvalid: | 1452 | case ZigTypeIdInvalid: |
| 1450 | zig_unreachable(); | 1453 | zig_unreachable(); |
| ... | @@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { | ... | @@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { |
| 1461 | case ZigTypeIdArgTuple: | 1464 | case ZigTypeIdArgTuple: |
| 1462 | case ZigTypeIdOpaque: | 1465 | case ZigTypeIdOpaque: |
| 1463 | case ZigTypeIdPromise: | 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 | case ZigTypeIdVoid: | 1471 | case ZigTypeIdVoid: |
| 1466 | case ZigTypeIdBool: | 1472 | case ZigTypeIdBool: |
| 1467 | case ZigTypeIdInt: | 1473 | case ZigTypeIdInt: |
| 1468 | case ZigTypeIdFloat: | 1474 | case ZigTypeIdFloat: |
| 1469 | case ZigTypeIdPointer: | 1475 | case ZigTypeIdPointer: |
| 1470 | case ZigTypeIdArray: | ||
| 1471 | case ZigTypeIdFn: | 1476 | case ZigTypeIdFn: |
| 1472 | case ZigTypeIdVector: | 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 | case ZigTypeIdStruct: | 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 | case ZigTypeIdUnion: | 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 | case ZigTypeIdOptional: | 1514 | case ZigTypeIdOptional: |
| 1479 | { | 1515 | if (get_codegen_ptr_type(type_entry) != nullptr) { |
| 1480 | ZigType *child_type = type_entry->data.maybe.child_type; | 1516 | return ErrorNone; |
| 1481 | return type_is_codegen_pointer(child_type); | 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: | 1523 | case ZigTypeIdEnum: { |
| 1484 | return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; | 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 | zig_unreachable(); | 1536 | zig_unreachable(); |
| 1487 | } | 1537 | } |
| ... | @@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { | ... | @@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2051 | type_struct_field->gen_index = gen_field_index; | 2101 | type_struct_field->gen_index = gen_field_index; |
| 2052 | 2102 | ||
| 2053 | if (packed) { | 2103 | if (packed) { |
| 2054 | if (!type_allowed_in_packed_struct(field_type)) { | 2104 | AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); |
| 2055 | 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))) { |
| 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))); | ||
| 2059 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; | 2106 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; |
| 2060 | break; | 2107 | break; |
| 2061 | } | 2108 | } |
src/codegen.cpp+43-7| ... | @@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, | ... | @@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, |
| 3281 | return nullptr; | 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 | ZigType *child_type = instruction->base.value.type; | 3285 | ZigType *child_type = instruction->base.value.type; |
| 3286 | if (!type_has_bits(child_type)) | 3286 | if (!type_has_bits(child_type)) |
| 3287 | return nullptr; | 3287 | return nullptr; |
| ... | @@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI | ... | @@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3296 | 3296 | ||
| 3297 | bool big_endian = g->is_big_endian; | 3297 | bool big_endian = g->is_big_endian; |
| 3298 | 3298 | ||
| 3299 | assert(!handle_is_ptr(child_type)); | ||
| 3300 | LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); | 3299 | LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); |
| 3301 | uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); | 3300 | uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); |
| 3302 | assert(host_bit_count == host_int_bytes * 8); | 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,7 +3307,16 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3308 | LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); | 3307 | LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); |
| 3309 | LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); | 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 | static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) { | 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,6 +5468,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5460 | case IrInstructionIdDeclVarSrc: | 5468 | case IrInstructionIdDeclVarSrc: |
| 5461 | case IrInstructionIdPtrCastSrc: | 5469 | case IrInstructionIdPtrCastSrc: |
| 5462 | case IrInstructionIdCmpxchgSrc: | 5470 | case IrInstructionIdCmpxchgSrc: |
| 5471 | case IrInstructionIdLoadPtr: | ||
| 5463 | zig_unreachable(); | 5472 | zig_unreachable(); |
| 5464 | 5473 | ||
| 5465 | case IrInstructionIdDeclVarGen: | 5474 | case IrInstructionIdDeclVarGen: |
| ... | @@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5478 | return ir_render_br(g, executable, (IrInstructionBr *)instruction); | 5487 | return ir_render_br(g, executable, (IrInstructionBr *)instruction); |
| 5479 | case IrInstructionIdUnOp: | 5488 | case IrInstructionIdUnOp: |
| 5480 | return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction); | 5489 | return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction); |
| 5481 | case IrInstructionIdLoadPtr: | 5490 | case IrInstructionIdLoadPtrGen: |
| 5482 | return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction); | 5491 | return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction); |
| 5483 | case IrInstructionIdStorePtr: | 5492 | case IrInstructionIdStorePtr: |
| 5484 | return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction); | 5493 | return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction); |
| 5485 | case IrInstructionIdVarPtr: | 5494 | case IrInstructionIdVarPtr: |
| ... | @@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con | ... | @@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con |
| 5836 | LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref); | 5845 | LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref); |
| 5837 | return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); | 5846 | return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); |
| 5838 | } | 5847 | } |
| 5839 | case ZigTypeIdArray: | 5848 | case ZigTypeIdArray: { |
| 5840 | zig_panic("TODO bit pack an array"); | 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 | case ZigTypeIdVector: | 5874 | case ZigTypeIdVector: |
| 5842 | zig_panic("TODO bit pack a vector"); | 5875 | zig_panic("TODO bit pack a vector"); |
| 5843 | case ZigTypeIdUnion: | 5876 | case ZigTypeIdUnion: |
| ... | @@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) { |
| 6728 | } else if (instruction->id == IrInstructionIdResizeSlice) { | 6761 | } else if (instruction->id == IrInstructionIdResizeSlice) { |
| 6729 | IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; | 6762 | IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; |
| 6730 | slot = &resize_slice_instruction->tmp_ptr; | 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 | } else if (instruction->id == IrInstructionIdVectorToArray) { | 6767 | } else if (instruction->id == IrInstructionIdVectorToArray) { |
| 6732 | IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; | 6768 | IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; |
| 6733 | alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); | 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,6 +416,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) { |
| 416 | return IrInstructionIdLoadPtr; | 416 | return IrInstructionIdLoadPtr; |
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) { | ||
| 420 | return IrInstructionIdLoadPtrGen; | ||
| 421 | } | ||
| 422 | |||
| 419 | static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { | 423 | static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { |
| 420 | return IrInstructionIdStorePtr; | 424 | return IrInstructionIdStorePtr; |
| 421 | } | 425 | } |
| ... | @@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc | ... | @@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc |
| 2292 | return &instruction->base; | 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 | static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2312 | static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2296 | IrInstruction *dest_type, IrInstruction *value) | 2313 | IrInstruction *dest_type, IrInstruction *value) |
| 2297 | { | 2314 | { |
| ... | @@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc | ... | @@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc |
| 11534 | IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr); | 11551 | IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr); |
| 11535 | return ref_inst->value; | 11552 | return ref_inst->value; |
| 11536 | } | 11553 | } |
| 11537 | IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, | 11554 | IrInstruction *result = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type); |
| 11538 | source_instruction->source_node, ptr); | 11555 | if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) { |
| 11539 | load_ptr_instruction->value.type = child_type; | 11556 | ir_add_alloca(ira, result, child_type); |
| 11540 | return load_ptr_instruction; | 11557 | } |
| 11558 | return result; | ||
| 11541 | } else { | 11559 | } else { |
| 11542 | ir_add_error_node(ira, source_instruction->source_node, | 11560 | ir_add_error_node(ira, source_instruction->source_node, |
| 11543 | buf_sprintf("attempt to dereference non-pointer type '%s'", | 11561 | buf_sprintf("attempt to dereference non-pointer type '%s'", |
| ... | @@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio | ... | @@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio |
| 13398 | } | 13416 | } |
| 13399 | 13417 | ||
| 13400 | // TODO audit the various ways to use @export | 13418 | // TODO audit the various ways to use @export |
| 13401 | if (want_var_export && target->id == IrInstructionIdLoadPtr) { | 13419 | if (want_var_export && target->id == IrInstructionIdLoadPtrGen) { |
| 13402 | IrInstructionLoadPtr *load_ptr = reinterpret_cast<IrInstructionLoadPtr *>(target); | 13420 | IrInstructionLoadPtrGen *load_ptr = reinterpret_cast<IrInstructionLoadPtrGen *>(target); |
| 13403 | if (load_ptr->ptr->id == IrInstructionIdVarPtr) { | 13421 | if (load_ptr->ptr->id == IrInstructionIdVarPtr) { |
| 13404 | IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr); | 13422 | IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr); |
| 13405 | ZigVar *var = var_ptr->var; | 13423 | ZigVar *var = var_ptr->var; |
| ... | @@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio | ... | @@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 22316 | case IrInstructionIdVectorToArray: | 22334 | case IrInstructionIdVectorToArray: |
| 22317 | case IrInstructionIdAssertZero: | 22335 | case IrInstructionIdAssertZero: |
| 22318 | case IrInstructionIdResizeSlice: | 22336 | case IrInstructionIdResizeSlice: |
| 22337 | case IrInstructionIdLoadPtrGen: | ||
| 22319 | zig_unreachable(); | 22338 | zig_unreachable(); |
| 22320 | 22339 | ||
| 22321 | case IrInstructionIdReturn: | 22340 | case IrInstructionIdReturn: |
| ... | @@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 22722 | case IrInstructionIdUnOp: | 22741 | case IrInstructionIdUnOp: |
| 22723 | case IrInstructionIdBinOp: | 22742 | case IrInstructionIdBinOp: |
| 22724 | case IrInstructionIdLoadPtr: | 22743 | case IrInstructionIdLoadPtr: |
| 22744 | case IrInstructionIdLoadPtrGen: | ||
| 22725 | case IrInstructionIdConst: | 22745 | case IrInstructionIdConst: |
| 22726 | case IrInstructionIdCast: | 22746 | case IrInstructionIdCast: |
| 22727 | case IrInstructionIdContainerInitList: | 22747 | case IrInstructionIdContainerInitList: |
src/ir_print.cpp+8| ... | @@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { | ... | @@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { |
| 336 | fprintf(irp->f, ".*"); | 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 | static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { | 344 | static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { |
| 340 | fprintf(irp->f, "*"); | 345 | fprintf(irp->f, "*"); |
| 341 | ir_print_var_instruction(irp, instruction->ptr); | 346 | ir_print_var_instruction(irp, instruction->ptr); |
| ... | @@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1468 | case IrInstructionIdLoadPtr: | 1473 | case IrInstructionIdLoadPtr: |
| 1469 | ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction); | 1474 | ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction); |
| 1470 | break; | 1475 | break; |
| 1476 | case IrInstructionIdLoadPtrGen: | ||
| 1477 | ir_print_load_ptr_gen(irp, (IrInstructionLoadPtrGen *)instruction); | ||
| 1478 | break; | ||
| 1471 | case IrInstructionIdStorePtr: | 1479 | case IrInstructionIdStorePtr: |
| 1472 | ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); | 1480 | ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); |
| 1473 | break; | 1481 | break; |
test/compile_errors.zig+54| ... | @@ -1,6 +1,60 @@ | ... | @@ -1,6 +1,60 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 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 | cases.addCase(x: { | 58 | cases.addCase(x: { |
| 5 | var tc = cases.create( | 59 | var tc = cases.create( |
| 6 | "deduplicate undeclared identifier", | 60 | "deduplicate undeclared identifier", |
test/stage1/behavior/struct.zig+34-11| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 2 | const expect = std.testing.expect; |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | ||
| 3 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 4 | const maxInt = std.math.maxInt; | 5 | const maxInt = std.math.maxInt; |
| 5 | 6 | ||
| ... | @@ -103,19 +104,20 @@ fn structInitializer() void { | ... | @@ -103,19 +104,20 @@ fn structInitializer() void { |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | test "fn call of struct field" { | 106 | test "fn call of struct field" { |
| 106 | expect(callStructField(Foo{ .ptr = aFunc }) == 13); | 107 | const Foo = struct { |
| 107 | } | 108 | ptr: fn () i32, |
| 108 | 109 | }; | |
| 109 | const Foo = struct { | 110 | const S = struct { |
| 110 | ptr: fn () i32, | 111 | fn aFunc() i32 { |
| 111 | }; | 112 | return 13; |
| 113 | } | ||
| 112 | 114 | ||
| 113 | fn aFunc() i32 { | 115 | fn callStructField(foo: Foo) i32 { |
| 114 | return 13; | 116 | return foo.ptr(); |
| 115 | } | 117 | } |
| 118 | }; | ||
| 116 | 119 | ||
| 117 | fn callStructField(foo: Foo) i32 { | 120 | expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); |
| 118 | return foo.ptr(); | ||
| 119 | } | 121 | } |
| 120 | 122 | ||
| 121 | test "store member function in variable" { | 123 | test "store member function in variable" { |
| ... | @@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" { | ... | @@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" { |
| 468 | b_ptr.* = 2; | 470 | b_ptr.* = 2; |
| 469 | expect(s.b == 2); | 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 | } |