Commit 7768d2024b

Jacob Young <jacobly0@users.noreply.github.com>
2023-02-17 11:00:17
CBE: use CType for type rendering
1 parent d8fada6
Changed files (1)
src
codegen
src/codegen/c.zig
@@ -1954,291 +1954,311 @@ pub const DeclGen = struct {
         return name;
     }
 
+    fn indexToCType(dg: *DeclGen, idx: CType.Index) CType {
+        return dg.ctypes.indexToCType(idx);
+    }
     fn typeToCType(dg: *DeclGen, ty: Type) !CType {
         return dg.ctypes.typeToCType(dg.gpa, ty, dg.module);
     }
-
-    /// Renders a type as a single identifier, generating intermediate typedefs
-    /// if necessary.
-    ///
-    /// This is guaranteed to be valid in both typedefs and declarations/definitions.
-    ///
-    /// There are three type formats in total that we support rendering:
-    ///   | Function            | Example 1 (*u8) | Example 2 ([10]*u8) |
-    ///   |---------------------|-----------------|---------------------|
-    ///   | `renderTypecast`    | "uint8_t *"     | "uint8_t *[10]"     |
-    ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
-    ///   | `renderType`        | "uint8_t *"     | "zig_A_uint8_t_10"  |
-    ///
-    fn renderType(
+    fn typeToIndex(dg: *DeclGen, ty: Type) !CType.Index {
+        return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module);
+    }
+
+    const CTypeFix = enum { prefix, suffix };
+    const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict });
+    const CTypeRenderTrailing = enum {
+        no_space,
+        maybe_space,
+
+        pub fn format(
+            self: @This(),
+            comptime fmt: []const u8,
+            _: std.fmt.FormatOptions,
+            w: anytype,
+        ) @TypeOf(w).Error!void {
+            if (fmt.len != 0)
+                @compileError("invalid format string '" ++ fmt ++ "' for type '" ++
+                    @typeName(@This()) ++ "'");
+            comptime assert(fmt.len == 0);
+            switch (self) {
+                .no_space => {},
+                .maybe_space => try w.writeByte(' '),
+            }
+        }
+    };
+    fn renderTypePrefix(
         dg: *DeclGen,
         w: anytype,
-        t: Type,
-        kind: TypedefKind,
-    ) error{ OutOfMemory, AnalysisFail }!void {
-        _ = try dg.typeToCType(t);
-
-        const target = dg.module.getTarget();
-
-        switch (t.zigTypeTag()) {
-            .Void => try w.writeAll("void"),
-            .Bool => try w.writeAll("bool"),
-            .NoReturn, .Float => {
-                try w.writeAll("zig_");
-                try t.print(w, dg.module);
-            },
-            .Int => {
-                if (t.isNamedInt()) {
-                    try w.writeAll("zig_");
-                    try t.print(w, dg.module);
-                } else {
-                    return renderTypeUnnamed(dg, w, t, kind);
-                }
-            },
-            .ErrorSet => {
-                return renderTypeUnnamed(dg, w, t, kind);
+        idx: CType.Index,
+        parent_fix: CTypeFix,
+        qualifiers: CQualifiers,
+    ) @TypeOf(w).Error!CTypeRenderTrailing {
+        var trailing = CTypeRenderTrailing.maybe_space;
+
+        const cty = dg.indexToCType(idx);
+        switch (cty.tag()) {
+            .void,
+            .char,
+            .@"signed char",
+            .short,
+            .int,
+            .long,
+            .@"long long",
+            ._Bool,
+            .@"unsigned char",
+            .@"unsigned short",
+            .@"unsigned int",
+            .@"unsigned long",
+            .@"unsigned long long",
+            .float,
+            .double,
+            .@"long double",
+            .bool,
+            .size_t,
+            .ptrdiff_t,
+            .zig_u8,
+            .zig_i8,
+            .zig_u16,
+            .zig_i16,
+            .zig_u32,
+            .zig_i32,
+            .zig_u64,
+            .zig_i64,
+            .zig_u128,
+            .zig_i128,
+            .zig_f16,
+            .zig_f32,
+            .zig_f64,
+            .zig_f80,
+            .zig_f128,
+            => |tag| try w.writeAll(@tagName(tag)),
+
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => |tag| {
+                const child_idx = cty.cast(CType.Payload.Child).?.data;
+                try w.print("{}*", .{try dg.renderTypePrefix(w, child_idx, .prefix, CQualifiers.init(.{
+                    .@"const" = switch (tag) {
+                        .pointer, .pointer_volatile => false,
+                        .pointer_const, .pointer_const_volatile => true,
+                        else => unreachable,
+                    },
+                    .@"volatile" = switch (tag) {
+                        .pointer, .pointer_const => false,
+                        .pointer_volatile, .pointer_const_volatile => true,
+                        else => unreachable,
+                    },
+                }))});
+                trailing = .no_space;
             },
-            .Pointer => {
-                const ptr_info = t.ptrInfo().data;
-                if (ptr_info.size == .Slice) {
-                    var slice_pl = Type.Payload.ElemType{
-                        .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice },
-                        .data = ptr_info.pointee_type,
-                    };
-                    const slice_ty = Type.initPayload(&slice_pl.base);
-
-                    const name = dg.getTypedefName(slice_ty) orelse
-                        try dg.renderSliceTypedef(slice_ty);
 
-                    return w.writeAll(name);
-                }
-
-                if (ptr_info.pointee_type.zigTypeTag() == .Fn) {
-                    const name = dg.getTypedefName(ptr_info.pointee_type) orelse
-                        try dg.renderPtrToFnTypedef(ptr_info.pointee_type);
-
-                    return w.writeAll(name);
+            .array,
+            .vector,
+            => {
+                const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type;
+                const child_trailing = try dg.renderTypePrefix(w, child_idx, .suffix, qualifiers);
+                switch (parent_fix) {
+                    .prefix => {
+                        try w.print("{}(", .{child_trailing});
+                        return .no_space;
+                    },
+                    .suffix => return child_trailing,
                 }
-
-                if (ptr_info.host_size != 0) {
-                    var host_pl = Type.Payload.Bits{
-                        .base = .{ .tag = .int_unsigned },
-                        .data = ptr_info.host_size * 8,
-                    };
-                    const host_ty = Type.initPayload(&host_pl.base);
-
-                    try dg.renderType(w, host_ty, .Forward);
-                } else if (t.isCPtr() and ptr_info.pointee_type.eql(Type.u8, dg.module) and
-                    (dg.decl.val.tag() == .extern_fn or
-                    std.mem.eql(u8, std.mem.span(dg.decl.name), "main")))
-                {
-                    // This is a hack, since the c compiler expects a lot of external
-                    // library functions to have char pointers in their signatures, but
-                    // u8 and i8 produce unsigned char and signed char respectively,
-                    // which in C are (not very usefully) different than char.
-                    try w.writeAll("char");
-                } else try dg.renderType(w, switch (ptr_info.pointee_type.tag()) {
-                    .anyopaque => Type.void,
-                    else => ptr_info.pointee_type,
-                }, .Forward);
-                if (t.isConstPtr()) try w.writeAll(" const");
-                if (t.isVolatilePtr()) try w.writeAll(" volatile");
-                return w.writeAll(" *");
-            },
-            .Array, .Vector => {
-                var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{
-                    .len = t.arrayLenIncludingSentinel(),
-                    .elem_type = t.childType(),
-                } };
-                const array_ty = Type.initPayload(&array_pl.base);
-
-                const name = dg.getTypedefName(array_ty) orelse
-                    try dg.renderArrayTypedef(array_ty);
-
-                return w.writeAll(name);
             },
-            .Optional => {
-                var opt_buf: Type.Payload.ElemType = undefined;
-                const child_ty = t.optionalChild(&opt_buf);
-
-                if (!child_ty.hasRuntimeBitsIgnoreComptime())
-                    return dg.renderType(w, Type.bool, kind);
-
-                if (t.optionalReprIsPayload())
-                    return dg.renderType(w, child_ty, kind);
 
-                switch (kind) {
-                    .Complete => {
-                        const name = dg.getTypedefName(t) orelse
-                            try dg.renderOptionalTypedef(t);
-
-                        try w.writeAll(name);
-                    },
-                    .Forward => {
-                        var ptr_pl = Type.Payload.ElemType{
-                            .base = .{ .tag = .single_const_pointer },
-                            .data = t,
-                        };
-                        const ptr_ty = Type.initPayload(&ptr_pl.base);
-
-                        const name = dg.getTypedefName(ptr_ty) orelse
-                            try dg.renderFwdTypedef(ptr_ty);
+            .fwd_struct,
+            .fwd_union,
+            .anon_struct,
+            .packed_anon_struct,
+            => |tag| try w.print("{s} {}__{d}", .{
+                switch (tag) {
+                    .fwd_struct,
+                    .anon_struct,
+                    .packed_anon_struct,
+                    => "struct",
+                    .fwd_union => "union",
+                    else => unreachable,
+                },
+                fmtIdent(switch (tag) {
+                    .fwd_struct,
+                    .fwd_union,
+                    => mem.span(dg.module.declPtr(cty.cast(CType.Payload.FwdDecl).?.data).name),
+                    .anon_struct,
+                    .packed_anon_struct,
+                    => "anon",
+                    else => unreachable,
+                }),
+                idx,
+            }),
 
-                        try w.writeAll(name);
+            .@"struct",
+            .packed_struct,
+            .@"union",
+            .packed_union,
+            => return dg.renderTypePrefix(
+                w,
+                cty.cast(CType.Payload.Aggregate).?.data.fwd_decl,
+                parent_fix,
+                qualifiers,
+            ),
+
+            .function,
+            .varargs_function,
+            => {
+                const child_trailing = try dg.renderTypePrefix(
+                    w,
+                    cty.cast(CType.Payload.Function).?.data.return_type,
+                    .suffix,
+                    CQualifiers.initEmpty(),
+                );
+                switch (parent_fix) {
+                    .prefix => {
+                        try w.print("{}(", .{child_trailing});
+                        return .no_space;
                     },
+                    .suffix => return child_trailing,
                 }
             },
-            .ErrorUnion => {
-                const payload_ty = t.errorUnionPayload();
+        }
 
-                if (!payload_ty.hasRuntimeBitsIgnoreComptime())
-                    return dg.renderType(w, Type.anyerror, kind);
+        var qualifier_it = qualifiers.iterator();
+        while (qualifier_it.next()) |qualifier| {
+            try w.print("{}{s}", .{ trailing, @tagName(qualifier) });
+            trailing = .maybe_space;
+        }
 
-                var error_union_pl = Type.Payload.ErrorUnion{
-                    .data = .{ .error_set = Type.anyerror, .payload = payload_ty },
-                };
-                const error_union_ty = Type.initPayload(&error_union_pl.base);
+        return trailing;
+    }
+    fn renderTypeSuffix(
+        dg: *DeclGen,
+        w: anytype,
+        idx: CType.Index,
+        parent_fix: CTypeFix,
+    ) @TypeOf(w).Error!void {
+        const cty = dg.indexToCType(idx);
+        switch (cty.tag()) {
+            .void,
+            .char,
+            .@"signed char",
+            .short,
+            .int,
+            .long,
+            .@"long long",
+            ._Bool,
+            .@"unsigned char",
+            .@"unsigned short",
+            .@"unsigned int",
+            .@"unsigned long",
+            .@"unsigned long long",
+            .float,
+            .double,
+            .@"long double",
+            .bool,
+            .size_t,
+            .ptrdiff_t,
+            .zig_u8,
+            .zig_i8,
+            .zig_u16,
+            .zig_i16,
+            .zig_u32,
+            .zig_i32,
+            .zig_u64,
+            .zig_i64,
+            .zig_u128,
+            .zig_i128,
+            .zig_f16,
+            .zig_f32,
+            .zig_f64,
+            .zig_f80,
+            .zig_f128,
+            => {},
+
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Child).?.data, .prefix),
+
+            .array,
+            .vector,
+            => {
+                switch (parent_fix) {
+                    .prefix => try w.writeByte(')'),
+                    .suffix => {},
+                }
 
-                switch (kind) {
-                    .Complete => {
-                        const name = dg.getTypedefName(error_union_ty) orelse
-                            try dg.renderErrorUnionTypedef(error_union_ty);
+                try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len});
+                try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix);
+            },
 
-                        try w.writeAll(name);
-                    },
-                    .Forward => {
-                        var ptr_pl = Type.Payload.ElemType{
-                            .base = .{ .tag = .single_const_pointer },
-                            .data = error_union_ty,
-                        };
-                        const ptr_ty = Type.initPayload(&ptr_pl.base);
+            .fwd_struct,
+            .fwd_union,
+            .anon_struct,
+            .packed_anon_struct,
+            .@"struct",
+            .@"union",
+            .packed_struct,
+            .packed_union,
+            => {},
+
+            .function,
+            .varargs_function,
+            => |tag| {
+                switch (parent_fix) {
+                    .prefix => try w.writeByte(')'),
+                    .suffix => {},
+                }
 
-                        const name = dg.getTypedefName(ptr_ty) orelse
-                            try dg.renderFwdTypedef(ptr_ty);
+                const data = cty.cast(CType.Payload.Function).?.data;
 
-                        try w.writeAll(name);
-                    },
+                try w.writeByte('(');
+                var need_comma = false;
+                for (data.param_types) |param_type| {
+                    if (need_comma) try w.writeAll(", ");
+                    need_comma = true;
+                    _ = try dg.renderTypePrefix(w, param_type, .suffix, CQualifiers.initEmpty());
+                    try dg.renderTypeSuffix(w, param_type, .suffix);
                 }
-            },
-            .Struct, .Union => |tag| if (t.containerLayout() == .Packed) {
-                if (t.castTag(.@"struct")) |struct_obj| {
-                    try dg.renderType(w, struct_obj.data.backing_int_ty, kind);
-                } else {
-                    var buf: Type.Payload.Bits = .{
-                        .base = .{ .tag = .int_unsigned },
-                        .data = @intCast(u16, t.bitSize(target)),
-                    };
-                    try dg.renderType(w, Type.initPayload(&buf.base), kind);
+                switch (tag) {
+                    .function => {},
+                    .varargs_function => {
+                        if (need_comma) try w.writeAll(", ");
+                        need_comma = true;
+                        try w.writeAll("...");
+                    },
+                    else => unreachable,
                 }
-            } else if (t.isSimpleTupleOrAnonStruct()) {
-                const ExpectedContents = struct { types: [8]Type, values: [8]Value };
-                var stack align(@alignOf(ExpectedContents)) =
-                    std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);
-                const allocator = stack.get();
-
-                var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){};
-                defer tuple_storage.deinit(allocator);
-                try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount());
-
-                const fields = t.tupleFields();
-                for (fields.values, 0..) |value, index|
-                    if (value.tag() == .unreachable_value)
-                        tuple_storage.appendAssumeCapacity(.{
-                            .type = fields.types[index],
-                            .value = value,
-                        });
-
-                const tuple_slice = tuple_storage.slice();
-                var tuple_pl = Type.Payload.Tuple{ .data = .{
-                    .types = tuple_slice.items(.type),
-                    .values = tuple_slice.items(.value),
-                } };
-                const tuple_ty = Type.initPayload(&tuple_pl.base);
-
-                const name = dg.getTypedefName(tuple_ty) orelse
-                    try dg.renderTupleTypedef(tuple_ty);
-
-                try w.writeAll(name);
-            } else switch (kind) {
-                .Complete => {
-                    const name = dg.getTypedefName(t) orelse switch (tag) {
-                        .Struct => try dg.renderStructTypedef(t),
-                        .Union => try dg.renderUnionTypedef(t),
-                        else => unreachable,
-                    };
-
-                    try w.writeAll(name);
-                },
-                .Forward => {
-                    var ptr_pl = Type.Payload.ElemType{
-                        .base = .{ .tag = .single_const_pointer },
-                        .data = t,
-                    };
-                    const ptr_ty = Type.initPayload(&ptr_pl.base);
-
-                    const name = dg.getTypedefName(ptr_ty) orelse
-                        try dg.renderFwdTypedef(ptr_ty);
-
-                    try w.writeAll(name);
-                },
-            },
-            .Enum => {
-                // For enums, we simply use the integer tag type.
-                var int_tag_buf: Type.Payload.Bits = undefined;
-                const int_tag_ty = t.intTagType(&int_tag_buf);
+                if (!need_comma) try w.writeAll("void");
+                try w.writeByte(')');
 
-                try dg.renderType(w, int_tag_ty, kind);
+                try dg.renderTypeSuffix(w, data.return_type, .suffix);
             },
-            .Opaque => switch (t.tag()) {
-                .@"opaque" => {
-                    const name = dg.getTypedefName(t) orelse
-                        try dg.renderOpaqueTypedef(t);
-
-                    try w.writeAll(name);
-                },
-                else => unreachable,
-            },
-
-            .Frame,
-            .AnyFrame,
-            => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
-                @tagName(tag),
-            }),
-
-            .Fn => unreachable, // This is a function body, not a function pointer.
-
-            .Null,
-            .Undefined,
-            .EnumLiteral,
-            .ComptimeFloat,
-            .ComptimeInt,
-            .Type,
-            => unreachable, // must be const or comptime
         }
     }
 
