authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-21 22:56:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-21 22:56:11-07:00
log0a6851cc6de540ed95c3ec1c78eb3da7897bd930
treeb929564a7b7cddafd8dfb5ad9157fffe3425a1f0
parent1bce0ed0460e2bdeb47d534e28090ed4b3794b97

stage2: implement comptime loads through casted pointers


8 files changed, 314 insertions(+), 193 deletions(-)

src/Sema.zig+177-14
......@@ -4570,7 +4570,7 @@ fn zirOptionalPayloadPtr(
45704570 });
45714571
45724572 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| {
45744574 if (val.isNull()) {
45754575 return sema.fail(block, src, "unable to unwrap null", .{});
45764576 }
......@@ -4689,7 +4689,7 @@ fn zirErrUnionPayloadPtr(
46894689 });
46904690
46914691 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| {
46934693 if (val.getError()) |name| {
46944694 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
46954695 }
......@@ -4748,7 +4748,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
47484748 const result_ty = operand_ty.elemType().errorUnionSet();
47494749
47504750 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| {
47524752 assert(val.getError() != null);
47534753 return sema.addConstant(result_ty, val);
47544754 }
......@@ -6912,8 +6912,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
69126912 const final_len = lhs_info.len + rhs_info.len;
69136913 const final_len_including_sent = final_len + @boolToInt(res_sent != null);
69146914 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;
69176917 var anon_decl = try block.startAnonDecl();
69186918 defer anon_decl.deinit();
69196919
......@@ -6992,7 +6992,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
69926992 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);
69936993
69946994 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;
69966996
69976997 var anon_decl = try block.startAnonDecl();
69986998 defer anon_decl.deinit();
......@@ -10092,7 +10092,8 @@ fn zirCmpxchg(
1009210092 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };
1009310093 // zig fmt: on
1009410094 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();
1009610097 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
1009710098 if (elem_ty.zigTypeTag() == .Float) {
1009810099 return sema.fail(
......@@ -10135,7 +10136,7 @@ fn zirCmpxchg(
1013510136 // to become undef as well
1013610137 return sema.addConstUndef(result_ty);
1013710138 }
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;
1013910140 const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: {
1014010141 try sema.storePtr(block, src, ptr, new_value);
1014110142 break :blk Value.@"null";
......@@ -10197,7 +10198,8 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1019710198 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
1019810199 // zig fmt: on
1019910200 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();
1020110203 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
1020210204 const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs);
1020310205
......@@ -10218,7 +10220,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1021810220 }
1021910221
1022010222 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| {
1022210224 return sema.addConstant(elem_ty, elem_val);
1022310225 }
1022410226 }
......@@ -10245,7 +10247,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1024510247 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
1024610248 // zig fmt: on
1024710249 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();
1024910252 try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
1025010253 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
1025110254
......@@ -10282,7 +10285,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1028210285 };
1028310286 if (ptr_val.isComptimeMutablePtr()) {
1028410287 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;
1028610289 const new_val = switch (op) {
1028710290 // zig fmt: off
1028810291 .Xchg => operand_val,
......@@ -11785,7 +11788,7 @@ fn elemVal(
1178511788 const ptr_val = maybe_ptr_val orelse break :rs array_src;
1178611789 const index_val = maybe_index_val orelse break :rs elem_index_src;
1178711790 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);
1178911792 const array_val = maybe_array_val orelse break :rs array_src;
1179011793 const elem_val = try array_val.elemValue(sema.arena, index);
1179111794 return sema.addConstant(array_ty.elemType2(), elem_val);
......@@ -11887,6 +11890,8 @@ fn coerce(
1188711890 assert(inst_ty.zigTypeTag() != .Undefined);
1188811891
1188911892 // comptime known number to other number
11893 // TODO why is this a separate function? should just be flattened into the
11894 // switch expression below.
1189011895 if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some|
1189111896 return some;
1189211897
......@@ -12514,6 +12519,122 @@ fn beginComptimePtrMutation(
1251412519 }
1251512520}
1251612521
12522const 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
12536const ComptimePtrLoadError = CompileError || error{
12537 RuntimeLoad,
12538};
12539
12540fn 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
1251712638fn bitCast(
1251812639 sema: *Sema,
1251912640 block: *Block,
......@@ -12819,7 +12940,7 @@ fn analyzeLoad(
1281912940 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
1282012941 };
1282112942 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| {
1282312944 return sema.addConstant(elem_ty, elem_val);
1282412945 }
1282512946 }
......@@ -14539,3 +14660,45 @@ pub fn analyzeAddrspace(
1453914660
1454014661 return address_space;
1454114662}
14663
14664/// Asserts the value is a pointer and dereferences it.
14665/// Returns `null` if the pointer contents cannot be loaded at comptime.
14666fn 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 {
17641764 }
17651765
17661766 /// Asserts the type has the ABI size already resolved.
1767 /// Types that return false for hasCodeGenBits() return 0.
17671768 pub fn abiSize(self: Type, target: Target) u64 {
17681769 return switch (self.tag()) {
17691770 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
......@@ -1771,53 +1772,32 @@ pub const Type = extern union {
17711772 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
17721773 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
17731774 .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
17781777 .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,
17841778 .inferred_alloc_const => unreachable,
17851779 .inferred_alloc_mut => unreachable,
1786 .@"opaque" => unreachable,
17871780 .var_args_param => unreachable,
17881781 .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
17931786
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,
18061799
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),
18211801 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
18221802 var buffer: Payload.Bits = undefined;
18231803 const int_tag_ty = self.intTagType(&buffer);
......@@ -1837,9 +1817,6 @@ pub const Type = extern union {
18371817 .address_space,
18381818 .float_mode,
18391819 .reduce_op,
1840 .call_options,
1841 .export_options,
1842 .extern_options,
18431820 => return 1,
18441821
18451822 .array_u8 => self.castTag(.array_u8).?.data,
......@@ -3414,6 +3391,36 @@ pub const Type = extern union {
34143391 }
34153392 }
34163393
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
34173424 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
34183425 return declSrcLocOrNull(ty).?;
34193426 }
src/value.zig-54
......@@ -1607,60 +1607,6 @@ pub const Value = extern union {
16071607 }
16081608 };
16091609
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
16641610 pub fn isComptimeMutablePtr(val: Value) bool {
16651611 return switch (val.tag()) {
16661612 .decl_ref_mut => true,
test/behavior.zig+2-1
......@@ -45,6 +45,7 @@ test {
4545 _ = @import("behavior/null.zig");
4646 _ = @import("behavior/optional.zig");
4747 _ = @import("behavior/pointers.zig");
48 _ = @import("behavior/ptrcast.zig");
4849 _ = @import("behavior/pub_enum.zig");
4950 _ = @import("behavior/sizeof_and_typeof.zig");
5051 _ = @import("behavior/slice.zig");
......@@ -146,7 +147,7 @@ test {
146147 _ = @import("behavior/optional_stage1.zig");
147148 _ = @import("behavior/pointers_stage1.zig");
148149 _ = @import("behavior/popcount.zig");
149 _ = @import("behavior/ptrcast.zig");
150 _ = @import("behavior/ptrcast_stage1.zig");
150151 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
151152 _ = @import("behavior/reflection.zig");
152153 {
test/behavior/cast.zig+12
......@@ -65,3 +65,15 @@ test "implicit cast comptime_int to comptime_float" {
6565 comptime try expect(@as(comptime_float, 10) == @as(f32, 10));
6666 try expect(2 == 2.0);
6767}
68
69test "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;
55const Vector = std.meta.Vector;
66const native_endian = @import("builtin").target.cpu.arch.endian();
77
8test "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
208test "implicitly cast indirect pointer to maybe-indirect pointer" {
219 const S = struct {
2210 const Self = @This();
test/behavior/ptrcast.zig-69
......@@ -2,72 +2,3 @@ const std = @import("std");
22const builtin = @import("builtin");
33const expect = std.testing.expect;
44const native_endian = builtin.target.cpu.arch.endian();
5
6test "reinterpret bytes as integer with nonzero offset" {
7 try testReinterpretBytesAsInteger();
8 comptime try testReinterpretBytesAsInteger();
9}
10
11fn 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
20test "reinterpret bytes of an array into an extern struct" {
21 try testReinterpretBytesAsExternStruct();
22 comptime try testReinterpretBytesAsExternStruct();
23}
24
25fn 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
39test "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
48const 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
59test "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
67test "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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4const native_endian = builtin.target.cpu.arch.endian();
5
6test "reinterpret bytes as integer with nonzero offset" {
7 try testReinterpretBytesAsInteger();
8 comptime try testReinterpretBytesAsInteger();
9}
10
11fn 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
20test "reinterpret bytes of an array into an extern struct" {
21 try testReinterpretBytesAsExternStruct();
22 comptime try testReinterpretBytesAsExternStruct();
23}
24
25fn 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
39test "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
48const 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
59test "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
67test "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}