| author | |
| committer | |
| log | 0a6851cc6de540ed95c3ec1c78eb3da7897bd930 |
| tree | b929564a7b7cddafd8dfb5ad9157fffe3425a1f0 |
| parent | 1bce0ed0460e2bdeb47d534e28090ed4b3794b97 |
8 files changed, 314 insertions(+), 193 deletions(-)
src/Sema.zig+177-14| ... | ... | @@ -4570,7 +4570,7 @@ fn zirOptionalPayloadPtr( |
| 4570 | 4570 | }); |
| 4571 | 4571 | |
| 4572 | 4572 | if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| { |
| 4573 | if (try pointer_val.pointerDeref(sema.arena)) |val| { | |
| 4573 | if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| { | |
| 4574 | 4574 | if (val.isNull()) { |
| 4575 | 4575 | return sema.fail(block, src, "unable to unwrap null", .{}); |
| 4576 | 4576 | } |
| ... | ... | @@ -4689,7 +4689,7 @@ fn zirErrUnionPayloadPtr( |
| 4689 | 4689 | }); |
| 4690 | 4690 | |
| 4691 | 4691 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 4692 | if (try pointer_val.pointerDeref(sema.arena)) |val| { | |
| 4692 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { | |
| 4693 | 4693 | if (val.getError()) |name| { |
| 4694 | 4694 | return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); |
| 4695 | 4695 | } |
| ... | ... | @@ -4748,7 +4748,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 4748 | 4748 | const result_ty = operand_ty.elemType().errorUnionSet(); |
| 4749 | 4749 | |
| 4750 | 4750 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 4751 | if (try pointer_val.pointerDeref(sema.arena)) |val| { | |
| 4751 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { | |
| 4752 | 4752 | assert(val.getError() != null); |
| 4753 | 4753 | return sema.addConstant(result_ty, val); |
| 4754 | 4754 | } |
| ... | ... | @@ -6912,8 +6912,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6912 | 6912 | const final_len = lhs_info.len + rhs_info.len; |
| 6913 | 6913 | const final_len_including_sent = final_len + @boolToInt(res_sent != null); |
| 6914 | 6914 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; |
| 6915 | const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(sema.arena)).? else lhs_val; | |
| 6916 | const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(sema.arena)).? else rhs_val; | |
| 6915 | const lhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; | |
| 6916 | const rhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; | |
| 6917 | 6917 | var anon_decl = try block.startAnonDecl(); |
| 6918 | 6918 | defer anon_decl.deinit(); |
| 6919 | 6919 | |
| ... | ... | @@ -6992,7 +6992,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6992 | 6992 | const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null); |
| 6993 | 6993 | |
| 6994 | 6994 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6995 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(sema.arena)).? else lhs_val; | |
| 6995 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; | |
| 6996 | 6996 | |
| 6997 | 6997 | var anon_decl = try block.startAnonDecl(); |
| 6998 | 6998 | defer anon_decl.deinit(); |
| ... | ... | @@ -10092,7 +10092,8 @@ fn zirCmpxchg( |
| 10092 | 10092 | const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; |
| 10093 | 10093 | // zig fmt: on |
| 10094 | 10094 | const ptr = sema.resolveInst(extra.ptr); |
| 10095 | const elem_ty = sema.typeOf(ptr).elemType(); | |
| 10095 | const ptr_ty = sema.typeOf(ptr); | |
| 10096 | const elem_ty = ptr_ty.elemType(); | |
| 10096 | 10097 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); |
| 10097 | 10098 | if (elem_ty.zigTypeTag() == .Float) { |
| 10098 | 10099 | return sema.fail( |
| ... | ... | @@ -10135,7 +10136,7 @@ fn zirCmpxchg( |
| 10135 | 10136 | // to become undef as well |
| 10136 | 10137 | return sema.addConstUndef(result_ty); |
| 10137 | 10138 | } |
| 10138 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; | |
| 10139 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; | |
| 10139 | 10140 | const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: { |
| 10140 | 10141 | try sema.storePtr(block, src, ptr, new_value); |
| 10141 | 10142 | break :blk Value.@"null"; |
| ... | ... | @@ -10197,7 +10198,8 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 10197 | 10198 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 10198 | 10199 | // zig fmt: on |
| 10199 | 10200 | const ptr = sema.resolveInst(extra.lhs); |
| 10200 | const elem_ty = sema.typeOf(ptr).elemType(); | |
| 10201 | const ptr_ty = sema.typeOf(ptr); | |
| 10202 | const elem_ty = ptr_ty.elemType(); | |
| 10201 | 10203 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); |
| 10202 | 10204 | const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs); |
| 10203 | 10205 | |
| ... | ... | @@ -10218,7 +10220,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 10218 | 10220 | } |
| 10219 | 10221 | |
| 10220 | 10222 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| 10221 | if (try ptr_val.pointerDeref(sema.arena)) |elem_val| { | |
| 10223 | if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { | |
| 10222 | 10224 | return sema.addConstant(elem_ty, elem_val); |
| 10223 | 10225 | } |
| 10224 | 10226 | } |
| ... | ... | @@ -10245,7 +10247,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 10245 | 10247 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; |
| 10246 | 10248 | // zig fmt: on |
| 10247 | 10249 | const ptr = sema.resolveInst(extra.ptr); |
| 10248 | const operand_ty = sema.typeOf(ptr).elemType(); | |
| 10250 | const ptr_ty = sema.typeOf(ptr); | |
| 10251 | const operand_ty = ptr_ty.elemType(); | |
| 10249 | 10252 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); |
| 10250 | 10253 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); |
| 10251 | 10254 | |
| ... | ... | @@ -10282,7 +10285,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 10282 | 10285 | }; |
| 10283 | 10286 | if (ptr_val.isComptimeMutablePtr()) { |
| 10284 | 10287 | const target = sema.mod.getTarget(); |
| 10285 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; | |
| 10288 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; | |
| 10286 | 10289 | const new_val = switch (op) { |
| 10287 | 10290 | // zig fmt: off |
| 10288 | 10291 | .Xchg => operand_val, |
| ... | ... | @@ -11785,7 +11788,7 @@ fn elemVal( |
| 11785 | 11788 | const ptr_val = maybe_ptr_val orelse break :rs array_src; |
| 11786 | 11789 | const index_val = maybe_index_val orelse break :rs elem_index_src; |
| 11787 | 11790 | const index = @intCast(usize, index_val.toUnsignedInt()); |
| 11788 | const maybe_array_val = try ptr_val.pointerDeref(sema.arena); | |
| 11791 | const maybe_array_val = try sema.pointerDeref(block, array_src, ptr_val, array_ty); | |
| 11789 | 11792 | const array_val = maybe_array_val orelse break :rs array_src; |
| 11790 | 11793 | const elem_val = try array_val.elemValue(sema.arena, index); |
| 11791 | 11794 | return sema.addConstant(array_ty.elemType2(), elem_val); |
| ... | ... | @@ -11887,6 +11890,8 @@ fn coerce( |
| 11887 | 11890 | assert(inst_ty.zigTypeTag() != .Undefined); |
| 11888 | 11891 | |
| 11889 | 11892 | // comptime known number to other number |
| 11893 | // TODO why is this a separate function? should just be flattened into the | |
| 11894 | // switch expression below. | |
| 11890 | 11895 | if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some| |
| 11891 | 11896 | return some; |
| 11892 | 11897 | |
| ... | ... | @@ -12514,6 +12519,122 @@ fn beginComptimePtrMutation( |
| 12514 | 12519 | } |
| 12515 | 12520 | } |
| 12516 | 12521 | |
| 12522 | const ComptimePtrLoadKit = struct { | |
| 12523 | /// The Value of the Decl that owns this memory. | |
| 12524 | root_val: Value, | |
| 12525 | /// Parent Value. | |
| 12526 | val: Value, | |
| 12527 | /// The Type of the parent Value. | |
| 12528 | ty: Type, | |
| 12529 | /// The starting byte offset of `val` from `root_val`. | |
| 12530 | byte_offset: usize, | |
| 12531 | /// Whether the `root_val` could be mutated by further | |
| 12532 | /// semantic analysis and a copy must be performed. | |
| 12533 | is_mutable: bool, | |
| 12534 | }; | |
| 12535 | ||
| 12536 | const ComptimePtrLoadError = CompileError || error{ | |
| 12537 | RuntimeLoad, | |
| 12538 | }; | |
| 12539 | ||
| 12540 | fn beginComptimePtrLoad( | |
| 12541 | sema: *Sema, | |
| 12542 | block: *Block, | |
| 12543 | src: LazySrcLoc, | |
| 12544 | ptr_val: Value, | |
| 12545 | ) ComptimePtrLoadError!ComptimePtrLoadKit { | |
| 12546 | const target = sema.mod.getTarget(); | |
| 12547 | switch (ptr_val.tag()) { | |
| 12548 | .decl_ref => { | |
| 12549 | const decl = ptr_val.castTag(.decl_ref).?.data; | |
| 12550 | const decl_val = try decl.value(); | |
| 12551 | if (decl_val.tag() == .variable) return error.RuntimeLoad; | |
| 12552 | return ComptimePtrLoadKit{ | |
| 12553 | .root_val = decl_val, | |
| 12554 | .val = decl_val, | |
| 12555 | .ty = decl.ty, | |
| 12556 | .byte_offset = 0, | |
| 12557 | .is_mutable = false, | |
| 12558 | }; | |
| 12559 | }, | |
| 12560 | .decl_ref_mut => { | |
| 12561 | const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; | |
| 12562 | const decl_val = try decl.value(); | |
| 12563 | if (decl_val.tag() == .variable) return error.RuntimeLoad; | |
| 12564 | return ComptimePtrLoadKit{ | |
| 12565 | .root_val = decl_val, | |
| 12566 | .val = decl_val, | |
| 12567 | .ty = decl.ty, | |
| 12568 | .byte_offset = 0, | |
| 12569 | .is_mutable = true, | |
| 12570 | }; | |
| 12571 | }, | |
| 12572 | .elem_ptr => { | |
| 12573 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; | |
| 12574 | const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr); | |
| 12575 | const elem_ty = parent.ty.childType(); | |
| 12576 | const elem_size = elem_ty.abiSize(target); | |
| 12577 | return ComptimePtrLoadKit{ | |
| 12578 | .root_val = parent.root_val, | |
| 12579 | .val = try parent.val.elemValue(sema.arena, elem_ptr.index), | |
| 12580 | .ty = elem_ty, | |
| 12581 | .byte_offset = parent.byte_offset + elem_size * elem_ptr.index, | |
| 12582 | .is_mutable = parent.is_mutable, | |
| 12583 | }; | |
| 12584 | }, | |
| 12585 | .field_ptr => { | |
| 12586 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | |
| 12587 | const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr); | |
| 12588 | const field_index = @intCast(u32, field_ptr.field_index); | |
| 12589 | try sema.resolveTypeLayout(block, src, parent.ty); | |
| 12590 | const field_offset = parent.ty.structFieldOffset(field_index, target); | |
| 12591 | return ComptimePtrLoadKit{ | |
| 12592 | .root_val = parent.root_val, | |
| 12593 | .val = try parent.val.fieldValue(sema.arena, field_index), | |
| 12594 | .ty = parent.ty.structFieldType(field_index), | |
| 12595 | .byte_offset = parent.byte_offset + field_offset, | |
| 12596 | .is_mutable = parent.is_mutable, | |
| 12597 | }; | |
| 12598 | }, | |
| 12599 | .eu_payload_ptr => { | |
| 12600 | const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; | |
| 12601 | const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr); | |
| 12602 | return ComptimePtrLoadKit{ | |
| 12603 | .root_val = parent.root_val, | |
| 12604 | .val = parent.val.castTag(.eu_payload).?.data, | |
| 12605 | .ty = parent.ty.errorUnionPayload(), | |
| 12606 | .byte_offset = undefined, | |
| 12607 | .is_mutable = parent.is_mutable, | |
| 12608 | }; | |
| 12609 | }, | |
| 12610 | .opt_payload_ptr => { | |
| 12611 | const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; | |
| 12612 | const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr); | |
| 12613 | var buf: Type.Payload.ElemType = undefined; | |
| 12614 | return ComptimePtrLoadKit{ | |
| 12615 | .root_val = parent.root_val, | |
| 12616 | .val = parent.val.castTag(.opt_payload).?.data, | |
| 12617 | .ty = parent.ty.optionalChild(&buf), | |
| 12618 | .byte_offset = undefined, | |
| 12619 | .is_mutable = parent.is_mutable, | |
| 12620 | }; | |
| 12621 | }, | |
| 12622 | ||
| 12623 | .zero, | |
| 12624 | .one, | |
| 12625 | .int_u64, | |
| 12626 | .int_i64, | |
| 12627 | .int_big_positive, | |
| 12628 | .int_big_negative, | |
| 12629 | .variable, | |
| 12630 | .extern_fn, | |
| 12631 | .function, | |
| 12632 | => return error.RuntimeLoad, | |
| 12633 | ||
| 12634 | else => unreachable, | |
| 12635 | } | |
| 12636 | } | |
| 12637 | ||
| 12517 | 12638 | fn bitCast( |
| 12518 | 12639 | sema: *Sema, |
| 12519 | 12640 | block: *Block, |
| ... | ... | @@ -12819,7 +12940,7 @@ fn analyzeLoad( |
| 12819 | 12940 | else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}), |
| 12820 | 12941 | }; |
| 12821 | 12942 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| 12822 | if (try ptr_val.pointerDeref(sema.arena)) |elem_val| { | |
| 12943 | if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { | |
| 12823 | 12944 | return sema.addConstant(elem_ty, elem_val); |
| 12824 | 12945 | } |
| 12825 | 12946 | } |
| ... | ... | @@ -14539,3 +14660,45 @@ pub fn analyzeAddrspace( |
| 14539 | 14660 | |
| 14540 | 14661 | return address_space; |
| 14541 | 14662 | } |
| 14663 | ||
| 14664 | /// Asserts the value is a pointer and dereferences it. | |
| 14665 | /// Returns `null` if the pointer contents cannot be loaded at comptime. | |
| 14666 | fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { | |
| 14667 | const target = sema.mod.getTarget(); | |
| 14668 | const load_ty = ptr_ty.childType(); | |
| 14669 | const parent = sema.beginComptimePtrLoad(block, src, ptr_val) catch |err| switch (err) { | |
| 14670 | error.RuntimeLoad => return null, | |
| 14671 | else => |e| return e, | |
| 14672 | }; | |
| 14673 | // We have a Value that lines up in virtual memory exactly with what we want to load. | |
| 14674 | // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications. | |
| 14675 | const coerce_in_mem_ok = | |
| 14676 | coerceInMemoryAllowed(load_ty, parent.ty, false, target) == .ok or | |
| 14677 | coerceInMemoryAllowed(parent.ty, load_ty, false, target) == .ok; | |
| 14678 | if (coerce_in_mem_ok) { | |
| 14679 | if (parent.is_mutable) { | |
| 14680 | // The decl whose value we are obtaining here may be overwritten with | |
| 14681 | // a different value upon further semantic analysis, which would | |
| 14682 | // invalidate this memory. So we must copy here. | |
| 14683 | return try parent.val.copy(sema.arena); | |
| 14684 | } | |
| 14685 | return parent.val; | |
| 14686 | } | |
| 14687 | ||
| 14688 | // The type is not in-memory coercable, so it must be bitcasted according | |
| 14689 | // to the pointer type we are performing the load through. | |
| 14690 | ||
| 14691 | // TODO emit a compile error if the types are not allowed to be bitcasted | |
| 14692 | ||
| 14693 | if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) { | |
| 14694 | // The Type it is stored as in the compiler has an ABI size greater or equal to | |
| 14695 | // the ABI size of `load_ty`. We may perform the bitcast based on | |
| 14696 | // `parent.val` alone (more efficient). | |
| 14697 | return try parent.val.bitCast(parent.ty, load_ty, target, sema.gpa, sema.arena); | |
| 14698 | } | |
| 14699 | ||
| 14700 | // The Type it is stored as in the compiler has an ABI size less than the ABI size | |
| 14701 | // of `load_ty`. The bitcast must be performed based on the `parent.root_val` | |
| 14702 | // and reinterpreted starting at `parent.byte_offset`. | |
| 14703 | return sema.fail(block, src, "TODO: implement bitcast with index offset", .{}); | |
| 14704 | } |
src/type.zig+50-43| ... | ... | @@ -1764,6 +1764,7 @@ pub const Type = extern union { |
| 1764 | 1764 | } |
| 1765 | 1765 | |
| 1766 | 1766 | /// Asserts the type has the ABI size already resolved. |
| 1767 | /// Types that return false for hasCodeGenBits() return 0. | |
| 1767 | 1768 | pub fn abiSize(self: Type, target: Target) u64 { |
| 1768 | 1769 | return switch (self.tag()) { |
| 1769 | 1770 | .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer |
| ... | ... | @@ -1771,53 +1772,32 @@ pub const Type = extern union { |
| 1771 | 1772 | .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer |
| 1772 | 1773 | .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer |
| 1773 | 1774 | .function => unreachable, // represents machine code; not a pointer |
| 1774 | .c_void => unreachable, | |
| 1775 | .type => unreachable, | |
| 1776 | .comptime_int => unreachable, | |
| 1777 | .comptime_float => unreachable, | |
| 1775 | .@"opaque" => unreachable, // no size available | |
| 1776 | .bound_fn => unreachable, // TODO remove from the language | |
| 1778 | 1777 | .noreturn => unreachable, |
| 1779 | .@"null" => unreachable, | |
| 1780 | .@"undefined" => unreachable, | |
| 1781 | .enum_literal => unreachable, | |
| 1782 | .single_const_pointer_to_comptime_int => unreachable, | |
| 1783 | .empty_struct_literal => unreachable, | |
| 1784 | 1778 | .inferred_alloc_const => unreachable, |
| 1785 | 1779 | .inferred_alloc_mut => unreachable, |
| 1786 | .@"opaque" => unreachable, | |
| 1787 | 1780 | .var_args_param => unreachable, |
| 1788 | 1781 | .generic_poison => unreachable, |
| 1789 | .type_info => unreachable, | |
| 1790 | .bound_fn => unreachable, | |
| 1791 | ||
| 1792 | .empty_struct, .void => 0, | |
| 1782 | .call_options => unreachable, // missing call to resolveTypeFields | |
| 1783 | .export_options => unreachable, // missing call to resolveTypeFields | |
| 1784 | .extern_options => unreachable, // missing call to resolveTypeFields | |
| 1785 | .type_info => unreachable, // missing call to resolveTypeFields | |
| 1793 | 1786 | |
| 1794 | .@"struct" => { | |
| 1795 | const fields = self.structFields(); | |
| 1796 | if (self.castTag(.@"struct")) |payload| { | |
| 1797 | const struct_obj = payload.data; | |
| 1798 | assert(struct_obj.status == .have_layout); | |
| 1799 | const is_packed = struct_obj.layout == .Packed; | |
| 1800 | if (is_packed) @panic("TODO packed structs"); | |
| 1801 | } | |
| 1802 | var size: u64 = 0; | |
| 1803 | var big_align: u32 = 0; | |
| 1804 | for (fields.values()) |field| { | |
| 1805 | if (!field.ty.hasCodeGenBits()) continue; | |
| 1787 | .c_void, | |
| 1788 | .type, | |
| 1789 | .comptime_int, | |
| 1790 | .comptime_float, | |
| 1791 | .@"null", | |
| 1792 | .@"undefined", | |
| 1793 | .enum_literal, | |
| 1794 | .single_const_pointer_to_comptime_int, | |
| 1795 | .empty_struct_literal, | |
| 1796 | .empty_struct, | |
| 1797 | .void, | |
| 1798 | => 0, | |
| 1806 | 1799 | |
| 1807 | const field_align = a: { | |
| 1808 | if (field.abi_align.tag() == .abi_align_default) { | |
| 1809 | break :a field.ty.abiAlignment(target); | |
| 1810 | } else { | |
| 1811 | break :a @intCast(u32, field.abi_align.toUnsignedInt()); | |
| 1812 | } | |
| 1813 | }; | |
| 1814 | big_align = @maximum(big_align, field_align); | |
| 1815 | size = std.mem.alignForwardGeneric(u64, size, field_align); | |
| 1816 | size += field.ty.abiSize(target); | |
| 1817 | } | |
| 1818 | size = std.mem.alignForwardGeneric(u64, size, big_align); | |
| 1819 | return size; | |
| 1820 | }, | |
| 1800 | .@"struct" => return self.structFieldOffset(self.structFieldCount(), target), | |
| 1821 | 1801 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 1822 | 1802 | var buffer: Payload.Bits = undefined; |
| 1823 | 1803 | const int_tag_ty = self.intTagType(&buffer); |
| ... | ... | @@ -1837,9 +1817,6 @@ pub const Type = extern union { |
| 1837 | 1817 | .address_space, |
| 1838 | 1818 | .float_mode, |
| 1839 | 1819 | .reduce_op, |
| 1840 | .call_options, | |
| 1841 | .export_options, | |
| 1842 | .extern_options, | |
| 1843 | 1820 | => return 1, |
| 1844 | 1821 | |
| 1845 | 1822 | .array_u8 => self.castTag(.array_u8).?.data, |
| ... | ... | @@ -3414,6 +3391,36 @@ pub const Type = extern union { |
| 3414 | 3391 | } |
| 3415 | 3392 | } |
| 3416 | 3393 | |
| 3394 | pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 { | |
| 3395 | const fields = ty.structFields(); | |
| 3396 | if (ty.castTag(.@"struct")) |payload| { | |
| 3397 | const struct_obj = payload.data; | |
| 3398 | assert(struct_obj.status == .have_layout); | |
| 3399 | const is_packed = struct_obj.layout == .Packed; | |
| 3400 | if (is_packed) @panic("TODO packed structs"); | |
| 3401 | } | |
| 3402 | ||
| 3403 | var offset: u64 = 0; | |
| 3404 | var big_align: u32 = 0; | |
| 3405 | for (fields.values()) |field, i| { | |
| 3406 | if (!field.ty.hasCodeGenBits()) continue; | |
| 3407 | ||
| 3408 | const field_align = a: { | |
| 3409 | if (field.abi_align.tag() == .abi_align_default) { | |
| 3410 | break :a field.ty.abiAlignment(target); | |
| 3411 | } else { | |
| 3412 | break :a @intCast(u32, field.abi_align.toUnsignedInt()); | |
| 3413 | } | |
| 3414 | }; | |
| 3415 | big_align = @maximum(big_align, field_align); | |
| 3416 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | |
| 3417 | if (i == index) return offset; | |
| 3418 | offset += field.ty.abiSize(target); | |
| 3419 | } | |
| 3420 | offset = std.mem.alignForwardGeneric(u64, offset, big_align); | |
| 3421 | return offset; | |
| 3422 | } | |
| 3423 | ||
| 3417 | 3424 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { |
| 3418 | 3425 | return declSrcLocOrNull(ty).?; |
| 3419 | 3426 | } |
src/value.zig-54| ... | ... | @@ -1607,60 +1607,6 @@ pub const Value = extern union { |
| 1607 | 1607 | } |
| 1608 | 1608 | }; |
| 1609 | 1609 | |
| 1610 | /// Asserts the value is a pointer and dereferences it. | |
| 1611 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. | |
| 1612 | pub fn pointerDeref(val: Value, arena: *Allocator) error{ AnalysisFail, OutOfMemory }!?Value { | |
| 1613 | const sub_val: Value = switch (val.tag()) { | |
| 1614 | .decl_ref_mut => sub_val: { | |
| 1615 | // The decl whose value we are obtaining here may be overwritten with | |
| 1616 | // a different value, which would invalidate this memory. So we must | |
| 1617 | // copy here. | |
| 1618 | const sub_val = try val.castTag(.decl_ref_mut).?.data.decl.value(); | |
| 1619 | break :sub_val try sub_val.copy(arena); | |
| 1620 | }, | |
| 1621 | .decl_ref => try val.castTag(.decl_ref).?.data.value(), | |
| 1622 | .elem_ptr => blk: { | |
| 1623 | const elem_ptr = val.castTag(.elem_ptr).?.data; | |
| 1624 | const array_val = (try elem_ptr.array_ptr.pointerDeref(arena)) orelse return null; | |
| 1625 | break :blk try array_val.elemValue(arena, elem_ptr.index); | |
| 1626 | }, | |
| 1627 | .field_ptr => blk: { | |
| 1628 | const field_ptr = val.castTag(.field_ptr).?.data; | |
| 1629 | const container_val = (try field_ptr.container_ptr.pointerDeref(arena)) orelse return null; | |
| 1630 | break :blk try container_val.fieldValue(arena, field_ptr.field_index); | |
| 1631 | }, | |
| 1632 | .eu_payload_ptr => blk: { | |
| 1633 | const err_union_ptr = val.castTag(.eu_payload_ptr).?.data; | |
| 1634 | const err_union_val = (try err_union_ptr.pointerDeref(arena)) orelse return null; | |
| 1635 | break :blk err_union_val.castTag(.eu_payload).?.data; | |
| 1636 | }, | |
| 1637 | .opt_payload_ptr => blk: { | |
| 1638 | const opt_ptr = val.castTag(.opt_payload_ptr).?.data; | |
| 1639 | const opt_val = (try opt_ptr.pointerDeref(arena)) orelse return null; | |
| 1640 | break :blk opt_val.castTag(.opt_payload).?.data; | |
| 1641 | }, | |
| 1642 | ||
| 1643 | .zero, | |
| 1644 | .one, | |
| 1645 | .int_u64, | |
| 1646 | .int_i64, | |
| 1647 | .int_big_positive, | |
| 1648 | .int_big_negative, | |
| 1649 | .variable, | |
| 1650 | .extern_fn, | |
| 1651 | .function, | |
| 1652 | => return null, | |
| 1653 | ||
| 1654 | else => unreachable, | |
| 1655 | }; | |
| 1656 | if (sub_val.tag() == .variable) { | |
| 1657 | // This would be loading a runtime value at compile-time so we return | |
| 1658 | // the indicator that this pointer dereference requires being done at runtime. | |
| 1659 | return null; | |
| 1660 | } | |
| 1661 | return sub_val; | |
| 1662 | } | |
| 1663 | ||
| 1664 | 1610 | pub fn isComptimeMutablePtr(val: Value) bool { |
| 1665 | 1611 | return switch (val.tag()) { |
| 1666 | 1612 | .decl_ref_mut => true, |
test/behavior.zig+2-1| ... | ... | @@ -45,6 +45,7 @@ test { |
| 45 | 45 | _ = @import("behavior/null.zig"); |
| 46 | 46 | _ = @import("behavior/optional.zig"); |
| 47 | 47 | _ = @import("behavior/pointers.zig"); |
| 48 | _ = @import("behavior/ptrcast.zig"); | |
| 48 | 49 | _ = @import("behavior/pub_enum.zig"); |
| 49 | 50 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| 50 | 51 | _ = @import("behavior/slice.zig"); |
| ... | ... | @@ -146,7 +147,7 @@ test { |
| 146 | 147 | _ = @import("behavior/optional_stage1.zig"); |
| 147 | 148 | _ = @import("behavior/pointers_stage1.zig"); |
| 148 | 149 | _ = @import("behavior/popcount.zig"); |
| 149 | _ = @import("behavior/ptrcast.zig"); | |
| 150 | _ = @import("behavior/ptrcast_stage1.zig"); | |
| 150 | 151 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| 151 | 152 | _ = @import("behavior/reflection.zig"); |
| 152 | 153 | { |
test/behavior/cast.zig+12| ... | ... | @@ -65,3 +65,15 @@ test "implicit cast comptime_int to comptime_float" { |
| 65 | 65 | comptime try expect(@as(comptime_float, 10) == @as(f32, 10)); |
| 66 | 66 | try expect(2 == 2.0); |
| 67 | 67 | } |
| 68 | ||
| 69 | test "pointer reinterpret const float to int" { | |
| 70 | // The hex representation is 0x3fe3333333333303. | |
| 71 | const float: f64 = 5.99999999999994648725e-01; | |
| 72 | const float_ptr = &float; | |
| 73 | const int_ptr = @ptrCast(*const i32, float_ptr); | |
| 74 | const int_val = int_ptr.*; | |
| 75 | if (native_endian == .Little) | |
| 76 | try expect(int_val == 0x33333303) | |
| 77 | else | |
| 78 | try expect(int_val == 0x3fe33333); | |
| 79 | } |
test/behavior/cast_stage1.zig-12| ... | ... | @@ -5,18 +5,6 @@ const maxInt = std.math.maxInt; |
| 5 | 5 | const Vector = std.meta.Vector; |
| 6 | 6 | const native_endian = @import("builtin").target.cpu.arch.endian(); |
| 7 | 7 | |
| 8 | test "pointer reinterpret const float to int" { | |
| 9 | // The hex representation is 0x3fe3333333333303. | |
| 10 | const float: f64 = 5.99999999999994648725e-01; | |
| 11 | const float_ptr = &float; | |
| 12 | const int_ptr = @ptrCast(*const i32, float_ptr); | |
| 13 | const int_val = int_ptr.*; | |
| 14 | if (native_endian == .Little) | |
| 15 | try expect(int_val == 0x33333303) | |
| 16 | else | |
| 17 | try expect(int_val == 0x3fe33333); | |
| 18 | } | |
| 19 | ||
| 20 | 8 | test "implicitly cast indirect pointer to maybe-indirect pointer" { |
| 21 | 9 | const S = struct { |
| 22 | 10 | const Self = @This(); |
test/behavior/ptrcast.zig-69| ... | ... | @@ -2,72 +2,3 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const native_endian = builtin.target.cpu.arch.endian(); |
| 5 | ||
| 6 | test "reinterpret bytes as integer with nonzero offset" { | |
| 7 | try testReinterpretBytesAsInteger(); | |
| 8 | comptime try testReinterpretBytesAsInteger(); | |
| 9 | } | |
| 10 | ||
| 11 | fn testReinterpretBytesAsInteger() !void { | |
| 12 | const bytes = "\x12\x34\x56\x78\xab"; | |
| 13 | const expected = switch (native_endian) { | |
| 14 | .Little => 0xab785634, | |
| 15 | .Big => 0x345678ab, | |
| 16 | }; | |
| 17 | try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); | |
| 18 | } | |
| 19 | ||
| 20 | test "reinterpret bytes of an array into an extern struct" { | |
| 21 | try testReinterpretBytesAsExternStruct(); | |
| 22 | comptime try testReinterpretBytesAsExternStruct(); | |
| 23 | } | |
| 24 | ||
| 25 | fn testReinterpretBytesAsExternStruct() !void { | |
| 26 | var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; | |
| 27 | ||
| 28 | const S = extern struct { | |
| 29 | a: u8, | |
| 30 | b: u16, | |
| 31 | c: u8, | |
| 32 | }; | |
| 33 | ||
| 34 | var ptr = @ptrCast(*const S, &bytes); | |
| 35 | var val = ptr.c; | |
| 36 | try expect(val == 5); | |
| 37 | } | |
| 38 | ||
| 39 | test "reinterpret struct field at comptime" { | |
| 40 | const numNative = comptime Bytes.init(0x12345678); | |
| 41 | if (native_endian != .Little) { | |
| 42 | try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); | |
| 43 | } else { | |
| 44 | try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); | |
| 45 | } | |
| 46 | } | |
| 47 | ||
| 48 | const Bytes = struct { | |
| 49 | bytes: [4]u8, | |
| 50 | ||
| 51 | pub fn init(v: u32) Bytes { | |
| 52 | var res: Bytes = undefined; | |
| 53 | @ptrCast(*align(1) u32, &res.bytes).* = v; | |
| 54 | ||
| 55 | return res; | |
| 56 | } | |
| 57 | }; | |
| 58 | ||
| 59 | test "comptime ptrcast keeps larger alignment" { | |
| 60 | comptime { | |
| 61 | const a: u32 = 1234; | |
| 62 | const p = @ptrCast([*]const u8, &a); | |
| 63 | try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | test "implicit optional pointer to optional c_void pointer" { | |
| 68 | var buf: [4]u8 = "aoeu".*; | |
| 69 | var x: ?[*]u8 = &buf; | |
| 70 | var y: ?*c_void = x; | |
| 71 | var z = @ptrCast(*[4]u8, y); | |
| 72 | try expect(std.mem.eql(u8, z, "aoeu")); | |
| 73 | } |
test/behavior/ptrcast_stage1.zig created+73| ... | ... | @@ -0,0 +1,73 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const expect = std.testing.expect; | |
| 4 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 5 | ||
| 6 | test "reinterpret bytes as integer with nonzero offset" { | |
| 7 | try testReinterpretBytesAsInteger(); | |
| 8 | comptime try testReinterpretBytesAsInteger(); | |
| 9 | } | |
| 10 | ||
| 11 | fn testReinterpretBytesAsInteger() !void { | |
| 12 | const bytes = "\x12\x34\x56\x78\xab"; | |
| 13 | const expected = switch (native_endian) { | |
| 14 | .Little => 0xab785634, | |
| 15 | .Big => 0x345678ab, | |
| 16 | }; | |
| 17 | try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); | |
| 18 | } | |
| 19 | ||
| 20 | test "reinterpret bytes of an array into an extern struct" { | |
| 21 | try testReinterpretBytesAsExternStruct(); | |
| 22 | comptime try testReinterpretBytesAsExternStruct(); | |
| 23 | } | |
| 24 | ||
| 25 | fn testReinterpretBytesAsExternStruct() !void { | |
| 26 | var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; | |
| 27 | ||
| 28 | const S = extern struct { | |
| 29 | a: u8, | |
| 30 | b: u16, | |
| 31 | c: u8, | |
| 32 | }; | |
| 33 | ||
| 34 | var ptr = @ptrCast(*const S, &bytes); | |
| 35 | var val = ptr.c; | |
| 36 | try expect(val == 5); | |
| 37 | } | |
| 38 | ||
| 39 | test "reinterpret struct field at comptime" { | |
| 40 | const numNative = comptime Bytes.init(0x12345678); | |
| 41 | if (native_endian != .Little) { | |
| 42 | try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); | |
| 43 | } else { | |
| 44 | try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); | |
| 45 | } | |
| 46 | } | |
| 47 | ||
| 48 | const Bytes = struct { | |
| 49 | bytes: [4]u8, | |
| 50 | ||
| 51 | pub fn init(v: u32) Bytes { | |
| 52 | var res: Bytes = undefined; | |
| 53 | @ptrCast(*align(1) u32, &res.bytes).* = v; | |
| 54 | ||
| 55 | return res; | |
| 56 | } | |
| 57 | }; | |
| 58 | ||
| 59 | test "comptime ptrcast keeps larger alignment" { | |
| 60 | comptime { | |
| 61 | const a: u32 = 1234; | |
| 62 | const p = @ptrCast([*]const u8, &a); | |
| 63 | try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | test "implicit optional pointer to optional c_void pointer" { | |
| 68 | var buf: [4]u8 = "aoeu".*; | |
| 69 | var x: ?[*]u8 = &buf; | |
| 70 | var y: ?*c_void = x; | |
| 71 | var z = @ptrCast(*[4]u8, y); | |
| 72 | try expect(std.mem.eql(u8, z, "aoeu")); | |
| 73 | } |