-    fn renderTypeUnnamed(
+    /// Renders a type as a single identifier, generating intermediate typedefs
+    /// if necessary.
+    ///
+    /// This is guaranteed to be valid in both typedefs and declarations/definitions.
+    ///
+    /// There are three type formats in total that we support rendering:
+    ///   | Function            | Example 1 (*u8) | Example 2 ([10]*u8) |
+    ///   |---------------------|-----------------|---------------------|
+    ///   | `renderTypecast`    | "uint8_t *"     | "uint8_t *[10]"     |
+    ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
+    ///   | `renderType`        | "uint8_t *"     | "uint8_t *[10]"     |
+    ///
+    fn renderType(
         dg: *DeclGen,
         w: anytype,
         t: Type,
-        kind: TypedefKind,
+        _: TypedefKind,
     ) error{ OutOfMemory, AnalysisFail }!void {
-        const target = dg.module.getTarget();
-        const int_info = t.intInfo(target);
-        if (toCIntBits(int_info.bits)) |c_bits|
-            return w.print("zig_{c}{d}", .{ signAbbrev(int_info.signedness), c_bits })
-        else if (loweredArrayInfo(t, target)) |array_info| {
-            assert(array_info.sentinel == null);
-            var array_pl = Type.Payload.Array{
-                .base = .{ .tag = .array },
-                .data = .{ .len = array_info.len, .elem_type = array_info.elem_type },
-            };
-            const array_ty = Type.initPayload(&array_pl.base);
-
-            return dg.renderType(w, array_ty, kind);
-        } else return dg.fail("C backend: Unable to lower unnamed integer type {}", .{
-            t.fmt(dg.module),
-        });
+        const idx = try dg.typeToIndex(t);
+        _ = try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.initEmpty());
+        try dg.renderTypeSuffix(w, idx, .suffix);
     }
 
     const IntCastContext = union(enum) {
@@ -2348,10 +2368,10 @@ pub const DeclGen = struct {
     ///   |---------------------|-----------------|---------------------|
     ///   | `renderTypecast`    | "uint8_t *"     | "uint8_t *[10]"     |
     ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
-    ///   | `renderType`        | "uint8_t *"     | "zig_A_uint8_t_10"  |
+    ///   | `renderType`        | "uint8_t *"     | "uint8_t *[10]"     |
     ///
     fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void {
-        return renderTypeAndName(dg, w, ty, .{ .bytes = "" }, .Mut, 0, .Complete);
+        try dg.renderType(w, ty, undefined);
     }
 
     /// Renders a type and name in field declaration/definition format.
@@ -2361,7 +2381,7 @@ pub const DeclGen = struct {
     ///   |---------------------|-----------------|---------------------|
     ///   | `renderTypecast`    | "uint8_t *"     | "uint8_t *[10]"     |
     ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
-    ///   | `renderType`        | "uint8_t *"     | "zig_A_uint8_t_10"  |
+    ///   | `renderType`        | "uint8_t *"     | "uint8_t *[10]"     |
     ///
     fn renderTypeAndName(
         dg: *DeclGen,
@@ -2370,46 +2390,26 @@ pub const DeclGen = struct {
         name: CValue,
         mutability: Mutability,
         alignment: u32,
-        kind: TypedefKind,
+        _: TypedefKind,
     ) error{ OutOfMemory, AnalysisFail }!void {
-        var suffix = std.ArrayList(u8).init(dg.gpa);
-        defer suffix.deinit();
-        const suffix_writer = suffix.writer();
-
-        // Any top-level array types are rendered here as a suffix, which
-        // avoids creating typedefs for every array type
-        const target = dg.module.getTarget();
-        var render_ty = ty;
-        var depth: u32 = 0;
-        while (loweredArrayInfo(render_ty, target)) |array_info| {
-            const c_len = array_info.len + @boolToInt(array_info.sentinel != null);
-            var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len };
-            const c_len_val = Value.initPayload(&c_len_pl.base);
-
-            try suffix_writer.writeByte('[');
-            if (mutability == .ConstArgument and depth == 0) try suffix_writer.writeAll("zig_const_arr ");
-            try suffix.writer().print("{}]", .{try dg.fmtIntLiteral(Type.usize, c_len_val)});
-            render_ty = array_info.elem_type;
-            depth += 1;
-        }
-
         if (alignment != 0) {
-            const abi_alignment = ty.abiAlignment(target);
+            const abi_alignment = ty.abiAlignment(dg.module.getTarget());
             if (alignment < abi_alignment) {
                 try w.print("zig_under_align({}) ", .{alignment});
             } else if (alignment > abi_alignment) {
                 try w.print("zig_align({}) ", .{alignment});
             }
         }
-        try dg.renderType(w, render_ty, kind);
 
-        const const_prefix = switch (mutability) {
-            .Const, .ConstArgument => "const ",
-            .Mut => "",
-        };
-        try w.print(" {s}", .{const_prefix});
+        const idx = try dg.typeToIndex(ty);
+        try w.print("{}", .{try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.init(.{
+            .@"const" = switch (mutability) {
+                .Const, .ConstArgument => true,
+                .Mut => false,
+            },
+        }))});
         try dg.writeCValue(w, name);
-        try w.writeAll(suffix.items);
+        try dg.renderTypeSuffix(w, idx, .suffix);
     }
 
     fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {