Commit 9a94415680

xackus <14938807+xackus@users.noreply.github.com>
2021-03-13 17:36:38
translate-c: improve std.meta.cast
1 parent 83d0c2e
Changed files (2)
lib/std/meta.zig
@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
 /// Given a type and value, cast the value to the type as c would.
 /// This is for translate-c and is not intended for general use.
 pub fn cast(comptime DestType: type, target: anytype) DestType {
-    const TargetType = @TypeOf(target);
+    // this function should behave like transCCast in translate-c, except it's for macros
+    const SourceType = @TypeOf(target);
     switch (@typeInfo(DestType)) {
-        .Pointer => |dest_ptr| {
-            switch (@typeInfo(TargetType)) {
+        .Pointer => {
+            switch (@typeInfo(SourceType)) {
                 .Int, .ComptimeInt => {
                     return @intToPtr(DestType, target);
                 },
-                .Pointer => |ptr| {
-                    return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
+                .Pointer => {
+                    return castPtr(DestType, target);
                 },
                 .Optional => |opt| {
                     if (@typeInfo(opt.child) == .Pointer) {
-                        return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
+                        return castPtr(DestType, target);
                     }
                 },
                 else => {},
@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
         },
         .Optional => |dest_opt| {
             if (@typeInfo(dest_opt.child) == .Pointer) {
-                const dest_ptr = @typeInfo(dest_opt.child).Pointer;
-                switch (@typeInfo(TargetType)) {
+                switch (@typeInfo(SourceType)) {
                     .Int, .ComptimeInt => {
                         return @intToPtr(DestType, target);
                     },
                     .Pointer => {
-                        return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
+                        return castPtr(DestType, target);
                     },
                     .Optional => |target_opt| {
                         if (@typeInfo(target_opt.child) == .Pointer) {
-                            return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
+                            return castPtr(DestType, target);
                         }
                     },
                     else => {},
@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
             }
         },
         .Enum => {
-            if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
+            if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
                 return @intToEnum(DestType, target);
             }
         },
-        .Int, .ComptimeInt => {
-            switch (@typeInfo(TargetType)) {
+        .Int => {
+            switch (@typeInfo(SourceType)) {
                 .Pointer => {
-                    return @intCast(DestType, @ptrToInt(target));
+                    return castInt(DestType, @ptrToInt(target));
                 },
                 .Optional => |opt| {
                     if (@typeInfo(opt.child) == .Pointer) {
-                        return @intCast(DestType, @ptrToInt(target));
+                        return castInt(DestType, @ptrToInt(target));
                     }
                 },
                 .Enum => {
-                    return @intCast(DestType, @enumToInt(target));
+                    return castInt(DestType, @enumToInt(target));
                 },
-                .Int, .ComptimeInt => {
-                    return @intCast(DestType, target);
+                .Int => {
+                    return castInt(DestType, target);
                 },
                 else => {},
             }
@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
     return @as(DestType, target);
 }
 
+fn castInt(comptime DestType: type, target: anytype) DestType {
+    const dest = @typeInfo(DestType).Int;
+    const source = @typeInfo(@TypeOf(target)).Int;
+
+    if (dest.bits < source.bits)
+        return @bitCast(DestType, @truncate(Int(source.signedness, dest.bits), target))
+    else
+        return @bitCast(DestType, @as(Int(source.signedness, dest.bits), target));
+}
+
+fn castPtr(comptime DestType: type, target: anytype) DestType {
+    const dest = ptrInfo(DestType);
+    const source = ptrInfo(@TypeOf(target));
+
+    if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
+        return @intToPtr(DestType, @ptrToInt(target))
+    else
+        return @ptrCast(DestType, @alignCast(dest.alignment, target));
+}
+
+fn ptrInfo(comptime PtrType: type) TypeInfo.Pointer {
+    return switch(@typeInfo(PtrType)){
+        .Optional => |opt_info| @typeInfo(opt_info.child).Pointer,
+        .Pointer => |ptr_info| ptr_info,
+        else => unreachable,
+    };
+}
+
 test "std.meta.cast" {
     const E = enum(u2) {
         Zero,
@@ -977,6 +1005,11 @@ test "std.meta.cast" {
     testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
     testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
     testing.expectEqual(@as(u8, 2), cast(u8, E.Two));
+
+    testing.expectEqual(@bitCast(i32, @as(u32, 0x8000_0000)), cast(i32, @as(u32, 0x8000_0000)));
+
+    testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2)));
+    testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
 }
 
 /// Given a value returns its size as C's sizeof operator would.
src/translate_c.zig
@@ -1836,6 +1836,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
     return enum_decl.getIntegerType();
 }
 
+// when modifying this function, make sure to also update std.meta.cast
 fn transCCast(
     c: *Context,
     scope: *Scope,