From 1033d71017c9c40bd55f1743cd7990024c892af7 Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 10 Oct 2023 12:30:44 +0100 Subject: [PATCH] Sema,type: unify type query functions The following pairs of functions have been combined using the "advanced" pattern used for other type queries: * `Sema.fnHasRuntimeBits`, `Type.isFnOrHasRuntimeBits` * `Sema.typeRequiresComptime`, `Type.comptimeOnly` --- src/Sema.zig | 199 ++------------------------------------------------- src/type.zig | 128 +++++++++++++++++++++++---------- 2 files changed, 94 insertions(+), 233 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a6f3f2154b..c9dacd1185 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -35301,7 +35301,7 @@ fn resolveSimpleType(sema: *Sema, simple_type: InternPool.SimpleType) CompileErr _ = try sema.getBuiltinType(builtin_type_name); } -fn resolveTypeFieldsStruct( +pub fn resolveTypeFieldsStruct( sema: *Sema, ty: InternPool.Index, struct_type: InternPool.Key.StructType, @@ -35340,7 +35340,7 @@ fn resolveTypeFieldsStruct( try semaStructFields(mod, sema.arena, struct_type); } -fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { +pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { const mod = sema.mod; const ip = &mod.intern_pool; const owner_decl = mod.declPtr(union_type.decl); @@ -37083,185 +37083,9 @@ fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { } /// `generic_poison` will return false. -/// This function returns false negatives when structs and unions are having their -/// field types resolved. -/// TODO assert the return value matches `ty.comptimeOnly` -/// TODO merge these implementations together with the "advanced"/opt_sema pattern seen -/// elsewhere in value.zig +/// May return false negatives when structs and unions are having their field types resolved. pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { - const mod = sema.mod; - const ip = &mod.intern_pool; - return switch (ty.toIntern()) { - .empty_struct_type => false, - - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => return false, - .ptr_type => |ptr_type| { - const child_ty = ptr_type.child.toType(); - switch (child_ty.zigTypeTag(mod)) { - .Fn => return !try sema.fnHasRuntimeBits(child_ty), - .Opaque => return false, - else => return sema.typeRequiresComptime(child_ty), - } - }, - .anyframe_type => |child| { - if (child == .none) return false; - return sema.typeRequiresComptime(child.toType()); - }, - .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()), - .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()), - .opt_type => |child| return sema.typeRequiresComptime(child.toType()), - - .error_union_type => |error_union_type| { - return sema.typeRequiresComptime(error_union_type.payload_type.toType()); - }, - - .error_set_type, .inferred_error_set_type => false, - - .func_type => true, - - .simple_type => |t| switch (t) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .void, - .anyerror, - .adhoc_inferred_error_set, - .noreturn, - .generic_poison, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_modifier, - .prefetch_options, - .export_options, - .extern_options, - => false, - - .type, - .comptime_int, - .comptime_float, - .null, - .undefined, - .enum_literal, - .type_info, - => true, - }, - .struct_type => |struct_type| { - if (struct_type.layout == .Packed) { - // packed structs cannot be comptime-only because they have a well-defined - // memory layout and every field has a well-defined bit pattern. - return false; - } - switch (struct_type.flagsPtr(ip).requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (struct_type.flagsPtr(ip).field_types_wip) - return false; - - try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type); - - struct_type.flagsPtr(ip).requires_comptime = .wip; - - for (0..struct_type.field_types.len) |i_usize| { - const i: u32 = @intCast(i_usize); - if (struct_type.fieldIsComptime(ip, i)) continue; - const field_ty = struct_type.field_types.get(ip)[i]; - if (try sema.typeRequiresComptime(field_ty.toType())) { - // Note that this does not cause the layout to - // be considered resolved. Comptime-only types - // still maintain a layout of their - // runtime-known fields. - struct_type.flagsPtr(ip).requires_comptime = .yes; - return true; - } - } - struct_type.flagsPtr(ip).requires_comptime = .no; - return false; - }, - } - }, - .anon_struct_type => |tuple| { - for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { - const have_comptime_val = val != .none; - if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) { - return true; - } - } - return false; - }, - - .union_type => |union_type| { - switch (union_type.flagsPtr(ip).requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (union_type.flagsPtr(ip).status == .field_types_wip) - return false; - - try sema.resolveTypeFieldsUnion(ty, union_type); - const union_obj = ip.loadUnionType(union_type); - - union_obj.flagsPtr(ip).requires_comptime = .wip; - for (0..union_obj.field_types.len) |field_index| { - const field_ty = union_obj.field_types.get(ip)[field_index]; - if (try sema.typeRequiresComptime(field_ty.toType())) { - union_obj.flagsPtr(ip).requires_comptime = .yes; - return true; - } - } - union_obj.flagsPtr(ip).requires_comptime = .no; - return false; - }, - } - }, - - .opaque_type => false, - .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), - - // values, not types - .undef, - .runtime_value, - .simple_value, - .variable, - .extern_func, - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - }, - }; + return ty.comptimeOnlyAdvanced(sema.mod, sema); } pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { @@ -37316,21 +37140,8 @@ fn structFieldAlignment( return ty_abi_align; } -/// Synchronize logic with `Type.isFnOrHasRuntimeBits`. pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { - const mod = sema.mod; - const fn_info = mod.typeToFunc(ty).?; - if (fn_info.is_generic) return false; - if (fn_info.is_var_args) return true; - switch (fn_info.cc) { - // If there was a comptime calling convention, it should also return false here. - .Inline => return false, - else => {}, - } - if (try sema.typeRequiresComptime(fn_info.return_type.toType())) { - return false; - } - return true; + return ty.fnHasRuntimeBitsAdvanced(sema.mod, sema); } fn unionFieldIndex( diff --git a/src/type.zig b/src/type.zig index 87b29731f6..5bdca27667 100644 --- a/src/type.zig +++ b/src/type.zig @@ -771,21 +771,25 @@ pub const Type = struct { return hasRuntimeBitsAdvanced(ty, mod, true, .eager) catch unreachable; } + pub fn fnHasRuntimeBits(ty: Type, mod: *Module) bool { + return ty.fnHasRuntimeBitsAdvanced(mod, null) catch unreachable; + } + + /// Determines whether a function type has runtime bits, i.e. whether a + /// function with this type can exist at runtime. + /// Asserts that `ty` is a function type. + /// If `opt_sema` is not provided, asserts that the return type is sufficiently resolved. + pub fn fnHasRuntimeBitsAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { + const fn_info = mod.typeToFunc(ty).?; + if (fn_info.is_generic) return false; + if (fn_info.is_var_args) return true; + if (fn_info.cc == .Inline) return false; + return !try fn_info.return_type.toType().comptimeOnlyAdvanced(mod, opt_sema); + } + pub fn isFnOrHasRuntimeBits(ty: Type, mod: *Module) bool { switch (ty.zigTypeTag(mod)) { - .Fn => { - const fn_info = mod.typeToFunc(ty).?; - if (fn_info.is_generic) return false; - if (fn_info.is_var_args) return true; - switch (fn_info.cc) { - // If there was a comptime calling convention, - // it should also return false here. - .Inline => return false, - else => {}, - } - if (fn_info.return_type.toType().comptimeOnly(mod)) return false; - return true; - }, + .Fn => return ty.fnHasRuntimeBits(mod), else => return ty.hasRuntimeBits(mod), } } @@ -2575,9 +2579,14 @@ pub const Type = struct { /// During semantic analysis, instead call `Sema.typeRequiresComptime` which /// resolves field types rather than asserting they are already resolved. - /// TODO merge these implementations together with the "advanced" pattern seen - /// elsewhere in this file. pub fn comptimeOnly(ty: Type, mod: *Module) bool { + return ty.comptimeOnlyAdvanced(mod, null) catch unreachable; + } + + /// `generic_poison` will return false. + /// May return false negatives when structs and unions are having their field types resolved. + /// If `opt_sema` is not provided, asserts that the type is sufficiently resolved. + pub fn comptimeOnlyAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { const ip = &mod.intern_pool; return switch (ty.toIntern()) { .empty_struct_type => false, @@ -2587,19 +2596,19 @@ pub const Type = struct { .ptr_type => |ptr_type| { const child_ty = ptr_type.child.toType(); switch (child_ty.zigTypeTag(mod)) { - .Fn => return !child_ty.isFnOrHasRuntimeBits(mod), + .Fn => return !try child_ty.fnHasRuntimeBitsAdvanced(mod, opt_sema), .Opaque => return false, - else => return child_ty.comptimeOnly(mod), + else => return child_ty.comptimeOnlyAdvanced(mod, opt_sema), } }, .anyframe_type => |child| { if (child == .none) return false; - return child.toType().comptimeOnly(mod); + return child.toType().comptimeOnlyAdvanced(mod, opt_sema); }, - .array_type => |array_type| array_type.child.toType().comptimeOnly(mod), - .vector_type => |vector_type| vector_type.child.toType().comptimeOnly(mod), - .opt_type => |child| child.toType().comptimeOnly(mod), - .error_union_type => |error_union_type| error_union_type.payload_type.toType().comptimeOnly(mod), + .array_type => |array_type| return array_type.child.toType().comptimeOnlyAdvanced(mod, opt_sema), + .vector_type => |vector_type| return vector_type.child.toType().comptimeOnlyAdvanced(mod, opt_sema), + .opt_type => |child| return child.toType().comptimeOnlyAdvanced(mod, opt_sema), + .error_union_type => |error_union_type| return error_union_type.payload_type.toType().comptimeOnlyAdvanced(mod, opt_sema), .error_set_type, .inferred_error_set_type, @@ -2662,39 +2671,80 @@ pub const Type = struct { // A struct with no fields is not comptime-only. return switch (struct_type.flagsPtr(ip).requires_comptime) { - // Return false to avoid incorrect dependency loops. - // This will be handled correctly once merged with - // `Sema.typeRequiresComptime`. - .wip, .unknown => false, - .no => false, + .no, .wip => false, .yes => true, + .unknown => { + // The type is not resolved; assert that we have a Sema. + const sema = opt_sema.?; + + if (struct_type.flagsPtr(ip).field_types_wip) + return false; + + try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type); + + struct_type.flagsPtr(ip).requires_comptime = .wip; + errdefer struct_type.flagsPtr(ip).requires_comptime = .unknown; + + for (0..struct_type.field_types.len) |i_usize| { + const i: u32 = @intCast(i_usize); + if (struct_type.fieldIsComptime(ip, i)) continue; + const field_ty = struct_type.field_types.get(ip)[i]; + if (try field_ty.toType().comptimeOnlyAdvanced(mod, opt_sema)) { + // Note that this does not cause the layout to + // be considered resolved. Comptime-only types + // still maintain a layout of their + // runtime-known fields. + struct_type.flagsPtr(ip).requires_comptime = .yes; + return true; + } + } + + struct_type.flagsPtr(ip).requires_comptime = .no; + return false; + }, }; }, .anon_struct_type => |tuple| { for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { const have_comptime_val = val != .none; - if (!have_comptime_val and field_ty.toType().comptimeOnly(mod)) return true; + if (!have_comptime_val and try field_ty.toType().comptimeOnlyAdvanced(mod, opt_sema)) return true; } return false; }, - .union_type => |union_type| { - switch (union_type.flagsPtr(ip).requires_comptime) { - .wip, .unknown => { - // Return false to avoid incorrect dependency loops. - // This will be handled correctly once merged with - // `Sema.typeRequiresComptime`. + .union_type => |union_type| switch (union_type.flagsPtr(ip).requires_comptime) { + .no, .wip => false, + .yes => true, + .unknown => { + // The type is not resolved; assert that we have a Sema. + const sema = opt_sema.?; + + if (union_type.flagsPtr(ip).status == .field_types_wip) return false; - }, - .no => return false, - .yes => return true, - } + + try sema.resolveTypeFieldsUnion(ty, union_type); + const union_obj = ip.loadUnionType(union_type); + + union_obj.flagsPtr(ip).requires_comptime = .wip; + errdefer union_obj.flagsPtr(ip).requires_comptime = .unknown; + + for (0..union_obj.field_types.len) |field_idx| { + const field_ty = union_obj.field_types.get(ip)[field_idx]; + if (try field_ty.toType().comptimeOnlyAdvanced(mod, opt_sema)) { + union_obj.flagsPtr(ip).requires_comptime = .yes; + return true; + } + } + + union_obj.flagsPtr(ip).requires_comptime = .no; + return false; + }, }, .opaque_type => false, - .enum_type => |enum_type| enum_type.tag_ty.toType().comptimeOnly(mod), + .enum_type => |enum_type| return enum_type.tag_ty.toType().comptimeOnlyAdvanced(mod, opt_sema), // values, not types .undef,