aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-09-20 22:02:36 -0400
committerGitHub <noreply@github.com>2022-09-20 22:02:36 -0400
commit62ecc154d9ad065aee57d81afd3a478dd8360fb7 (patch)
tree52f17b58d87a2ef83f2a1607f0aecbee31c33661 /src/Sema.zig
parent521c753fda3cd5ebb79752287ef3c87d84f8d5c3 (diff)
parenteef653904916dc19540458199366807f8837bf98 (diff)
downloadzig-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.zig177
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()))