//! Things relevant to the next trait solver.

// Note: in interned types defined in this module, we generally treat the lifetime as advisory
// and transmute it as needed. This is because no real memory unsafety can be caused from an
// incorrect lifetime here.

pub mod abi;
mod allocation;
mod binder;
mod consts;
mod def_id;
pub mod fold;
pub mod format_proof_tree;
pub mod fulfill;
mod generic_arg;
pub mod generics;
pub mod infer;
pub(crate) mod inspect;
pub mod interner;
mod ir_print;
pub mod normalize;
pub mod obligation_ctxt;
mod opaques;
pub mod predicate;
mod region;
mod solver;
mod structural_normalize;
mod ty;
pub mod util;

use std::{mem::ManuallyDrop, sync::OnceLock};

pub use allocation::*;
pub use binder::*;
pub use consts::*;
pub use def_id::*;
pub use generic_arg::*;
pub use interner::*;
pub use opaques::*;
pub use predicate::*;
pub use region::*;
use rustc_type_ir::MayBeErased;
pub use solver::*;
pub use ty::*;

use crate::db::HirDatabase;
pub use crate::lower::ImplTraitIdx;
pub use rustc_ast_ir::Mutability;

pub type Binder<'db, T> = rustc_type_ir::Binder<DbInterner<'db>, T>;
pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder<DbInterner<'db>, T>;
pub type Unnormalized<'db, T> = rustc_type_ir::Unnormalized<DbInterner<'db>, T>;
pub type Canonical<'db, T> = rustc_type_ir::Canonical<DbInterner<'db>, T>;
pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues<DbInterner<'db>>;
pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind<DbInterner<'db>>;
pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput<DbInterner<'db>, V>;
pub type AliasTy<'db> = rustc_type_ir::AliasTy<DbInterner<'db>>;
pub type FnSig<'db> = rustc_type_ir::FnSig<DbInterner<'db>>;
pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>;
pub type TypingMode<'db, S = MayBeErased> = rustc_type_ir::TypingMode<DbInterner<'db>, S>;
pub type TypeError<'db> = rustc_type_ir::error::TypeError<DbInterner<'db>>;
pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult<DbInterner<'db>>;
pub type FxIndexMap<K, V> = rustc_type_ir::data_structures::IndexMap<K, V>;

pub struct DefaultTypes<'db> {
    pub usize: Ty<'db>,
    pub u8: Ty<'db>,
    pub u16: Ty<'db>,
    pub u32: Ty<'db>,
    pub u64: Ty<'db>,
    pub u128: Ty<'db>,
    pub isize: Ty<'db>,
    pub i8: Ty<'db>,
    pub i16: Ty<'db>,
    pub i32: Ty<'db>,
    pub i64: Ty<'db>,
    pub i128: Ty<'db>,
    pub f16: Ty<'db>,
    pub f32: Ty<'db>,
    pub f64: Ty<'db>,
    pub f128: Ty<'db>,
    pub unit: Ty<'db>,
    pub bool: Ty<'db>,
    pub char: Ty<'db>,
    pub str: Ty<'db>,
    pub never: Ty<'db>,
    pub error: Ty<'db>,
    /// `&'static str`
    pub static_str_ref: Ty<'db>,
    /// `[u8]`
    pub u8_slice: Ty<'db>,
    /// `&'static [u8]`
    pub static_u8_slice: Ty<'db>,
    /// `*mut ()`
    pub mut_unit_ptr: Ty<'db>,
    pub dyn_trait_dummy_self: Ty<'db>,
}

pub struct DefaultConsts<'db> {
    pub error: Const<'db>,
    pub u8_values: [Const<'db>; 256],
}

pub struct DefaultRegions<'db> {
    pub error: Region<'db>,
    pub statik: Region<'db>,
    pub erased: Region<'db>,
}

pub struct DefaultEmpty<'db> {
    pub tys: Tys<'db>,
    pub generic_args: GenericArgs<'db>,
    pub bound_var_kinds: BoundVarKinds<'db>,
    pub canonical_vars: CanonicalVarKinds<'db>,
    pub variances: VariancesOf<'db>,
    pub pat_list: PatList<'db>,
    pub predefined_opaques: PredefinedOpaques<'db>,
    pub def_ids: SolverDefIds<'db>,
    pub bound_existential_predicates: BoundExistentialPredicates<'db>,
    pub clauses: Clauses<'db>,
    pub region_assumptions: RegionAssumptions<'db>,
    pub consts: Consts<'db>,
}

pub struct DefaultAny<'db> {
    pub types: DefaultTypes<'db>,
    pub consts: DefaultConsts<'db>,
    pub regions: DefaultRegions<'db>,
    pub empty: DefaultEmpty<'db>,
    /// `[Invariant]`
    pub one_invariant: VariancesOf<'db>,
    /// `[Covariant]`
    pub one_covariant: VariancesOf<'db>,
    /// `for<'env>`
    pub coroutine_captures_by_ref_bound_var_kinds: BoundVarKinds<'db>,
}

