diff options
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 116 |
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, } |
