From 47de73980e9dbbf85fee754b579273a9b7b57c53 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 14:12:59 +0300 Subject: [PATCH 1/7] Sema: use `resolveMaybeUndefValIntable` in `zirIntToPtr` --- src/Sema.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ce14b2ae45..18a09b826b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1663,11 +1663,15 @@ fn resolveMaybeUndefValIntable( inst: Air.Inst.Ref, ) CompileError!?Value { const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null; - switch (val.tag()) { - .variable, .decl_ref, .decl_ref_mut => return null, + var check = val; + while (true) switch (check.tag()) { + .variable, .decl_ref, .decl_ref_mut, .comptime_field_ptr => return null, + .field_ptr => check = check.castTag(.field_ptr).?.data.container_ptr, + .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr, + .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr, .generic_poison => return error.GenericPoison, else => return val, - } + }; } /// Returns all Value tags including `variable` and `undef`. @@ -7893,7 +7897,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (!ptr_ty.isPtrAtRuntime()) { return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}); } - if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { + if (try sema.resolveMaybeUndefValIntable(block, ptr_src, ptr)) |ptr_val| { return sema.addConstant(Type.usize, ptr_val); } try sema.requireRuntimeBlock(block, ptr_src, ptr_src); From 8a488fcdb8982201bca91ce2f0c6316d3e471186 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 14:45:44 +0300 Subject: [PATCH 2/7] Sema: validate empty array init --- src/Sema.zig | 26 +++++++++++++----- .../array_init_invalid_elem_count.zig | 27 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 test/cases/compile_errors/array_init_invalid_elem_count.zig diff --git a/src/Sema.zig b/src/Sema.zig index 18a09b826b..a5b1a18eaa 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3875,9 +3875,15 @@ fn zirValidateArrayInit( const array_len = array_ty.arrayLen(); if (instrs.len != array_len) { - return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ - array_len, instrs.len, - }); + if (array_ty.zigTypeTag() == .Array) { + return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ + array_len, instrs.len, + }); + } else { + return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ + array_len, instrs.len, + }); + } } if ((is_comptime or block.is_comptime) and @@ -14265,7 +14271,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE switch (obj_ty.zigTypeTag()) { .Struct => return sema.structInitEmpty(block, obj_ty, src, src), - .Array => return arrayInitEmpty(sema, obj_ty), + .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty), .Void => return sema.addConstant(obj_ty, Value.void), else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), } @@ -14290,7 +14296,15 @@ fn structInitEmpty( return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false); } -fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref { +fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { + const arr_len = obj_ty.arrayLen(); + if (arr_len != 0) { + if (obj_ty.zigTypeTag() == .Array) { + return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); + } else { + return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); + } + } if (obj_ty.sentinel()) |sentinel| { const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel); return sema.addConstant(obj_ty, val); @@ -20978,7 +20992,7 @@ fn coerceExtra( .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), .Struct => { if (inst == .empty_struct) { - return arrayInitEmpty(sema, dest_ty); + return sema.arrayInitEmpty(block, inst_src, dest_ty); } if (inst_ty.isTuple()) { return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); diff --git a/test/cases/compile_errors/array_init_invalid_elem_count.zig b/test/cases/compile_errors/array_init_invalid_elem_count.zig new file mode 100644 index 0000000000..d8df149d18 --- /dev/null +++ b/test/cases/compile_errors/array_init_invalid_elem_count.zig @@ -0,0 +1,27 @@ +const V = @Vector(8, u8); +const A = [8]u8; +comptime { + var v: V = V{1}; + _ = v; +} +comptime { + var v: V = V{}; + _ = v; +} +comptime { + var a: A = A{1}; + _ = a; +} +comptime { + var a: A = A{}; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :4:17: error: expected 8 vector elements; found 1 +// :8:17: error: expected 8 vector elements; found 0 +// :12:17: error: expected 8 array elements; found 1 +// :16:17: error: expected 8 array elements; found 0 From 393d59bb721c2c8f0597d2b496e3721a809ae9fc Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 15:13:23 +0300 Subject: [PATCH 3/7] Sema: allow C pointers in fieldCallBind --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index a5b1a18eaa..5889e3ecdb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19466,7 +19466,7 @@ fn fieldCallBind( const raw_ptr_src = src; // TODO better source location const raw_ptr_ty = sema.typeOf(raw_ptr); - const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One) + const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and (raw_ptr_ty.ptrSize() == .One or raw_ptr_ty.ptrSize() == .C)) raw_ptr_ty.childType() else return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)}); From c619371ec1ae51d083703e187cae2d5b98c28741 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 17:01:57 +0300 Subject: [PATCH 4/7] Sema: fix loading and storing of optional pointers represented as pointers --- src/Sema.zig | 6 ++++-- test/behavior/eval.zig | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5889e3ecdb..e534a86aee 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22625,7 +22625,9 @@ fn beginComptimePtrMutation( } }, .opt_payload_ptr => { - const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; + const opt_ptr = if (ptr_val.castTag(.opt_payload_ptr)) |some| some.data else { + return sema.beginComptimePtrMutation(block, src, ptr_val, try ptr_elem_ty.optionalChildAlloc(sema.arena)); + }; var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty); switch (parent.pointee) { .direct => |val_ptr| { @@ -22941,7 +22943,7 @@ fn beginComptimePtrLoad( if (coerce_in_mem_ok) { const payload_val = switch (ptr_val.tag()) { .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data, - .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data, + .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else tv.val, else => unreachable, }; tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 6c86185afb..3286b812bf 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1272,3 +1272,13 @@ test "continue nested in a conditional in an inline for" { } try expect(x == 0); } + +test "optional pointer represented as a pointer value" { + comptime { + var val: u8 = 15; + const opt_ptr: ?*u8 = &val; + + const payload_ptr = &opt_ptr.?; + try expect(payload_ptr.*.* == 15); + } +} From 1f748fe42662da3ee2c977ba638a714e15acb433 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 19:48:58 +0300 Subject: [PATCH 5/7] Sema: fix mutation of optional ptr represented as regular ptr --- src/Sema.zig | 6 +++++- test/behavior/eval.zig | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index e534a86aee..76b5dcb8c0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22660,7 +22660,11 @@ fn beginComptimePtrMutation( .ty = payload_ty, }, - else => unreachable, + else => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = val_ptr }, + .ty = payload_ty, + }, } }, .bad_decl_ty, .bad_ptr_ty => return parent, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 3286b812bf..d2a75e18df 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1282,3 +1282,14 @@ test "optional pointer represented as a pointer value" { try expect(payload_ptr.*.* == 15); } } + +test "mutate through pointer-like optional at comptime" { + comptime { + var val: u8 = 15; + var opt_ptr: ?*const u8 = &val; + + const payload_ptr = &opt_ptr.?; + payload_ptr.* = &@as(u8, 16); + try expect(payload_ptr.*.* == 16); + } +} From 03b1fbe50d302cdb961661c10bb51699b4dcbaf2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 21:07:55 +0300 Subject: [PATCH 6/7] stage2: fix airIsErr when `is_ptr == true` --- src/codegen/c.zig | 5 +++-- src/codegen/llvm.zig | 3 ++- test/behavior/error.zig | 12 ++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 15eb917fda..1e629d7cb0 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3618,8 +3618,9 @@ fn airIsErr( const operand = try f.resolveInst(un_op); const operand_ty = f.air.typeOf(un_op); const local = try f.allocLocal(Type.initTag(.bool), .Const); - const payload_ty = operand_ty.errorUnionPayload(); - const error_ty = operand_ty.errorUnionSet(); + const err_union_ty = if (is_ptr) operand_ty.childType() else operand_ty; + const payload_ty = err_union_ty.errorUnionPayload(); + const error_ty = err_union_ty.errorUnionSet(); try writer.writeAll(" = "); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9dc20755eb..af610c8601 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5693,7 +5693,8 @@ pub const FuncGen = struct { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); - const err_union_ty = self.air.typeOf(un_op); + const operand_ty = self.air.typeOf(un_op); + const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; const payload_ty = err_union_ty.errorUnionPayload(); const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror)); const zero = err_set_ty.constNull(); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 0e3767a4ca..306dad5d9e 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -724,3 +724,15 @@ test "simple else prong allowed even when all errors handled" { }; try expect(value == 255); } + +test { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var err_union: anyerror!u8 = 15; + + const payload_ptr = &(err_union catch unreachable); + try expect(payload_ptr.* == 15); +} From 423a19fa60d2b0a11fc2fb43b12001320c2c53c5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Jul 2022 21:10:50 +0300 Subject: [PATCH 7/7] Sema: add error for dereferencing invalid payload ptr at comptime --- src/Sema.zig | 12 +++++-- ...encing_invalid_payload_ptr_at_comptime.zig | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/dereferencing_invalid_payload_ptr_at_comptime.zig diff --git a/src/Sema.zig b/src/Sema.zig index 76b5dcb8c0..8023e60c7d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22946,8 +22946,13 @@ fn beginComptimePtrLoad( (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok; if (coerce_in_mem_ok) { const payload_val = switch (ptr_val.tag()) { - .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data, - .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else tv.val, + .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else { + return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name}); + }, + .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: { + if (tv.val.isNull()) return sema.fail(block, src, "attempt to use null value", .{}); + break :opt tv.val; + }, else => unreachable, }; tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; @@ -22957,6 +22962,9 @@ fn beginComptimePtrLoad( deref.pointee = null; break :blk deref; }, + .null_value => { + return sema.fail(block, src, "attempt to use null value", .{}); + }, .zero, .one, diff --git a/test/cases/compile_errors/dereferencing_invalid_payload_ptr_at_comptime.zig b/test/cases/compile_errors/dereferencing_invalid_payload_ptr_at_comptime.zig new file mode 100644 index 0000000000..69457965bb --- /dev/null +++ b/test/cases/compile_errors/dereferencing_invalid_payload_ptr_at_comptime.zig @@ -0,0 +1,33 @@ +const std = @import("std"); + +comptime { + var val: u8 = 15; + var opt_ptr: ?*const u8 = &val; + + const payload_ptr = &opt_ptr.?; + opt_ptr = null; + _ = payload_ptr.*.*; +} +comptime { + var opt: ?u8 = 15; + + const payload_ptr = &opt.?; + opt = null; + _ = payload_ptr.*; +} +comptime { + var val: u8 = 15; + var err_union: anyerror!u8 = val; + + const payload_ptr = &(err_union catch unreachable); + err_union = error.Foo; + _ = payload_ptr.*; +} + +// error +// backend=stage2 +// target=native +// +// :9:20: error: attempt to use null value +// :16:20: error: attempt to use null value +// :24:20: error: attempt to unwrap error: Foo