@@ -2429,6 +2429,16 @@ fn failWithComptimeErrorRetTrace(
24292429 return sema.failWithOwnedErrorMsg(block, msg);
24302430}
24312431
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+
24322442/// We don't return a pointer to the new error note because the pointer
24332443/// becomes invalid when you add another one.
24342444pub fn errNote(
@@ -15146,7 +15156,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1514615156 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1514715157 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1514815158 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
1515115161 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1515215162 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
1531215322 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1531315323 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1531415324 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
1531715327 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1531815328 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
1547815488 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1547915489 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1548015490 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
1548315493 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1548415494 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
1558915599 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1559015600 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1559115601 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
1559415604 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1559515605 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.
1583315843 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1583415844 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1583515845 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
1583815848 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1583915849 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
1601916029 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1602016030 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1602116031 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
1602416034 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1602516035 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
1611516125 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod);
1611616126 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1611716127 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
1612016130 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1612116131 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -16458,17 +16468,78 @@ fn analyzeArithmetic(
1645816468 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod);
1645916469 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)) {
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+ }
1647216543
1647316544 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1647416545 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
@@ -23762,7 +23833,7 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
2376223833 }
2376323834}
2376423835
23765- fn checkInvalidPtrArithmetic (
23836+ fn checkInvalidPtrIntArithmetic (
2376623837 sema: *Sema,
2376723838 block: *Block,
2376823839 src: LazySrcLoc,
@@ -23773,12 +23844,7 @@ fn checkInvalidPtrArithmetic(
2377323844 switch (try ty.zigTypeTagOrPoison(mod)) {
2377423845 .Pointer => switch (ty.ptrSize(mod)) {
2377523846 .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"),
2378223848 },
2378323849 else => return,
2378423850 }
0 commit comments