authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-11 14:18:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-14 21:42:42-07:00
log34a6fcd88e20a2491bf6a5396d583a7449cac20b
tree02eba522b43876b9145d8369332557d47d3f25c5
parentbbd750ff05895f29be646bf51e8932c3c9fb14f3

stage2: Add hasWellDefinedLayout() to type.zig and Sema.zig

This follows the same strategy as sema.typeRequiresComptime() and type.comptimeOnly(): Two versions of the function, one which performs resolution just-in-time and another which asserts that resolution is complete. Thankfully, this doesn't cause very viral type resolution, since auto-layout structs and unions are very common and are known to not have a well-defined layout without resolving their fields.

3 files changed, 348 insertions(+), 14 deletions(-)

src/Module.zig+7-3
...@@ -852,7 +852,7 @@ pub const ErrorSet = struct {...@@ -852,7 +852,7 @@ pub const ErrorSet = struct {
852 }852 }
853};853};
854854
855pub const RequiresComptime = enum { no, yes, unknown, wip };855pub const PropertyBoolean = enum { no, yes, unknown, wip };
856856
857/// Represents the data that a struct declaration provides.857/// Represents the data that a struct declaration provides.
858pub const Struct = struct {858pub const Struct = struct {
...@@ -884,7 +884,8 @@ pub const Struct = struct {...@@ -884,7 +884,8 @@ pub const Struct = struct {
884 /// If false, resolving the fields is necessary to determine whether the type has only884 /// If false, resolving the fields is necessary to determine whether the type has only
885 /// one possible value.885 /// one possible value.
886 known_non_opv: bool,886 known_non_opv: bool,
887 requires_comptime: RequiresComptime = .unknown,887 requires_comptime: PropertyBoolean = .unknown,
888 has_well_defined_layout: PropertyBoolean = .unknown,
888889
889 pub const Fields = std.StringArrayHashMapUnmanaged(Field);890 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
890891
...@@ -1079,6 +1080,8 @@ pub const EnumFull = struct {...@@ -1079,6 +1080,8 @@ pub const EnumFull = struct {
1079 /// An integer type which is used for the numerical value of the enum.1080 /// An integer type which is used for the numerical value of the enum.
1080 /// Whether zig chooses this type or the user specifies it, it is stored here.1081 /// Whether zig chooses this type or the user specifies it, it is stored here.
1081 tag_ty: Type,1082 tag_ty: Type,
1083 /// true if zig inferred this tag type, false if user specified it
1084 tag_ty_inferred: bool,
1082 /// Set of field names in declaration order.1085 /// Set of field names in declaration order.
1083 fields: NameMap,1086 fields: NameMap,
1084 /// Maps integer tag value to field index.1087 /// Maps integer tag value to field index.
...@@ -1132,7 +1135,8 @@ pub const Union = struct {...@@ -1132,7 +1135,8 @@ pub const Union = struct {
1132 // which `have_layout` does not ensure.1135 // which `have_layout` does not ensure.
1133 fully_resolved,1136 fully_resolved,
1134 },1137 },
1135 requires_comptime: RequiresComptime = .unknown,1138 requires_comptime: PropertyBoolean = .unknown,
1139 has_well_defined_layout: PropertyBoolean = .unknown,
11361140
1137 pub const Field = struct {1141 pub const Field = struct {
1138 /// undefined until `status` is `have_field_types` or `have_layout`.1142 /// undefined until `status` is `have_field_types` or `have_layout`.
src/Sema.zig+197-11
...@@ -1579,6 +1579,8 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -1579,6 +1579,8 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1579 const target = sema.mod.getTarget();1579 const target = sema.mod.getTarget();
1580 const addr_space = target_util.defaultAddressSpace(target, .local);1580 const addr_space = target_util.defaultAddressSpace(target, .local);
15811581
1582 try sema.resolveTypeLayout(block, src, pointee_ty);
1583
1582 if (Air.refToIndex(ptr)) |ptr_inst| {1584 if (Air.refToIndex(ptr)) |ptr_inst| {
1583 if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) {1585 if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) {
1584 const air_datas = sema.air_instructions.items(.data);1586 const air_datas = sema.air_instructions.items(.data);
...@@ -1885,6 +1887,7 @@ fn zirEnumDecl(...@@ -1885,6 +1887,7 @@ fn zirEnumDecl(
1885 enum_obj.* = .{1887 enum_obj.* = .{
1886 .owner_decl = new_decl,1888 .owner_decl = new_decl,
1887 .tag_ty = Type.initTag(.@"null"),1889 .tag_ty = Type.initTag(.@"null"),
1890 .tag_ty_inferred = true,
1888 .fields = .{},1891 .fields = .{},
1889 .values = .{},1892 .values = .{},
1890 .node_offset = src.node_offset,1893 .node_offset = src.node_offset,
...@@ -1907,6 +1910,7 @@ fn zirEnumDecl(...@@ -1907,6 +1910,7 @@ fn zirEnumDecl(
1907 // TODO better source location1910 // TODO better source location
1908 const ty = try sema.resolveType(block, src, tag_type_ref);1911 const ty = try sema.resolveType(block, src, tag_type_ref);
1909 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);1912 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1913 enum_obj.tag_ty_inferred = false;
1910 }1914 }
1911 try new_decl.finalizeNewArena(&new_decl_arena);1915 try new_decl.finalizeNewArena(&new_decl_arena);
1912 return sema.analyzeDeclVal(block, src, new_decl);1916 return sema.analyzeDeclVal(block, src, new_decl);
...@@ -1956,16 +1960,16 @@ fn zirEnumDecl(...@@ -1956,16 +1960,16 @@ fn zirEnumDecl(
19561960
1957 try wip_captures.finalize();1961 try wip_captures.finalize();
19581962
1959 const tag_ty = blk: {1963 if (tag_type_ref != .none) {
1960 if (tag_type_ref != .none) {1964 // TODO better source location
1961 // TODO better source location1965 const ty = try sema.resolveType(block, src, tag_type_ref);
1962 const ty = try sema.resolveType(block, src, tag_type_ref);1966 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1963 break :blk try ty.copy(new_decl_arena_allocator);1967 enum_obj.tag_ty_inferred = false;
1964 }1968 } else {
1965 const bits = std.math.log2_int_ceil(usize, fields_len);1969 const bits = std.math.log2_int_ceil(usize, fields_len);
1966 break :blk try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);1970 enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
1967 };1971 enum_obj.tag_ty_inferred = true;
1968 enum_obj.tag_ty = tag_ty;1972 }
1969 }1973 }
19701974
1971 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);1975 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
...@@ -2417,13 +2421,13 @@ fn zirAllocExtended(...@@ -2417,13 +2421,13 @@ fn zirAllocExtended(
2417 try sema.validateVarType(block, ty_src, var_ty, false);2421 try sema.validateVarType(block, ty_src, var_ty, false);
2418 }2422 }
2419 const target = sema.mod.getTarget();2423 const target = sema.mod.getTarget();
2424 try sema.requireRuntimeBlock(block, src);
2425 try sema.resolveTypeLayout(block, src, var_ty);
2420 const ptr_type = try Type.ptr(sema.arena, target, .{2426 const ptr_type = try Type.ptr(sema.arena, target, .{
2421 .pointee_type = var_ty,2427 .pointee_type = var_ty,
2422 .@"align" = alignment,2428 .@"align" = alignment,
2423 .@"addrspace" = target_util.defaultAddressSpace(target, .local),2429 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2424 });2430 });
2425 try sema.requireRuntimeBlock(block, src);
2426 try sema.resolveTypeLayout(block, src, var_ty);
2427 return block.addTy(.alloc, ptr_type);2431 return block.addTy(.alloc, ptr_type);
2428 }2432 }
24292433
...@@ -21209,6 +21213,182 @@ fn typePtrOrOptionalPtrTy(...@@ -21209,6 +21213,182 @@ fn typePtrOrOptionalPtrTy(
21209 }21213 }
21210}21214}
2121121215
21216fn typeHasWellDefinedLayout(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
21217 return switch (ty.tag()) {
21218 .u1,
21219 .u8,
21220 .i8,
21221 .u16,
21222 .i16,
21223 .u32,
21224 .i32,
21225 .u64,
21226 .i64,
21227 .u128,
21228 .i128,
21229 .usize,
21230 .isize,
21231 .c_short,
21232 .c_ushort,
21233 .c_int,
21234 .c_uint,
21235 .c_long,
21236 .c_ulong,
21237 .c_longlong,
21238 .c_ulonglong,
21239 .c_longdouble,
21240 .f16,
21241 .f32,
21242 .f64,
21243 .f80,
21244 .f128,
21245 .bool,
21246 .void,
21247 .manyptr_u8,
21248 .manyptr_const_u8,
21249 .manyptr_const_u8_sentinel_0,
21250 .anyerror_void_error_union,
21251 .empty_struct_literal,
21252 .empty_struct,
21253 .array_u8,
21254 .array_u8_sentinel_0,
21255 .int_signed,
21256 .int_unsigned,
21257 .pointer,
21258 .single_const_pointer,
21259 .single_mut_pointer,
21260 .many_const_pointer,
21261 .many_mut_pointer,
21262 .c_const_pointer,
21263 .c_mut_pointer,
21264 .single_const_pointer_to_comptime_int,
21265 .enum_numbered,
21266 => true,
21267
21268 .anyopaque,
21269 .anyerror,
21270 .noreturn,
21271 .@"null",
21272 .@"anyframe",
21273 .@"undefined",
21274 .atomic_order,
21275 .atomic_rmw_op,
21276 .calling_convention,
21277 .address_space,
21278 .float_mode,
21279 .reduce_op,
21280 .call_options,
21281 .prefetch_options,
21282 .export_options,
21283 .extern_options,
21284 .error_set,
21285 .error_set_single,
21286 .error_set_inferred,
21287 .error_set_merged,
21288 .@"opaque",
21289 .generic_poison,
21290 .type,
21291 .comptime_int,
21292 .comptime_float,
21293 .enum_literal,
21294 .type_info,
21295 // These are function bodies, not function pointers.
21296 .fn_noreturn_no_args,
21297 .fn_void_no_args,
21298 .fn_naked_noreturn_no_args,
21299 .fn_ccc_void_no_args,
21300 .function,
21301 .const_slice_u8,
21302 .const_slice_u8_sentinel_0,
21303 .const_slice,
21304 .mut_slice,
21305 .enum_simple,
21306 .error_union,
21307 .anyframe_T,
21308 .tuple,
21309 .anon_struct,
21310 => false,
21311
21312 .enum_full,
21313 .enum_nonexhaustive,
21314 => !ty.cast(Type.Payload.EnumFull).?.data.tag_ty_inferred,
21315
21316 .var_args_param => unreachable,
21317 .inferred_alloc_mut => unreachable,
21318 .inferred_alloc_const => unreachable,
21319 .bound_fn => unreachable,
21320
21321 .array,
21322 .array_sentinel,
21323 .vector,
21324 => sema.typeHasWellDefinedLayout(block, src, ty.childType()),
21325
21326 .optional,
21327 .optional_single_mut_pointer,
21328 .optional_single_const_pointer,
21329 => blk: {
21330 var buf: Type.Payload.ElemType = undefined;
21331 break :blk sema.typeHasWellDefinedLayout(block, src, ty.optionalChild(&buf));
21332 },
21333
21334 .@"struct" => {
21335 const struct_obj = ty.castTag(.@"struct").?.data;
21336 if (struct_obj.layout == .Auto) {
21337 struct_obj.has_well_defined_layout = .no;
21338 return false;
21339 }
21340 switch (struct_obj.has_well_defined_layout) {
21341 .no => return false,
21342 .yes, .wip => return true,
21343 .unknown => {
21344 if (struct_obj.status == .field_types_wip)
21345 return true;
21346
21347 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
21348
21349 struct_obj.has_well_defined_layout = .wip;
21350 for (struct_obj.fields.values()) |field| {
21351 if (!(try sema.typeHasWellDefinedLayout(block, src, field.ty))) {
21352 struct_obj.has_well_defined_layout = .no;
21353 return false;
21354 }
21355 }
21356 struct_obj.has_well_defined_layout = .yes;
21357 return true;
21358 },
21359 }
21360 },
21361
21362 .@"union", .union_tagged => {
21363 const union_obj = ty.cast(Type.Payload.Union).?.data;
21364 if (union_obj.layout == .Auto) {
21365 union_obj.has_well_defined_layout = .no;
21366 return false;
21367 }
21368 switch (union_obj.has_well_defined_layout) {
21369 .no => return false,
21370 .yes, .wip => return true,
21371 .unknown => {
21372 if (union_obj.status == .field_types_wip)
21373 return true;
21374
21375 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
21376
21377 union_obj.has_well_defined_layout = .wip;
21378 for (union_obj.fields.values()) |field| {
21379 if (!(try sema.typeHasWellDefinedLayout(block, src, field.ty))) {
21380 union_obj.has_well_defined_layout = .no;
21381 return false;
21382 }
21383 }
21384 union_obj.has_well_defined_layout = .yes;
21385 return true;
21386 },
21387 }
21388 },
21389 };
21390}
21391
21212/// `generic_poison` will return false.21392/// `generic_poison` will return false.
21213/// This function returns false negatives when structs and unions are having their21393/// This function returns false negatives when structs and unions are having their
21214/// field types resolved.21394/// field types resolved.
...@@ -21412,6 +21592,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -21412,6 +21592,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
21412 return true;21592 return true;
21413}21593}
2141421594
21595fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 {
21596 try sema.resolveTypeLayout(block, src, ty);
21597 const target = sema.mod.getTarget();
21598 return ty.abiSize(target);
21599}
21600
21415fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 {21601fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 {
21416 try sema.resolveTypeLayout(block, src, ty);21602 try sema.resolveTypeLayout(block, src, ty);
21417 const target = sema.mod.getTarget();21603 const target = sema.mod.getTarget();
src/type.zig+144
...@@ -2173,6 +2173,149 @@ pub const Type = extern union {...@@ -2173,6 +2173,149 @@ pub const Type = extern union {
2173 };2173 };
2174 }2174 }
21752175
2176 /// true if and only if the type has a well-defined memory layout
2177 /// readFrom/writeToMemory are supported only for types with a well-
2178 /// defined memory layout
2179 pub fn hasWellDefinedLayout(ty: Type) bool {
2180 return switch (ty.tag()) {
2181 .u1,
2182 .u8,
2183 .i8,
2184 .u16,
2185 .i16,
2186 .u32,
2187 .i32,
2188 .u64,
2189 .i64,
2190 .u128,
2191 .i128,
2192 .usize,
2193 .isize,
2194 .c_short,
2195 .c_ushort,
2196 .c_int,
2197 .c_uint,
2198 .c_long,
2199 .c_ulong,
2200 .c_longlong,
2201 .c_ulonglong,
2202 .c_longdouble,
2203 .f16,
2204 .f32,
2205 .f64,
2206 .f80,
2207 .f128,
2208 .bool,
2209 .void,
2210 .manyptr_u8,
2211 .manyptr_const_u8,
2212 .manyptr_const_u8_sentinel_0,
2213 .anyerror_void_error_union,
2214 .empty_struct_literal,
2215 .empty_struct,
2216 .array_u8,
2217 .array_u8_sentinel_0,
2218 .int_signed,
2219 .int_unsigned,
2220 .pointer,
2221 .single_const_pointer,
2222 .single_mut_pointer,
2223 .many_const_pointer,
2224 .many_mut_pointer,
2225 .c_const_pointer,
2226 .c_mut_pointer,
2227 .single_const_pointer_to_comptime_int,
2228 .enum_numbered,
2229 => true,
2230
2231 .anyopaque,
2232 .anyerror,
2233 .noreturn,
2234 .@"null",
2235 .@"anyframe",
2236 .@"undefined",
2237 .atomic_order,
2238 .atomic_rmw_op,
2239 .calling_convention,
2240 .address_space,
2241 .float_mode,
2242 .reduce_op,
2243 .call_options,
2244 .prefetch_options,
2245 .export_options,
2246 .extern_options,
2247 .error_set,
2248 .error_set_single,
2249 .error_set_inferred,
2250 .error_set_merged,
2251 .@"opaque",
2252 .generic_poison,
2253 .type,
2254 .comptime_int,
2255 .comptime_float,
2256 .enum_literal,
2257 .type_info,
2258 // These are function bodies, not function pointers.
2259 .fn_noreturn_no_args,
2260 .fn_void_no_args,
2261 .fn_naked_noreturn_no_args,
2262 .fn_ccc_void_no_args,
2263 .function,
2264 .const_slice_u8,
2265 .const_slice_u8_sentinel_0,
2266 .const_slice,
2267 .mut_slice,
2268 .enum_simple,
2269 .error_union,
2270 .anyframe_T,
2271 .tuple,
2272 .anon_struct,
2273 => false,
2274
2275 .enum_full,
2276 .enum_nonexhaustive,
2277 => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred,
2278
2279 .var_args_param => unreachable,
2280 .inferred_alloc_mut => unreachable,
2281 .inferred_alloc_const => unreachable,
2282 .bound_fn => unreachable,
2283
2284 .array,
2285 .array_sentinel,
2286 .vector,
2287 => ty.childType().hasWellDefinedLayout(),
2288
2289 .optional,
2290 .optional_single_mut_pointer,
2291 .optional_single_const_pointer,
2292 => {
2293 var buf: Type.Payload.ElemType = undefined;
2294 return ty.optionalChild(&buf).hasWellDefinedLayout();
2295 },
2296
2297 .@"struct" => {
2298 const struct_obj = ty.castTag(.@"struct").?.data;
2299 if (struct_obj.layout == .Auto) return false;
2300 switch (struct_obj.has_well_defined_layout) {
2301 .wip, .unknown => unreachable, // This function asserts types already resolved.
2302 .no => return false,
2303 .yes => return true,
2304 }
2305 },
2306
2307 .@"union", .union_tagged => {
2308 const union_obj = ty.cast(Type.Payload.Union).?.data;
2309 if (union_obj.layout == .Auto) return false;
2310 switch (union_obj.has_well_defined_layout) {
2311 .wip, .unknown => unreachable, // This function asserts types already resolved.
2312 .no => return false,
2313 .yes => return true,
2314 }
2315 },
2316 };
2317 }
2318
2176 pub fn hasRuntimeBits(ty: Type) bool {2319 pub fn hasRuntimeBits(ty: Type) bool {
2177 return hasRuntimeBitsAdvanced(ty, false);2320 return hasRuntimeBitsAdvanced(ty, false);
2178 }2321 }
...@@ -3263,6 +3406,7 @@ pub const Type = extern union {...@@ -3263,6 +3406,7 @@ pub const Type = extern union {
3263 /// For ?[*]T, returns T.3406 /// For ?[*]T, returns T.
3264 /// For *T, returns T.3407 /// For *T, returns T.
3265 /// For [*]T, returns T.3408 /// For [*]T, returns T.
3409 /// For [N]T, returns T.
3266 /// For []T, returns T.3410 /// For []T, returns T.
3267 pub fn elemType2(ty: Type) Type {3411 pub fn elemType2(ty: Type) Type {
3268 return switch (ty.tag()) {3412 return switch (ty.tag()) {