From 57f9405a8fcaec6043d680fa47ae0e98709160c2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Jul 2022 15:00:40 +0300 Subject: Sema: validate bitcast operand type --- src/Sema.zig | 86 ++++++++++++++++++++-- test/behavior/bitcast.zig | 16 ---- test/cases/compile_errors/bitCast_to_enum_type.zig | 2 +- .../intToPtr_with_misaligned_address.zig | 10 +++ ...issue_3818_bitcast_from_parray-slice_to_u16.zig | 19 +++++ .../obj/intToPtr_with_misaligned_address.zig | 10 --- ...issue_3818_bitcast_from_parray-slice_to_u16.zig | 17 ----- 7 files changed, 111 insertions(+), 49 deletions(-) create mode 100644 test/cases/compile_errors/intToPtr_with_misaligned_address.zig create mode 100644 test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig delete mode 100644 test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig delete mode 100644 test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig diff --git a/src/Sema.zig b/src/Sema.zig index a0829d6eb7..efdc0f8262 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8288,6 +8288,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); + const operand_ty = sema.typeOf(operand); switch (dest_ty.zigTypeTag()) { .AnyFrame, .ComptimeFloat, @@ -8310,8 +8311,8 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const msg = msg: { const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); - switch (sema.typeOf(operand).zigTypeTag()) { - .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum for type coercion", .{}), + switch (operand_ty.zigTypeTag()) { + .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum to cast from '{}'", .{operand_ty.fmt(sema.mod)}), else => {}, } @@ -8320,9 +8321,20 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.failWithOwnedErrorMsg(block, msg); }, - .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{ - dest_ty.fmt(sema.mod), - }), + .Pointer => { + const msg = msg: { + const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + switch (operand_ty.zigTypeTag()) { + .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToPtr to cast from '{}'", .{operand_ty.fmt(sema.mod)}), + .Pointer => try sema.errNote(block, dest_ty_src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(sema.mod)}), + else => {}, + } + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, .Struct, .Union => if (dest_ty.containerLayout() == .Auto) { const container = switch (dest_ty.zigTypeTag()) { .Struct => "struct", @@ -8342,6 +8354,70 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Vector, => {}, } + switch (operand_ty.zigTypeTag()) { + .AnyFrame, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .ErrorSet, + .ErrorUnion, + .Fn, + .Frame, + .NoReturn, + .Null, + .Opaque, + .Optional, + .Type, + .Undefined, + .Void, + => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}), + + .Enum => { + const msg = msg: { + const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + switch (dest_ty.zigTypeTag()) { + .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @enumToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}), + else => {}, + } + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, + .Pointer => { + const msg = msg: { + const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + switch (dest_ty.zigTypeTag()) { + .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @ptrToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}), + .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(sema.mod)}), + else => {}, + } + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, + .Struct, .Union => if (operand_ty.containerLayout() == .Auto) { + const container = switch (operand_ty.zigTypeTag()) { + .Struct => "struct", + .Union => "union", + else => unreachable, + }; + return sema.fail(block, operand_src, "cannot @bitCast from '{}', {s} does not have a guaranteed in-memory layout", .{ + operand_ty.fmt(sema.mod), container, + }); + }, + .BoundFn => @panic("TODO remove this type from the language and compiler"), + + .Array, + .Bool, + .Float, + .Int, + .Vector, + => {}, + } return sema.bitCast(block, dest_ty, operand, operand_src); } diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index b0c66fd92b..27a0692a44 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -90,22 +90,6 @@ test "nested bitcast" { comptime try S.foo(42); } -test "@bitCast enum to its integer type" { - const SOCK = enum(c_int) { - A, - B, - - fn testBitCastExternEnum() !void { - var SOCK_DGRAM = @This().B; - var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - try expect(sock_dgram == 1); - } - }; - - try SOCK.testBitCastExternEnum(); - comptime try SOCK.testBitCastExternEnum(); -} - // issue #3010: compiler segfault test "bitcast literal [4]u8 param to u32" { const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 }); diff --git a/test/cases/compile_errors/bitCast_to_enum_type.zig b/test/cases/compile_errors/bitCast_to_enum_type.zig index add531627f..a8fedb7d54 100644 --- a/test/cases/compile_errors/bitCast_to_enum_type.zig +++ b/test/cases/compile_errors/bitCast_to_enum_type.zig @@ -9,4 +9,4 @@ export fn entry() void { // target=native // // :3:24: error: cannot @bitCast to 'tmp.entry.E' -// :3:24: note: use @intToEnum for type coercion +// :3:24: note: use @intToEnum to cast from 'u32' diff --git a/test/cases/compile_errors/intToPtr_with_misaligned_address.zig b/test/cases/compile_errors/intToPtr_with_misaligned_address.zig new file mode 100644 index 0000000000..43f89ab3b5 --- /dev/null +++ b/test/cases/compile_errors/intToPtr_with_misaligned_address.zig @@ -0,0 +1,10 @@ +pub export fn entry() void { + var y = @intToPtr([*]align(4) u8, 5); + _ = y; +} + +// error +// backend=stage2 +// target=native +// +// :2:39: error: pointer type '[*]align(4) u8' requires aligned address diff --git a/test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig b/test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig new file mode 100644 index 0000000000..874f015ffb --- /dev/null +++ b/test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig @@ -0,0 +1,19 @@ +export fn foo1() void { + var bytes = [_]u8{1, 2}; + const word: u16 = @bitCast(u16, bytes[0..]); + _ = word; +} +export fn foo2() void { + var bytes: []const u8 = &[_]u8{1, 2}; + const word: u16 = @bitCast(u16, bytes); + _ = word; +} + +// error +// backend=stage2 +// target=native +// +// :3:42: error: cannot @bitCast from '*[2]u8' +// :3:42: note: use @ptrToInt to cast to 'u16' +// :8:37: error: cannot @bitCast from '[]const u8' +// :8:37: note: use @ptrToInt to cast to 'u16' diff --git a/test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig b/test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig deleted file mode 100644 index 16b6bf565e..0000000000 --- a/test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig +++ /dev/null @@ -1,10 +0,0 @@ -pub fn main() void { - var y = @intToPtr([*]align(4) u8, 5); - _ = y; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address diff --git a/test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig b/test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig deleted file mode 100644 index cdc1def677..0000000000 --- a/test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig +++ /dev/null @@ -1,17 +0,0 @@ -export fn foo1() void { - var bytes = [_]u8{1, 2}; - const word: u16 = @bitCast(u16, bytes[0..]); - _ = word; -} -export fn foo2() void { - var bytes: []const u8 = &[_]u8{1, 2}; - const word: u16 = @bitCast(u16, bytes); - _ = word; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:42: error: unable to @bitCast from pointer type '*[2]u8' -// tmp.zig:8:32: error: destination type 'u16' has size 2 but source type '[]const u8' has size 16 -- cgit v1.2.3