diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 2c94569bf1..b6107e6fa5 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -979,9 +979,59 @@ test "std.meta.cast" { /// Given a value returns its size as C's sizeof operator would. /// This is for translate-c and is not intended for general use. pub fn sizeof(target: anytype) usize { - switch (@typeInfo(@TypeOf(target))) { - .Type => return @sizeOf(target), - .Float, .Int, .Struct, .Union, .Enum => return @sizeOf(@TypeOf(target)), + const T: type = if (@TypeOf(target) == type) target else @TypeOf(target); + switch (@typeInfo(T)) { + .Float, .Int, .Struct, .Union, .Enum, .Array, .Bool, .Vector => return @sizeOf(T), + .Fn => { + // sizeof(main) returns 1, sizeof(&main) returns pointer size. + // We cannot distinguish those types in Zig, so use pointer size. + return @sizeOf(T); + }, + .Null => return @sizeOf(*c_void), + .Void => { + // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC. + return 1; + }, + .Opaque => { + if (T == c_void) { + // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC. + return 1; + } else { + @compileError("Cannot use C sizeof on opaque type "++@typeName(T)); + } + }, + .Optional => |opt| { + if (@typeInfo(opt.child) == .Pointer) { + return sizeof(opt.child); + } else { + @compileError("Cannot use C sizeof on non-pointer optional "++@typeName(T)); + } + }, + .Pointer => |ptr| { + if (ptr.size == .Slice) { + @compileError("Cannot use C sizeof on slice type "++@typeName(T)); + } + // for strings, sizeof("a") returns 2. + // normal pointer decay scenarios from C are handled + // in the .Array case above, but strings remain literals + // and are therefore always pointers, so they need to be + // specially handled here. + if (ptr.size == .One and ptr.is_const and @typeInfo(ptr.child) == .Array) { + const array_info = @typeInfo(ptr.child).Array; + if ((array_info.child == u8 or array_info.child == u16) and + array_info.sentinel != null and + array_info.sentinel.? == 0) { + // length of the string plus one for the null terminator. + return (array_info.len + 1) * @sizeOf(array_info.child); + } + } + // When zero sized pointers are removed, this case will no + // longer be reachable and can be deleted. + if (@sizeOf(T) == 0) { + return @sizeOf(*c_void); + } + return @sizeOf(T); + }, .ComptimeFloat => return @sizeOf(f64), // TODO c_double #3999 .ComptimeInt => { // TODO to get the correct result we have to translate @@ -991,7 +1041,7 @@ pub fn sizeof(target: anytype) usize { // TODO test if target fits in int, long or long long return @sizeOf(c_int); }, - else => @compileError("TODO implement std.meta.sizeof for type " ++ @typeName(@TypeOf(target))), + else => @compileError("std.meta.sizeof does not support type " ++ @typeName(T)), } } @@ -999,12 +1049,45 @@ test "sizeof" { const E = extern enum(c_int) { One, _ }; const S = extern struct { a: u32 }; + const ptr_size = @sizeOf(*c_void); + testing.expect(sizeof(u32) == 4); testing.expect(sizeof(@as(u32, 2)) == 4); testing.expect(sizeof(2) == @sizeOf(c_int)); + + testing.expect(sizeof(2.0) == @sizeOf(f64)); + testing.expect(sizeof(E) == @sizeOf(c_int)); testing.expect(sizeof(E.One) == @sizeOf(c_int)); + testing.expect(sizeof(S) == 4); + + testing.expect(sizeof([_]u32{4, 5, 6}) == 12); + testing.expect(sizeof([3]u32) == 12); + testing.expect(sizeof([3:0]u32) == 16); + testing.expect(sizeof(&[_]u32{4, 5, 6}) == ptr_size); + + testing.expect(sizeof(*u32) == ptr_size); + testing.expect(sizeof([*]u32) == ptr_size); + testing.expect(sizeof([*c]u32) == ptr_size); + testing.expect(sizeof(?*u32) == ptr_size); + testing.expect(sizeof(?[*]u32) == ptr_size); + testing.expect(sizeof(*c_void) == ptr_size); + testing.expect(sizeof(*void) == ptr_size); + testing.expect(sizeof(null) == ptr_size); + + testing.expect(sizeof("foobar") == 7); + testing.expect(sizeof(&[_:0]u16{'f','o','o','b','a','r'}) == 14); + testing.expect(sizeof(*const [4:0]u8) == 5); + testing.expect(sizeof(*[4:0]u8) == ptr_size); + testing.expect(sizeof([*]const [4:0]u8) == ptr_size); + testing.expect(sizeof(*const *const [4:0]u8) == ptr_size); + testing.expect(sizeof(*const [4]u8) == ptr_size); + + testing.expect(sizeof(sizeof) == @sizeOf(@TypeOf(sizeof))); + + testing.expect(sizeof(void) == 1); + testing.expect(sizeof(c_void) == 1); } /// For a given function type, returns a tuple type which fields will