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 {
852852 }
853853};
854854
855pub const RequiresComptime = enum { no, yes, unknown, wip };
855pub const PropertyBoolean = enum { no, yes, unknown, wip };
856856
857857/// Represents the data that a struct declaration provides.
858858pub const Struct = struct {
......@@ -884,7 +884,8 @@ pub const Struct = struct {
884884 /// If false, resolving the fields is necessary to determine whether the type has only
885885 /// one possible value.
886886 known_non_opv: bool,
887 requires_comptime: RequiresComptime = .unknown,
887 requires_comptime: PropertyBoolean = .unknown,
888 has_well_defined_layout: PropertyBoolean = .unknown,
888889
889890 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
890891
......@@ -1079,6 +1080,8 @@ pub const EnumFull = struct {
10791080 /// An integer type which is used for the numerical value of the enum.
10801081 /// Whether zig chooses this type or the user specifies it, it is stored here.
10811082 tag_ty: Type,
1083 /// true if zig inferred this tag type, false if user specified it
1084 tag_ty_inferred: bool,
10821085 /// Set of field names in declaration order.
10831086 fields: NameMap,
10841087 /// Maps integer tag value to field index.
......@@ -1132,7 +1135,8 @@ pub const Union = struct {
11321135 // which `have_layout` does not ensure.
11331136 fully_resolved,
11341137 },
1135 requires_comptime: RequiresComptime = .unknown,
1138 requires_comptime: PropertyBoolean = .unknown,
1139 has_well_defined_layout: PropertyBoolean = .unknown,
11361140
11371141 pub const Field = struct {
11381142 /// 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
15791579 const target = sema.mod.getTarget();
15801580 const addr_space = target_util.defaultAddressSpace(target, .local);
15811581
1582 try sema.resolveTypeLayout(block, src, pointee_ty);
1583
15821584 if (Air.refToIndex(ptr)) |ptr_inst| {
15831585 if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) {
15841586 const air_datas = sema.air_instructions.items(.data);
......@@ -1885,6 +1887,7 @@ fn zirEnumDecl(
18851887 enum_obj.* = .{
18861888 .owner_decl = new_decl,
18871889 .tag_ty = Type.initTag(.@"null"),
1890 .tag_ty_inferred = true,
18881891 .fields = .{},
18891892 .values = .{},
18901893 .node_offset = src.node_offset,
......@@ -1907,6 +1910,7 @@ fn zirEnumDecl(
19071910 // TODO better source location
19081911 const ty = try sema.resolveType(block, src, tag_type_ref);
19091912 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1913 enum_obj.tag_ty_inferred = false;
19101914 }
19111915 try new_decl.finalizeNewArena(&new_decl_arena);
19121916 return sema.analyzeDeclVal(block, src, new_decl);
......@@ -1956,16 +1960,16 @@ fn zirEnumDecl(
19561960
19571961 try wip_captures.finalize();
19581962
1959 const tag_ty = blk: {
1960 if (tag_type_ref != .none) {
1961 // TODO better source location
1962 const ty = try sema.resolveType(block, src, tag_type_ref);
1963 break :blk try ty.copy(new_decl_arena_allocator);
1964 }
1963 if (tag_type_ref != .none) {
1964 // TODO better source location
1965 const ty = try sema.resolveType(block, src, tag_type_ref);
1966 enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
1967 enum_obj.tag_ty_inferred = false;
1968 } else {
19651969 const bits = std.math.log2_int_ceil(usize, fields_len);
1966 break :blk try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
1967 };
1968 enum_obj.tag_ty = tag_ty;
1970 enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
1971 enum_obj.tag_ty_inferred = true;
1972 }
19691973 }
19701974
19711975 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
......@@ -2417,13 +2421,13 @@ fn zirAllocExtended(
24172421 try sema.validateVarType(block, ty_src, var_ty, false);
24182422 }
24192423 const target = sema.mod.getTarget();
2424 try sema.requireRuntimeBlock(block, src);
2425 try sema.resolveTypeLayout(block, src, var_ty);
24202426 const ptr_type = try Type.ptr(sema.arena, target, .{
24212427 .pointee_type = var_ty,
24222428 .@"align" = alignment,
24232429 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
24242430 });
2425 try sema.requireRuntimeBlock(block, src);
2426 try sema.resolveTypeLayout(block, src, var_ty);
24272431 return block.addTy(.alloc, ptr_type);
24282432 }
24292433
......@@ -21209,6 +21213,182 @@ fn typePtrOrOptionalPtrTy(
2120921213 }
2121021214}
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
2121221392/// `generic_poison` will return false.
2121321393/// This function returns false negatives when structs and unions are having their
2121421394/// field types resolved.
......@@ -21412,6 +21592,12 @@ pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2141221592 return true;
2141321593}
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
2141521601fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 {
2141621602 try sema.resolveTypeLayout(block, src, ty);
2141721603 const target = sema.mod.getTarget();
src/type.zig+144
......@@ -2173,6 +2173,149 @@ pub const Type = extern union {
21732173 };
21742174 }
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
21762319 pub fn hasRuntimeBits(ty: Type) bool {
21772320 return hasRuntimeBitsAdvanced(ty, false);
21782321 }
......@@ -3263,6 +3406,7 @@ pub const Type = extern union {
32633406 /// For ?[*]T, returns T.
32643407 /// For *T, returns T.
32653408 /// For [*]T, returns T.
3409 /// For [N]T, returns T.
32663410 /// For []T, returns T.
32673411 pub fn elemType2(ty: Type) Type {
32683412 return switch (ty.tag()) {