enumstd.zig.llvm.Builder.Type[src]

Fields

void
half
bfloat
float
double
fp128
x86_fp80
ppc_fp128
x86_amx
x86_mmx
label
token
metadata
i1
i8
i16
i29
i32
i64
i80
i128
ptr
@"ptr addrspace(4)"
none = std.math.maxInt(u32)
_

Values

Constantptr_amdgpu_constant[src]

Source Code

Source code
pub const ptr_amdgpu_constant =
    @field(Type, std.fmt.comptimePrint("ptr{ }", .{AddrSpace.amdgpu.constant}))

Functions

Functiontag[src]

pub fn tag(self: Type, builder: *const Builder) Tag

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn tag(self: Type, builder: *const Builder) Tag {
    return builder.type_items.items[@intFromEnum(self)].tag;
}

FunctionunnamedTag[src]

pub fn unnamedTag(self: Type, builder: *const Builder) Tag

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn unnamedTag(self: Type, builder: *const Builder) Tag {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
            .unnamedTag(builder),
        else => item.tag,
    };
}

FunctionscalarTag[src]

pub fn scalarTag(self: Type, builder: *const Builder) Tag

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn scalarTag(self: Type, builder: *const Builder) Tag {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .vector, .scalable_vector => builder.typeExtraData(Type.Vector, item.data)
            .child.tag(builder),
        else => item.tag,
    };
}

FunctionisFloatingPoint[src]

pub fn isFloatingPoint(self: Type) bool

Parameters

self: Type

Source Code

Source code
pub fn isFloatingPoint(self: Type) bool {
    return switch (self) {
        .half, .bfloat, .float, .double, .fp128, .x86_fp80, .ppc_fp128 => true,
        else => false,
    };
}

FunctionisInteger[src]

