use crate::module::AddrSpace;
use either::Either;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
#[allow(non_camel_case_types)]
pub enum Type {
    
    VoidType,
    
    IntegerType { bits: u32 },
    
    PointerType {
        pointee_type: TypeRef,
        addr_space: AddrSpace,
    },
    
    FPType(FPType),
    
    FuncType {
        result_type: TypeRef,
        param_types: Vec<TypeRef>,
        is_var_arg: bool,
    },
    
    
    
    VectorType {
        element_type: TypeRef,
        num_elements: usize,
        #[cfg(feature="llvm-11-or-greater")]
        scalable: bool,
    },
    
    
    
    ArrayType {
        element_type: TypeRef,
        num_elements: usize,
    },
    
    
    StructType {
        element_types: Vec<TypeRef>,
        is_packed: bool,
    },
    
    
    
    NamedStructType {
        
        name: String, 
    },
    
    X86_MMXType,
    
    
    #[cfg(feature="llvm-12-or-greater")]
    X86_AMXType,
    
    MetadataType,
    
    
    LabelType,
    
    TokenType,
}
impl Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Type::VoidType => write!(f, "void"),
            Type::IntegerType { bits } => write!(f, "i{}", bits),
            Type::PointerType { pointee_type, .. } => write!(f, "{}*", pointee_type),
            Type::FPType(fpt) => write!(f, "{}", fpt),
            Type::FuncType {
                result_type,
                param_types,
                is_var_arg,
            } => {
                write!(f, "{} (", result_type)?;
                for (i, param_ty) in param_types.iter().enumerate() {
                    if i == param_types.len() - 1 {
                        write!(f, "{}", param_ty)?;
                    } else {
                        write!(f, "{}, ", param_ty)?;
                    }
                }
                if *is_var_arg {
                    write!(f, ", ...")?;
                }
                write!(f, ")")?;
                Ok(())
            },
            Type::VectorType {
                element_type,
                num_elements,
                #[cfg(feature="llvm-11-or-greater")]
                scalable,
            } => {
                #[cfg(feature="llvm-11-or-greater")]
                if *scalable {
                    write!(f, "<vscale x {} x {}>", num_elements, element_type)
                } else {
                    write!(f, "<{} x {}>", num_elements, element_type)
                }
                #[cfg(feature="llvm-10-or-lower")]
                write!(f, "<{} x {}>", num_elements, element_type)
            },
            Type::ArrayType {
                element_type,
                num_elements,
            } => write!(f, "[{} x {}]", num_elements, element_type),
            Type::StructType {
                element_types,
                is_packed,
            } => {
                if *is_packed {
                    write!(f, "<")?;
                }
                write!(f, "{{ ")?;
                for (i, element_ty) in element_types.iter().enumerate() {
                    if i == element_types.len() - 1 {
                        write!(f, "{}", element_ty)?;
                    } else {
                        write!(f, "{}, ", element_ty)?;
                    }
                }
                write!(f, " }}")?;
                if *is_packed {
                    write!(f, ">")?;
                }
                Ok(())
            },
            Type::NamedStructType { name } => write!(f, "%{}", name),
            Type::X86_MMXType => write!(f, "x86_mmx"),
            #[cfg(feature="llvm-12-or-greater")]
            Type::X86_AMXType => write!(f, "x86_amx"),
            Type::MetadataType => write!(f, "metadata"),
            Type::LabelType => write!(f, "label"),
            Type::TokenType => write!(f, "token"),
        }
    }
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
#[allow(non_camel_case_types)]
pub enum FPType {
    Half,
    #[cfg(feature="llvm-11-or-greater")]
    BFloat,
    Single,
    Double,
    FP128,
    X86_FP80,
    PPC_FP128,
}
impl From<FPType> for Type {
    fn from(fpt: FPType) -> Type {
        Type::FPType(fpt)
    }
}
impl Display for FPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FPType::Half => write!(f, "half"),
            #[cfg(feature="llvm-11-or-greater")]
            FPType::BFloat => write!(f, "bfloat"),
            FPType::Single => write!(f, "float"),
            FPType::Double => write!(f, "double"),
            FPType::FP128 => write!(f, "fp128"),
            FPType::X86_FP80 => write!(f, "x86_fp80"),
            FPType::PPC_FP128 => write!(f, "ppc_fp128"),
        }
    }
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct TypeRef(Arc<Type>);
impl AsRef<Type> for TypeRef {
    fn as_ref(&self) -> &Type {
        self.0.as_ref()
    }
}
impl Deref for TypeRef {
    type Target = Type;
    fn deref(&self) -> &Type {
        self.0.deref()
    }
}
impl Display for TypeRef {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.0)
    }
}
impl TypeRef {
    
    
    fn new(ty: Type) -> Self {
        Self(Arc::new(ty))
    }
}
pub trait Typed {
    fn get_type(&self, types: &Types) -> TypeRef;
}
impl Typed for TypeRef {
    fn get_type(&self, _types: &Types) -> TypeRef {
        self.clone()
    }
}
impl Typed for Type {
    fn get_type(&self, types: &Types) -> TypeRef {
        types.get_for_type(&self)
    }
}
impl Typed for FPType {
    fn get_type(&self, types: &Types) -> TypeRef {
        types.fp(*self)
    }
}
impl<A, B> Typed for Either<A, B>
where
    A: Typed,
    B: Typed,
{
    fn get_type(&self, types: &Types) -> TypeRef {
        match self {
            Either::Left(x) => types.type_of(x),
            Either::Right(y) => types.type_of(y),
        }
    }
}
#[derive(Clone)]
pub(crate) struct TypesBuilder {
    
