Commit 1033d71017

mlugg <mlugg@mlugg.co.uk>
2023-10-10 13:30:44
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`
1 parent 7edba14
Changed files (2)
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(
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,