pub fn isInteger(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isInteger(self: Type, builder: *const Builder) bool {
    return switch (self) {
        .i1, .i8, .i16, .i29, .i32, .i64, .i80, .i128 => true,
        else => switch (self.tag(builder)) {
            .integer => true,
            else => false,
        },
    };
}

FunctionisPointer[src]

pub fn isPointer(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isPointer(self: Type, builder: *const Builder) bool {
    return switch (self) {
        .ptr => true,
        else => switch (self.tag(builder)) {
            .pointer => true,
            else => false,
        },
    };
}

FunctionpointerAddrSpace[src]

pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace {
    switch (self) {
        .ptr => return .default,
        else => {
            const item = builder.type_items.items[@intFromEnum(self)];
            assert(item.tag == .pointer);
            return @enumFromInt(item.data);
        },
    }
}

FunctionisFunction[src]

pub fn isFunction(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isFunction(self: Type, builder: *const Builder) bool {
    return switch (self.tag(builder)) {
        .function, .vararg_function => true,
        else => false,
    };
}

FunctionfunctionKind[src]

pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind {
    return switch (self.tag(builder)) {
        .function => .normal,
        .vararg_function => .vararg,
        else => unreachable,
    };
}

FunctionfunctionParameters[src]

pub fn functionParameters(self: Type, builder: *const Builder) []const Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn functionParameters(self: Type, builder: *const Builder) []const Type {
    const item = builder.type_items.items[@intFromEnum(self)];
    switch (item.tag) {
        .function,
        .vararg_function,
        => {
            var extra = builder.typeExtraDataTrail(Type.Function, item.data);
            return extra.trail.next(extra.data.params_len, Type, builder);
        },
        else => unreachable,
    }
}

FunctionfunctionReturn[src]

pub fn functionReturn(self: Type, builder: *const Builder) Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn functionReturn(self: Type, builder: *const Builder) Type {
    const item = builder.type_items.items[@intFromEnum(self)];
    switch (item.tag) {
        .function,
        .vararg_function,
        => return builder.typeExtraData(Type.Function, item.data).ret,
        else => unreachable,
    }
}

FunctionisVector[src]

pub fn isVector(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isVector(self: Type, builder: *const Builder) bool {
    return switch (self.tag(builder)) {
        .vector, .scalable_vector => true,
        else => false,
    };
}

FunctionvectorKind[src]

pub fn vectorKind(self: Type, builder: *const Builder) Type.Vector.Kind

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn vectorKind(self: Type, builder: *const Builder) Type.Vector.Kind {
    return switch (self.tag(builder)) {
        .vector => .normal,
        .scalable_vector => .scalable,
        else => unreachable,
    };
}

FunctionisStruct[src]

pub fn isStruct(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isStruct(self: Type, builder: *const Builder) bool {
    return switch (self.tag(builder)) {
        .structure, .packed_structure, .named_structure => true,
        else => false,
    };
}

FunctionstructKind[src]

pub fn structKind(self: Type, builder: *const Builder) Type.Structure.Kind

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn structKind(self: Type, builder: *const Builder) Type.Structure.Kind {
    return switch (self.unnamedTag(builder)) {
        .structure => .normal,
        .packed_structure => .@"packed",
        else => unreachable,
    };
}

FunctionisAggregate[src]

pub fn isAggregate(self: Type, builder: *const Builder) bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isAggregate(self: Type, builder: *const Builder) bool {
    return switch (self.tag(builder)) {
        .small_array, .array, .structure, .packed_structure, .named_structure => true,
        else => false,
    };
}

FunctionscalarBits[src]

pub fn scalarBits(self: Type, builder: *const Builder) u24

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn scalarBits(self: Type, builder: *const Builder) u24 {
    return switch (self) {
        .void, .label, .token, .metadata, .none, .x86_amx => unreachable,
        .i1 => 1,
        .i8 => 8,
        .half, .bfloat, .i16 => 16,
        .i29 => 29,
        .float, .i32 => 32,
        .double, .i64, .x86_mmx => 64,
        .x86_fp80, .i80 => 80,
        .fp128, .ppc_fp128, .i128 => 128,
        .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"),
        _ => {
            const item = builder.type_items.items[@intFromEnum(self)];
            return switch (item.tag) {
                .simple,
                .function,
                .vararg_function,
                => unreachable,
                .integer => @intCast(item.data),
                .pointer => @panic("TODO: query data layout"),
                .target => unreachable,
                .vector,
                .scalable_vector,
                => builder.typeExtraData(Type.Vector, item.data).child.scalarBits(builder),
                .small_array,
                .array,
                .structure,
                .packed_structure,
                .named_structure,
                => unreachable,
            };
        },
    };
}

FunctionchildType[src]

pub fn childType(self: Type, builder: *const Builder) Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn childType(self: Type, builder: *const Builder) Type {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .vector,
        .scalable_vector,
        .small_array,
        => builder.typeExtraData(Type.Vector, item.data).child,
        .array => builder.typeExtraData(Type.Array, item.data).child,
        .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body,
        else => unreachable,
    };
}

FunctionscalarType[src]

pub fn scalarType(self: Type, builder: *const Builder) Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn scalarType(self: Type, builder: *const Builder) Type {
    if (self.isFloatingPoint()) return self;
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .integer,
        .pointer,
        => self,
        .vector,
        .scalable_vector,
        => builder.typeExtraData(Type.Vector, item.data).child,
        else => unreachable,
    };
}

FunctionchangeScalar[src]

pub fn changeScalar(self: Type, scalar: Type, builder: *Builder) Allocator.Error!Type

Parameters

self: Type
scalar: Type
builder: *Builder

Source Code

Source code
pub fn changeScalar(self: Type, scalar: Type, builder: *Builder) Allocator.Error!Type {
    try builder.ensureUnusedTypeCapacity(1, Type.Vector, 0);
    return self.changeScalarAssumeCapacity(scalar, builder);
}

FunctionchangeScalarAssumeCapacity[src]

pub fn changeScalarAssumeCapacity(self: Type, scalar: Type, builder: *Builder) Type

Parameters

self: Type
scalar: Type
builder: *Builder

Source Code

Source code
pub fn changeScalarAssumeCapacity(self: Type, scalar: Type, builder: *Builder) Type {
    if (self.isFloatingPoint()) return scalar;
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .integer,
        .pointer,
        => scalar,
        inline .vector,
        .scalable_vector,
        => |kind| builder.vectorTypeAssumeCapacity(
            switch (kind) {
                .vector => .normal,
                .scalable_vector => .scalable,
                else => unreachable,
            },
            builder.typeExtraData(Type.Vector, item.data).len,
            scalar,
        ),
        else => unreachable,
    };
}

FunctionvectorLen[src]

pub fn vectorLen(self: Type, builder: *const Builder) u32

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn vectorLen(self: Type, builder: *const Builder) u32 {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .vector,
        .scalable_vector,
        => builder.typeExtraData(Type.Vector, item.data).len,
        else => unreachable,
    };
}

FunctionchangeLength[src]

pub fn changeLength(self: Type, len: u32, builder: *Builder) Allocator.Error!Type

Parameters

self: Type
len: u32
builder: *Builder

Source Code

Source code
pub fn changeLength(self: Type, len: u32, builder: *Builder) Allocator.Error!Type {
    try builder.ensureUnusedTypeCapacity(1, Type.Array, 0);
    return self.changeLengthAssumeCapacity(len, builder);
}

FunctionchangeLengthAssumeCapacity[src]

pub fn changeLengthAssumeCapacity(self: Type, len: u32, builder: *Builder) Type

Parameters

self: Type
len: u32
builder: *Builder

Source Code

Source code
pub fn changeLengthAssumeCapacity(self: Type, len: u32, builder: *Builder) Type {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        inline .vector,
        .scalable_vector,
        => |kind| builder.vectorTypeAssumeCapacity(
            switch (kind) {
                .vector => .normal,
                .scalable_vector => .scalable,
                else => unreachable,
            },
            len,
            builder.typeExtraData(Type.Vector, item.data).child,
        ),
        .small_array => builder.arrayTypeAssumeCapacity(
            len,
            builder.typeExtraData(Type.Vector, item.data).child,
        ),
        .array => builder.arrayTypeAssumeCapacity(
            len,
            builder.typeExtraData(Type.Array, item.data).child,
        ),
        else => unreachable,
    };
}