    void_type: TypeRef,
    
    int_types: TypeCache<u32>,
    
    pointer_types: TypeCache<(TypeRef, AddrSpace)>,
    
    fp_types: TypeCache<FPType>,
    
    func_types: TypeCache<(TypeRef, Vec<TypeRef>, bool)>,
    
    vec_types: TypeCache<(TypeRef, usize, bool)>,
    
    arr_types: TypeCache<(TypeRef, usize)>,
    
    struct_types: TypeCache<(Vec<TypeRef>, bool)>,
    
    named_struct_types: TypeCache<String>,
    
    named_struct_defs: HashMap<String, NamedStructDef>,
    
    x86_mmx_type: TypeRef,
    
    #[cfg(feature="llvm-12-or-greater")]
    x86_amx_type: TypeRef,
    
    metadata_type: TypeRef,
    
    label_type: TypeRef,
    
    token_type: TypeRef,
    
    
    llvm_type_map: HashMap<LLVMTypeRef, TypeRef>,
}
impl TypesBuilder {
    pub fn new() -> Self {
        Self {
            void_type: TypeRef::new(Type::VoidType),
            int_types: TypeCache::new(),
            pointer_types: TypeCache::new(),
            fp_types: TypeCache::new(),
            func_types: TypeCache::new(),
            vec_types: TypeCache::new(),
            arr_types: TypeCache::new(),
            struct_types: TypeCache::new(),
            named_struct_types: TypeCache::new(),
            named_struct_defs: HashMap::new(),
            x86_mmx_type: TypeRef::new(Type::X86_MMXType),
            #[cfg(feature="llvm-12-or-greater")]
            x86_amx_type: TypeRef::new(Type::X86_AMXType),
            metadata_type: TypeRef::new(Type::MetadataType),
            label_type: TypeRef::new(Type::LabelType),
            token_type: TypeRef::new(Type::TokenType),
            llvm_type_map: HashMap::new(),
        }
    }
    
    
    
    pub fn build(self) -> Types {
        Types {
            void_type: self.void_type,
            int_types: self.int_types,
            pointer_types: self.pointer_types,
            fp_types: self.fp_types,
            func_types: self.func_types,
            vec_types: self.vec_types,
            arr_types: self.arr_types,
            struct_types: self.struct_types,
            named_struct_types: self.named_struct_types,
            named_struct_defs: self.named_struct_defs,
            x86_mmx_type: self.x86_mmx_type,
            #[cfg(feature="llvm-12-or-greater")]
            x86_amx_type: self.x86_amx_type,
            metadata_type: self.metadata_type,
            label_type: self.label_type,
            token_type: self.token_type,
        }
    }
}
#[allow(dead_code)]
impl TypesBuilder {
    
    pub fn void(&self) -> TypeRef {
        self.void_type.clone()
    }
    
    pub fn int(&mut self, bits: u32) -> TypeRef {
        self.int_types
            .lookup_or_insert(bits, || Type::IntegerType { bits })
    }
    
    pub fn bool(&mut self) -> TypeRef {
        self.int(1)
    }
    
    pub fn i8(&mut self) -> TypeRef {
        self.int(8)
    }
    
    pub fn i16(&mut self) -> TypeRef {
        self.int(16)
    }
    
