Commit 57f9405a8f

Veikka Tuominen <git@vexu.eu>
2022-07-28 14:00:40
Sema: validate bitcast operand type
1 parent e7b6a18
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);
 }
 
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 });
test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig
@@ -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
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'
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
test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig → test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig
@@ -10,8 +10,10 @@ export fn foo2() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // 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
+// :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'