diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-09-20 22:02:36 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-20 22:02:36 -0400 |
| commit | 62ecc154d9ad065aee57d81afd3a478dd8360fb7 (patch) | |
| tree | 52f17b58d87a2ef83f2a1607f0aecbee31c33661 /src/Sema.zig | |
| parent | 521c753fda3cd5ebb79752287ef3c87d84f8d5c3 (diff) | |
| parent | eef653904916dc19540458199366807f8837bf98 (diff) | |
| download | zig-62ecc154d9ad065aee57d81afd3a478dd8360fb7.tar.gz zig-62ecc154d9ad065aee57d81afd3a478dd8360fb7.zip | |
Merge pull request #12904 from Vexu/ptrcast
stage2: pointer casting fixes and improvements
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 177 |
1 files changed, 132 insertions, 45 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 0a9be797a4..171b349758 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) { @@ -17772,6 +17775,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 +17787,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 +17806,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 +17839,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 { @@ -17931,23 +17975,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 +17995,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 +18020,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( @@ -22660,7 +22695,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()) { @@ -22779,6 +22814,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); } @@ -22811,14 +22847,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, @@ -22829,14 +22863,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)) { @@ -22873,6 +22904,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(); @@ -22888,7 +22920,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); } @@ -22922,6 +22954,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, @@ -22952,7 +22985,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); } @@ -22961,7 +22994,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); } @@ -22970,7 +23003,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); } @@ -22979,10 +23012,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); } @@ -23011,6 +23042,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( @@ -25158,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); @@ -25406,6 +25443,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, @@ -25413,13 +25501,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())) |