    pub fn i32(&mut self) -> TypeRef {
        self.int(32)
    }
    
    pub fn i64(&mut self) -> TypeRef {
        self.int(64)
    }
    
    pub fn pointer_to(&mut self, pointee_type: TypeRef) -> TypeRef {
        self.pointer_in_addr_space(pointee_type, 0) 
    }
    
    pub fn pointer_in_addr_space(
        &mut self,
        pointee_type: TypeRef,
        addr_space: AddrSpace,
    ) -> TypeRef {
        self.pointer_types
            .lookup_or_insert((pointee_type.clone(), addr_space), || Type::PointerType {
                pointee_type,
                addr_space,
            })
    }
    
    pub fn fp(&mut self, fpt: FPType) -> TypeRef {
        self.fp_types.lookup_or_insert(fpt, || Type::FPType(fpt))
    }
    
    pub fn single(&mut self) -> TypeRef {
        self.fp(FPType::Single)
    }
    
    pub fn double(&mut self) -> TypeRef {
        self.fp(FPType::Double)
    }
    
    pub fn func_type(
        &mut self,
        result_type: TypeRef,
        param_types: Vec<TypeRef>,
        is_var_arg: bool,
    ) -> TypeRef {
        self.func_types.lookup_or_insert(
            (result_type.clone(), param_types.clone(), is_var_arg),
            || Type::FuncType {
                result_type,
                param_types,
                is_var_arg,
            },
        )
    }
    
    #[cfg(feature="llvm-11-or-greater")]
    pub fn vector_of(&mut self, element_type: TypeRef, num_elements: usize, scalable: bool) -> TypeRef {
        self.vec_types
            .lookup_or_insert((element_type.clone(), num_elements, scalable), || Type::VectorType {
                element_type,
                num_elements,
                scalable,
            })
    }
    
    #[cfg(feature="llvm-10-or-lower")]
    pub fn vector_of(&mut self, element_type: TypeRef, num_elements: usize) -> TypeRef {
        self.vec_types
            .lookup_or_insert((element_type.clone(), num_elements, false), || Type::VectorType {
                element_type,
                num_elements,
            })
    }
    
    pub fn array_of(&mut self, element_type: TypeRef, num_elements: usize) -> TypeRef {
        self.arr_types
            .lookup_or_insert((element_type.clone(), num_elements), || Type::ArrayType {
                element_type,
                num_elements,
            })
    }
    
    pub fn struct_of(&mut self, element_types: Vec<TypeRef>, is_packed: bool) -> TypeRef {
        self.struct_types
            .lookup_or_insert((element_types.clone(), is_packed), || Type::StructType {
                element_types,
                is_packed,
            })
    }
    
    
    
    
    
    pub fn named_struct(&mut self, name: String) -> TypeRef {
        self.named_struct_types
            .lookup_or_insert(name.clone(), || Type::NamedStructType { name })
    }
    
    
    
    
    
    
    pub fn named_struct_def(&self, name: &str) -> &NamedStructDef {
        self.named_struct_defs
            .get(name)
            .expect("Named struct has not been defined")
    }
    
    
    
    pub fn add_named_struct_def(&mut self, name: String, def: NamedStructDef) {
        match self.named_struct_defs.entry(name) {
            Entry::Occupied(_) => {
                panic!("Trying to redefine named struct");
            },
            Entry::Vacant(ventry) => {
                ventry.insert(def);
            },
        }
    }
    
    pub fn x86_mmx(&self) -> TypeRef {
        self.x86_mmx_type.clone()
    }
    
    #[cfg(feature="llvm-12-or-greater")]
    pub fn x86_amx(&self) -> TypeRef {
        self.x86_amx_type.clone()
    }
    
    pub fn metadata_type(&self) -> TypeRef {
        self.metadata_type.clone()
    }
    
    pub fn label_type(&self) -> TypeRef {
        self.label_type.clone()
    }
    
    pub fn token_type(&self) -> TypeRef {
        self.token_type.clone()
    }
}
#[derive(Clone, Debug, Hash)]
pub enum NamedStructDef {
    
    Opaque,
    
    Defined(TypeRef),
}
#[derive(Clone)]
pub struct Types {
    
    void_type: TypeRef,
    
    int_types: TypeCache<u32>,
    
    pointer_types: TypeCache<(TypeRef, AddrSpace)>,
    
    fp_types: TypeCache<FPType>,
    