FunctionaggregateLen[src]

pub fn aggregateLen(self: Type, builder: *const Builder) usize

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn aggregateLen(self: Type, builder: *const Builder) usize {
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .vector,
        .scalable_vector,
        .small_array,
        => builder.typeExtraData(Type.Vector, item.data).len,
        .array => @intCast(builder.typeExtraData(Type.Array, item.data).length()),
        .structure,
        .packed_structure,
        => builder.typeExtraData(Type.Structure, item.data).fields_len,
        .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
            .aggregateLen(builder),
        else => unreachable,
    };
}

FunctionstructFields[src]

pub fn structFields(self: Type, builder: *const Builder) []const Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn structFields(self: Type, builder: *const Builder) []const Type {
    const item = builder.type_items.items[@intFromEnum(self)];
    switch (item.tag) {
        .structure,
        .packed_structure,
        => {
            var extra = builder.typeExtraDataTrail(Type.Structure, item.data);
            return extra.trail.next(extra.data.fields_len, Type, builder);
        },
        .named_structure => return builder.typeExtraData(Type.NamedStructure, item.data).body
            .structFields(builder),
        else => unreachable,
    }
}

FunctionchildTypeAt[src]

pub fn childTypeAt(self: Type, indices: []const u32, builder: *const Builder) Type

Parameters

self: Type
indices: []const u32
builder: *const Builder

Source Code

Source code
pub fn childTypeAt(self: Type, indices: []const u32, builder: *const Builder) Type {
    if (indices.len == 0) return self;
    const item = builder.type_items.items[@intFromEnum(self)];
    return switch (item.tag) {
        .small_array => builder.typeExtraData(Type.Vector, item.data).child
            .childTypeAt(indices[1..], builder),
        .array => builder.typeExtraData(Type.Array, item.data).child
            .childTypeAt(indices[1..], builder),
        .structure,
        .packed_structure,
        => {
            var extra = builder.typeExtraDataTrail(Type.Structure, item.data);
            const fields = extra.trail.next(extra.data.fields_len, Type, builder);
            return fields[indices[0]].childTypeAt(indices[1..], builder);
        },
        .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
            .childTypeAt(indices, builder),
        else => unreachable,
    };
}

FunctiontargetLayoutType[src]

