authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-22 20:47:40-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-22 20:47:40-07:00
logf591936480730a279d55a051f92806e19834faf7
treedda39b1ee5d5e8cb1d9cc222a19188cabcb1329f
parenteb1b2f5c58888bfcab7e00378502f82594536aec
parent423a19fa60d2b0a11fc2fb43b12001320c2c53c5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12198 from Vexu/stage2

Sema: fix loading and storing of optional pointers represented as pointers

7 files changed, 145 insertions(+), 18 deletions(-)

src/Sema.zig+47-15
...@@ -1663,11 +1663,15 @@ fn resolveMaybeUndefValIntable(...@@ -1663,11 +1663,15 @@ fn resolveMaybeUndefValIntable(
1663 inst: Air.Inst.Ref,1663 inst: Air.Inst.Ref,
1664) CompileError!?Value {1664) CompileError!?Value {
1665 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;1665 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
1666 switch (val.tag()) {1666 var check = val;
1667 .variable, .decl_ref, .decl_ref_mut => return null,1667 while (true) switch (check.tag()) {
1668 .variable, .decl_ref, .decl_ref_mut, .comptime_field_ptr => return null,
1669 .field_ptr => check = check.castTag(.field_ptr).?.data.container_ptr,
1670 .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr,
1671 .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr,
1668 .generic_poison => return error.GenericPoison,1672 .generic_poison => return error.GenericPoison,
1669 else => return val,1673 else => return val,
1670 }1674 };
1671}1675}
16721676
1673/// Returns all Value tags including `variable` and `undef`.1677/// Returns all Value tags including `variable` and `undef`.
...@@ -3871,9 +3875,15 @@ fn zirValidateArrayInit(...@@ -3871,9 +3875,15 @@ fn zirValidateArrayInit(
3871 const array_len = array_ty.arrayLen();3875 const array_len = array_ty.arrayLen();
38723876
3873 if (instrs.len != array_len) {3877 if (instrs.len != array_len) {
3874 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{3878 if (array_ty.zigTypeTag() == .Array) {
3875 array_len, instrs.len,3879 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
3876 });3880 array_len, instrs.len,
3881 });
3882 } else {
3883 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
3884 array_len, instrs.len,
3885 });
3886 }
3877 }3887 }
38783888
3879 if ((is_comptime or block.is_comptime) and3889 if ((is_comptime or block.is_comptime) and
...@@ -7893,7 +7903,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -7893,7 +7903,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
7893 if (!ptr_ty.isPtrAtRuntime()) {7903 if (!ptr_ty.isPtrAtRuntime()) {
7894 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)});7904 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)});
7895 }7905 }
7896 if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {7906 if (try sema.resolveMaybeUndefValIntable(block, ptr_src, ptr)) |ptr_val| {
7897 return sema.addConstant(Type.usize, ptr_val);7907 return sema.addConstant(Type.usize, ptr_val);
7898 }7908 }
7899 try sema.requireRuntimeBlock(block, ptr_src, ptr_src);7909 try sema.requireRuntimeBlock(block, ptr_src, ptr_src);
...@@ -14261,7 +14271,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -14261,7 +14271,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1426114271
14262 switch (obj_ty.zigTypeTag()) {14272 switch (obj_ty.zigTypeTag()) {
14263 .Struct => return sema.structInitEmpty(block, obj_ty, src, src),14273 .Struct => return sema.structInitEmpty(block, obj_ty, src, src),
14264 .Array => return arrayInitEmpty(sema, obj_ty),14274 .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty),
14265 .Void => return sema.addConstant(obj_ty, Value.void),14275 .Void => return sema.addConstant(obj_ty, Value.void),
14266 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),14276 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
14267 }14277 }
...@@ -14286,7 +14296,15 @@ fn structInitEmpty(...@@ -14286,7 +14296,15 @@ fn structInitEmpty(
14286 return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);14296 return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false);
14287}14297}
1428814298
14289fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {14299fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
14300 const arr_len = obj_ty.arrayLen();
14301 if (arr_len != 0) {
14302 if (obj_ty.zigTypeTag() == .Array) {
14303 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
14304 } else {
14305 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
14306 }
14307 }
14290 if (obj_ty.sentinel()) |sentinel| {14308 if (obj_ty.sentinel()) |sentinel| {
14291 const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);14309 const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);
14292 return sema.addConstant(obj_ty, val);14310 return sema.addConstant(obj_ty, val);
...@@ -19448,7 +19466,7 @@ fn fieldCallBind(...@@ -19448,7 +19466,7 @@ fn fieldCallBind(
1944819466
19449 const raw_ptr_src = src; // TODO better source location19467 const raw_ptr_src = src; // TODO better source location
19450 const raw_ptr_ty = sema.typeOf(raw_ptr);19468 const raw_ptr_ty = sema.typeOf(raw_ptr);
19451 const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One)19469 const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and (raw_ptr_ty.ptrSize() == .One or raw_ptr_ty.ptrSize() == .C))
19452 raw_ptr_ty.childType()19470 raw_ptr_ty.childType()
19453 else19471 else
19454 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)});19472 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)});
...@@ -20974,7 +20992,7 @@ fn coerceExtra(...@@ -20974,7 +20992,7 @@ fn coerceExtra(
20974 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),20992 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
20975 .Struct => {20993 .Struct => {
20976 if (inst == .empty_struct) {20994 if (inst == .empty_struct) {
20977 return arrayInitEmpty(sema, dest_ty);20995 return sema.arrayInitEmpty(block, inst_src, dest_ty);
20978 }20996 }
20979 if (inst_ty.isTuple()) {20997 if (inst_ty.isTuple()) {
20980 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);20998 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
...@@ -22607,7 +22625,9 @@ fn beginComptimePtrMutation(...@@ -22607,7 +22625,9 @@ fn beginComptimePtrMutation(
22607 }22625 }
22608 },22626 },
22609 .opt_payload_ptr => {22627 .opt_payload_ptr => {
22610 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;22628 const opt_ptr = if (ptr_val.castTag(.opt_payload_ptr)) |some| some.data else {
22629 return sema.beginComptimePtrMutation(block, src, ptr_val, try ptr_elem_ty.optionalChildAlloc(sema.arena));
22630 };
22611 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);22631 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);
22612 switch (parent.pointee) {22632 switch (parent.pointee) {
22613 .direct => |val_ptr| {22633 .direct => |val_ptr| {
...@@ -22640,7 +22660,11 @@ fn beginComptimePtrMutation(...@@ -22640,7 +22660,11 @@ fn beginComptimePtrMutation(
22640 .ty = payload_ty,22660 .ty = payload_ty,
22641 },22661 },
2264222662
22643 else => unreachable,22663 else => return ComptimePtrMutationKit{
22664 .decl_ref_mut = parent.decl_ref_mut,
22665 .pointee = .{ .direct = val_ptr },
22666 .ty = payload_ty,
22667 },
22644 }22668 }
22645 },22669 },
22646 .bad_decl_ty, .bad_ptr_ty => return parent,22670 .bad_decl_ty, .bad_ptr_ty => return parent,
...@@ -22922,8 +22946,13 @@ fn beginComptimePtrLoad(...@@ -22922,8 +22946,13 @@ fn beginComptimePtrLoad(
22922 (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;22946 (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;
22923 if (coerce_in_mem_ok) {22947 if (coerce_in_mem_ok) {
22924 const payload_val = switch (ptr_val.tag()) {22948 const payload_val = switch (ptr_val.tag()) {
22925 .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data,22949 .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else {
22926 .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data,22950 return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name});
22951 },
22952 .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: {
22953 if (tv.val.isNull()) return sema.fail(block, src, "attempt to use null value", .{});
22954 break :opt tv.val;
22955 },
22927 else => unreachable,22956 else => unreachable,
22928 };22957 };
22929 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };22958 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
...@@ -22933,6 +22962,9 @@ fn beginComptimePtrLoad(...@@ -22933,6 +22962,9 @@ fn beginComptimePtrLoad(
22933 deref.pointee = null;22962 deref.pointee = null;
22934 break :blk deref;22963 break :blk deref;
22935 },22964 },
22965 .null_value => {
22966 return sema.fail(block, src, "attempt to use null value", .{});
22967 },
2293622968
22937 .zero,22969 .zero,
22938 .one,22970 .one,
src/codegen/c.zig+3-2
...@@ -3618,8 +3618,9 @@ fn airIsErr(...@@ -3618,8 +3618,9 @@ fn airIsErr(
3618 const operand = try f.resolveInst(un_op);3618 const operand = try f.resolveInst(un_op);
3619 const operand_ty = f.air.typeOf(un_op);3619 const operand_ty = f.air.typeOf(un_op);
3620 const local = try f.allocLocal(Type.initTag(.bool), .Const);3620 const local = try f.allocLocal(Type.initTag(.bool), .Const);
3621 const payload_ty = operand_ty.errorUnionPayload();3621 const err_union_ty = if (is_ptr) operand_ty.childType() else operand_ty;
3622 const error_ty = operand_ty.errorUnionSet();3622 const payload_ty = err_union_ty.errorUnionPayload();
3623 const error_ty = err_union_ty.errorUnionSet();
36233624
3624 try writer.writeAll(" = ");3625 try writer.writeAll(" = ");
36253626
src/codegen/llvm.zig+2-1
...@@ -5697,7 +5697,8 @@ pub const FuncGen = struct {...@@ -5697,7 +5697,8 @@ pub const FuncGen = struct {
56975697
5698 const un_op = self.air.instructions.items(.data)[inst].un_op;5698 const un_op = self.air.instructions.items(.data)[inst].un_op;
5699 const operand = try self.resolveInst(un_op);5699 const operand = try self.resolveInst(un_op);
5700 const err_union_ty = self.air.typeOf(un_op);5700 const operand_ty = self.air.typeOf(un_op);
5701 const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
5701 const payload_ty = err_union_ty.errorUnionPayload();5702 const payload_ty = err_union_ty.errorUnionPayload();
5702 const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror));5703 const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror));
5703 const zero = err_set_ty.constNull();5704 const zero = err_set_ty.constNull();
test/behavior/error.zig+12
...@@ -724,3 +724,15 @@ test "simple else prong allowed even when all errors handled" {...@@ -724,3 +724,15 @@ test "simple else prong allowed even when all errors handled" {
724 };724 };
725 try expect(value == 255);725 try expect(value == 255);
726}726}
727
728test {
729 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
730 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
731 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
732 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
733
734 var err_union: anyerror!u8 = 15;
735
736 const payload_ptr = &(err_union catch unreachable);
737 try expect(payload_ptr.* == 15);
738}
test/behavior/eval.zig+21
...@@ -1272,3 +1272,24 @@ test "continue nested in a conditional in an inline for" {...@@ -1272,3 +1272,24 @@ test "continue nested in a conditional in an inline for" {
1272 }1272 }
1273 try expect(x == 0);1273 try expect(x == 0);
1274}1274}
1275
1276test "optional pointer represented as a pointer value" {
1277 comptime {
1278 var val: u8 = 15;
1279 const opt_ptr: ?*u8 = &val;
1280
1281 const payload_ptr = &opt_ptr.?;
1282 try expect(payload_ptr.*.* == 15);
1283 }
1284}
1285
1286test "mutate through pointer-like optional at comptime" {
1287 comptime {
1288 var val: u8 = 15;
1289 var opt_ptr: ?*const u8 = &val;
1290
1291 const payload_ptr = &opt_ptr.?;
1292 payload_ptr.* = &@as(u8, 16);
1293 try expect(payload_ptr.*.* == 16);
1294 }
1295}
test/cases/compile_errors/array_init_invalid_elem_count.zig created+27
...@@ -0,0 +1,27 @@
1const V = @Vector(8, u8);
2const A = [8]u8;
3comptime {
4 var v: V = V{1};
5 _ = v;
6}
7comptime {
8 var v: V = V{};
9 _ = v;
10}
11comptime {
12 var a: A = A{1};
13 _ = a;
14}
15comptime {
16 var a: A = A{};
17 _ = a;
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :4:17: error: expected 8 vector elements; found 1
25// :8:17: error: expected 8 vector elements; found 0
26// :12:17: error: expected 8 array elements; found 1
27// :16:17: error: expected 8 array elements; found 0
test/cases/compile_errors/dereferencing_invalid_payload_ptr_at_comptime.zig created+33
...@@ -0,0 +1,33 @@
1const std = @import("std");
2
3comptime {
4 var val: u8 = 15;
5 var opt_ptr: ?*const u8 = &val;
6
7 const payload_ptr = &opt_ptr.?;
8 opt_ptr = null;
9 _ = payload_ptr.*.*;
10}
11comptime {
12 var opt: ?u8 = 15;
13
14 const payload_ptr = &opt.?;
15 opt = null;
16 _ = payload_ptr.*;
17}
18comptime {
19 var val: u8 = 15;
20 var err_union: anyerror!u8 = val;
21
22 const payload_ptr = &(err_union catch unreachable);
23 err_union = error.Foo;
24 _ = payload_ptr.*;
25}
26
27// error
28// backend=stage2
29// target=native
30//
31// :9:20: error: attempt to use null value
32// :16:20: error: attempt to use null value
33// :24:20: error: attempt to unwrap error: Foo