    func_types: TypeCache<(TypeRef, Vec<TypeRef>, bool)>,
    
    
    vec_types: TypeCache<(TypeRef, usize, bool)>,
    
    arr_types: TypeCache<(TypeRef, usize)>,
    
    struct_types: TypeCache<(Vec<TypeRef>, bool)>,
    
    named_struct_types: TypeCache<String>,
    
    named_struct_defs: HashMap<String, NamedStructDef>,
    
    x86_mmx_type: TypeRef,
    
    #[cfg(feature="llvm-12-or-greater")]
    x86_amx_type: TypeRef,
    
    metadata_type: TypeRef,
    
    label_type: TypeRef,
    
    token_type: TypeRef,
}
impl Types {
    
    pub fn type_of<T: Typed + ?Sized>(&self, t: &T) -> TypeRef {
        t.get_type(&self)
    }
    
    pub fn void(&self) -> TypeRef {
        self.void_type.clone()
    }
    
    pub fn int(&self, bits: u32) -> TypeRef {
        self.int_types
            .lookup(&bits)
            .unwrap_or_else(|| TypeRef::new(Type::IntegerType { bits }))
    }
    
    pub fn bool(&self) -> TypeRef {
        self.int(1)
    }
    
    pub fn i8(&self) -> TypeRef {
        self.int(8)
    }
    
    pub fn i16(&self) -> TypeRef {
        self.int(16)
    }
    
    pub fn i32(&self) -> TypeRef {
        self.int(32)
    }
    
    pub fn i64(&self) -> TypeRef {
        self.int(64)
    }
    
    pub fn pointer_to(&self, pointee_type: TypeRef) -> TypeRef {
        self.pointer_in_addr_space(pointee_type, 0)
    }
    
    pub fn pointer_in_addr_space(&self, pointee_type: TypeRef, addr_space: AddrSpace) -> TypeRef {
        self.pointer_types
            .lookup(&(pointee_type.clone(), addr_space))
            .unwrap_or_else(|| {
                TypeRef::new(Type::PointerType {
                    pointee_type,
                    addr_space,
                })
            })
    }
    
    pub fn fp(&self, fpt: FPType) -> TypeRef {
        self.fp_types
            .lookup(&fpt)
            .unwrap_or_else(|| TypeRef::new(Type::FPType(fpt)))
    }
    
    pub fn single(&self) -> TypeRef {
        self.fp(FPType::Single)
    }
    
    pub fn double(&self) -> TypeRef {
        self.fp(FPType::Double)
    }
    
    pub fn func_type(
        &self,
        result_type: TypeRef,
        param_types: Vec<TypeRef>,
        is_var_arg: bool,
    ) -> TypeRef {
        self.func_types
            .lookup(&(result_type.clone(), param_types.clone(), is_var_arg))
            .unwrap_or_else(|| {
                TypeRef::new(Type::FuncType {
                    result_type,
                    param_types,
                    is_var_arg,
                })
            })
    }
    
    #[cfg(feature="llvm-11-or-greater")]
    pub fn vector_of(&self, element_type: TypeRef, num_elements: usize, scalable: bool) -> TypeRef {
        self.vec_types
            .lookup(&(element_type.clone(), num_elements, scalable))
            .unwrap_or_else(|| {
                TypeRef::new(Type::VectorType {
                    element_type,
                    num_elements,
                    scalable,
                })
            })
    }
    #[cfg(feature="llvm-10-or-lower")]
    pub fn vector_of(&self, element_type: TypeRef, num_elements: usize) -> TypeRef {
        self.vec_types
            .lookup(&(element_type.clone(), num_elements, false))
            .unwrap_or_else(|| {
                TypeRef::new(Type::VectorType {
                    element_type,
                    num_elements,
                })
            })
    }
    
    pub fn array_of(&self, element_type: TypeRef, num_elements: usize) -> TypeRef {
        self.arr_types
            .lookup(&(element_type.clone(), num_elements))
            .unwrap_or_else(|| {
                TypeRef::new(Type::ArrayType {
                    element_type,
                    num_elements,
                })
            })
    }
    
    pub fn struct_of(&self, element_types: Vec<TypeRef>, is_packed: bool) -> TypeRef {
        self.struct_types
            .lookup(&(element_types.clone(), is_packed))
            .unwrap_or_else(|| {
                TypeRef::new(Type::StructType {
                    element_types,
                    is_packed,
                })
            })
    }
    
    
    
    
    
    
    pub fn named_struct(&self, name: &str) -> Option<TypeRef> {
        self.named_struct_types.lookup(name)
    }
    
    
    
    
    
