authorgravatar for r00ster91@proton.meWooster <r00ster91@proton.me> 2024-07-16 03:18:38+09:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-15 18:18:38+00:00
log888708ec8af9b60681ef14fb0a5c265f2a30b41f
tree95da742d1d0082ae0150ea109f57e5ce2eebddb8
parent89942ebd03b2943cbbe84b575a024e156ca5bf52
signaturebadge-check Signed by PGP key B5690EEEBB952194

Sema: support pointer subtraction


7 files changed, 230 insertions(+), 46 deletions(-)

doc/langref.html.in+6-3
...@@ -1935,16 +1935,18 @@ or...@@ -1935,16 +1935,18 @@ or
1935 <li>{#syntax#}*T{#endsyntax#} - single-item pointer to exactly one item.1935 <li>{#syntax#}*T{#endsyntax#} - single-item pointer to exactly one item.
1936 <ul>1936 <ul>
1937 <li>Supports deref syntax: {#syntax#}ptr.*{#endsyntax#}</li>1937 <li>Supports deref syntax: {#syntax#}ptr.*{#endsyntax#}</li>
1938 <li>Supports pointer subtraction: {#syntax#}ptr - ptr{#endsyntax#}</li>
1938 </ul>1939 </ul>
1939 </li>1940 </li>
1940 <li>{#syntax#}[*]T{#endsyntax#} - many-item pointer to unknown number of items.1941 <li>{#syntax#}[*]T{#endsyntax#} - many-item pointer to unknown number of items.
1941 <ul>1942 <ul>
1942 <li>Supports index syntax: {#syntax#}ptr[i]{#endsyntax#}</li>1943 <li>Supports index syntax: {#syntax#}ptr[i]{#endsyntax#}</li>
1943 <li>Supports slice syntax: {#syntax#}ptr[start..end]{#endsyntax#} and {#syntax#}ptr[start..]{#endsyntax#}</li>1944 <li>Supports slice syntax: {#syntax#}ptr[start..end]{#endsyntax#} and {#syntax#}ptr[start..]{#endsyntax#}</li>
1944 <li>Supports pointer arithmetic: {#syntax#}ptr + x{#endsyntax#}, {#syntax#}ptr - x{#endsyntax#}</li>1945 <li>Supports pointer-integer arithmetic: {#syntax#}ptr + int{#endsyntax#}, {#syntax#}ptr - int{#endsyntax#}</li>
1945 <li>{#syntax#}T{#endsyntax#} must have a known size, which means that it cannot be1946 <li>Supports pointer subtraction: {#syntax#}ptr - ptr{#endsyntax#}</li>
1946 {#syntax#}anyopaque{#endsyntax#} or any other {#link|opaque type|opaque#}.</li>
1947 </ul>1947 </ul>
1948 {#syntax#}T{#endsyntax#} must have a known size, which means that it cannot be
1949 {#syntax#}anyopaque{#endsyntax#} or any other {#link|opaque type|opaque#}.
1948 </li>1950 </li>
1949 </ul>1951 </ul>
1950 <p>These types are closely related to {#link|Arrays#} and {#link|Slices#}:</p>1952 <p>These types are closely related to {#link|Arrays#} and {#link|Slices#}:</p>
...@@ -1954,6 +1956,7 @@ or...@@ -1954,6 +1956,7 @@ or
1954 <li>Supports index syntax: {#syntax#}array_ptr[i]{#endsyntax#}</li>1956 <li>Supports index syntax: {#syntax#}array_ptr[i]{#endsyntax#}</li>
1955 <li>Supports slice syntax: {#syntax#}array_ptr[start..end]{#endsyntax#}</li>1957 <li>Supports slice syntax: {#syntax#}array_ptr[start..end]{#endsyntax#}</li>
1956 <li>Supports len property: {#syntax#}array_ptr.len{#endsyntax#}</li>1958 <li>Supports len property: {#syntax#}array_ptr.len{#endsyntax#}</li>
1959 <li>Supports pointer subtraction: {#syntax#}array_ptr - array_ptr{#endsyntax#}</li>
1957 </ul>1960 </ul>
1958 </li>1961 </li>
1959 </ul>1962 </ul>
doc/langref/test_pointer_arithmetic.zig+3
...@@ -11,6 +11,9 @@ test "pointer arithmetic with many-item pointer" {...@@ -11,6 +11,9 @@ test "pointer arithmetic with many-item pointer" {
11 // slicing a many-item pointer without an end is equivalent to11 // slicing a many-item pointer without an end is equivalent to
12 // pointer arithmetic: `ptr[start..] == ptr + start`12 // pointer arithmetic: `ptr[start..] == ptr + start`
13 try expect(ptr[1..] == ptr + 1);13 try expect(ptr[1..] == ptr + 1);
14
15 // subtraction between any two pointers except slices based on element size is supported
16 try expect(&ptr[1] - &ptr[0] == 1);
14}17}
1518
16test "pointer arithmetic with slices" {19test "pointer arithmetic with slices" {
src/InternPool.zig+19-15
...@@ -1931,6 +1931,23 @@ pub const Key = union(enum) {...@@ -1931,6 +1931,23 @@ pub const Key = union(enum) {
1931 /// the original pointer type alignment must be used.1931 /// the original pointer type alignment must be used.
1932 orig_ty: Index,1932 orig_ty: Index,
1933 };1933 };
1934
1935 pub fn eql(a: BaseAddr, b: BaseAddr) bool {
1936 if (@as(Key.Ptr.BaseAddr.Tag, a) != @as(Key.Ptr.BaseAddr.Tag, b)) return false;
1937
1938 return switch (a) {
1939 .decl => |a_decl| a_decl == b.decl,
1940 .comptime_alloc => |a_alloc| a_alloc == b.comptime_alloc,
1941 .anon_decl => |ad| ad.val == b.anon_decl.val and
1942 ad.orig_ty == b.anon_decl.orig_ty,
1943 .int => true,
1944 .eu_payload => |a_eu_payload| a_eu_payload == b.eu_payload,
1945 .opt_payload => |a_opt_payload| a_opt_payload == b.opt_payload,
1946 .comptime_field => |a_comptime_field| a_comptime_field == b.comptime_field,
1947 .arr_elem => |a_elem| std.meta.eql(a_elem, b.arr_elem),
1948 .field => |a_field| std.meta.eql(a_field, b.field),
1949 };
1950 }
1934 };1951 };
1935 };1952 };
19361953
...@@ -2369,21 +2386,8 @@ pub const Key = union(enum) {...@@ -2369,21 +2386,8 @@ pub const Key = union(enum) {
2369 const b_info = b.ptr;2386 const b_info = b.ptr;
2370 if (a_info.ty != b_info.ty) return false;2387 if (a_info.ty != b_info.ty) return false;
2371 if (a_info.byte_offset != b_info.byte_offset) return false;2388 if (a_info.byte_offset != b_info.byte_offset) return false;
23722389 if (!a_info.base_addr.eql(b_info.base_addr)) return false;
2373 if (@as(Key.Ptr.BaseAddr.Tag, a_info.base_addr) != @as(Key.Ptr.BaseAddr.Tag, b_info.base_addr)) return false;2390 return true;
2374
2375 return switch (a_info.base_addr) {
2376 .decl => |a_decl| a_decl == b_info.base_addr.decl,
2377 .comptime_alloc => |a_alloc| a_alloc == b_info.base_addr.comptime_alloc,
2378 .anon_decl => |ad| ad.val == b_info.base_addr.anon_decl.val and
2379 ad.orig_ty == b_info.base_addr.anon_decl.orig_ty,
2380 .int => true,
2381 .eu_payload => |a_eu_payload| a_eu_payload == b_info.base_addr.eu_payload,
2382 .opt_payload => |a_opt_payload| a_opt_payload == b_info.base_addr.opt_payload,
2383 .comptime_field => |a_comptime_field| a_comptime_field == b_info.base_addr.comptime_field,
2384 .arr_elem => |a_elem| std.meta.eql(a_elem, b_info.base_addr.arr_elem),
2385 .field => |a_field| std.meta.eql(a_field, b_info.base_addr.field),
2386 };
2387 },2391 },
23882392
2389 .int => |a_info| {2393 .int => |a_info| {
src/Sema.zig+91-25
...@@ -2429,6 +2429,16 @@ fn failWithComptimeErrorRetTrace(...@@ -2429,6 +2429,16 @@ fn failWithComptimeErrorRetTrace(
2429 return sema.failWithOwnedErrorMsg(block, msg);2429 return sema.failWithOwnedErrorMsg(block, msg);
2430}2430}
24312431
2432fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError {
2433 const msg = msg: {
2434 const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic});
2435 errdefer msg.destroy(sema.gpa);
2436 try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports });
2437 break :msg msg;
2438 };
2439 return sema.failWithOwnedErrorMsg(block, msg);
2440}
2441
2432/// We don't return a pointer to the new error note because the pointer2442/// We don't return a pointer to the new error note because the pointer
2433/// becomes invalid when you add another one.2443/// becomes invalid when you add another one.
2434pub fn errNote(2444pub fn errNote(
...@@ -15146,7 +15156,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -15146,7 +15156,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
15146 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);15156 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
15147 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);15157 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
15148 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);15158 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15149 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);15159 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1515015160
15151 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };15161 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15152 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{15162 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...@@ -15312,7 +15322,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
15312 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);15322 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
15313 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);15323 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
15314 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);15324 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15315 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);15325 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1531615326
15317 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };15327 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15318 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{15328 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...@@ -15478,7 +15488,7 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
15478 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);15488 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
15479 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);15489 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
15480 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);15490 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15481 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);15491 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1548215492
15483 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };15493 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15484 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{15494 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...@@ -15589,7 +15599,7 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
15589 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);15599 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
15590 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);15600 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
15591 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);15601 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15592 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);15602 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1559315603
15594 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };15604 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15595 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{15605 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....@@ -15833,7 +15843,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
15833 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);15843 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
15834 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);15844 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
15835 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);15845 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15836 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);15846 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1583715847
15838 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };15848 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15839 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{15849 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...@@ -16019,7 +16029,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
16019 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);16029 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
16020 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);16030 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
16021 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);16031 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
16022 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);16032 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1602316033
16024 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };16034 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
16025 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{16035 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...@@ -16115,7 +16125,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
16115 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);16125 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
16116 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);16126 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
16117 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);16127 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
16118 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);16128 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
1611916129
16120 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };16130 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
16121 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{16131 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
...@@ -16458,17 +16468,78 @@ fn analyzeArithmetic(...@@ -16458,17 +16468,78 @@ fn analyzeArithmetic(
16458 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);16468 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
16459 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);16469 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
1646016470
16461 if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) {16471 if (lhs_zig_ty_tag == .Pointer) {
16462 .One, .Slice => {},16472 if (rhs_zig_ty_tag == .Pointer) {
16463 .Many, .C => {16473 if (lhs_ty.ptrSize(mod) != .Slice and rhs_ty.ptrSize(mod) != .Slice) {
16464 const air_tag: Air.Inst.Tag = switch (zir_tag) {16474 if (zir_tag != .sub) {
16465 .add => .ptr_add,16475 return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
16466 .sub => .ptr_sub,16476 }
16467 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}),16477 if (!lhs_ty.elemType2(mod).eql(rhs_ty.elemType2(mod), mod)) {
16468 };16478 return sema.fail(block, src, "incompatible pointer arithmetic operands '{}' and '{}'", .{
16469 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);16479 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
16470 },16480 });
16471 };16481 }
16482
16483 const elem_size = lhs_ty.elemType2(mod).abiSize(pt);
16484 if (elem_size == 0) {
16485 return sema.fail(block, src, "pointer arithmetic requires element type '{}' to have runtime bits", .{
16486 lhs_ty.elemType2(mod).fmt(pt),
16487 });
16488 }
16489
16490 const runtime_src = runtime_src: {
16491 if (try sema.resolveValue(lhs)) |lhs_value| {
16492 if (try sema.resolveValue(rhs)) |rhs_value| {
16493 const lhs_ptr = switch (mod.intern_pool.indexToKey(lhs_value.toIntern())) {
16494 .undef => return sema.failWithUseOfUndef(block, lhs_src),
16495 .ptr => |ptr| ptr,
16496 else => unreachable,
16497 };
16498 const rhs_ptr = switch (mod.intern_pool.indexToKey(rhs_value.toIntern())) {
16499 .undef => return sema.failWithUseOfUndef(block, rhs_src),
16500 .ptr => |ptr| ptr,
16501 else => unreachable,
16502 };
16503 // Make sure the pointers point to the same data.
16504 if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src;
16505 const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch
16506 return sema.fail(block, src, "operation results in overflow", .{});
16507 const result = address / elem_size;
16508 return try pt.intRef(Type.usize, result);
16509 } else {
16510 break :runtime_src lhs_src;
16511 }
16512 } else {
16513 break :runtime_src rhs_src;
16514 }
16515 };
16516
16517 try sema.requireRuntimeBlock(block, src, runtime_src);
16518 const lhs_int = try block.addUnOp(.int_from_ptr, lhs);
16519 const rhs_int = try block.addUnOp(.int_from_ptr, rhs);
16520 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
16521 return try block.addBinOp(.div_exact, address, try pt.intRef(Type.usize, elem_size));
16522 }
16523 } else {
16524 switch (lhs_ty.ptrSize(mod)) {
16525 .One, .Slice => {},
16526 .Many, .C => {
16527 const air_tag: Air.Inst.Tag = switch (zir_tag) {
16528 .add => .ptr_add,
16529 .sub => .ptr_sub,
16530 else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
16531 };
16532
16533 if (!try sema.typeHasRuntimeBits(lhs_ty.elemType2(mod))) {
16534 return sema.fail(block, src, "pointer arithmetic requires element type '{}' to have runtime bits", .{
16535 lhs_ty.elemType2(mod).fmt(pt),
16536 });
16537 }
16538 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
16539 },
16540 }
16541 }
16542 }
1647216543
16473 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };16544 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
16474 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{16545 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
...@@ -23762,7 +23833,7 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr...@@ -23762,7 +23833,7 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
23762 }23833 }
23763}23834}
2376423835
23765fn checkInvalidPtrArithmetic(23836fn checkInvalidPtrIntArithmetic(
23766 sema: *Sema,23837 sema: *Sema,
23767 block: *Block,23838 block: *Block,
23768 src: LazySrcLoc,23839 src: LazySrcLoc,
...@@ -23773,12 +23844,7 @@ fn checkInvalidPtrArithmetic(...@@ -23773,12 +23844,7 @@ fn checkInvalidPtrArithmetic(
23773 switch (try ty.zigTypeTagOrPoison(mod)) {23844 switch (try ty.zigTypeTagOrPoison(mod)) {
23774 .Pointer => switch (ty.ptrSize(mod)) {23845 .Pointer => switch (ty.ptrSize(mod)) {
23775 .One, .Slice => return,23846 .One, .Slice => return,
23776 .Many, .C => return sema.fail(23847 .Many, .C => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
23777 block,
23778 src,
23779 "invalid pointer arithmetic operator",
23780 .{},
23781 ),
23782 },23848 },
23783 else => return,23849 else => return,
23784 }23850 }
src/Value.zig+1-1
...@@ -3752,7 +3752,7 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {...@@ -3752,7 +3752,7 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
3752 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);3752 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
3753 assert(parent_ptr_info.flags.size == .One);3753 assert(parent_ptr_info.flags.size == .One);
37543754
3755 // Exiting this `switch` indicates that the `field` pointer repsentation should be used.3755 // Exiting this `switch` indicates that the `field` pointer representation should be used.
3756 // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily.3756 // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily.
3757 const field_ty: Type, const field_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {3757 const field_ty: Type, const field_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
3758 .Struct => field: {3758 .Struct => field: {
test/behavior/pointers.zig+58-2
...@@ -17,7 +17,7 @@ fn testDerefPtr() !void {...@@ -17,7 +17,7 @@ fn testDerefPtr() !void {
17 try expect(x == 1235);17 try expect(x == 1235);
18}18}
1919
20test "pointer arithmetic" {20test "pointer-integer arithmetic" {
21 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;21 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
22 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO22 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
...@@ -43,6 +43,62 @@ test "pointer arithmetic" {...@@ -43,6 +43,62 @@ test "pointer arithmetic" {
43 try expect(ptr[0] == 'a');43 try expect(ptr[0] == 'a');
44}44}
4545
46test "pointer subtraction" {
47 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
49
50 {
51 const a: *u8 = @ptrFromInt(100);
52 const b: *u8 = @ptrFromInt(50);
53 try expect(a - b == 50);
54 }
55 {
56 var ptr: [*]const u8 = "abc";
57 try expect(&ptr[1] - &ptr[0] == 1);
58 try expect(&ptr[2] - &ptr[0] == 2);
59 }
60 {
61 const a: *[100]u16 = @ptrFromInt(100);
62 const b: *[100]u16 = @ptrFromInt(50);
63 try expect(a - b == 25);
64 }
65 {
66 var x: struct { a: u32, b: u32 } = undefined;
67 const a = &x.a;
68 const b = &x.b;
69 try expect(a - a == 0);
70 try expect(b - b == 0);
71 try expect(b - a == 1);
72 }
73 comptime {
74 var x: packed struct { a: u1, b: u1 } = undefined;
75 const a = &x.a;
76 const b = &x.b;
77 try expect(a - a == 0);
78 try expect(b - b == 0);
79 try expect(b - a == 0);
80 }
81 comptime {
82 var x: extern struct { a: u32, b: u32 } = undefined;
83 const a = &x.a;
84 const b = &x.b;
85 try expect(a - a == 0);
86 try expect(b - b == 0);
87 try expect(b - a == 1);
88 }
89 comptime {
90 const a: *const [3]u8 = "abc";
91 const b: [*]const u8 = @ptrCast(a);
92 try expect(&a[1] - &b[0] == 1);
93 }
94 comptime {
95 var x: [64][64]u8 = undefined;
96 const a = &x[0][12];
97 const b = &x[15][3];
98 try expect(b - a == 951);
99 }
100}
101
46test "double pointer parsing" {102test "double pointer parsing" {
47 comptime assert(PtrOf(PtrOf(i32)) == **i32);103 comptime assert(PtrOf(PtrOf(i32)) == **i32);
48}104}
...@@ -382,7 +438,7 @@ test "pointer to array at fixed address" {...@@ -382,7 +438,7 @@ test "pointer to array at fixed address" {
382 try expect(@intFromPtr(&array[1]) == 0x14);438 try expect(@intFromPtr(&array[1]) == 0x14);
383}439}
384440
385test "pointer arithmetic affects the alignment" {441test "pointer-integer arithmetic affects the alignment" {
386 {442 {
387 var ptr: [*]align(8) u32 = undefined;443 var ptr: [*]align(8) u32 = undefined;
388 var x: usize = 1;444 var x: usize = 1;
test/cases/compile_errors/invalid_pointer_arithmetic.zig created+52
...@@ -0,0 +1,52 @@
1export fn a(x: [*]u8) void {
2 _ = x * 1;
3}
4
5export fn b(x: *u8) void {
6 _ = x * x;
7}
8
9export fn c() void {
10 const x: []u8 = undefined;
11 const y: []u8 = undefined;
12 _ = x - y;
13}
14
15export fn d() void {
16 var x: [*]u8 = undefined;
17 var y: [*]u16 = undefined;
18 _ = &x;
19 _ = &y;
20 _ = x - y;
21}
22
23comptime {
24 const x: *u8 = @ptrFromInt(1);
25 const y: *u16 = @ptrFromInt(2);
26 _ = x - y;
27}
28
29comptime {
30 const x: [*]u0 = @ptrFromInt(1);
31 _ = x + 1;
32}
33
34comptime {
35 const x: *u0 = @ptrFromInt(1);
36 const y: *u0 = @ptrFromInt(2);
37 _ = x - y;
38}
39
40// error
41// backend=stage2
42// target=native
43//
44// :2:11: error: invalid pointer-integer arithmetic operator
45// :2:11: note: pointer-integer arithmetic only supports addition and subtraction
46// :6:11: error: invalid pointer-pointer arithmetic operator
47// :6:11: note: pointer-pointer arithmetic only supports subtraction
48// :12:11: error: invalid operands to binary expression: 'Pointer' and 'Pointer'
49// :20:11: error: incompatible pointer arithmetic operands '[*]u8' and '[*]u16'
50// :26:11: error: incompatible pointer arithmetic operands '*u8' and '*u16'
51// :31:11: error: pointer arithmetic requires element type 'u0' to have runtime bits
52// :37:11: error: pointer arithmetic requires element type 'u0' to have runtime bits