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(...@@ -4570,7 +4570,7 @@ fn zirOptionalPayloadPtr(
4570 });4570 });
45714571
4572 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {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 if (val.isNull()) {4574 if (val.isNull()) {
4575 return sema.fail(block, src, "unable to unwrap null", .{});4575 return sema.fail(block, src, "unable to unwrap null", .{});
4576 }4576 }
...@@ -4689,7 +4689,7 @@ fn zirErrUnionPayloadPtr(...@@ -4689,7 +4689,7 @@ fn zirErrUnionPayloadPtr(
4689 });4689 });
46904690
4691 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {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 if (val.getError()) |name| {4693 if (val.getError()) |name| {
4694 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});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,7 +4748,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
4748 const result_ty = operand_ty.elemType().errorUnionSet();4748 const result_ty = operand_ty.elemType().errorUnionSet();
47494749
4750 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {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 assert(val.getError() != null);4752 assert(val.getError() != null);
4753 return sema.addConstant(result_ty, val);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,8 +6912,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
6912 const final_len = lhs_info.len + rhs_info.len;6912 const final_len = lhs_info.len + rhs_info.len;
6913 const final_len_including_sent = final_len + @boolToInt(res_sent != null);6913 const final_len_including_sent = final_len + @boolToInt(res_sent != null);
6914 const is_pointer = lhs_ty.zigTypeTag() == .Pointer;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;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 rhs_val.pointerDeref(sema.arena)).? else rhs_val;6916 const rhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;
6917 var anon_decl = try block.startAnonDecl();6917 var anon_decl = try block.startAnonDecl();
6918 defer anon_decl.deinit();6918 defer anon_decl.deinit();
69196919
...@@ -6992,7 +6992,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -6992,7 +6992,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
6992 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);6992 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);
69936993
6994 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {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;
69966996
6997 var anon_decl = try block.startAnonDecl();6997 var anon_decl = try block.startAnonDecl();
6998 defer anon_decl.deinit();6998 defer anon_decl.deinit();
...@@ -10092,7 +10092,8 @@ fn zirCmpxchg(...@@ -10092,7 +10092,8 @@ fn zirCmpxchg(
10092 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };10092 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };
10093 // zig fmt: on10093 // zig fmt: on
10094 const ptr = sema.resolveInst(extra.ptr);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 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);10097 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
10097 if (elem_ty.zigTypeTag() == .Float) {10098 if (elem_ty.zigTypeTag() == .Float) {
10098 return sema.fail(10099 return sema.fail(
...@@ -10135,7 +10136,7 @@ fn zirCmpxchg(...@@ -10135,7 +10136,7 @@ fn zirCmpxchg(
10135 // to become undef as well10136 // to become undef as well
10136 return sema.addConstUndef(result_ty);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 const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: {10140 const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: {
10140 try sema.storePtr(block, src, ptr, new_value);10141 try sema.storePtr(block, src, ptr, new_value);
10141 break :blk Value.@"null";10142 break :blk Value.@"null";
...@@ -10197,7 +10198,8 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -10197,7 +10198,8 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
10197 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };10198 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
10198 // zig fmt: on10199 // zig fmt: on
10199 const ptr = sema.resolveInst(extra.lhs);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 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);10203 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
10202 const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs);10204 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!...@@ -10218,7 +10220,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
10218 }10220 }
1021910221
10220 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {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 return sema.addConstant(elem_ty, elem_val);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,7 +10247,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
10245 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };10247 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
10246 // zig fmt: on10248 // zig fmt: on
10247 const ptr = sema.resolveInst(extra.ptr);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 try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);10252 try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
10250 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);10253 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...@@ -10282,7 +10285,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
10282 };10285 };
10283 if (ptr_val.isComptimeMutablePtr()) {10286 if (ptr_val.isComptimeMutablePtr()) {
10284 const target = sema.mod.getTarget();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 const new_val = switch (op) {10289 const new_val = switch (op) {
10287 // zig fmt: off10290 // zig fmt: off
10288 .Xchg => operand_val,10291 .Xchg => operand_val,
...@@ -11785,7 +11788,7 @@ fn elemVal(...@@ -11785,7 +11788,7 @@ fn elemVal(
11785 const ptr_val = maybe_ptr_val orelse break :rs array_src;11788 const ptr_val = maybe_ptr_val orelse break :rs array_src;
11786 const index_val = maybe_index_val orelse break :rs elem_index_src;11789 const index_val = maybe_index_val orelse break :rs elem_index_src;
11787 const index = @intCast(usize, index_val.toUnsignedInt());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 const array_val = maybe_array_val orelse break :rs array_src;11792 const array_val = maybe_array_val orelse break :rs array_src;
11790 const elem_val = try array_val.elemValue(sema.arena, index);11793 const elem_val = try array_val.elemValue(sema.arena, index);
11791 return sema.addConstant(array_ty.elemType2(), elem_val);11794 return sema.addConstant(array_ty.elemType2(), elem_val);
...@@ -11887,6 +11890,8 @@ fn coerce(...@@ -11887,6 +11890,8 @@ fn coerce(
11887 assert(inst_ty.zigTypeTag() != .Undefined);11890 assert(inst_ty.zigTypeTag() != .Undefined);
1188811891
11889 // comptime known number to other number11892 // 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 if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some|11895 if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some|
11891 return some;11896 return some;
1189211897
...@@ -12514,6 +12519,122 @@ fn beginComptimePtrMutation(...@@ -12514,6 +12519,122 @@ fn beginComptimePtrMutation(
12514 }12519 }
12515}12520}
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
12517fn bitCast(12638fn bitCast(
12518 sema: *Sema,12639 sema: *Sema,
12519 block: *Block,12640 block: *Block,
...@@ -12819,7 +12940,7 @@ fn analyzeLoad(...@@ -12819,7 +12940,7 @@ fn analyzeLoad(
12819 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),12940 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
12820 };12941 };
12821 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {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 return sema.addConstant(elem_ty, elem_val);12944 return sema.addConstant(elem_ty, elem_val);
12824 }12945 }
12825 }12946 }
...@@ -14539,3 +14660,45 @@ pub fn analyzeAddrspace(...@@ -14539,3 +14660,45 @@ pub fn analyzeAddrspace(
1453914660
14540 return address_space;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.
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 {...@@ -1764,6 +1764,7 @@ pub const Type = extern union {
1764 }1764 }
17651765
1766 /// Asserts the type has the ABI size already resolved.1766 /// Asserts the type has the ABI size already resolved.
1767 /// Types that return false for hasCodeGenBits() return 0.
1767 pub fn abiSize(self: Type, target: Target) u64 {1768 pub fn abiSize(self: Type, target: Target) u64 {
1768 return switch (self.tag()) {1769 return switch (self.tag()) {
1769 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer1770 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
...@@ -1771,53 +1772,32 @@ pub const Type = extern union {...@@ -1771,53 +1772,32 @@ pub const Type = extern union {
1771 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer1772 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
1772 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer1773 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
1773 .function => unreachable, // represents machine code; not a pointer1774 .function => unreachable, // represents machine code; not a pointer
1774 .c_void => unreachable,1775 .@"opaque" => unreachable, // no size available
1775 .type => unreachable,1776 .bound_fn => unreachable, // TODO remove from the language
1776 .comptime_int => unreachable,
1777 .comptime_float => unreachable,
1778 .noreturn => unreachable,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 .inferred_alloc_const => unreachable,1778 .inferred_alloc_const => unreachable,
1785 .inferred_alloc_mut => unreachable,1779 .inferred_alloc_mut => unreachable,
1786 .@"opaque" => unreachable,
1787 .var_args_param => unreachable,1780 .var_args_param => unreachable,
1788 .generic_poison => unreachable,1781 .generic_poison => unreachable,
1789 .type_info => unreachable,1782 .call_options => unreachable, // missing call to resolveTypeFields
1790 .bound_fn => unreachable,1783 .export_options => unreachable, // missing call to resolveTypeFields
17911784 .extern_options => unreachable, // missing call to resolveTypeFields
1792 .empty_struct, .void => 0,1785 .type_info => unreachable, // missing call to resolveTypeFields
17931786
1794 .@"struct" => {1787 .c_void,
1795 const fields = self.structFields();1788 .type,
1796 if (self.castTag(.@"struct")) |payload| {1789 .comptime_int,
1797 const struct_obj = payload.data;1790 .comptime_float,
1798 assert(struct_obj.status == .have_layout);1791 .@"null",
1799 const is_packed = struct_obj.layout == .Packed;1792 .@"undefined",
1800 if (is_packed) @panic("TODO packed structs");1793 .enum_literal,
1801 }1794 .single_const_pointer_to_comptime_int,
1802 var size: u64 = 0;1795 .empty_struct_literal,
1803 var big_align: u32 = 0;1796 .empty_struct,
1804 for (fields.values()) |field| {1797 .void,
1805 if (!field.ty.hasCodeGenBits()) continue;1798 => 0,
18061799
1807 const field_align = a: {1800 .@"struct" => return self.structFieldOffset(self.structFieldCount(), target),
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 },
1821 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {1801 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
1822 var buffer: Payload.Bits = undefined;1802 var buffer: Payload.Bits = undefined;
1823 const int_tag_ty = self.intTagType(&buffer);1803 const int_tag_ty = self.intTagType(&buffer);
...@@ -1837,9 +1817,6 @@ pub const Type = extern union {...@@ -1837,9 +1817,6 @@ pub const Type = extern union {
1837 .address_space,1817 .address_space,
1838 .float_mode,1818 .float_mode,
1839 .reduce_op,1819 .reduce_op,
1840 .call_options,
1841 .export_options,
1842 .extern_options,
1843 => return 1,1820 => return 1,
18441821
1845 .array_u8 => self.castTag(.array_u8).?.data,1822 .array_u8 => self.castTag(.array_u8).?.data,
...@@ -3414,6 +3391,36 @@ pub const Type = extern union {...@@ -3414,6 +3391,36 @@ pub const Type = extern union {
3414 }3391 }
3415 }3392 }
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
3417 pub fn declSrcLoc(ty: Type) Module.SrcLoc {3424 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
3418 return declSrcLocOrNull(ty).?;3425 return declSrcLocOrNull(ty).?;
3419 }3426 }
src/value.zig-54
...@@ -1607,60 +1607,6 @@ pub const Value = extern union {...@@ -1607,60 +1607,6 @@ pub const Value = extern union {
1607 }1607 }
1608 };1608 };
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
1664 pub fn isComptimeMutablePtr(val: Value) bool {1610 pub fn isComptimeMutablePtr(val: Value) bool {
1665 return switch (val.tag()) {1611 return switch (val.tag()) {
1666 .decl_ref_mut => true,1612 .decl_ref_mut => true,
test/behavior.zig+2-1
...@@ -45,6 +45,7 @@ test {...@@ -45,6 +45,7 @@ test {
45 _ = @import("behavior/null.zig");45 _ = @import("behavior/null.zig");
46 _ = @import("behavior/optional.zig");46 _ = @import("behavior/optional.zig");
47 _ = @import("behavior/pointers.zig");47 _ = @import("behavior/pointers.zig");
48 _ = @import("behavior/ptrcast.zig");
48 _ = @import("behavior/pub_enum.zig");49 _ = @import("behavior/pub_enum.zig");
49 _ = @import("behavior/sizeof_and_typeof.zig");50 _ = @import("behavior/sizeof_and_typeof.zig");
50 _ = @import("behavior/slice.zig");51 _ = @import("behavior/slice.zig");
...@@ -146,7 +147,7 @@ test {...@@ -146,7 +147,7 @@ test {
146 _ = @import("behavior/optional_stage1.zig");147 _ = @import("behavior/optional_stage1.zig");
147 _ = @import("behavior/pointers_stage1.zig");148 _ = @import("behavior/pointers_stage1.zig");
148 _ = @import("behavior/popcount.zig");149 _ = @import("behavior/popcount.zig");
149 _ = @import("behavior/ptrcast.zig");150 _ = @import("behavior/ptrcast_stage1.zig");
150 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");151 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
151 _ = @import("behavior/reflection.zig");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,3 +65,15 @@ test "implicit cast comptime_int to comptime_float" {
65 comptime try expect(@as(comptime_float, 10) == @as(f32, 10));65 comptime try expect(@as(comptime_float, 10) == @as(f32, 10));
66 try expect(2 == 2.0);66 try expect(2 == 2.0);
67}67}
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;...@@ -5,18 +5,6 @@ const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;5const Vector = std.meta.Vector;
6const native_endian = @import("builtin").target.cpu.arch.endian();6const 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
20test "implicitly cast indirect pointer to maybe-indirect pointer" {8test "implicitly cast indirect pointer to maybe-indirect pointer" {
21 const S = struct {9 const S = struct {
22 const Self = @This();10 const Self = @This();
test/behavior/ptrcast.zig-69
...@@ -2,72 +2,3 @@ const std = @import("std");...@@ -2,72 +2,3 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
4const native_endian = builtin.target.cpu.arch.endian();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}
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}