impl std::fmt::Debug for DefaultAny<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("DefaultAny").finish_non_exhaustive()
    }
}

#[inline]
pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
    static TYPES: OnceLock<DefaultAny<'static>> = OnceLock::new();

    let interner = DbInterner::new_no_crate(db);
    TYPES.get_or_init(|| {
        let create_ty = |kind| {
            let ty = Ty::new(interner, kind);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_const = |kind| {
            let ty = Const::new(interner, kind);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_region = |kind| {
            let ty = Region::new(interner, kind);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_generic_args = |slice| {
            let ty = GenericArgs::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_bound_var_kinds = |slice| {
            let ty = BoundVarKinds::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_canonical_vars = |slice| {
            let ty = CanonicalVarKinds::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_variances_of = |slice| {
            let ty = VariancesOf::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_pat_list = |slice| {
            let ty = PatList::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_predefined_opaques = |slice| {
            let ty = PredefinedOpaques::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_solver_def_ids = |slice| {
            let ty = SolverDefIds::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_bound_existential_predicates = |slice| {
            let ty = BoundExistentialPredicates::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_clauses = |slice| {
            let ty = Clauses::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_region_assumptions = |slice| {
            let ty = RegionAssumptions::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_tys = |slice| {
            let ty = Tys::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };
        let create_consts = |slice| {
            let ty = Consts::new_from_slice(slice);
            // We need to increase the refcount (forever), so that the types won't be freed.
            let ty = ManuallyDrop::new(ty.store());
            ty.as_ref()
        };

        let str = create_ty(TyKind::Str);
        let statik = create_region(RegionKind::ReStatic);
        let empty_tys = create_tys(&[]);
        let unit = create_ty(TyKind::Tuple(empty_tys));
        let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8));
        let u8_slice = create_ty(TyKind::Slice(u8));
        let static_u8_slice = create_ty(TyKind::Ref(statik, u8_slice, Mutability::Not));
        DefaultAny {
            types: DefaultTypes {
                usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)),
                u8,
                u16: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U16)),
                u32: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U32)),
                u64: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U64)),
                u128: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U128)),
                isize: create_ty(TyKind::Int(rustc_ast_ir::IntTy::Isize)),
                i8: create_ty(TyKind::Int(rustc_ast_ir::IntTy::I8)),
                i16: create_ty(TyKind::Int(rustc_ast_ir::IntTy::I16)),
                i32: create_ty(TyKind::Int(rustc_ast_ir::IntTy::I32)),
                i64: create_ty(TyKind::Int(rustc_ast_ir::IntTy::I64)),
                i128: create_ty(TyKind::Int(rustc_ast_ir::IntTy::I128)),
                f16: create_ty(TyKind::Float(rustc_ast_ir::FloatTy::F16)),
                f32: create_ty(TyKind::Float(rustc_ast_ir::FloatTy::F32)),
                f64: create_ty(TyKind::Float(rustc_ast_ir::FloatTy::F64)),
                f128: create_ty(TyKind::Float(rustc_ast_ir::FloatTy::F128)),
                unit,
                bool: create_ty(TyKind::Bool),
                char: create_ty(TyKind::Char),
                str,
                never: create_ty(TyKind::Never),
                error: create_ty(TyKind::Error(ErrorGuaranteed)),
                static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)),
                u8_slice,
                static_u8_slice,
                mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)),
                // This type must not appear anywhere except here.
                dyn_trait_dummy_self: create_ty(TyKind::Infer(rustc_type_ir::InferTy::FreshTy(0))),
            },
            consts: DefaultConsts {
                error: create_const(ConstKind::Error(ErrorGuaranteed)),
                u8_values: std::array::from_fn(|u8_value| {
                    create_const(ConstKind::Value(ValueConst {
                        ty: u8,
                        value: ValTree::new(ValTreeKind::Leaf(ScalarInt::from(u8_value as u8))),
                    }))
                }),
            },
            regions: DefaultRegions {
                error: create_region(RegionKind::ReError(ErrorGuaranteed)),
                statik,
                erased: create_region(RegionKind::ReErased),
            },
            empty: DefaultEmpty {
                tys: empty_tys,
                generic_args: create_generic_args(&[]),
                bound_var_kinds: create_bound_var_kinds(&[]),
                canonical_vars: create_canonical_vars(&[]),
                variances: create_variances_of(&[]),
                pat_list: create_pat_list(&[]),
                predefined_opaques: create_predefined_opaques(&[]),
                def_ids: create_solver_def_ids(&[]),
                bound_existential_predicates: create_bound_existential_predicates(&[]),
                clauses: create_clauses(&[]),
                region_assumptions: create_region_assumptions(&[]),
                consts: create_consts(&[]),
            },
            one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]),
            one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]),
            coroutine_captures_by_ref_bound_var_kinds: create_bound_var_kinds(&[
                BoundVariableKind::Region(BoundRegionKind::ClosureEnv),
            ]),
        }
    })
}
