//! A subset of a mir body used for const evaluability checking.

use rustc_errors::ErrorGuaranteed;
use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeVisitable};

use crate::ty::{
    self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
    TypeVisitableExt,
};

#[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
#[derive(TyDecodable, TyEncodable, StableHash, TypeVisitable, TypeFoldable)]
pub enum CastKind {
    /// thir::ExprKind::As
    As,
    /// thir::ExprKind::Use
    Use,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)]
pub enum NotConstEvaluatable {
    Error(ErrorGuaranteed),
    MentionsInfer,
    MentionsParam,
}

impl From<ErrorGuaranteed> for NotConstEvaluatable {
    fn from(e: ErrorGuaranteed) -> NotConstEvaluatable {
        NotConstEvaluatable::Error(e)
    }
}

pub type BoundAbstractConst<'tcx> =
    Result<Option<EarlyBinder<'tcx, ty::Const<'tcx>>>, ErrorGuaranteed>;

impl<'tcx> TyCtxt<'tcx> {
    pub fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, ac: T) -> T {
        struct Expander<'tcx> {
            tcx: TyCtxt<'tcx>,
        }

        impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Expander<'tcx> {
            fn cx(&self) -> TyCtxt<'tcx> {
                self.tcx
            }
            fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
                if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
                    ty.super_fold_with(self)
                } else {
                    ty
                }
            }
            fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
                let ct = match c.kind() {
                    ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) {
                        Err(e) => ty::Const::new_error(self.tcx, e),
                        Ok(Some(bac)) => {
                            let args = self.tcx.erase_and_anonymize_regions(uv.args);
                            let bac = bac.instantiate(self.tcx, args).skip_norm_wip();
                            return bac.fold_with(self);
                        }
                        Ok(None) => c,
                    },
                    _ => c,
                };
                ct.super_fold_with(self)
            }
        }
        ac.fold_with(&mut Expander { tcx: self })
    }
}
