aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorWooster <wooster0@proton.me>2024-07-16 03:18:38 +0900
committerGitHub <noreply@github.com>2024-07-15 18:18:38 +0000
commit888708ec8af9b60681ef14fb0a5c265f2a30b41f (patch)
tree95da742d1d0082ae0150ea109f57e5ce2eebddb8 /src/Sema.zig
parent89942ebd03b2943cbbe84b575a024e156ca5bf52 (diff)
downloadzig-888708ec8af9b60681ef14fb0a5c265f2a30b41f.tar.gz
zig-888708ec8af9b60681ef14fb0a5c265f2a30b41f.zip
Sema: support pointer subtraction
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig116
1 files changed, 91 insertions, 25 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 5c43c2e431..671448b5b4 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -2429,6 +2429,16 @@ fn failWithComptimeErrorRetTrace(
return sema.failWithOwnedErrorMsg(block, msg);
}
+fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError {
+ const msg = msg: {
+ const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic});
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports });
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+}
+
/// We don't return a pointer to the new error note because the pointer
/// becomes invalid when you add another one.
pub fn errNote(
@@ -15146,7 +15156,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -15312,7 +15322,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -15478,7 +15488,7 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -15589,7 +15599,7 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -15833,7 +15843,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -16019,7 +16029,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -16115,7 +16125,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
+ try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -16458,17 +16468,78 @@ fn analyzeArithmetic(
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
- if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) {
- .One, .Slice => {},
- .Many, .C => {
- const air_tag: Air.Inst.Tag = switch (zir_tag) {
- .add => .ptr_add,
- .sub => .ptr_sub,
- else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}),
- };
- return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
- },
- };
+ if (lhs_zig_ty_tag == .Pointer) {
+ if (rhs_zig_ty_tag == .Pointer) {
+ if (lhs_ty.ptrSize(mod) != .Slice and rhs_ty.ptrSize(mod) != .Slice) {
+ if (zir_tag != .sub) {
+ return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
+ }
+ if (!lhs_ty.elemType2(mod).eql(rhs_ty.elemType2(mod), mod)) {
+ return sema.fail(block, src, "incompatible pointer arithmetic operands '{}' and '{}'", .{
+ lhs_ty.fmt(pt), rhs_ty.fmt(pt),
+ });
+ }
+
+ const elem_size = lhs_ty.elemType2(mod).abiSize(pt);
+ if (elem_size == 0) {
+ return sema.fail(block, src, "pointer arithmetic requires element type '{}' to have runtime bits", .{
+ lhs_ty.elemType2(mod).fmt(pt),
+ });
+ }
+
+ const runtime_src = runtime_src: {
+ if (try sema.resolveValue(lhs)) |lhs_value| {
+ if (try sema.resolveValue(rhs)) |rhs_value| {
+ const lhs_ptr = switch (mod.intern_pool.indexToKey(lhs_value.toIntern())) {
+ .undef => return sema.failWithUseOfUndef(block, lhs_src),
+ .ptr => |ptr| ptr,
+ else => unreachable,
+ };
+ const rhs_ptr = switch (mod.intern_pool.indexToKey(rhs_value.toIntern())) {
+ .undef => return sema.failWithUseOfUndef(block, rhs_src),
+ .ptr => |ptr| ptr,
+ else => unreachable,
+ };
+ // Make sure the pointers point to the same data.
+ if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src;
+ const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch
+ return sema.fail(block, src, "operation results in overflow", .{});
+ const result = address / elem_size;
+ return try pt.intRef(Type.usize, result);
+ } else {
+ break :runtime_src lhs_src;
+ }
+ } else {
+ break :runtime_src rhs_src;
+ }
+ };
+
+ try sema.requireRuntimeBlock(block, src, runtime_src);
+ const lhs_int = try block.addUnOp(.int_from_ptr, lhs);
+ const rhs_int = try block.addUnOp(.int_from_ptr, rhs);
+ const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
+ return try block.addBinOp(.div_exact, address, try pt.intRef(Type.usize, elem_size));
+ }
+ } else {
+ switch (lhs_ty.ptrSize(mod)) {
+ .One, .Slice => {},
+ .Many, .C => {
+ const air_tag: Air.Inst.Tag = switch (zir_tag) {
+ .add => .ptr_add,
+ .sub => .ptr_sub,
+ else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
+ };
+
+ if (!try sema.typeHasRuntimeBits(lhs_ty.elemType2(mod))) {
+ return sema.fail(block, src, "pointer arithmetic requires element type '{}' to have runtime bits", .{
+ lhs_ty.elemType2(mod).fmt(pt),
+ });
+ }
+ return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
+ },
+ }
+ }
+ }
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -23762,7 +23833,7 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
}
}
-fn checkInvalidPtrArithmetic(
+fn checkInvalidPtrIntArithmetic(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -23773,12 +23844,7 @@ fn checkInvalidPtrArithmetic(
switch (try ty.zigTypeTagOrPoison(mod)) {
.Pointer => switch (ty.ptrSize(mod)) {
.One, .Slice => return,
- .Many, .C => return sema.fail(
- block,
- src,
- "invalid pointer arithmetic operator",
- .{},
- ),
+ .Many, .C => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
},
else => return,
}