    pub fn named_struct_def(&self, name: &str) -> Option<&NamedStructDef> {
        self.named_struct_defs.get(name)
    }
    
    pub fn all_struct_names(&self) -> impl Iterator<Item = &String> {
        self.named_struct_defs.keys()
    }
    
    pub fn x86_mmx(&self) -> TypeRef {
        self.x86_mmx_type.clone()
    }
    
    #[cfg(feature="llvm-12-or-greater")]
    pub fn x86_amx(&self) -> TypeRef {
        self.x86_amx_type.clone()
    }
    
    pub fn metadata_type(&self) -> TypeRef {
        self.metadata_type.clone()
    }
    
    pub fn label_type(&self) -> TypeRef {
        self.label_type.clone()
    }
    
    pub fn token_type(&self) -> TypeRef {
        self.token_type.clone()
    }
    
    #[rustfmt::skip] 
    pub fn get_for_type(&self, ty: &Type) -> TypeRef {
        match ty {
            Type::VoidType => self.void(),
            Type::IntegerType{ bits } => self.int(*bits),
            Type::PointerType { pointee_type, addr_space } => {
                self.pointer_in_addr_space(pointee_type.clone(), *addr_space)
            },
            Type::FPType(fpt) => self.fp(*fpt),
            Type::FuncType { result_type, param_types, is_var_arg } => {
                self.func_type(result_type.clone(), param_types.clone(), *is_var_arg)
            },
            #[cfg(feature="llvm-11-or-greater")]
            Type::VectorType { element_type, num_elements, scalable } => {
                self.vector_of(element_type.clone(), *num_elements, *scalable)
            },
            #[cfg(feature="llvm-10-or-lower")]
            Type::VectorType { element_type, num_elements } => {
                self.vector_of(element_type.clone(), *num_elements)
            },
            Type::ArrayType { element_type, num_elements } => {
                self.array_of(element_type.clone(), *num_elements)
            },
            Type::StructType { element_types, is_packed } => {
                self.struct_of(element_types.clone(), *is_packed)
            },
            Type::NamedStructType { name  } => {
                self.named_struct(name).unwrap_or_else(|| TypeRef::new(Type::NamedStructType { name: name.clone() }))
            },
            Type::X86_MMXType => self.x86_mmx(),
            #[cfg(feature="llvm-12-or-greater")]
            Type::X86_AMXType => self.x86_amx(),
            Type::MetadataType => self.metadata_type(),
            Type::LabelType => self.label_type(),
            Type::TokenType => self.token_type(),
        }
    }
}
impl Types {
    
    
    
