From e584558bd8533ce91d1683cdbc6b77d7bb652acf Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 12:38:31 +0300 Subject: [PATCH 1/6] Sema: do not use coerceCompatiblePtr for alignCast Closes #12902 --- src/Sema.zig | 25 ++++++++----------------- test/behavior/align.zig | 8 ++++++++ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0a9be797a4..4b61266558 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17931,23 +17931,14 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const ptr = try sema.resolveInst(extra.rhs); const ptr_ty = sema.typeOf(ptr); - // TODO in addition to pointers, this instruction is supposed to work for - // pointer-like optionals and slices. try sema.checkPtrOperand(block, ptr_src, ptr_ty); - // TODO compile error if the result pointer is comptime known and would have an - // alignment that disagrees with the Decl's alignment. - - const ptr_info = ptr_ty.ptrInfo().data; - const dest_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = ptr_info.pointee_type, - .@"align" = dest_align, - .@"addrspace" = ptr_info.@"addrspace", - .mutable = ptr_info.mutable, - .@"allowzero" = ptr_info.@"allowzero", - .@"volatile" = ptr_info.@"volatile", - .size = ptr_info.size, - }); + var ptr_info = ptr_ty.ptrInfo().data; + ptr_info.@"align" = dest_align; + var dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); + if (ptr_ty.zigTypeTag() == .Optional) { + dest_ty = try Type.Tag.optional.create(sema.arena, dest_ty); + } if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |val| { if (try val.getUnsignedIntAdvanced(sema.mod.getTarget(), null)) |addr| { @@ -17960,7 +17951,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); if (block.wantSafety() and dest_align > 1 and - try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2())) + try sema.typeHasRuntimeBits(block, sema.src, ptr_info.pointee_type)) { const val_payload = try sema.arena.create(Value.Payload.U64); val_payload.* = .{ @@ -17985,7 +17976,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else is_aligned; try sema.addSafetyCheck(block, ok, .incorrect_alignment); } - return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src); + return sema.bitCast(block, dest_ty, ptr, ptr_src); } fn zirBitCount( diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ad857fb9c2..51481580cf 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -556,3 +556,11 @@ test "comptime alloc alignment" { var bytes2_addr = @ptrToInt(&bytes2); try expect(bytes2_addr & 0xff == 0); } + +test "@alignCast null" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var ptr: ?*anyopaque = null; + const aligned: ?*anyopaque = @alignCast(@alignOf(?*anyopaque), ptr); + try expect(aligned == null); +} From fb91483e48fe6cfa21edc613f266e27bd6bf9dbf Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 13:11:19 +0300 Subject: [PATCH 2/6] Sema: do not use coerceCompatiblePtr for ptrCast --- src/Sema.zig | 61 ++++++++++++++++--- .../increase_pointer_alignment_in_ptrCast.zig | 8 +-- .../ptrCast_discards_const_qualifier.zig | 4 +- 3 files changed, 57 insertions(+), 16 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/increase_pointer_alignment_in_ptrCast.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/ptrCast_discards_const_qualifier.zig (63%) diff --git a/src/Sema.zig b/src/Sema.zig index 4b61266558..748b1de78f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17772,6 +17772,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; @@ -17783,6 +17784,15 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.checkPtrType(block, dest_ty_src, dest_ty); try sema.checkPtrOperand(block, operand_src, operand_ty); + const operand_info = operand_ty.ptrInfo().data; + const dest_info = dest_ty.ptrInfo().data; + if (!operand_info.mutable and dest_info.mutable) { + return sema.fail(block, src, "cast discards const qualifier", .{}); + } + if (operand_info.@"volatile" and !dest_info.@"volatile") { + return sema.fail(block, src, "cast discards volatile qualifier", .{}); + } + const dest_is_slice = dest_ty.isSlice(); const operand_is_slice = operand_ty.isSlice(); if (dest_is_slice and !operand_is_slice) { @@ -17793,15 +17803,6 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else operand; - if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { - if (!dest_ty.ptrAllowsZero() and operand_val.isUndef()) { - return sema.failWithUseOfUndef(block, operand_src); - } - if (!dest_ty.ptrAllowsZero() and operand_val.isNull()) { - return sema.fail(block, operand_src, "null pointer casted to type {}", .{dest_ty.fmt(sema.mod)}); - } - } - const dest_elem_ty = dest_ty.elemType2(); try sema.resolveTypeLayout(block, dest_ty_src, dest_elem_ty); const dest_align = dest_ty.ptrAlignment(target); @@ -17835,7 +17836,47 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } } - return sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src); + if (dest_align > operand_align) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ + operand_ty.fmt(sema.mod), operand_align, + }); + try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ + dest_ty.fmt(sema.mod), dest_align, + }); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { + if (!dest_ty.ptrAllowsZero() and operand_val.isUndef()) { + return sema.failWithUseOfUndef(block, operand_src); + } + if (!dest_ty.ptrAllowsZero() and operand_val.isNull()) { + return sema.fail(block, operand_src, "null pointer casted to type {}", .{dest_ty.fmt(sema.mod)}); + } + return sema.addConstant(aligned_dest_ty, operand_val); + } + + try sema.requireRuntimeBlock(block, src, null); + if (block.wantSafety() and operand_ty.ptrAllowsZero() and !dest_ty.ptrAllowsZero() and + try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2())) + { + const ptr_int = try block.addUnOp(.ptrtoint, ptr); + const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); + const ok = if (operand_is_slice) ok: { + const len = try sema.analyzeSliceLen(block, operand_src, operand); + const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); + break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); + } else is_non_zero; + try sema.addSafetyCheck(block, ok, .cast_to_null); + } + + return block.addBitCast(aligned_dest_ty, ptr); } fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/test/cases/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig b/test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig rename to test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig index f3f67ca4c6..9cc5ed3a42 100644 --- a/test/cases/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig +++ b/test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig @@ -5,9 +5,9 @@ export fn entry() u32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:17: error: cast increases pointer alignment -// tmp.zig:3:38: note: '*u8' has alignment 1 -// tmp.zig:3:26: note: '*u32' has alignment 4 +// :3:17: error: cast increases pointer alignment +// :3:32: note: '*u8' has alignment '1' +// :3:26: note: '*u32' has alignment '4' diff --git a/test/cases/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig b/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig rename to test/cases/compile_errors/ptrCast_discards_const_qualifier.zig index 1a90ff73f8..a2fea4ff11 100644 --- a/test/cases/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig +++ b/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:15: error: cast discards const qualifier +// :3:15: error: cast discards const qualifier From 541b3e3a31946475f29d21e7a742bf80c5952791 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 14:36:41 +0300 Subject: [PATCH 3/6] Sema: check pointer qualifiers before implicit cast Closes #12881 --- lib/std/json.zig | 6 +- lib/std/mem.zig | 23 +++--- lib/std/meta.zig | 4 +- lib/std/os/linux.zig | 2 +- lib/std/zig/c_translation.zig | 2 +- src/Sema.zig | 81 +++++++++++++++---- test/behavior/bugs/4328.zig | 2 +- test/behavior/cast.zig | 4 +- test/behavior/slice.zig | 3 +- test/behavior/struct.zig | 2 +- test/behavior/type.zig | 4 +- test/behavior/type_info.zig | 6 +- ...d_implicit_cast_from_const_T_to_sliceT.zig | 1 + ..._and_Zig_pointer-bad_const-align-child.zig | 47 +++++++++++ ...icit_cast_const_array_to_mutable_slice.zig | 26 ++++++ .../implicitly_increasing_slice_alignment.zig | 7 +- ...d_by_typeInfo_and_passed_into_function.zig | 6 +- ...es_from_comptime_reinterpreted_pointer.zig | 2 +- ...ast_const_pointer_to_mutable_C_pointer.zig | 11 +++ ..._when_coercing_pointer_to_anon_literal.zig | 24 ++++++ ...ading_past_end_of_pointer_casted_array.zig | 2 +- ...ce_cannot_have_its_bytes_reinterpreted.zig | 2 +- ..._and_Zig_pointer-bad_const-align-child.zig | 42 ---------- ...icit_cast_const_array_to_mutable_slice.zig | 12 --- ...ast_const_pointer_to_mutable_C_pointer.zig | 11 --- ..._when_coercing_pointer_to_anon_literal.zig | 24 ------ 26 files changed, 212 insertions(+), 144 deletions(-) create mode 100644 test/cases/compile_errors/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig create mode 100644 test/cases/compile_errors/implicit_cast_const_array_to_mutable_slice.zig rename test/cases/compile_errors/{stage1/obj => }/implicitly_increasing_slice_alignment.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig (63%) create mode 100644 test/cases/compile_errors/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig create mode 100644 test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig delete mode 100644 test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig delete mode 100644 test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig delete mode 100644 test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig delete mode 100644 test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig diff --git a/lib/std/json.zig b/lib/std/json.zig index 548a3db94e..87fe1c9dea 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1560,7 +1560,7 @@ fn parseInternal( } } if (field.is_comptime) { - if (!try parsesTo(field.field_type, @ptrCast(*const field.field_type, field.default_value.?).*, tokens, child_options)) { + if (!try parsesTo(field.field_type, @ptrCast(*align(1) const field.field_type, field.default_value.?).*, tokens, child_options)) { return error.UnexpectedValue; } } else { @@ -1587,7 +1587,7 @@ fn parseInternal( if (!fields_seen[i]) { if (field.default_value) |default_ptr| { if (!field.is_comptime) { - const default = @ptrCast(*const field.field_type, default_ptr).*; + const default = @ptrCast(*align(1) const field.field_type, default_ptr).*; @field(r, field.name) = default; } } else { @@ -1667,7 +1667,7 @@ fn parseInternal( } if (ptrInfo.sentinel) |some| { - const sentinel_value = @ptrCast(*const ptrInfo.child, some).*; + const sentinel_value = @ptrCast(*align(1) const ptrInfo.child, some).*; try arraylist.append(sentinel_value); const output = arraylist.toOwnedSlice(); return output[0 .. output.len - 1 :sentinel_value]; diff --git a/lib/std/mem.zig b/lib/std/mem.zig index bf841bff90..4000030fc0 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -297,7 +297,7 @@ pub fn zeroes(comptime T: type) T { }, .Array => |info| { if (info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*; return [_:sentinel]info.child{zeroes(info.child)} ** info.len; } return [_]info.child{zeroes(info.child)} ** info.len; @@ -443,7 +443,7 @@ pub fn zeroInit(comptime T: type, init: anytype) T { inline for (struct_info.fields) |field| { if (field.default_value) |default_value_ptr| { - const default_value = @ptrCast(*const field.field_type, default_value_ptr).*; + const default_value = @ptrCast(*align(1) const field.field_type, default_value_ptr).*; @field(value, field.name) = default_value; } } @@ -687,7 +687,7 @@ pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { const l = len(ptr); const ptr_info = @typeInfo(Result).Pointer; if (ptr_info.sentinel) |s_ptr| { - const s = @ptrCast(*const ptr_info.child, s_ptr).*; + const s = @ptrCast(*align(1) const ptr_info.child, s_ptr).*; return ptr[0..l :s]; } else { return ptr[0..l]; @@ -719,7 +719,7 @@ fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { // to find the value searched for, which is only the case if it matches // the sentinel of the type passed. if (array_info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const array_info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*; if (end == sentinel) { new_ptr_info.sentinel = &end; } else { @@ -734,7 +734,7 @@ fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { // to find the value searched for, which is only the case if it matches // the sentinel of the type passed. if (ptr_info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const ptr_info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*; if (end == sentinel) { new_ptr_info.sentinel = &end; } else { @@ -772,7 +772,7 @@ pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@Typ const length = lenSliceTo(ptr, end); const ptr_info = @typeInfo(Result).Pointer; if (ptr_info.sentinel) |s_ptr| { - const s = @ptrCast(*const ptr_info.child, s_ptr).*; + const s = @ptrCast(*align(1) const ptr_info.child, s_ptr).*; return ptr[0..length :s]; } else { return ptr[0..length]; @@ -825,7 +825,7 @@ fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { .One => switch (@typeInfo(ptr_info.child)) { .Array => |array_info| { if (array_info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const array_info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*; if (sentinel == end) { return indexOfSentinel(array_info.child, end, ptr); } @@ -835,7 +835,7 @@ fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { else => {}, }, .Many => if (ptr_info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const ptr_info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*; // We may be looking for something other than the sentinel, // but iterating past the sentinel would be a bug so we need // to check for both. @@ -849,7 +849,7 @@ fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { }, .Slice => { if (ptr_info.sentinel) |sentinel_ptr| { - const sentinel = @ptrCast(*const ptr_info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*; if (sentinel == end) { return indexOfSentinel(ptr_info.child, sentinel, ptr); } @@ -911,7 +911,7 @@ pub fn len(value: anytype) usize { .Many => { const sentinel_ptr = info.sentinel orelse @compileError("length of pointer with no sentinel"); - const sentinel = @ptrCast(*const info.child, sentinel_ptr).*; + const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*; return indexOfSentinel(info.child, sentinel, value); }, .C => { @@ -2882,7 +2882,8 @@ fn AsBytesReturnType(comptime P: type) type { /// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes. pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { const P = @TypeOf(ptr); - return @ptrCast(AsBytesReturnType(P), ptr); + const T = AsBytesReturnType(P); + return @ptrCast(T, @alignCast(meta.alignment(T), ptr)); } test "asBytes" { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7e64eff49f..993d88eb04 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -204,12 +204,12 @@ pub fn sentinel(comptime T: type) ?Elem(T) { switch (info.size) { .Many, .Slice => { const sentinel_ptr = info.sentinel orelse return null; - return @ptrCast(*const info.child, sentinel_ptr).*; + return @ptrCast(*align(1) const info.child, sentinel_ptr).*; }, .One => switch (@typeInfo(info.child)) { .Array => |array_info| { const sentinel_ptr = array_info.sentinel orelse return null; - return @ptrCast(*const array_info.child, sentinel_ptr).*; + return @ptrCast(*align(1) const array_info.child, sentinel_ptr).*; }, else => {}, }, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index c4dafcd3b8..892cb1e8d3 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -888,7 +888,7 @@ else const vdso_clock_gettime_ty = if (builtin.zig_backend == .stage1) fn (i32, *timespec) callconv(.C) usize else - *const fn (i32, *timespec) callconv(.C) usize; + *align(1) const fn (i32, *timespec) callconv(.C) usize; pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (@hasDecl(VDSO, "CGT_SYM")) { diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 6847a92eae..9fd89ef8fb 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -193,7 +193,7 @@ pub fn sizeof(target: anytype) usize { const array_info = @typeInfo(ptr.child).Array; if ((array_info.child == u8 or array_info.child == u16) and array_info.sentinel != null and - @ptrCast(*const array_info.child, array_info.sentinel.?).* == 0) + @ptrCast(*align(1) const array_info.child, array_info.sentinel.?).* == 0) { // length of the string plus one for the null terminator. return (array_info.len + 1) * @sizeOf(array_info.child); diff --git a/src/Sema.zig b/src/Sema.zig index 748b1de78f..4402a28100 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22811,6 +22811,7 @@ fn coerceExtra( if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and inst_ty.isPtrLikeOptional() and inst_ty.elemType2().zigTypeTag() != .Pointer) { + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); } @@ -22843,14 +22844,12 @@ fn coerceExtra( single_item: { if (dest_info.size != .One) break :single_item; if (!inst_ty.isSinglePointer()) break :single_item; + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; const ptr_elem_ty = inst_ty.childType(); const array_ty = dest_info.pointee_type; if (array_ty.zigTypeTag() != .Array) break :single_item; const array_elem_ty = array_ty.childType(); const dest_is_mut = dest_info.mutable; - if (inst_ty.isConstPtr() and dest_is_mut) break :single_item; - if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item; - if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item; switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { .ok => {}, else => break :single_item, @@ -22861,14 +22860,11 @@ fn coerceExtra( // Coercions where the source is a single pointer to an array. src_array_ptr: { if (!inst_ty.isSinglePointer()) break :src_array_ptr; + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; const array_ty = inst_ty.childType(); if (array_ty.zigTypeTag() != .Array) break :src_array_ptr; - const len0 = array_ty.arrayLen() == 0; const array_elem_type = array_ty.childType(); const dest_is_mut = dest_info.mutable; - if (inst_ty.isConstPtr() and dest_is_mut and !len0) break :src_array_ptr; - if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr; - if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :src_array_ptr; const dst_elem_type = dest_info.pointee_type; switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src)) { @@ -22905,6 +22901,7 @@ fn coerceExtra( // coercion from C pointer if (inst_ty.isCPtr()) src_c_ptr: { + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; // In this case we must add a safety check because the C pointer // could be null. const src_elem_ty = inst_ty.childType(); @@ -22920,7 +22917,7 @@ fn coerceExtra( // cast from *T and [*]T to *anyopaque // but don't do it if the source type is a double pointer if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer and - inst_ty.childType().zigTypeTag() != .Pointer) + inst_ty.childType().zigTypeTag() != .Pointer and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); } @@ -22954,6 +22951,7 @@ fn coerceExtra( return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); }, .Pointer => p: { + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; const inst_info = inst_ty.ptrInfo().data; switch (try sema.coerceInMemoryAllowed( block, @@ -22984,7 +22982,7 @@ fn coerceExtra( // pointer to anonymous struct to pointer to union if (inst_ty.isSinglePointer() and inst_ty.childType().isAnonStruct() and - !dest_info.mutable) + sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); } @@ -22993,7 +22991,7 @@ fn coerceExtra( // pointer to anonymous struct to pointer to struct if (inst_ty.isSinglePointer() and inst_ty.childType().isAnonStruct() and - !dest_info.mutable) + sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src); } @@ -23002,7 +23000,7 @@ fn coerceExtra( // pointer to tuple to pointer to array if (inst_ty.isSinglePointer() and inst_ty.childType().isTuple() and - !dest_info.mutable) + sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); } @@ -23011,10 +23009,8 @@ fn coerceExtra( }, .Slice => { // pointer to tuple to slice - if (inst_ty.isSinglePointer() and - inst_ty.childType().isTuple() and - (!dest_info.mutable or inst_ty.ptrIsMutable() or inst_ty.childType().tupleFields().types.len == 0) and - dest_info.size == .Slice) + if (inst_ty.isSinglePointer() and inst_ty.childType().isTuple() and dest_info.size == .Slice and + sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); } @@ -23043,6 +23039,7 @@ fn coerceExtra( }, .Many => p: { if (!inst_ty.isSlice()) break :p; + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; const inst_info = inst_ty.ptrInfo().data; switch (try sema.coerceInMemoryAllowed( @@ -25438,6 +25435,57 @@ fn coerceArrayPtrToSlice( return block.addTyOp(.array_to_slice, dest_ty, inst); } +fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { + const dest_info = dest_ty.ptrInfo().data; + const inst_info = inst_ty.ptrInfo().data; + const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or + (inst_info.pointee_type.arrayLen() == 0 and dest_info.sentinel == null and dest_info.size != .C and dest_info.size != .Many))) or + (inst_info.pointee_type.isTuple() and inst_info.pointee_type.tupleFields().types.len == 0); + + const ok_cv_qualifiers = + ((inst_info.mutable or !dest_info.mutable) or len0) and + (!inst_info.@"volatile" or dest_info.@"volatile"); + + if (!ok_cv_qualifiers) { + in_memory_result.* = .{ .ptr_qualifiers = .{ + .actual_const = !inst_info.mutable, + .wanted_const = !dest_info.mutable, + .actual_volatile = inst_info.@"volatile", + .wanted_volatile = dest_info.@"volatile", + } }; + return false; + } + if (dest_info.@"addrspace" != inst_info.@"addrspace") { + in_memory_result.* = .{ .ptr_addrspace = .{ + .actual = inst_info.@"addrspace", + .wanted = dest_info.@"addrspace", + } }; + return false; + } + if (inst_info.@"align" == 0 and dest_info.@"align" == 0) return true; + if (len0) return true; + const target = sema.mod.getTarget(); + + const inst_align = if (inst_info.@"align" != 0) + inst_info.@"align" + else + inst_info.pointee_type.abiAlignment(target); + + const dest_align = if (dest_info.@"align" != 0) + dest_info.@"align" + else + dest_info.pointee_type.abiAlignment(target); + + if (dest_align > inst_align) { + in_memory_result.* = .{ .ptr_alignment = .{ + .actual = inst_align, + .wanted = dest_align, + } }; + return false; + } + return true; +} + fn coerceCompatiblePtrs( sema: *Sema, block: *Block, @@ -25445,13 +25493,12 @@ fn coerceCompatiblePtrs( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - // TODO check const/volatile/alignment + const inst_ty = sema.typeOf(inst); if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { // The comptime Value representation is compatible with both types. return sema.addConstant(dest_ty, val); } try sema.requireRuntimeBlock(block, inst_src, null); - const inst_ty = sema.typeOf(inst); const inst_allows_zero = (inst_ty.zigTypeTag() == .Pointer and inst_ty.ptrAllowsZero()) or true; if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2())) diff --git a/test/behavior/bugs/4328.zig b/test/behavior/bugs/4328.zig index 3158cb2c0d..5cacd29044 100644 --- a/test/behavior/bugs/4328.zig +++ b/test/behavior/bugs/4328.zig @@ -13,7 +13,7 @@ extern fn fopen([*c]const u8, [*c]const u8) [*c]FILE; const S = extern struct { state: c_short, - extern fn s_do_thing([*c]S, b: c_int) c_short; + extern fn s_do_thing([*c]const S, b: c_int) c_short; }; test "Extern function calls in @TypeOf" { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index dac3c12b0d..9a02e74853 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1242,13 +1242,13 @@ test "implicit cast *[0]T to E![]const u8" { var global_array: [4]u8 = undefined; test "cast from array reference to fn: comptime fn ptr" { - const f = @ptrCast(*const fn () callconv(.C) void, &global_array); + const f = @ptrCast(*align(1) const fn () callconv(.C) void, &global_array); try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } test "cast from array reference to fn: runtime fn ptr" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var f = @ptrCast(*const fn () callconv(.C) void, &global_array); + var f = @ptrCast(*align(1) const fn () callconv(.C) void, &global_array); try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index b9bae08878..e5cb8ea408 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -322,11 +322,12 @@ test "empty array to slice" { test "@ptrCast slice to pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { fn doTheTest() !void { var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; - var slice: []u8 = &array; + var slice: []align(@alignOf(u16)) u8 = &array; var ptr = @ptrCast(*u16, slice); try expect(ptr.* == 65535); } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index e0be677b3c..7a650e1a33 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -521,7 +521,7 @@ test "packed struct fields are ordered from LSB to MSB" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var all: u64 = 0x7765443322221111; - var bytes: [8]u8 = undefined; + var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined; @memcpy(&bytes, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, &bytes).*; diff --git a/test/behavior/type.zig b/test/behavior/type.zig index f59ac7cb27..4b70f502c7 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -293,7 +293,7 @@ test "Type.Struct" { try testing.expectEqual(@as(?*const anyopaque, null), infoB.fields[0].default_value); try testing.expectEqualSlices(u8, "y", infoB.fields[1].name); try testing.expectEqual(u32, infoB.fields[1].field_type); - try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoB.fields[1].default_value.?).*); + try testing.expectEqual(@as(u32, 5), @ptrCast(*align(1) const u32, infoB.fields[1].default_value.?).*); try testing.expectEqual(@as(usize, 0), infoB.decls.len); try testing.expectEqual(@as(bool, false), infoB.is_tuple); @@ -305,7 +305,7 @@ test "Type.Struct" { try testing.expectEqual(@as(u8, 3), @ptrCast(*const u8, infoC.fields[0].default_value.?).*); try testing.expectEqualSlices(u8, "y", infoC.fields[1].name); try testing.expectEqual(u32, infoC.fields[1].field_type); - try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*); + try testing.expectEqual(@as(u32, 5), @ptrCast(*align(1) const u32, infoC.fields[1].default_value.?).*); try testing.expectEqual(@as(usize, 0), infoC.decls.len); try testing.expectEqual(@as(bool, false), infoC.is_tuple); diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 76d67c8aec..b85d39d7bb 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -295,8 +295,8 @@ fn testStruct() !void { try expect(unpacked_struct_info.Struct.is_tuple == false); try expect(unpacked_struct_info.Struct.backing_integer == null); try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); - try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4); - try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*)); + try expect(@ptrCast(*align(1) const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4); + try expect(mem.eql(u8, "foobar", @ptrCast(*align(1) const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*)); } const TestStruct = struct { @@ -321,7 +321,7 @@ fn testPackedStruct() !void { try expect(struct_info.Struct.fields[0].alignment == 0); try expect(struct_info.Struct.fields[2].field_type == f32); try expect(struct_info.Struct.fields[2].default_value == null); - try expect(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4); + try expect(@ptrCast(*align(1) const u32, struct_info.Struct.fields[3].default_value.?).* == 4); try expect(struct_info.Struct.fields[3].alignment == 0); try expect(struct_info.Struct.decls.len == 2); try expect(struct_info.Struct.decls[0].is_pub); diff --git a/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig b/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig index 0634b4696f..ad3b0a7254 100644 --- a/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig +++ b/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig @@ -9,3 +9,4 @@ export fn entry() void { // target=native // // :3:22: error: expected type '[]u32', found '*const u32' +// :3:22: note: cast discards const qualifier diff --git a/test/cases/compile_errors/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig b/test/cases/compile_errors/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig new file mode 100644 index 0000000000..78261f182f --- /dev/null +++ b/test/cases/compile_errors/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig @@ -0,0 +1,47 @@ +export fn a() void { + var x: [*c]u8 = undefined; + var y: *align(4) u8 = x; + _ = y; +} +export fn b() void { + var x: [*c]const u8 = undefined; + var y: *u8 = x; + _ = y; +} +export fn c() void { + var x: [*c]u8 = undefined; + var y: *u32 = x; + _ = y; +} +export fn d() void { + var y: *align(1) u32 = undefined; + var x: [*c]u32 = y; + _ = x; +} +export fn e() void { + var y: *const u8 = undefined; + var x: [*c]u8 = y; + _ = x; +} +export fn f() void { + var y: *u8 = undefined; + var x: [*c]u32 = y; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :3:27: error: expected type '*align(4) u8', found '[*c]u8' +// :3:27: note: pointer alignment '1' cannot cast into pointer alignment '4' +// :8:18: error: expected type '*u8', found '[*c]const u8' +// :8:18: note: cast discards const qualifier +// :13:19: error: expected type '*u32', found '[*c]u8' +// :13:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32' +// :18:22: error: expected type '[*c]u32', found '*align(1) u32' +// :18:22: note: pointer alignment '1' cannot cast into pointer alignment '4' +// :23:21: error: expected type '[*c]u8', found '*const u8' +// :23:21: note: cast discards const qualifier +// :28:22: error: expected type '[*c]u32', found '*u8' +// :28:22: note: pointer type child 'u8' cannot cast into pointer type child 'u32' diff --git a/test/cases/compile_errors/implicit_cast_const_array_to_mutable_slice.zig b/test/cases/compile_errors/implicit_cast_const_array_to_mutable_slice.zig new file mode 100644 index 0000000000..2a80b9ffbc --- /dev/null +++ b/test/cases/compile_errors/implicit_cast_const_array_to_mutable_slice.zig @@ -0,0 +1,26 @@ +export fn entry() void { + const buffer: [1]u8 = [_]u8{8}; + const sliceA: []u8 = &buffer; + _ = sliceA; +} +export fn entry1() void { + const str: *const [0:0]u8 = ""; + const slice: [:0]u8 = str; + _ = slice; +} +export fn entry2() void { + const str: *const [0:0]u8 = ""; + const many: [*]u8 = str; + _ = many; +} + +// error +// backend=stage2 +// target=native +// +// :3:26: error: expected type '[]u8', found '*const [1]u8' +// :3:26: note: cast discards const qualifier +// :8:27: error: expected type '[:0]u8', found '*const [0:0]u8' +// :8:27: note: cast discards const qualifier +// :13:25: error: expected type '[*]u8', found '*const [0:0]u8' +// :13:25: note: cast discards const qualifier diff --git a/test/cases/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig b/test/cases/compile_errors/implicitly_increasing_slice_alignment.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig rename to test/cases/compile_errors/implicitly_increasing_slice_alignment.zig index 2e5c201b5d..84ec6464f4 100644 --- a/test/cases/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig +++ b/test/cases/compile_errors/implicitly_increasing_slice_alignment.zig @@ -14,9 +14,8 @@ fn bar(x: []u32) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:26: error: cast increases pointer alignment -// tmp.zig:9:26: note: '*align(1) u32' has alignment 1 -// tmp.zig:9:26: note: '*[1]u32' has alignment 4 +// :9:22: error: expected type '*[1]u32', found '*align(1) u32' +// :9:22: note: pointer alignment '1' cannot cast into pointer alignment '4' diff --git a/test/cases/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig b/test/cases/compile_errors/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig rename to test/cases/compile_errors/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig index a622ceb44e..abcccb659e 100644 --- a/test/cases/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig +++ b/test/cases/compile_errors/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig @@ -9,8 +9,8 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// :5:28: error: cannot cast pointer to array literal to slice type '[]u8' -// :5:28: note: cast discards const qualifier +// :4:22: error: expected type '[]u8', found '*const [3:0]u8' +// :4:22: note: cast discards const qualifier diff --git a/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig b/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig index a86fbc0127..baeb3e8c82 100644 --- a/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig +++ b/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig @@ -1,5 +1,5 @@ export fn entry() void { - const float: f32 = 5.99999999999994648725e-01; + const float: f32 align(@alignOf(i64)) = 5.99999999999994648725e-01; const float_ptr = &float; const int_ptr = @ptrCast(*const i64, float_ptr); const int_val = int_ptr.*; diff --git a/test/cases/compile_errors/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig b/test/cases/compile_errors/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig new file mode 100644 index 0000000000..fd01927889 --- /dev/null +++ b/test/cases/compile_errors/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig @@ -0,0 +1,11 @@ +export fn func() void { + var strValue: [*c]u8 = undefined; + strValue = strValue orelse ""; +} + +// error +// backend=stage2 +// target=native +// +// :3:32: error: expected type '[*c]u8', found '*const [0:0]u8' +// :3:32: note: cast discards const qualifier diff --git a/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig new file mode 100644 index 0000000000..c0a0b06af0 --- /dev/null +++ b/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig @@ -0,0 +1,24 @@ +comptime { + const c: [][]const u8 = &.{ "hello", "world" }; + _ = c; +} +comptime { + const c: *[2][]const u8 = &.{ "hello", "world" }; + _ = c; +} +const S = struct { a: u8 = 1, b: u32 = 2 }; +comptime { + const c: *S = &.{ .a = 2 }; + _ = c; +} + +// error +// backend=stage2 +// target=native +// +// :2:29: error: expected type '[][]const u8', found '*const tuple{comptime *const [5:0]u8 = "hello", comptime *const [5:0]u8 = "world"}' +// :2:29: note: cast discards const qualifier +// :6:31: error: expected type '*[2][]const u8', found '*const tuple{comptime *const [5:0]u8 = "hello", comptime *const [5:0]u8 = "world"}' +// :6:31: note: cast discards const qualifier +// :11:19: error: expected type '*tmp.S', found '*const struct{comptime a: comptime_int = 2}' +// :11:19: note: cast discards const qualifier diff --git a/test/cases/compile_errors/reading_past_end_of_pointer_casted_array.zig b/test/cases/compile_errors/reading_past_end_of_pointer_casted_array.zig index 906b292753..d3d9b03ff5 100644 --- a/test/cases/compile_errors/reading_past_end_of_pointer_casted_array.zig +++ b/test/cases/compile_errors/reading_past_end_of_pointer_casted_array.zig @@ -1,7 +1,7 @@ comptime { const array: [4]u8 = "aoeu".*; const sub_array = array[1..]; - const int_ptr = @ptrCast(*const u24, sub_array); + const int_ptr = @ptrCast(*const u24, @alignCast(@alignOf(u24), sub_array)); const deref = int_ptr.*; _ = deref; } diff --git a/test/cases/compile_errors/slice_cannot_have_its_bytes_reinterpreted.zig b/test/cases/compile_errors/slice_cannot_have_its_bytes_reinterpreted.zig index 2280345e15..5fab9c90a9 100644 --- a/test/cases/compile_errors/slice_cannot_have_its_bytes_reinterpreted.zig +++ b/test/cases/compile_errors/slice_cannot_have_its_bytes_reinterpreted.zig @@ -1,5 +1,5 @@ export fn foo() void { - const bytes = [1]u8{ 0xfa } ** 16; + const bytes align(@alignOf([]const u8)) = [1]u8{0xfa} ** 16; var value = @ptrCast(*const []const u8, &bytes).*; _ = value; } diff --git a/test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig deleted file mode 100644 index 6ff155b591..0000000000 --- a/test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig +++ /dev/null @@ -1,42 +0,0 @@ -export fn a() void { - var x: [*c]u8 = undefined; - var y: *align(4) u8 = x; - _ = y; -} -export fn b() void { - var x: [*c]const u8 = undefined; - var y: *u8 = x; - _ = y; -} -export fn c() void { - var x: [*c]u8 = undefined; - var y: *u32 = x; - _ = y; -} -export fn d() void { - var y: *align(1) u32 = undefined; - var x: [*c]u32 = y; - _ = x; -} -export fn e() void { - var y: *const u8 = undefined; - var x: [*c]u8 = y; - _ = x; -} -export fn f() void { - var y: *u8 = undefined; - var x: [*c]u32 = y; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:27: error: cast increases pointer alignment -// tmp.zig:8:18: error: cast discards const qualifier -// tmp.zig:13:19: error: expected type '*u32', found '[*c]u8' -// tmp.zig:13:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32' -// tmp.zig:18:22: error: cast increases pointer alignment -// tmp.zig:23:21: error: cast discards const qualifier -// tmp.zig:28:22: error: expected type '[*c]u32', found '*u8' diff --git a/test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig deleted file mode 100644 index 3f22cbed6b..0000000000 --- a/test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn entry() void { - const buffer: [1]u8 = [_]u8{8}; - const sliceA: []u8 = &buffer; - _ = sliceA; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:27: error: cannot cast pointer to array literal to slice type '[]u8' -// tmp.zig:3:27: note: cast discards const qualifier diff --git a/test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig b/test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig deleted file mode 100644 index 7d18e760c3..0000000000 --- a/test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn func() void { - var strValue: [*c]u8 = undefined; - strValue = strValue orelse ""; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8' -// tmp.zig:3:32: note: cast discards const qualifier diff --git a/test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig deleted file mode 100644 index 8456c8afcb..0000000000 --- a/test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig +++ /dev/null @@ -1,24 +0,0 @@ -comptime { - const c: [][]const u8 = &.{"hello", "world" }; - _ = c; -} -comptime { - const c: *[2][]const u8 = &.{"hello", "world" }; - _ = c; -} -const S = struct {a: u8 = 1, b: u32 = 2}; -comptime { - const c: *S = &.{}; - _ = c; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:31: error: cannot cast pointer to array literal to slice type '[][]const u8' -// tmp.zig:2:31: note: cast discards const qualifier -// tmp.zig:6:33: error: cannot cast pointer to array literal to '*[2][]const u8' -// tmp.zig:6:33: note: cast discards const qualifier -// tmp.zig:11:21: error: expected type '*S', found '*const struct:11:21' -// tmp.zig:11:21: note: cast discards const qualifier From 540130650f843f0c6aa79c2a2f29e79b33846994 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 17:06:57 +0300 Subject: [PATCH 4/6] Sema: pass calculated offset to `elemPtrType` in `elemPtrSlice` --- src/Sema.zig | 2 +- test/behavior/align.zig | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4402a28100..773b7aa66c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22692,7 +22692,7 @@ fn elemPtrSlice( break :o index; } else null; - const elem_ptr_ty = try sema.elemPtrType(slice_ty, null); + const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); if (maybe_undef_slice_val) |slice_val| { if (slice_val.isUndef()) { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 51481580cf..5270aa9692 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -564,3 +564,8 @@ test "@alignCast null" { const aligned: ?*anyopaque = @alignCast(@alignOf(?*anyopaque), ptr); try expect(aligned == null); } + +test "alignment of slice element" { + const a: []align(1024) const u8 = undefined; + try expect(@TypeOf(&a[0]) == *align(1024) const u8); +} From 3e80aa9079cfad8c95f506b1eb644daeed6cbb7b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 17:50:58 +0300 Subject: [PATCH 5/6] Sema: validate `@alignOf` type --- src/Sema.zig | 3 +++ test/cases/compile_errors/alignOf_bad_type.zig | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/cases/compile_errors/alignOf_bad_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index 773b7aa66c..2b8705339c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16492,6 +16492,9 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); + if (ty.isNoReturn()) { + return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)}); + } const target = sema.mod.getTarget(); const val = try ty.lazyAbiAlignment(target, sema.arena); if (val.tag() == .lazy_align) { diff --git a/test/cases/compile_errors/alignOf_bad_type.zig b/test/cases/compile_errors/alignOf_bad_type.zig new file mode 100644 index 0000000000..5408c21314 --- /dev/null +++ b/test/cases/compile_errors/alignOf_bad_type.zig @@ -0,0 +1,9 @@ +export fn entry() usize { + return @alignOf(noreturn); +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: no align available for type 'noreturn' From eef653904916dc19540458199366807f8837bf98 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 19 Sep 2022 18:21:26 +0300 Subject: [PATCH 6/6] Sema: handle `Value.slice` in `beginComptimePtrLoad` --- src/Sema.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 2b8705339c..171b349758 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25190,6 +25190,11 @@ fn beginComptimePtrLoad( break :blk deref; }, + .slice => blk: { + const slice = ptr_val.castTag(.slice).?.data; + break :blk try beginComptimePtrLoad(sema, block, src, slice.ptr, null); + }, + .field_ptr => blk: { const field_ptr = ptr_val.castTag(.field_ptr).?.data; const field_index = @intCast(u32, field_ptr.field_index);