| author | |
| committer | |
| log | 888708ec8af9b60681ef14fb0a5c265f2a30b41f |
| tree | 95da742d1d0082ae0150ea109f57e5ce2eebddb8 |
| parent | 89942ebd03b2943cbbe84b575a024e156ca5bf52 |
| signature |
7 files changed, 230 insertions(+), 46 deletions(-)
doc/langref.html.in+6-3| ... | ... | @@ -1935,16 +1935,18 @@ or |
| 1935 | 1935 | <li>{#syntax#}*T{#endsyntax#} - single-item pointer to exactly one item. |
| 1936 | 1936 | <ul> |
| 1937 | 1937 | <li>Supports deref syntax: {#syntax#}ptr.*{#endsyntax#}</li> |
| 1938 | <li>Supports pointer subtraction: {#syntax#}ptr - ptr{#endsyntax#}</li> | |
| 1938 | 1939 | </ul> |
| 1939 | 1940 | </li> |
| 1940 | 1941 | <li>{#syntax#}[*]T{#endsyntax#} - many-item pointer to unknown number of items. |
| 1941 | 1942 | <ul> |
| 1942 | 1943 | <li>Supports index syntax: {#syntax#}ptr[i]{#endsyntax#}</li> |
| 1943 | 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>{#syntax#}T{#endsyntax#} must have a known size, which means that it cannot be | |
| 1946 | {#syntax#}anyopaque{#endsyntax#} or any other {#link|opaque type|opaque#}.</li> | |
| 1945 | <li>Supports pointer-integer arithmetic: {#syntax#}ptr + int{#endsyntax#}, {#syntax#}ptr - int{#endsyntax#}</li> | |
| 1946 | <li>Supports pointer subtraction: {#syntax#}ptr - ptr{#endsyntax#}</li> | |
| 1947 | 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 | 1950 | </li> |
| 1949 | 1951 | </ul> |
| 1950 | 1952 | <p>These types are closely related to {#link|Arrays#} and {#link|Slices#}:</p> |
| ... | ... | @@ -1954,6 +1956,7 @@ or |
| 1954 | 1956 | <li>Supports index syntax: {#syntax#}array_ptr[i]{#endsyntax#}</li> |
| 1955 | 1957 | <li>Supports slice syntax: {#syntax#}array_ptr[start..end]{#endsyntax#}</li> |
| 1956 | 1958 | <li>Supports len property: {#syntax#}array_ptr.len{#endsyntax#}</li> |
| 1959 | <li>Supports pointer subtraction: {#syntax#}array_ptr - array_ptr{#endsyntax#}</li> | |
| 1957 | 1960 | </ul> |
| 1958 | 1961 | </li> |
| 1959 | 1962 | </ul> |
doc/langref/test_pointer_arithmetic.zig+3| ... | ... | @@ -11,6 +11,9 @@ test "pointer arithmetic with many-item pointer" { |
| 11 | 11 | // slicing a many-item pointer without an end is equivalent to |
| 12 | 12 | // pointer arithmetic: `ptr[start..] == ptr + start` |
| 13 | 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 | } |
| 15 | 18 | |
| 16 | 19 | test "pointer arithmetic with slices" { |
src/InternPool.zig+19-15| ... | ... | @@ -1931,6 +1931,23 @@ pub const Key = union(enum) { |
| 1931 | 1931 | /// the original pointer type alignment must be used. |
| 1932 | 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 | }; |
| 1936 | 1953 | |
| ... | ... | @@ -2369,21 +2386,8 @@ pub const Key = union(enum) { |
| 2369 | 2386 | const b_info = b.ptr; |
| 2370 | 2387 | if (a_info.ty != b_info.ty) return false; |
| 2371 | 2388 | if (a_info.byte_offset != b_info.byte_offset) return false; |
| 2372 | ||
| 2373 | if (@as(Key.Ptr.BaseAddr.Tag, a_info.base_addr) != @as(Key.Ptr.BaseAddr.Tag, b_info.base_addr)) return false; | |
| 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 | }; | |
| 2389 | if (!a_info.base_addr.eql(b_info.base_addr)) return false; | |
| 2390 | return true; | |
| 2387 | 2391 | }, |
| 2388 | 2392 | |
| 2389 | 2393 | .int => |a_info| { |
src/Sema.zig+91-25| ... | ... | @@ -2429,6 +2429,16 @@ fn failWithComptimeErrorRetTrace( |
| 2429 | 2429 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2430 | 2430 | } |
| 2431 | 2431 | |
| 2432 | fn 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 | 2442 | /// We don't return a pointer to the new error note because the pointer |
| 2433 | 2443 | /// becomes invalid when you add another one. |
| 2434 | 2444 | pub fn errNote( |
| ... | ... | @@ -15146,7 +15156,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 15146 | 15156 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 15147 | 15157 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 15148 | 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); | |
| 15150 | 15160 | |
| 15151 | 15161 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15152 | 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 | 15322 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 15313 | 15323 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 15314 | 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); | |
| 15316 | 15326 | |
| 15317 | 15327 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15318 | 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 | 15488 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 15479 | 15489 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 15480 | 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); | |
| 15482 | 15492 | |
| 15483 | 15493 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15484 | 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 | 15599 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 15590 | 15600 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 15591 | 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); | |
| 15593 | 15603 | |
| 15594 | 15604 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15595 | 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 | 15843 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 15834 | 15844 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 15835 | 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); | |
| 15837 | 15847 | |
| 15838 | 15848 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15839 | 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 | 16029 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 16020 | 16030 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 16021 | 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); | |
| 16023 | 16033 | |
| 16024 | 16034 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 16025 | 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 | 16125 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); |
| 16116 | 16126 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 16117 | 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); | |
| 16119 | 16129 | |
| 16120 | 16130 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 16121 | 16131 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| ... | ... | @@ -16458,17 +16468,78 @@ fn analyzeArithmetic( |
| 16458 | 16468 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); |
| 16459 | 16469 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 16460 | 16470 | |
| 16461 | if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) { | |
| 16462 | .One, .Slice => {}, | |
| 16463 | .Many, .C => { | |
| 16464 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | |
| 16465 | .add => .ptr_add, | |
| 16466 | .sub => .ptr_sub, | |
| 16467 | else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}), | |
| 16468 | }; | |
| 16469 | return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); | |
| 16470 | }, | |
| 16471 | }; | |
| 16471 | if (lhs_zig_ty_tag == .Pointer) { | |
| 16472 | if (rhs_zig_ty_tag == .Pointer) { | |
| 16473 | if (lhs_ty.ptrSize(mod) != .Slice and rhs_ty.ptrSize(mod) != .Slice) { | |
| 16474 | if (zir_tag != .sub) { | |
| 16475 | return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction"); | |
| 16476 | } | |
| 16477 | if (!lhs_ty.elemType2(mod).eql(rhs_ty.elemType2(mod), mod)) { | |
| 16478 | return sema.fail(block, src, "incompatible pointer arithmetic operands '{}' and '{}'", .{ | |
| 16479 | lhs_ty.fmt(pt), rhs_ty.fmt(pt), | |
| 16480 | }); | |
| 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 | } | |
| 16472 | 16543 | |
| 16473 | 16544 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 16474 | 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 | 23833 | } |
| 23763 | 23834 | } |
| 23764 | 23835 | |
| 23765 | fn checkInvalidPtrArithmetic( | |
| 23836 | fn checkInvalidPtrIntArithmetic( | |
| 23766 | 23837 | sema: *Sema, |
| 23767 | 23838 | block: *Block, |
| 23768 | 23839 | src: LazySrcLoc, |
| ... | ... | @@ -23773,12 +23844,7 @@ fn checkInvalidPtrArithmetic( |
| 23773 | 23844 | switch (try ty.zigTypeTagOrPoison(mod)) { |
| 23774 | 23845 | .Pointer => switch (ty.ptrSize(mod)) { |
| 23775 | 23846 | .One, .Slice => return, |
| 23776 | .Many, .C => return sema.fail( | |
| 23777 | block, | |
| 23778 | src, | |
| 23779 | "invalid pointer arithmetic operator", | |
| 23780 | .{}, | |
| 23781 | ), | |
| 23847 | .Many, .C => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), | |
| 23782 | 23848 | }, |
| 23783 | 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 | 3752 | const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); |
| 3753 | 3753 | assert(parent_ptr_info.flags.size == .One); |
| 3754 | 3754 | |
| 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 | 3756 | // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily. |
| 3757 | 3757 | const field_ty: Type, const field_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) { |
| 3758 | 3758 | .Struct => field: { |
test/behavior/pointers.zig+58-2| ... | ... | @@ -17,7 +17,7 @@ fn testDerefPtr() !void { |
| 17 | 17 | try expect(x == 1235); |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | test "pointer arithmetic" { | |
| 20 | test "pointer-integer arithmetic" { | |
| 21 | 21 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 22 | 22 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 23 | 23 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| ... | ... | @@ -43,6 +43,62 @@ test "pointer arithmetic" { |
| 43 | 43 | try expect(ptr[0] == 'a'); |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | test "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 | ||
| 46 | 102 | test "double pointer parsing" { |
| 47 | 103 | comptime assert(PtrOf(PtrOf(i32)) == **i32); |
| 48 | 104 | } |
| ... | ... | @@ -382,7 +438,7 @@ test "pointer to array at fixed address" { |
| 382 | 438 | try expect(@intFromPtr(&array[1]) == 0x14); |
| 383 | 439 | } |
| 384 | 440 | |
| 385 | test "pointer arithmetic affects the alignment" { | |
| 441 | test "pointer-integer arithmetic affects the alignment" { | |
| 386 | 442 | { |
| 387 | 443 | var ptr: [*]align(8) u32 = undefined; |
| 388 | 444 | var x: usize = 1; |
test/cases/compile_errors/invalid_pointer_arithmetic.zig created+52| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | export fn a(x: [*]u8) void { | |
| 2 | _ = x * 1; | |
| 3 | } | |
| 4 | ||
| 5 | export fn b(x: *u8) void { | |
| 6 | _ = x * x; | |
| 7 | } | |
| 8 | ||
| 9 | export fn c() void { | |
| 10 | const x: []u8 = undefined; | |
| 11 | const y: []u8 = undefined; | |
| 12 | _ = x - y; | |
| 13 | } | |
| 14 | ||
| 15 | export fn d() void { | |
| 16 | var x: [*]u8 = undefined; | |
| 17 | var y: [*]u16 = undefined; | |
| 18 | _ = &x; | |
| 19 | _ = &y; | |
| 20 | _ = x - y; | |
| 21 | } | |
| 22 | ||
| 23 | comptime { | |
| 24 | const x: *u8 = @ptrFromInt(1); | |
| 25 | const y: *u16 = @ptrFromInt(2); | |
| 26 | _ = x - y; | |
| 27 | } | |
| 28 | ||
| 29 | comptime { | |
| 30 | const x: [*]u0 = @ptrFromInt(1); | |
| 31 | _ = x + 1; | |
| 32 | } | |
| 33 | ||
| 34 | comptime { | |
| 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 |