authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 16:02:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 16:02:42-07:00
log3b6e8fa59e6fb933e8279ba676ef986739665247
tree5308495e3c5600b17af1bf96fdb68d23ecd4e4a2
parentfd85cfe15457bd695b978e327ce8af84c7990c28

Sema: fix crash with `@sizeOf` on unions


5 files changed, 35 insertions(+), 17 deletions(-)

src/Module.zig+1-1
...@@ -1281,7 +1281,7 @@ pub const Union = struct {...@@ -1281,7 +1281,7 @@ pub const Union = struct {
1281 var payload_align: u32 = 0;1281 var payload_align: u32 = 0;
1282 const fields = u.fields.values();1282 const fields = u.fields.values();
1283 for (fields) |field, i| {1283 for (fields) |field, i| {
1284 if (!field.ty.hasRuntimeBits()) continue;1284 if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue;
12851285
1286 const field_align = a: {1286 const field_align = a: {
1287 if (field.abi_align.tag() == .abi_align_default) {1287 if (field.abi_align.tag() == .abi_align_default) {
src/Sema.zig+1
...@@ -9955,6 +9955,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -9955,6 +9955,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
9955 .BoundFn,9955 .BoundFn,
9956 .Opaque,9956 .Opaque,
9957 => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty}),9957 => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty}),
9958
9958 .Type,9959 .Type,
9959 .EnumLiteral,9960 .EnumLiteral,
9960 .ComptimeFloat,9961 .ComptimeFloat,
src/type.zig+23-11
...@@ -1967,7 +1967,9 @@ pub const Type = extern union {...@@ -1967,7 +1967,9 @@ pub const Type = extern union {
1967 /// There are two reasons a type will return false:1967 /// There are two reasons a type will return false:
1968 /// * the type is a comptime-only type. For example, the type `type` itself.1968 /// * the type is a comptime-only type. For example, the type `type` itself.
1969 /// * the type has only one possible value, making its ABI size 0.1969 /// * the type has only one possible value, making its ABI size 0.
1970 pub fn hasRuntimeBits(ty: Type) bool {1970 /// When `ignore_comptime_only` is true, then types that are comptime only
1971 /// may return false positives.
1972 pub fn hasRuntimeBitsAdvanced(ty: Type, ignore_comptime_only: bool) bool {
1971 return switch (ty.tag()) {1973 return switch (ty.tag()) {
1972 .u1,1974 .u1,
1973 .u8,1975 .u8,
...@@ -2063,7 +2065,7 @@ pub const Type = extern union {...@@ -2063,7 +2065,7 @@ pub const Type = extern union {
2063 .const_slice,2065 .const_slice,
2064 .mut_slice,2066 .mut_slice,
2065 .pointer,2067 .pointer,
2066 => !ty.comptimeOnly(),2068 => if (ignore_comptime_only) true else !comptimeOnly(ty),
20672069
2068 .@"struct" => {2070 .@"struct" => {
2069 const struct_obj = ty.castTag(.@"struct").?.data;2071 const struct_obj = ty.castTag(.@"struct").?.data;
...@@ -2075,7 +2077,7 @@ pub const Type = extern union {...@@ -2075,7 +2077,7 @@ pub const Type = extern union {
2075 }2077 }
2076 assert(struct_obj.haveFieldTypes());2078 assert(struct_obj.haveFieldTypes());
2077 for (struct_obj.fields.values()) |value| {2079 for (struct_obj.fields.values()) |value| {
2078 if (value.ty.hasRuntimeBits())2080 if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only))
2079 return true;2081 return true;
2080 } else {2082 } else {
2081 return false;2083 return false;
...@@ -2093,14 +2095,14 @@ pub const Type = extern union {...@@ -2093,14 +2095,14 @@ pub const Type = extern union {
2093 .enum_numbered, .enum_nonexhaustive => {2095 .enum_numbered, .enum_nonexhaustive => {
2094 var buffer: Payload.Bits = undefined;2096 var buffer: Payload.Bits = undefined;
2095 const int_tag_ty = ty.intTagType(&buffer);2097 const int_tag_ty = ty.intTagType(&buffer);
2096 return int_tag_ty.hasRuntimeBits();2098 return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only);
2097 },2099 },
20982100
2099 .@"union" => {2101 .@"union" => {
2100 const union_obj = ty.castTag(.@"union").?.data;2102 const union_obj = ty.castTag(.@"union").?.data;
2101 assert(union_obj.haveFieldTypes());2103 assert(union_obj.haveFieldTypes());
2102 for (union_obj.fields.values()) |value| {2104 for (union_obj.fields.values()) |value| {
2103 if (value.ty.hasRuntimeBits())2105 if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only))
2104 return true;2106 return true;
2105 } else {2107 } else {
2106 return false;2108 return false;
...@@ -2108,27 +2110,29 @@ pub const Type = extern union {...@@ -2108,27 +2110,29 @@ pub const Type = extern union {
2108 },2110 },
2109 .union_tagged => {2111 .union_tagged => {
2110 const union_obj = ty.castTag(.union_tagged).?.data;2112 const union_obj = ty.castTag(.union_tagged).?.data;
2111 if (union_obj.tag_ty.hasRuntimeBits()) {2113 if (union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) {
2112 return true;2114 return true;
2113 }2115 }
2114 assert(union_obj.haveFieldTypes());2116 assert(union_obj.haveFieldTypes());
2115 for (union_obj.fields.values()) |value| {2117 for (union_obj.fields.values()) |value| {
2116 if (value.ty.hasRuntimeBits())2118 if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only))
2117 return true;2119 return true;
2118 } else {2120 } else {
2119 return false;2121 return false;
2120 }2122 }
2121 },2123 },
21222124
2123 .array, .vector => ty.arrayLen() != 0 and ty.elemType().hasRuntimeBits(),2125 .array, .vector => ty.arrayLen() != 0 and
2126 ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only),
2124 .array_u8 => ty.arrayLen() != 0,2127 .array_u8 => ty.arrayLen() != 0,
2125 .array_sentinel => ty.childType().hasRuntimeBits(),2128 .array_sentinel => ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only),
21262129
2127 .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data != 0,2130 .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data != 0,
21282131
2129 .error_union => {2132 .error_union => {
2130 const payload = ty.castTag(.error_union).?.data;2133 const payload = ty.castTag(.error_union).?.data;
2131 return payload.error_set.hasRuntimeBits() or payload.payload.hasRuntimeBits();2134 return payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only) or
2135 payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only);
2132 },2136 },
21332137
2134 .tuple, .anon_struct => {2138 .tuple, .anon_struct => {
...@@ -2136,7 +2140,7 @@ pub const Type = extern union {...@@ -2136,7 +2140,7 @@ pub const Type = extern union {
2136 for (tuple.types) |field_ty, i| {2140 for (tuple.types) |field_ty, i| {
2137 const val = tuple.values[i];2141 const val = tuple.values[i];
2138 if (val.tag() != .unreachable_value) continue; // comptime field2142 if (val.tag() != .unreachable_value) continue; // comptime field
2139 if (field_ty.hasRuntimeBits()) return true;2143 if (field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) return true;
2140 }2144 }
2141 return false;2145 return false;
2142 },2146 },
...@@ -2148,6 +2152,14 @@ pub const Type = extern union {...@@ -2148,6 +2152,14 @@ pub const Type = extern union {
2148 };2152 };
2149 }2153 }
21502154
2155 pub fn hasRuntimeBits(ty: Type) bool {
2156 return hasRuntimeBitsAdvanced(ty, false);
2157 }
2158
2159 pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool {
2160 return hasRuntimeBitsAdvanced(ty, true);
2161 }
2162
2151 pub fn isFnOrHasRuntimeBits(ty: Type) bool {2163 pub fn isFnOrHasRuntimeBits(ty: Type) bool {
2152 switch (ty.zigTypeTag()) {2164 switch (ty.zigTypeTag()) {
2153 .Fn => {2165 .Fn => {
test/behavior/for.zig+3-2
...@@ -178,7 +178,8 @@ fn mangleString(s: []u8) void {...@@ -178,7 +178,8 @@ fn mangleString(s: []u8) void {
178}178}
179179
180test "for copies its payload" {180test "for copies its payload" {
181 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
182 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
182183
183 const S = struct {184 const S = struct {
184 fn doTheTest() !void {185 fn doTheTest() !void {
...@@ -186,7 +187,7 @@ test "for copies its payload" {...@@ -186,7 +187,7 @@ test "for copies its payload" {
186 for (x) |value, i| {187 for (x) |value, i| {
187 // Modify the original array188 // Modify the original array
188 x[i] += 99;189 x[i] += 99;
189 try expectEqual(value, i + 1);190 try expect(value == i + 1);
190 }191 }
191 }192 }
192 };193 };
test/behavior/sizeof_and_typeof.zig+7-3
...@@ -233,7 +233,10 @@ test "@bitSizeOf" {...@@ -233,7 +233,10 @@ test "@bitSizeOf" {
233}233}
234234
235test "@sizeOf comparison against zero" {235test "@sizeOf comparison against zero" {
236 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO236 if (builtin.zig_backend == .stage1) {
237 // stage1 gets the wrong answer for size of pointers to zero bit types
238 return error.SkipZigTest;
239 }
237240
238 const S0 = struct {241 const S0 = struct {
239 f: *@This(),242 f: *@This(),
...@@ -263,12 +266,13 @@ test "@sizeOf comparison against zero" {...@@ -263,12 +266,13 @@ test "@sizeOf comparison against zero" {
263 };266 };
264 const S = struct {267 const S = struct {
265 fn doTheTest(comptime T: type, comptime result: bool) !void {268 fn doTheTest(comptime T: type, comptime result: bool) !void {
266 try expectEqual(result, @sizeOf(T) > 0);269 try expect(result == (@sizeOf(T) > 0));
267 }270 }
268 };271 };
269 // Zero-sized type272 // Zero-sized type
270 try S.doTheTest(u0, false);273 try S.doTheTest(u0, false);
271 try S.doTheTest(*u0, false);274 // Pointers to zero sized types still have addresses.
275 try S.doTheTest(*u0, true);
272 // Non byte-sized type276 // Non byte-sized type
273 try S.doTheTest(u1, true);277 try S.doTheTest(u1, true);
274 try S.doTheTest(*u1, true);278 try S.doTheTest(*u1, true);