    pub fn blank_for_testing() -> Self {
        TypesBuilder::new().build()
    }
}
#[derive(Clone, Debug)]
struct TypeCache<K: Eq + Hash + Clone> {
    map: HashMap<K, TypeRef>,
}
#[allow(dead_code)]
impl<K: Eq + Hash + Clone> TypeCache<K> {
    fn new() -> Self {
        Self {
            map: HashMap::new(),
        }
    }
    
    
    fn lookup<Q: ?Sized>(&self, key: &Q) -> Option<TypeRef>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.map.get(key).cloned()
    }
    
    
    
    fn lookup_or_insert(&mut self, key: K, if_missing: impl FnOnce() -> Type) -> TypeRef {
        self.map
            .entry(key)
            .or_insert_with(|| TypeRef::new(if_missing()))
            .clone()
    }
    
    fn contains_key(&self, key: &K) -> bool {
        self.map.contains_key(key)
    }
}
use crate::from_llvm::*;
use crate::llvm_sys::*;
use llvm_sys::LLVMTypeKind;
use std::collections::hash_map::Entry;
impl TypesBuilder {
    pub(crate) fn type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
        if let Some(typeref) = self.llvm_type_map.get(&ty) {
            return typeref.clone();
        }
        let typeref = self.parse_type_from_llvm_ref(ty);
        self.llvm_type_map.insert(ty, typeref.clone());
        typeref
    }
    fn parse_type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
        let kind = unsafe { LLVMGetTypeKind(ty) };
        match kind {
            LLVMTypeKind::LLVMVoidTypeKind => self.void(),
            LLVMTypeKind::LLVMIntegerTypeKind => self.int(unsafe { LLVMGetIntTypeWidth(ty) }),
            LLVMTypeKind::LLVMPointerTypeKind => {
                let pointee_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
                self.pointer_in_addr_space(pointee_type, unsafe { LLVMGetPointerAddressSpace(ty) })
            },
            LLVMTypeKind::LLVMArrayTypeKind => {
                let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
                self.array_of(element_type, unsafe { LLVMGetArrayLength(ty) as usize })
            },
            LLVMTypeKind::LLVMVectorTypeKind => {
                let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
                #[cfg(feature="llvm-11-or-greater")]
                let ret = self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize }, false);
                #[cfg(feature="llvm-10-or-lower")]
                let ret = self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize });
                ret
            },
            #[cfg(feature="llvm-11-or-greater")]
            LLVMTypeKind::LLVMScalableVectorTypeKind => {
                let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
                self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize }, true)
            },
            LLVMTypeKind::LLVMStructTypeKind => {
                let name = if unsafe { LLVMIsLiteralStruct(ty) } != 0 {
                    None
                } else {
                    unsafe { get_struct_name(ty) }
                };
                match name {
                    Some(s) if !s.is_empty() => {
                        if self.named_struct_types.contains_key(&s) {
                            
                            self.named_struct(s)
                        } else if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
                            
                            self.add_named_struct_def(s.clone(), NamedStructDef::Opaque);
                            
                            self.named_struct(s)
                        } else {
                            
                            let named_struct_typeref = self.named_struct(s.clone());
                            
                            let actual_struct_type = self.struct_type_from_llvm_ref(ty);
                            
                            self.add_named_struct_def(
                                s,
                                NamedStructDef::Defined(actual_struct_type),
                            );
                            
                            named_struct_typeref
                        }
                    },
                    _ => self.struct_type_from_llvm_ref(ty),
                }
            },
            LLVMTypeKind::LLVMFunctionTypeKind => {
                let result_type = self.type_from_llvm_ref(unsafe { LLVMGetReturnType(ty) });
                let param_types = {
                    let num_types = unsafe { LLVMCountParamTypes(ty) };
                    let mut types: Vec<LLVMTypeRef> = Vec::with_capacity(num_types as usize);
                    unsafe {
                        LLVMGetParamTypes(ty, types.as_mut_ptr());
                        types.set_len(num_types as usize);
                    };
                    types
                        .into_iter()
                        .map(|t| self.type_from_llvm_ref(t))
                        .collect()
                };
                self.func_type(
                    result_type,
                    param_types,
                    unsafe { LLVMIsFunctionVarArg(ty) } != 0,
                )
            },
            LLVMTypeKind::LLVMHalfTypeKind => self.fp(FPType::Half),
            #[cfg(feature="llvm-11-or-greater")]
            LLVMTypeKind::LLVMBFloatTypeKind => self.fp(FPType::BFloat),
            LLVMTypeKind::LLVMFloatTypeKind => self.fp(FPType::Single),
            LLVMTypeKind::LLVMDoubleTypeKind => self.fp(FPType::Double),
            LLVMTypeKind::LLVMFP128TypeKind => self.fp(FPType::FP128),
            LLVMTypeKind::LLVMX86_FP80TypeKind => self.fp(FPType::X86_FP80),
            LLVMTypeKind::LLVMPPC_FP128TypeKind => self.fp(FPType::PPC_FP128),
            LLVMTypeKind::LLVMX86_MMXTypeKind => self.x86_mmx(),
            #[cfg(feature="llvm-12-or-greater")]
            LLVMTypeKind::LLVMX86_AMXTypeKind => self.x86_amx(),
            LLVMTypeKind::LLVMMetadataTypeKind => self.metadata_type(),
            LLVMTypeKind::LLVMLabelTypeKind => self.label_type(),
            LLVMTypeKind::LLVMTokenTypeKind => self.token_type(),
        }
    }
    
    
    
    fn struct_type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
        if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
            panic!(
                "struct_type_from_llvm_ref: shouldn't pass an opaque struct type to this function"
            );
        }
        let element_types = {
            let num_types = unsafe { LLVMCountStructElementTypes(ty) };
            let mut types: Vec<LLVMTypeRef> = Vec::with_capacity(num_types as usize);
            unsafe {
                LLVMGetStructElementTypes(ty, types.as_mut_ptr());
                types.set_len(num_types as usize);
            };
            types
                .into_iter()
                .map(|t| self.type_from_llvm_ref(t))
                .collect()
        };
        self.struct_of(element_types, unsafe { LLVMIsPackedStruct(ty) } != 0)
    }
}