Skip to main content

rustc_incremental/persist/
load.rs

1//! Code to load the dep-graph from files.
2
3use std::path::PathBuf;
4use std::sync::Arc;
5
6use rustc_data_structures::unord::UnordMap;
7use rustc_hashes::Hash64;
8use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProductMap};
9use rustc_middle::query::on_disk_cache::OnDiskCache;
10use rustc_serialize::Decodable;
11use rustc_serialize::opaque::MemDecoder;
12use rustc_session::config::IncrementalStateAssertion;
13use rustc_session::{Session, StableCrateId};
14use rustc_span::Symbol;
15use tracing::{debug, warn};
16
17use super::data::*;
18use super::fs::*;
19use super::save::build_dep_graph;
20use super::{file_format, work_product};
21use crate::errors;
22use crate::persist::file_format::{OpenFile, OpenFileError};
23
24#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for LoadResult<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            LoadResult::Ok { data: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Ok",
                    "data", &__self_0),
            LoadResult::DataOutOfDate =>
                ::core::fmt::Formatter::write_str(f, "DataOutOfDate"),
            LoadResult::LoadDepGraph(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f,
                    "LoadDepGraph", __self_0, &__self_1),
        }
    }
}Debug)]
25/// Represents the result of an attempt to load incremental compilation data.
26pub enum LoadResult<T> {
27    /// Loading was successful.
28    Ok {
29        #[allow(missing_docs)]
30        data: T,
31    },
32    /// The file either didn't exist or was produced by an incompatible compiler version.
33    DataOutOfDate,
34    /// Loading the dep graph failed.
35    LoadDepGraph(PathBuf, std::io::Error),
36}
37
38impl<T: Default> LoadResult<T> {
39    /// Accesses the data returned in [`LoadResult::Ok`].
40    pub fn open(self, sess: &Session) -> T {
41        // Emit a fatal error if `-Zassert-incr-state` is present and unsatisfied.
42        maybe_assert_incr_state(sess, &self);
43
44        match self {
45            LoadResult::LoadDepGraph(path, err) => {
46                sess.dcx().emit_warn(errors::LoadDepGraph { path, err });
47                Default::default()
48            }
49            LoadResult::DataOutOfDate => {
50                if let Err(err) = delete_all_session_dir_contents(sess) {
51                    sess.dcx()
52                        .emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err });
53                }
54                Default::default()
55            }
56            LoadResult::Ok { data } => data,
57        }
58    }
59}
60
61fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
62    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/load.rs:62",
                        "rustc_incremental::persist::load", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/load.rs"),
                        ::tracing_core::__macro_support::Option::Some(62u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::load"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("delete_dirty_work_product({0:?})",
                                                    swp) as &dyn Value))])
            });
    } else { ; }
};debug!("delete_dirty_work_product({:?})", swp);
63    work_product::delete_workproduct_files(sess, &swp.work_product);
64}
65
66fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkProductMap)> {
67    let prof = sess.prof.clone();
68
69    if sess.opts.incremental.is_none() {
70        // No incremental compilation.
71        return LoadResult::Ok { data: Default::default() };
72    }
73
74    let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
75
76    // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
77    // Fortunately, we just checked that this isn't the case.
78    let path = dep_graph_path(sess);
79    let expected_hash = sess.opts.dep_tracking_hash(false);
80
81    let mut prev_work_products = UnordMap::default();
82
83    // If we are only building with -Zquery-dep-graph but without an actual
84    // incr. comp. session directory, we skip this. Otherwise we'd fail
85    // when trying to load work products.
86    if sess.incr_comp_session_dir_opt().is_some() {
87        let work_products_path = work_products_path(sess);
88
89        if let Ok(OpenFile { mmap, start_pos }) =
90            file_format::open_incremental_file(sess, &work_products_path)
91        {
92            // Decode the list of work_products
93            let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else {
94                sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
95                return LoadResult::DataOutOfDate;
96            };
97            let work_products: Vec<SerializedWorkProduct> =
98                Decodable::decode(&mut work_product_decoder);
99
100            for swp in work_products {
101                let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| {
102                    let exists = in_incr_comp_dir_sess(sess, path).exists();
103                    if !exists && sess.opts.unstable_opts.incremental_info {
104                        {
    ::std::io::_eprint(format_args!("incremental: could not find file for work product: {0}\n",
            path));
};eprintln!("incremental: could not find file for work product: {path}",);
105                    }
106                    exists
107                });
108
109                if all_files_exist {
110                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/load.rs:110",
                        "rustc_incremental::persist::load", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/load.rs"),
                        ::tracing_core::__macro_support::Option::Some(110u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::load"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("reconcile_work_products: all files for {0:?} exist",
                                                    swp) as &dyn Value))])
            });
    } else { ; }
};debug!("reconcile_work_products: all files for {:?} exist", swp);
111                    prev_work_products.insert(swp.id, swp.work_product);
112                } else {
113                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/load.rs:113",
                        "rustc_incremental::persist::load", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/load.rs"),
                        ::tracing_core::__macro_support::Option::Some(113u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::load"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("reconcile_work_products: some file for {0:?} does not exist",
                                                    swp) as &dyn Value))])
            });
    } else { ; }
};debug!("reconcile_work_products: some file for {:?} does not exist", swp);
114                    delete_dirty_work_product(sess, swp);
115                }
116            }
117        }
118    }
119
120    let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
121
122    match file_format::open_incremental_file(sess, &path) {
123        Err(OpenFileError::NotFoundOrHeaderMismatch) => LoadResult::DataOutOfDate,
124        Err(OpenFileError::IoError { err }) => LoadResult::LoadDepGraph(path.to_owned(), err),
125        Ok(OpenFile { mmap, start_pos }) => {
126            let Ok(mut decoder) = MemDecoder::new(&mmap, start_pos) else {
127                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
128                return LoadResult::DataOutOfDate;
129            };
130            let prev_commandline_args_hash = Hash64::decode(&mut decoder);
131
132            if prev_commandline_args_hash != expected_hash {
133                if sess.opts.unstable_opts.incremental_info {
134                    {
    ::std::io::_eprint(format_args!("[incremental] completely ignoring cache because of differing commandline arguments\n"));
};eprintln!(
135                        "[incremental] completely ignoring cache because of \
136                                    differing commandline arguments"
137                    );
138                }
139                // We can't reuse the cache, purge it.
140                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/load.rs:140",
                        "rustc_incremental::persist::load", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/load.rs"),
                        ::tracing_core::__macro_support::Option::Some(140u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::load"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("load_dep_graph_new: differing commandline arg hashes")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("load_dep_graph_new: differing commandline arg hashes");
141
142                // No need to do any further work
143                return LoadResult::DataOutOfDate;
144            }
145
146            let dep_graph = SerializedDepGraph::decode(&mut decoder);
147
148            LoadResult::Ok { data: (dep_graph, prev_work_products) }
149        }
150    }
151}
152
153/// Attempts to load the query result cache from disk
154///
155/// If we are not in incremental compilation mode, returns `None`.
156/// Otherwise, tries to load the query result cache from disk,
157/// creating an empty cache if it could not be loaded.
158pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
159    if sess.opts.incremental.is_none() {
160        return None;
161    }
162
163    let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
164
165    let path = query_cache_path(sess);
166    match file_format::open_incremental_file(sess, &path) {
167        Ok(OpenFile { mmap, start_pos }) => {
168            let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| {
169                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
170                OnDiskCache::new_empty()
171            });
172            Some(cache)
173        }
174        Err(OpenFileError::NotFoundOrHeaderMismatch | OpenFileError::IoError { .. }) => {
175            Some(OnDiskCache::new_empty())
176        }
177    }
178}
179
180/// Emits a fatal error if the assertion in `-Zassert-incr-state` doesn't match
181/// the outcome of trying to load previous-session state.
182fn maybe_assert_incr_state(sess: &Session, load_result: &LoadResult<impl Sized>) {
183    // Return immediately if there's nothing to assert.
184    let Some(assertion) = sess.opts.unstable_opts.assert_incr_state else { return };
185
186    // Match exhaustively to make sure we don't miss any cases.
187    let loaded = match load_result {
188        LoadResult::Ok { .. } => true,
189        LoadResult::DataOutOfDate | LoadResult::LoadDepGraph(..) => false,
190    };
191
192    match assertion {
193        IncrementalStateAssertion::Loaded => {
194            if !loaded {
195                sess.dcx().emit_fatal(errors::AssertLoaded);
196            }
197        }
198        IncrementalStateAssertion::NotLoaded => {
199            if loaded {
200                sess.dcx().emit_fatal(errors::AssertNotLoaded)
201            }
202        }
203    }
204}
205
206/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
207/// new graph to an incremental session directory.
208pub fn setup_dep_graph(
209    sess: &Session,
210    crate_name: Symbol,
211    stable_crate_id: StableCrateId,
212) -> DepGraph {
213    // `load_dep_graph` can only be called after `prepare_session_directory`.
214    prepare_session_directory(sess, crate_name, stable_crate_id);
215
216    let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
217
218    if sess.opts.incremental.is_some() {
219        sess.time("incr_comp_garbage_collect_session_directories", || {
220            if let Err(e) = garbage_collect_session_directories(sess) {
221                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/load.rs:221",
                        "rustc_incremental::persist::load", ::tracing::Level::WARN,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/load.rs"),
                        ::tracing_core::__macro_support::Option::Some(221u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::load"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::WARN <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Error while trying to garbage collect incremental compilation cache directory: {0}",
                                                    e) as &dyn Value))])
            });
    } else { ; }
};warn!(
222                    "Error while trying to garbage collect incremental \
223                     compilation cache directory: {}",
224                    e
225                );
226            }
227        });
228    }
229
230    res.and_then(|result| {
231        let (prev_graph, prev_work_products) = result.open(sess);
232        build_dep_graph(sess, prev_graph, prev_work_products)
233    })
234    .unwrap_or_else(DepGraph::new_disabled)
235}