//! Support for serializing the dep-graph and reloading it.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
// this shouldn't be necessary, but the check for `&mut _` is too naive and denies returning a function pointer that takes a mut ref
#![feature(const_mut_refs)]
#![feature(const_refs_to_cell)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability, unused_parens)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]

#[macro_use]
extern crate rustc_middle;

use crate::plumbing::{__rust_begin_short_backtrace, encode_all_query_results, try_mark_green};
use field_offset::offset_of;
use rustc_data_structures::stable_hasher::HashStable;
use rustc_data_structures::sync::AtomicU64;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepNodeIndex;
use rustc_middle::dep_graph::{self, DepKind, DepKindStruct};
use rustc_middle::query::erase::{erase, restore, Erase};
use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
use rustc_middle::query::plumbing::{
    DynamicQuery, QueryKeyStringCache, QuerySystem, QuerySystemFns,
};
use rustc_middle::query::AsLocalKey;
use rustc_middle::query::{
    queries, DynamicQueries, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates,
};
use rustc_middle::ty::TyCtxt;
use rustc_query_system::dep_graph::SerializedDepNodeIndex;
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
    get_query_incr, get_query_non_incr, HashResult, QueryCache, QueryConfig, QueryInfo, QueryMap,
    QueryMode, QueryState,
};
use rustc_query_system::HandleCycleError;
use rustc_query_system::Value;
use rustc_span::Span;

#[macro_use]
mod plumbing;
pub use crate::plumbing::QueryCtxt;

mod profiling_support;
pub use self::profiling_support::alloc_self_profile_query_strings;

struct DynamicConfig<
    'tcx,
    C: QueryCache,
    const ANON: bool,
    const DEPTH_LIMIT: bool,
    const FEEDABLE: bool,
> {
    dynamic: &'tcx DynamicQuery<'tcx, C>,
}

impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy
    for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
{
}
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone
    for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
{
    fn clone(&self) -> Self {
        DynamicConfig { dynamic: self.dynamic }
    }
}

impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool>
    QueryConfig<QueryCtxt<'tcx>> for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
where
    for<'a> C::Key: HashStable<StableHashingContext<'a>>,
{
    type Key = C::Key;
    type Value = C::Value;
    type Cache = C;

    #[inline(always)]
    fn name(self) -> &'static str {
        self.dynamic.name
    }

    #[inline(always)]
    fn cache_on_disk(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool {
        (self.dynamic.cache_on_disk)(tcx, key)
    }

    #[inline(always)]
    fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key, DepKind>
    where
        QueryCtxt<'tcx>: 'a,
    {
        self.dynamic.query_state.apply(&qcx.tcx.query_system.states)
    }

    #[inline(always)]
    fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
    where
        'tcx: 'a,
    {
        self.dynamic.query_cache.apply(&qcx.tcx.query_system.caches)
    }

    #[inline(always)]
    fn execute_query(self, tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value {
        (self.dynamic.execute_query)(tcx, key)
    }

    #[inline(always)]
    fn compute(self, qcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value {
        (self.dynamic.compute)(qcx.tcx, key)
    }

    #[inline(always)]
    fn try_load_from_disk(
        self,
        qcx: QueryCtxt<'tcx>,
        key: &Self::Key,
        prev_index: SerializedDepNodeIndex,
        index: DepNodeIndex,
    ) -> Option<Self::Value> {
        if self.dynamic.can_load_from_disk {
            (self.dynamic.try_load_from_disk)(qcx.tcx, key, prev_index, index)
        } else {
            None
        }
    }

    #[inline]
    fn loadable_from_disk(
        self,
        qcx: QueryCtxt<'tcx>,
        key: &Self::Key,
        index: SerializedDepNodeIndex,
    ) -> bool {
        (self.dynamic.loadable_from_disk)(qcx.tcx, key, index)
    }

    fn value_from_cycle_error(
        self,
        tcx: TyCtxt<'tcx>,
        cycle: &[QueryInfo<DepKind>],
    ) -> Self::Value {
        (self.dynamic.value_from_cycle_error)(tcx, cycle)
    }

    #[inline(always)]
    fn format_value(self) -> fn(&Self::Value) -> String {
        self.dynamic.format_value
    }

    #[inline(always)]
    fn anon(self) -> bool {
        ANON
    }

    #[inline(always)]
    fn eval_always(self) -> bool {
        self.dynamic.eval_always
    }

    #[inline(always)]
    fn depth_limit(self) -> bool {
        DEPTH_LIMIT
    }

    #[inline(always)]
    fn feedable(self) -> bool {
        FEEDABLE
    }

    #[inline(always)]
    fn dep_kind(self) -> DepKind {
        self.dynamic.dep_kind
    }

    #[inline(always)]
    fn handle_cycle_error(self) -> HandleCycleError {
        self.dynamic.handle_cycle_error
    }

    #[inline(always)]
    fn hash_result(self) -> HashResult<Self::Value> {
        self.dynamic.hash_result
    }
}

/// This is implemented per query. It allows restoring query values from their erased state
/// and constructing a QueryConfig.
trait QueryConfigRestored<'tcx> {
    type RestoredValue;
    type Config: QueryConfig<QueryCtxt<'tcx>>;

    fn config(tcx: TyCtxt<'tcx>) -> Self::Config;
    fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value)
    -> Self::RestoredValue;
}

pub fn query_system<'tcx>(
    local_providers: Providers,
    extern_providers: ExternProviders,
    on_disk_cache: Option<OnDiskCache<'tcx>>,
    incremental: bool,
) -> QuerySystem<'tcx> {
    QuerySystem {
        states: Default::default(),
        arenas: Default::default(),
        caches: Default::default(),
        dynamic_queries: dynamic_queries(),
        on_disk_cache,
        fns: QuerySystemFns {
            engine: engine(incremental),
            local_providers,
            extern_providers,
            encode_query_results: encode_all_query_results,
            try_mark_green: try_mark_green,
        },
        jobs: AtomicU64::new(1),
    }
}

rustc_query_append! { define_queries! }