pub fn targetLayoutType(self: Type, builder: *const Builder) Type

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn targetLayoutType(self: Type, builder: *const Builder) Type {
    _ = self;
    _ = builder;
    @panic("TODO: implement targetLayoutType");
}

FunctionisSized[src]

pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool {
    var visited: IsSizedVisited = .{};
    defer visited.deinit(builder.gpa);
    const result = try self.isSizedVisited(&visited, builder);
    return result;
}

Functionfmt[src]

pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format)

Parameters

self: Type
builder: *const Builder

Source Code

Source code
pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) {
    return .{ .data = .{ .type = self, .builder = builder } };
}

Source Code

Source code
pub const Type = enum(u32) {
    void,
    half,
    bfloat,
    float,
    double,
    fp128,
    x86_fp80,
    ppc_fp128,
    x86_amx,
    x86_mmx,
    label,
    token,
    metadata,

    i1,
    i8,
    i16,
    i29,
    i32,
    i64,
    i80,
    i128,
    ptr,
    @"ptr addrspace(4)",

    none = std.math.maxInt(u32),
    _,

    pub const ptr_amdgpu_constant =
        @field(Type, std.fmt.comptimePrint("ptr{ }", .{AddrSpace.amdgpu.constant}));

    pub const Tag = enum(u4) {
        simple,
        function,
        vararg_function,
        integer,
        pointer,
        target,
        vector,
        scalable_vector,
        small_array,
        array,
        structure,
        packed_structure,
        named_structure,
    };

    pub const Simple = enum(u5) {
        void = 2,
        half = 10,
        bfloat = 23,
        float = 3,
        double = 4,
        fp128 = 14,
        x86_fp80 = 13,
        ppc_fp128 = 15,
        x86_amx = 24,
        x86_mmx = 17,
        label = 5,
        token = 22,
        metadata = 16,
    };

    pub const Function = struct {
        ret: Type,
        params_len: u32,
        //params: [params_len]Value,

        pub const Kind = enum { normal, vararg };
    };

    pub const Target = extern struct {
        name: String,
        types_len: u32,
        ints_len: u32,
        //types: [types_len]Type,
        //ints: [ints_len]u32,
    };

    pub const Vector = extern struct {
        len: u32,
        child: Type,

        fn length(self: Vector) u32 {
            return self.len;
        }

        pub const Kind = enum { normal, scalable };
    };

    pub const Array = extern struct {
        len_lo: u32,
        len_hi: u32,
        child: Type,

        fn length(self: Array) u64 {
            return @as(u64, self.len_hi) << 32 | self.len_lo;
        }
    };

    pub const Structure = struct {
        fields_len: u32,
        //fields: [fields_len]Type,

        pub const Kind = enum { normal, @"packed" };
    };

    pub const NamedStructure = struct {
        id: String,
        body: Type,
    };

    pub const Item = packed struct(u32) {
        tag: Tag,
        data: ExtraIndex,

        pub const ExtraIndex = u28;
    };

    pub fn tag(self: Type, builder: *const Builder) Tag {
        return builder.type_items.items[@intFromEnum(self)].tag;
    }

    pub fn unnamedTag(self: Type, builder: *const Builder) Tag {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
                .unnamedTag(builder),
            else => item.tag,
        };
    }

    pub fn scalarTag(self: Type, builder: *const Builder) Tag {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .vector, .scalable_vector => builder.typeExtraData(Type.Vector, item.data)
                .child.tag(builder),
            else => item.tag,
        };
    }

    pub fn isFloatingPoint(self: Type) bool {
        return switch (self) {
            .half, .bfloat, .float, .double, .fp128, .x86_fp80, .ppc_fp128 => true,
            else => false,
        };
    }

    pub fn isInteger(self: Type, builder: *const Builder) bool {
        return switch (self) {
            .i1, .i8, .i16, .i29, .i32, .i64, .i80, .i128 => true,
            else => switch (self.tag(builder)) {
                .integer => true,
                else => false,
            },
        };
    }

    pub fn isPointer(self: Type, builder: *const Builder) bool {
        return switch (self) {
            .ptr => true,
            else => switch (self.tag(builder)) {
                .pointer => true,
                else => false,
            },
        };
    }

    pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace {
        switch (self) {
            .ptr => return .default,
            else => {
                const item = builder.type_items.items[@intFromEnum(self)];
                assert(item.tag == .pointer);
                return @enumFromInt(item.data);
            },
        }
    }

    pub fn isFunction(self: Type, builder: *const Builder) bool {
        return switch (self.tag(builder)) {
            .function, .vararg_function => true,
            else => false,
        };
    }

    pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind {
        return switch (self.tag(builder)) {
            .function => .normal,
            .vararg_function => .vararg,
            else => unreachable,
        };
    }

    pub fn functionParameters(self: Type, builder: *const Builder) []const Type {
        const item = builder.type_items.items[@intFromEnum(self)];
        switch (item.tag) {
            .function,
            .vararg_function,
            => {
                var extra = builder.typeExtraDataTrail(Type.Function, item.data);
                return extra.trail.next(extra.data.params_len, Type, builder);
            },
            else => unreachable,
        }
    }

    pub fn functionReturn(self: Type, builder: *const Builder) Type {
        const item = builder.type_items.items[@intFromEnum(self)];
        switch (item.tag) {
            .function,
            .vararg_function,
            => return builder.typeExtraData(Type.Function, item.data).ret,
            else => unreachable,
        }
    }

    pub fn isVector(self: Type, builder: *const Builder) bool {
        return switch (self.tag(builder)) {
            .vector, .scalable_vector => true,
            else => false,
        };
    }

    pub fn vectorKind(self: Type, builder: *const Builder) Type.Vector.Kind {
        return switch (self.tag(builder)) {
            .vector => .normal,
            .scalable_vector => .scalable,
            else => unreachable,
        };
    }

    pub fn isStruct(self: Type, builder: *const Builder) bool {
        return switch (self.tag(builder)) {
            .structure, .packed_structure, .named_structure => true,
            else => false,
        };
    }

    pub fn structKind(self: Type, builder: *const Builder) Type.Structure.Kind {
        return switch (self.unnamedTag(builder)) {
            .structure => .normal,
            .packed_structure => .@"packed",
            else => unreachable,
        };
    }

    pub fn isAggregate(self: Type, builder: *const Builder) bool {
        return switch (self.tag(builder)) {
            .small_array, .array, .structure, .packed_structure, .named_structure => true,
            else => false,
        };
    }

    pub fn scalarBits(self: Type, builder: *const Builder) u24 {
        return switch (self) {
            .void, .label, .token, .metadata, .none, .x86_amx => unreachable,
            .i1 => 1,
            .i8 => 8,
            .half, .bfloat, .i16 => 16,
            .i29 => 29,
            .float, .i32 => 32,
            .double, .i64, .x86_mmx => 64,
            .x86_fp80, .i80 => 80,
            .fp128, .ppc_fp128, .i128 => 128,
            .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"),
            _ => {
                const item = builder.type_items.items[@intFromEnum(self)];
                return switch (item.tag) {
                    .simple,
                    .function,
                    .vararg_function,
                    => unreachable,
                    .integer => @intCast(item.data),
                    .pointer => @panic("TODO: query data layout"),
                    .target => unreachable,
                    .vector,
                    .scalable_vector,
                    => builder.typeExtraData(Type.Vector, item.data).child.scalarBits(builder),
                    .small_array,
                    .array,
                    .structure,
                    .packed_structure,
                    .named_structure,
                    => unreachable,
                };
            },
        };
    }

    pub fn childType(self: Type, builder: *const Builder) Type {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .vector,
            .scalable_vector,
            .small_array,
            => builder.typeExtraData(Type.Vector, item.data).child,
            .array => builder.typeExtraData(Type.Array, item.data).child,
            .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body,
            else => unreachable,
        };
    }

    pub fn scalarType(self: Type, builder: *const Builder) Type {
        if (self.isFloatingPoint()) return self;
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .integer,
            .pointer,
            => self,
            .vector,
            .scalable_vector,
            => builder.typeExtraData(Type.Vector, item.data).child,
            else => unreachable,
        };
    }

    pub fn changeScalar(self: Type, scalar: Type, builder: *Builder) Allocator.Error!Type {
        try builder.ensureUnusedTypeCapacity(1, Type.Vector, 0);
        return self.changeScalarAssumeCapacity(scalar, builder);
    }

    pub fn changeScalarAssumeCapacity(self: Type, scalar: Type, builder: *Builder) Type {
        if (self.isFloatingPoint()) return scalar;
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .integer,
            .pointer,
            => scalar,
            inline .vector,
            .scalable_vector,
            => |kind| builder.vectorTypeAssumeCapacity(
                switch (kind) {
                    .vector => .normal,
                    .scalable_vector => .scalable,
                    else => unreachable,
                },
                builder.typeExtraData(Type.Vector, item.data).len,
                scalar,
            ),
            else => unreachable,
        };
    }

    pub fn vectorLen(self: Type, builder: *const Builder) u32 {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .vector,
            .scalable_vector,
            => builder.typeExtraData(Type.Vector, item.data).len,
            else => unreachable,
        };
    }

    pub fn changeLength(self: Type, len: u32, builder: *Builder) Allocator.Error!Type {
        try builder.ensureUnusedTypeCapacity(1, Type.Array, 0);
        return self.changeLengthAssumeCapacity(len, builder);
    }

    pub fn changeLengthAssumeCapacity(self: Type, len: u32, builder: *Builder) Type {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            inline .vector,
            .scalable_vector,
            => |kind| builder.vectorTypeAssumeCapacity(
                switch (kind) {
                    .vector => .normal,
                    .scalable_vector => .scalable,
                    else => unreachable,
                },
                len,
                builder.typeExtraData(Type.Vector, item.data).child,
            ),
            .small_array => builder.arrayTypeAssumeCapacity(
                len,
                builder.typeExtraData(Type.Vector, item.data).child,
            ),
            .array => builder.arrayTypeAssumeCapacity(
                len,
                builder.typeExtraData(Type.Array, item.data).child,
            ),
            else => unreachable,
        };
    }

    pub fn aggregateLen(self: Type, builder: *const Builder) usize {
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .vector,
            .scalable_vector,
            .small_array,
            => builder.typeExtraData(Type.Vector, item.data).len,
            .array => @intCast(builder.typeExtraData(Type.Array, item.data).length()),
            .structure,
            .packed_structure,
            => builder.typeExtraData(Type.Structure, item.data).fields_len,
            .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
                .aggregateLen(builder),
            else => unreachable,
        };
    }

    pub fn structFields(self: Type, builder: *const Builder) []const Type {
        const item = builder.type_items.items[@intFromEnum(self)];
        switch (item.tag) {
            .structure,
            .packed_structure,
            => {
                var extra = builder.typeExtraDataTrail(Type.Structure, item.data);
                return extra.trail.next(extra.data.fields_len, Type, builder);
            },
            .named_structure => return builder.typeExtraData(Type.NamedStructure, item.data).body
                .structFields(builder),
            else => unreachable,
        }
    }

    pub fn childTypeAt(self: Type, indices: []const u32, builder: *const Builder) Type {
        if (indices.len == 0) return self;
        const item = builder.type_items.items[@intFromEnum(self)];
        return switch (item.tag) {
            .small_array => builder.typeExtraData(Type.Vector, item.data).child
                .childTypeAt(indices[1..], builder),
            .array => builder.typeExtraData(Type.Array, item.data).child
                .childTypeAt(indices[1..], builder),
            .structure,
            .packed_structure,
            => {
                var extra = builder.typeExtraDataTrail(Type.Structure, item.data);
                const fields = extra.trail.next(extra.data.fields_len, Type, builder);
                return fields[indices[0]].childTypeAt(indices[1..], builder);
            },
            .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body
                .childTypeAt(indices, builder),
            else => unreachable,
        };
    }

    pub fn targetLayoutType(self: Type, builder: *const Builder) Type {
        _ = self;
        _ = builder;
        @panic("TODO: implement targetLayoutType");
    }

    pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool {
        var visited: IsSizedVisited = .{};
        defer visited.deinit(builder.gpa);
        const result = try self.isSizedVisited(&visited, builder);
        return result;
    }

    const FormatData = struct {
        type: Type,
        builder: *const Builder,
    };
    fn format(
        data: FormatData,
        comptime fmt_str: []const u8,
        fmt_opts: std.fmt.FormatOptions,
        writer: anytype,
    ) @TypeOf(writer).Error!void {
        assert(data.type != .none);
        if (comptime std.mem.eql(u8, fmt_str, "m")) {
            const item = data.builder.type_items.items[@intFromEnum(data.type)];
            switch (item.tag) {
                .simple => try writer.writeAll(switch (@as(Simple, @enumFromInt(item.data))) {
                    .void => "isVoid",
                    .half => "f16",
                    .bfloat => "bf16",
                    .float => "f32",
                    .double => "f64",
                    .fp128 => "f128",
                    .x86_fp80 => "f80",
                    .ppc_fp128 => "ppcf128",
                    .x86_amx => "x86amx",
                    .x86_mmx => "x86mmx",
                    .label, .token => unreachable,
                    .metadata => "Metadata",
                }),
                .function, .vararg_function => |kind| {
                    var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
                    const params = extra.trail.next(extra.data.params_len, Type, data.builder);
                    try writer.print("f_{m}", .{extra.data.ret.fmt(data.builder)});
                    for (params) |param| try writer.print("{m}", .{param.fmt(data.builder)});
                    switch (kind) {
                        .function => {},
                        .vararg_function => try writer.writeAll("vararg"),
                        else => unreachable,
                    }
                    try writer.writeByte('f');
                },
                .integer => try writer.print("i{d}", .{item.data}),
                .pointer => try writer.print("p{d}", .{item.data}),
                .target => {
                    var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
                    const types = extra.trail.next(extra.data.types_len, Type, data.builder);
                    const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
                    try writer.print("t{s}", .{extra.data.name.slice(data.builder).?});
                    for (types) |ty| try writer.print("_{m}", .{ty.fmt(data.builder)});
                    for (ints) |int| try writer.print("_{d}", .{int});
                    try writer.writeByte('t');
                },
                .vector, .scalable_vector => |kind| {
                    const extra = data.builder.typeExtraData(Type.Vector, item.data);
                    try writer.print("{s}v{d}{m}", .{
                        switch (kind) {
                            .vector => "",
                            .scalable_vector => "nx",
                            else => unreachable,
                        },
                        extra.len,
                        extra.child.fmt(data.builder),
                    });
                },
                inline .small_array, .array => |kind| {
                    const extra = data.builder.typeExtraData(switch (kind) {
                        .small_array => Type.Vector,
                        .array => Type.Array,
                        else => unreachable,
                    }, item.data);
                    try writer.print("a{d}{m}", .{ extra.length(), extra.child.fmt(data.builder) });
                },
                .structure, .packed_structure => {
                    var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
                    const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
                    try writer.writeAll("sl_");
                    for (fields) |field| try writer.print("{m}", .{field.fmt(data.builder)});
                    try writer.writeByte('s');
                },
                .named_structure => {
                    const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
                    try writer.writeAll("s_");
                    if (extra.id.slice(data.builder)) |id| try writer.writeAll(id);
                },
            }
            return;
        }
        if (std.enums.tagName(Type, data.type)) |name| return writer.writeAll(name);
        const item = data.builder.type_items.items[@intFromEnum(data.type)];
        switch (item.tag) {
            .simple => unreachable,
            .function, .vararg_function => |kind| {
                var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
                const params = extra.trail.next(extra.data.params_len, Type, data.builder);
                if (!comptime std.mem.eql(u8, fmt_str, ">"))
                    try writer.print("{%} ", .{extra.data.ret.fmt(data.builder)});
                if (!comptime std.mem.eql(u8, fmt_str, "<")) {
                    try writer.writeByte('(');
                    for (params, 0..) |param, index| {
                        if (index > 0) try writer.writeAll(", ");
                        try writer.print("{%}", .{param.fmt(data.builder)});
                    }
                    switch (kind) {
                        .function => {},
                        .vararg_function => {
                            if (params.len > 0) try writer.writeAll(", ");
                            try writer.writeAll("...");
                        },
                        else => unreachable,
                    }
                    try writer.writeByte(')');
                }
            },
            .integer => try writer.print("i{d}", .{item.data}),
            .pointer => try writer.print("ptr{ }", .{@as(AddrSpace, @enumFromInt(item.data))}),
            .target => {
                var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
                const types = extra.trail.next(extra.data.types_len, Type, data.builder);
                const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
                try writer.print(
                    \\target({"}
                , .{extra.data.name.fmt(data.builder)});
                for (types) |ty| try writer.print(", {%}", .{ty.fmt(data.builder)});
                for (ints) |int| try writer.print(", {d}", .{int});
                try writer.writeByte(')');
            },
            .vector, .scalable_vector => |kind| {
                const extra = data.builder.typeExtraData(Type.Vector, item.data);
                try writer.print("<{s}{d} x {%}>", .{
                    switch (kind) {
                        .vector => "",
                        .scalable_vector => "vscale x ",
                        else => unreachable,
                    },
                    extra.len,
                    extra.child.fmt(data.builder),
                });
            },
            inline .small_array, .array => |kind| {
                const extra = data.builder.typeExtraData(switch (kind) {
                    .small_array => Type.Vector,
                    .array => Type.Array,
                    else => unreachable,
                }, item.data);
                try writer.print("[{d} x {%}]", .{ extra.length(), extra.child.fmt(data.builder) });
            },
            .structure, .packed_structure => |kind| {
                var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
                const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
                switch (kind) {
                    .structure => {},
                    .packed_structure => try writer.writeByte('<'),
                    else => unreachable,
                }
                try writer.writeAll("{ ");
                for (fields, 0..) |field, index| {
                    if (index > 0) try writer.writeAll(", ");
                    try writer.print("{%}", .{field.fmt(data.builder)});
                }
                try writer.writeAll(" }");
                switch (kind) {
                    .structure => {},
                    .packed_structure => try writer.writeByte('>'),
                    else => unreachable,
                }
            },
            .named_structure => {
                const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
                if (comptime std.mem.eql(u8, fmt_str, "%")) try writer.print("%{}", .{
                    extra.id.fmt(data.builder),
                }) else switch (extra.body) {
                    .none => try writer.writeAll("opaque"),
                    else => try format(.{
                        .type = extra.body,
                        .builder = data.builder,
                    }, fmt_str, fmt_opts, writer),
                }
            },
        }
    }
    pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) {
        return .{ .data = .{ .type = self, .builder = builder } };
    }

    const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void);
    fn isSizedVisited(
        self: Type,
        visited: *IsSizedVisited,
        builder: *const Builder,
    ) Allocator.Error!bool {
        return switch (self) {
            .void,
            .label,
            .token,
            .metadata,
            => false,
            .half,
            .bfloat,
            .float,
            .double,
            .fp128,
            .x86_fp80,
            .ppc_fp128,
            .x86_amx,
            .x86_mmx,
            .i1,
            .i8,
            .i16,
            .i29,
            .i32,
            .i64,
            .i80,
            .i128,
            .ptr,
            .@"ptr addrspace(4)",
            => true,
            .none => unreachable,
            _ => {
                const item = builder.type_items.items[@intFromEnum(self)];
                return switch (item.tag) {
                    .simple => unreachable,
                    .function,
                    .vararg_function,
                    => false,
                    .integer,
                    .pointer,
                    => true,
                    .target => self.targetLayoutType(builder).isSizedVisited(visited, builder),
                    .vector,
                    .scalable_vector,
                    .small_array,
                    => builder.typeExtraData(Type.Vector, item.data)
                        .child.isSizedVisited(visited, builder),
                    .array => builder.typeExtraData(Type.Array, item.data)
                        .child.isSizedVisited(visited, builder),
                    .structure,
                    .packed_structure,
                    => {
                        if (try visited.fetchPut(builder.gpa, self, {})) |_| return false;

                        var extra = builder.typeExtraDataTrail(Type.Structure, item.data);
                        const fields = extra.trail.next(extra.data.fields_len, Type, builder);
                        for (fields) |field| {
                            if (field.isVector(builder) and field.vectorKind(builder) == .scalable)
                                return false;
                            if (!try field.isSizedVisited(visited, builder))
                                return false;
                        }
                        return true;
                    },
                    .named_structure => {
                        const body = builder.typeExtraData(Type.NamedStructure, item.data).body;
                        return body != .none and try body.isSizedVisited(visited, builder);
                    },
                };
            },
        };
    }
}