| //! Validates all used crates and extern libraries and loads their metadata |
| |
| use crate::locator::{CrateLocator, CratePaths}; |
| use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; |
| |
| use rustc::hir::map::Definitions; |
| use rustc::middle::cstore::DepKind; |
| use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn}; |
| use rustc::session::config; |
| use rustc::session::search_paths::PathKind; |
| use rustc::session::{CrateDisambiguator, Session}; |
| use rustc::ty::TyCtxt; |
| use rustc_data_structures::svh::Svh; |
| use rustc_data_structures::sync::Lrc; |
| use rustc_errors::struct_span_err; |
| use rustc_expand::base::SyntaxExtension; |
| use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; |
| use rustc_index::vec::IndexVec; |
| use rustc_span::edition::Edition; |
| use rustc_span::symbol::{sym, Symbol}; |
| use rustc_span::{Span, DUMMY_SP}; |
| use rustc_target::spec::{PanicStrategy, TargetTriple}; |
| use syntax::ast; |
| use syntax::attr; |
| use syntax::expand::allocator::{global_allocator_spans, AllocatorKind}; |
| |
| use log::{debug, info, log_enabled}; |
| use proc_macro::bridge::client::ProcMacro; |
| use std::path::Path; |
| use std::{cmp, fs}; |
| |
| #[derive(Clone)] |
| pub struct CStore { |
| metas: IndexVec<CrateNum, Option<Lrc<CrateMetadata>>>, |
| injected_panic_runtime: Option<CrateNum>, |
| /// This crate needs an allocator and either provides it itself, or finds it in a dependency. |
| /// If the above is true, then this field denotes the kind of the found allocator. |
| allocator_kind: Option<AllocatorKind>, |
| /// This crate has a `#[global_allocator]` item. |
| has_global_allocator: bool, |
| } |
| |
| pub struct CrateLoader<'a> { |
| // Immutable configuration. |
| sess: &'a Session, |
| metadata_loader: &'a MetadataLoaderDyn, |
| local_crate_name: Symbol, |
| // Mutable output. |
| cstore: CStore, |
| } |
| |
| pub enum LoadedMacro { |
| MacroDef(ast::Item, Edition), |
| ProcMacro(SyntaxExtension), |
| } |
| |
| crate struct Library { |
| pub source: CrateSource, |
| pub metadata: MetadataBlob, |
| } |
| |
| enum LoadResult { |
| Previous(CrateNum), |
| Loaded(Library), |
| } |
| |
| enum LoadError<'a> { |
| LocatorError(CrateLocator<'a>), |
| } |
| |
| impl<'a> LoadError<'a> { |
| fn report(self) -> ! { |
| match self { |
| LoadError::LocatorError(locator) => locator.report_errs(), |
| } |
| } |
| } |
| |
| fn dump_crates(cstore: &CStore) { |
| info!("resolved crates:"); |
| cstore.iter_crate_data(|cnum, data| { |
| info!(" name: {}", data.name()); |
| info!(" cnum: {}", cnum); |
| info!(" hash: {}", data.hash()); |
| info!(" reqd: {:?}", data.dep_kind()); |
| let CrateSource { dylib, rlib, rmeta } = data.source(); |
| dylib.as_ref().map(|dl| info!(" dylib: {}", dl.0.display())); |
| rlib.as_ref().map(|rl| info!(" rlib: {}", rl.0.display())); |
| rmeta.as_ref().map(|rl| info!(" rmeta: {}", rl.0.display())); |
| }); |
| } |
| |
| impl CStore { |
| crate fn from_tcx(tcx: TyCtxt<'_>) -> &CStore { |
| tcx.cstore_as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`") |
| } |
| |
| fn alloc_new_crate_num(&mut self) -> CrateNum { |
| self.metas.push(None); |
| CrateNum::new(self.metas.len() - 1) |
| } |
| |
| crate fn get_crate_data(&self, cnum: CrateNum) -> &CrateMetadata { |
| self.metas[cnum] |
| .as_ref() |
| .unwrap_or_else(|| panic!("Failed to get crate data for {:?}", cnum)) |
| } |
| |
| fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) { |
| assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry"); |
| self.metas[cnum] = Some(Lrc::new(data)); |
| } |
| |
| crate fn iter_crate_data(&self, mut f: impl FnMut(CrateNum, &CrateMetadata)) { |
| for (cnum, data) in self.metas.iter_enumerated() { |
| if let Some(data) = data { |
| f(cnum, data); |
| } |
| } |
| } |
| |
| fn push_dependencies_in_postorder(&self, deps: &mut Vec<CrateNum>, cnum: CrateNum) { |
| if !deps.contains(&cnum) { |
| let data = self.get_crate_data(cnum); |
| for &dep in data.dependencies().iter() { |
| if dep != cnum { |
| self.push_dependencies_in_postorder(deps, dep); |
| } |
| } |
| |
| deps.push(cnum); |
| } |
| } |
| |
| crate fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> Vec<CrateNum> { |
| let mut deps = Vec::new(); |
| if cnum == LOCAL_CRATE { |
| self.iter_crate_data(|cnum, _| self.push_dependencies_in_postorder(&mut deps, cnum)); |
| } else { |
| self.push_dependencies_in_postorder(&mut deps, cnum); |
| } |
| deps |
| } |
| |
| fn crate_dependencies_in_reverse_postorder(&self, cnum: CrateNum) -> Vec<CrateNum> { |
| let mut deps = self.crate_dependencies_in_postorder(cnum); |
| deps.reverse(); |
| deps |
| } |
| |
| crate fn injected_panic_runtime(&self) -> Option<CrateNum> { |
| self.injected_panic_runtime |
| } |
| |
| crate fn allocator_kind(&self) -> Option<AllocatorKind> { |
| self.allocator_kind |
| } |
| |
| crate fn has_global_allocator(&self) -> bool { |
| self.has_global_allocator |
| } |
| } |
| |
| impl<'a> CrateLoader<'a> { |
| pub fn new( |
| sess: &'a Session, |
| metadata_loader: &'a MetadataLoaderDyn, |
| local_crate_name: &str, |
| ) -> Self { |
| CrateLoader { |
| sess, |
| metadata_loader, |
| local_crate_name: Symbol::intern(local_crate_name), |
| cstore: CStore { |
| // We add an empty entry for LOCAL_CRATE (which maps to zero) in |
| // order to make array indices in `metas` match with the |
| // corresponding `CrateNum`. This first entry will always remain |
| // `None`. |
| metas: IndexVec::from_elem_n(None, 1), |
| injected_panic_runtime: None, |
| allocator_kind: None, |
| has_global_allocator: false, |
| }, |
| } |
| } |
| |
| pub fn cstore(&self) -> &CStore { |
| &self.cstore |
| } |
| |
| pub fn into_cstore(self) -> CStore { |
| self.cstore |
| } |
| |
| fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> { |
| let mut ret = None; |
| self.cstore.iter_crate_data(|cnum, data| { |
| if data.name() != name { |
| return; |
| } |
| |
| match hash { |
| Some(hash) if hash == data.hash() => { |
| ret = Some(cnum); |
| return; |
| } |
| Some(..) => return, |
| None => {} |
| } |
| |
| // When the hash is None we're dealing with a top-level dependency |
| // in which case we may have a specification on the command line for |
| // this library. Even though an upstream library may have loaded |
| // something of the same name, we have to make sure it was loaded |
| // from the exact same location as well. |
| // |
| // We're also sure to compare *paths*, not actual byte slices. The |
| // `source` stores paths which are normalized which may be different |
| // from the strings on the command line. |
| let source = self.cstore.get_crate_data(cnum).source(); |
| if let Some(entry) = self.sess.opts.externs.get(&name.as_str()) { |
| // Only use `--extern crate_name=path` here, not `--extern crate_name`. |
| if let Some(mut files) = entry.files() { |
| if files.any(|l| { |
| let l = fs::canonicalize(l).ok(); |
| source.dylib.as_ref().map(|p| &p.0) == l.as_ref() |
| || source.rlib.as_ref().map(|p| &p.0) == l.as_ref() |
| }) { |
| ret = Some(cnum); |
| } |
| } |
| return; |
| } |
| |
| // Alright, so we've gotten this far which means that `data` has the |
| // right name, we don't have a hash, and we don't have a --extern |
| // pointing for ourselves. We're still not quite yet done because we |
| // have to make sure that this crate was found in the crate lookup |
| // path (this is a top-level dependency) as we don't want to |
| // implicitly load anything inside the dependency lookup path. |
| let prev_kind = source |
| .dylib |
| .as_ref() |
| .or(source.rlib.as_ref()) |
| .or(source.rmeta.as_ref()) |
| .expect("No sources for crate") |
| .1; |
| if kind.matches(prev_kind) { |
| ret = Some(cnum); |
| } |
| }); |
| return ret; |
| } |
| |
| fn verify_no_symbol_conflicts(&self, span: Span, root: &CrateRoot<'_>) { |
| // Check for (potential) conflicts with the local crate |
| if self.local_crate_name == root.name() |
| && self.sess.local_crate_disambiguator() == root.disambiguator() |
| { |
| struct_span_err!( |
| self.sess, |
| span, |
| E0519, |
| "the current crate is indistinguishable from one of its \ |
| dependencies: it has the same crate-name `{}` and was \ |
| compiled with the same `-C metadata` arguments. This \ |
| will result in symbol conflicts between the two.", |
| root.name() |
| ) |
| .emit() |
| } |
| |
| // Check for conflicts with any crate loaded so far |
| self.cstore.iter_crate_data(|_, other| { |
| if other.name() == root.name() && // same crate-name |
| other.disambiguator() == root.disambiguator() && // same crate-disambiguator |
| other.hash() != root.hash() |
| { |
| // but different SVH |
| struct_span_err!( |
| self.sess, |
| span, |
| E0523, |
| "found two different crates with name `{}` that are \ |
| not distinguished by differing `-C metadata`. This \ |
| will result in symbol conflicts between the two.", |
| root.name() |
| ) |
| .emit(); |
| } |
| }); |
| } |
| |
| fn register_crate( |
| &mut self, |
| host_lib: Option<Library>, |
| root: Option<&CratePaths>, |
| span: Span, |
| lib: Library, |
| dep_kind: DepKind, |
| name: Symbol, |
| ) -> CrateNum { |
| let _prof_timer = self.sess.prof.generic_activity("metadata_register_crate"); |
| |
| let Library { source, metadata } = lib; |
| let crate_root = metadata.get_root(); |
| let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); |
| self.verify_no_symbol_conflicts(span, &crate_root); |
| |
| let private_dep = |
| self.sess.opts.externs.get(&name.as_str()).map(|e| e.is_private_dep).unwrap_or(false); |
| |
| info!("register crate `{}` (private_dep = {})", crate_root.name(), private_dep); |
| |
| // Claim this crate number and cache it |
| let cnum = self.cstore.alloc_new_crate_num(); |
| |
| // Maintain a reference to the top most crate. |
| // Stash paths for top-most crate locally if necessary. |
| let crate_paths; |
| let root = if let Some(root) = root { |
| root |
| } else { |
| crate_paths = CratePaths::new(crate_root.name(), source.clone()); |
| &crate_paths |
| }; |
| |
| let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span, dep_kind); |
| |
| let raw_proc_macros = if crate_root.is_proc_macro_crate() { |
| let temp_root; |
| let (dlsym_source, dlsym_root) = match &host_lib { |
| Some(host_lib) => (&host_lib.source, { |
| temp_root = host_lib.metadata.get_root(); |
| &temp_root |
| }), |
| None => (&source, &crate_root), |
| }; |
| let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate"); |
| Some(self.dlsym_proc_macros(&dlsym_dylib.0, dlsym_root.disambiguator(), span)) |
| } else { |
| None |
| }; |
| |
| self.cstore.set_crate_data( |
| cnum, |
| CrateMetadata::new( |
| self.sess, |
| metadata, |
| crate_root, |
| raw_proc_macros, |
| cnum, |
| cnum_map, |
| dep_kind, |
| source, |
| private_dep, |
| host_hash, |
| ), |
| ); |
| |
| cnum |
| } |
| |
| fn load_proc_macro<'b>( |
| &self, |
| locator: &mut CrateLocator<'b>, |
| path_kind: PathKind, |
| ) -> Option<(LoadResult, Option<Library>)> |
| where |
| 'a: 'b, |
| { |
| // Use a new crate locator so trying to load a proc macro doesn't affect the error |
| // message we emit |
| let mut proc_macro_locator = locator.clone(); |
| |
| // Try to load a proc macro |
| proc_macro_locator.is_proc_macro = Some(true); |
| |
| // Load the proc macro crate for the target |
| let (locator, target_result) = if self.sess.opts.debugging_opts.dual_proc_macros { |
| proc_macro_locator.reset(); |
| let result = match self.load(&mut proc_macro_locator)? { |
| LoadResult::Previous(cnum) => return Some((LoadResult::Previous(cnum), None)), |
| LoadResult::Loaded(library) => Some(LoadResult::Loaded(library)), |
| }; |
| locator.hash = locator.host_hash; |
| // Use the locator when looking for the host proc macro crate, as that is required |
| // so we want it to affect the error message |
| (locator, result) |
| } else { |
| (&mut proc_macro_locator, None) |
| }; |
| |
| // Load the proc macro crate for the host |
| |
| locator.reset(); |
| locator.is_proc_macro = Some(true); |
| locator.target = &self.sess.host; |
| locator.triple = TargetTriple::from_triple(config::host_triple()); |
| locator.filesearch = self.sess.host_filesearch(path_kind); |
| |
| let host_result = self.load(locator)?; |
| |
| Some(if self.sess.opts.debugging_opts.dual_proc_macros { |
| let host_result = match host_result { |
| LoadResult::Previous(..) => { |
| panic!("host and target proc macros must be loaded in lock-step") |
| } |
| LoadResult::Loaded(library) => library, |
| }; |
| (target_result.unwrap(), Some(host_result)) |
| } else { |
| (host_result, None) |
| }) |
| } |
| |
| fn resolve_crate<'b>( |
| &'b mut self, |
| name: Symbol, |
| span: Span, |
| dep_kind: DepKind, |
| dep: Option<(&'b CratePaths, &'b CrateDep)>, |
| ) -> CrateNum { |
| self.maybe_resolve_crate(name, span, dep_kind, dep).unwrap_or_else(|err| err.report()) |
| } |
| |
| fn maybe_resolve_crate<'b>( |
| &'b mut self, |
| name: Symbol, |
| span: Span, |
| mut dep_kind: DepKind, |
| dep: Option<(&'b CratePaths, &'b CrateDep)>, |
| ) -> Result<CrateNum, LoadError<'b>> { |
| info!("resolving crate `{}`", name); |
| let (root, hash, host_hash, extra_filename, path_kind) = match dep { |
| Some((root, dep)) => ( |
| Some(root), |
| Some(dep.hash), |
| dep.host_hash, |
| Some(&dep.extra_filename[..]), |
| PathKind::Dependency, |
| ), |
| None => (None, None, None, None, PathKind::Crate), |
| }; |
| let result = if let Some(cnum) = self.existing_match(name, hash, path_kind) { |
| (LoadResult::Previous(cnum), None) |
| } else { |
| info!("falling back to a load"); |
| let mut locator = CrateLocator::new( |
| self.sess, |
| self.metadata_loader, |
| name, |
| hash, |
| host_hash, |
| extra_filename, |
| false, // is_host |
| path_kind, |
| span, |
| root, |
| Some(false), // is_proc_macro |
| ); |
| |
| self.load(&mut locator) |
| .map(|r| (r, None)) |
| .or_else(|| { |
| dep_kind = DepKind::UnexportedMacrosOnly; |
| self.load_proc_macro(&mut locator, path_kind) |
| }) |
| .ok_or_else(move || LoadError::LocatorError(locator))? |
| }; |
| |
| match result { |
| (LoadResult::Previous(cnum), None) => { |
| let data = self.cstore.get_crate_data(cnum); |
| if data.is_proc_macro_crate() { |
| dep_kind = DepKind::UnexportedMacrosOnly; |
| } |
| data.update_dep_kind(|data_dep_kind| cmp::max(data_dep_kind, dep_kind)); |
| Ok(cnum) |
| } |
| (LoadResult::Loaded(library), host_library) => { |
| Ok(self.register_crate(host_library, root, span, library, dep_kind, name)) |
| } |
| _ => panic!(), |
| } |
| } |
| |
| fn load(&self, locator: &mut CrateLocator<'_>) -> Option<LoadResult> { |
| let library = locator.maybe_load_library_crate()?; |
| |
| // In the case that we're loading a crate, but not matching |
| // against a hash, we could load a crate which has the same hash |
| // as an already loaded crate. If this is the case prevent |
| // duplicates by just using the first crate. |
| // |
| // Note that we only do this for target triple crates, though, as we |
| // don't want to match a host crate against an equivalent target one |
| // already loaded. |
| let root = library.metadata.get_root(); |
| if locator.triple == self.sess.opts.target_triple { |
| let mut result = LoadResult::Loaded(library); |
| self.cstore.iter_crate_data(|cnum, data| { |
| if data.name() == root.name() && root.hash() == data.hash() { |
| assert!(locator.hash.is_none()); |
| info!("load success, going to previous cnum: {}", cnum); |
| result = LoadResult::Previous(cnum); |
| } |
| }); |
| Some(result) |
| } else { |
| Some(LoadResult::Loaded(library)) |
| } |
| } |
| |
| fn update_extern_crate(&self, cnum: CrateNum, extern_crate: ExternCrate) { |
| let cmeta = self.cstore.get_crate_data(cnum); |
| if cmeta.update_extern_crate(extern_crate) { |
| // Propagate the extern crate info to dependencies if it was updated. |
| let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate }; |
| for &dep_cnum in cmeta.dependencies().iter() { |
| self.update_extern_crate(dep_cnum, extern_crate); |
| } |
| } |
| } |
| |
| // Go through the crate metadata and load any crates that it references |
| fn resolve_crate_deps( |
| &mut self, |
| root: &CratePaths, |
| crate_root: &CrateRoot<'_>, |
| metadata: &MetadataBlob, |
| krate: CrateNum, |
| span: Span, |
| dep_kind: DepKind, |
| ) -> CrateNumMap { |
| debug!("resolving deps of external crate"); |
| if crate_root.is_proc_macro_crate() { |
| return CrateNumMap::new(); |
| } |
| |
| // The map from crate numbers in the crate we're resolving to local crate numbers. |
| // We map 0 and all other holes in the map to our parent crate. The "additional" |
| // self-dependencies should be harmless. |
| std::iter::once(krate) |
| .chain(crate_root.decode_crate_deps(metadata).map(|dep| { |
| info!( |
| "resolving dep crate {} hash: `{}` extra filename: `{}`", |
| dep.name, dep.hash, dep.extra_filename |
| ); |
| if dep.kind == DepKind::UnexportedMacrosOnly { |
| return krate; |
| } |
| let dep_kind = match dep_kind { |
| DepKind::MacrosOnly => DepKind::MacrosOnly, |
| _ => dep.kind, |
| }; |
| self.resolve_crate(dep.name, span, dep_kind, Some((root, &dep))) |
| })) |
| .collect() |
| } |
| |
| fn dlsym_proc_macros( |
| &self, |
| path: &Path, |
| disambiguator: CrateDisambiguator, |
| span: Span, |
| ) -> &'static [ProcMacro] { |
| use crate::dynamic_lib::DynamicLibrary; |
| use std::env; |
| |
| // Make sure the path contains a / or the linker will search for it. |
| let path = env::current_dir().unwrap().join(path); |
| let lib = match DynamicLibrary::open(Some(&path)) { |
| Ok(lib) => lib, |
| Err(err) => self.sess.span_fatal(span, &err), |
| }; |
| |
| let sym = self.sess.generate_proc_macro_decls_symbol(disambiguator); |
| let decls = unsafe { |
| let sym = match lib.symbol(&sym) { |
| Ok(f) => f, |
| Err(err) => self.sess.span_fatal(span, &err), |
| }; |
| *(sym as *const &[ProcMacro]) |
| }; |
| |
| // Intentionally leak the dynamic library. We can't ever unload it |
| // since the library can make things that will live arbitrarily long. |
| std::mem::forget(lib); |
| |
| decls |
| } |
| |
| fn inject_panic_runtime(&mut self, krate: &ast::Crate) { |
| // If we're only compiling an rlib, then there's no need to select a |
| // panic runtime, so we just skip this section entirely. |
| let any_non_rlib = |
| self.sess.crate_types.borrow().iter().any(|ct| *ct != config::CrateType::Rlib); |
| if !any_non_rlib { |
| info!("panic runtime injection skipped, only generating rlib"); |
| return; |
| } |
| |
| // If we need a panic runtime, we try to find an existing one here. At |
| // the same time we perform some general validation of the DAG we've got |
| // going such as ensuring everything has a compatible panic strategy. |
| // |
| // The logic for finding the panic runtime here is pretty much the same |
| // as the allocator case with the only addition that the panic strategy |
| // compilation mode also comes into play. |
| let desired_strategy = self.sess.panic_strategy(); |
| let mut runtime_found = false; |
| let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); |
| |
| self.cstore.iter_crate_data(|cnum, data| { |
| needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime(); |
| if data.is_panic_runtime() { |
| // Inject a dependency from all #![needs_panic_runtime] to this |
| // #![panic_runtime] crate. |
| self.inject_dependency_if(cnum, "a panic runtime", &|data| { |
| data.needs_panic_runtime() |
| }); |
| runtime_found = runtime_found || data.dep_kind() == DepKind::Explicit; |
| } |
| }); |
| |
| // If an explicitly linked and matching panic runtime was found, or if |
| // we just don't need one at all, then we're done here and there's |
| // nothing else to do. |
| if !needs_panic_runtime || runtime_found { |
| return; |
| } |
| |
| // By this point we know that we (a) need a panic runtime and (b) no |
| // panic runtime was explicitly linked. Here we just load an appropriate |
| // default runtime for our panic strategy and then inject the |
| // dependencies. |
| // |
| // We may resolve to an already loaded crate (as the crate may not have |
| // been explicitly linked prior to this) and we may re-inject |
| // dependencies again, but both of those situations are fine. |
| // |
| // Also note that we have yet to perform validation of the crate graph |
| // in terms of everyone has a compatible panic runtime format, that's |
| // performed later as part of the `dependency_format` module. |
| let name = match desired_strategy { |
| PanicStrategy::Unwind => Symbol::intern("panic_unwind"), |
| PanicStrategy::Abort => Symbol::intern("panic_abort"), |
| }; |
| info!("panic runtime not found -- loading {}", name); |
| |
| let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Implicit, None); |
| let data = self.cstore.get_crate_data(cnum); |
| |
| // Sanity check the loaded crate to ensure it is indeed a panic runtime |
| // and the panic strategy is indeed what we thought it was. |
| if !data.is_panic_runtime() { |
| self.sess.err(&format!("the crate `{}` is not a panic runtime", name)); |
| } |
| if data.panic_strategy() != desired_strategy { |
| self.sess.err(&format!( |
| "the crate `{}` does not have the panic \ |
| strategy `{}`", |
| name, |
| desired_strategy.desc() |
| )); |
| } |
| |
| self.cstore.injected_panic_runtime = Some(cnum); |
| self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime()); |
| } |
| |
| fn inject_profiler_runtime(&mut self) { |
| if self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled() { |
| info!("loading profiler"); |
| |
| let name = Symbol::intern("profiler_builtins"); |
| let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Implicit, None); |
| let data = self.cstore.get_crate_data(cnum); |
| |
| // Sanity check the loaded crate to ensure it is indeed a profiler runtime |
| if !data.is_profiler_runtime() { |
| self.sess.err(&format!( |
| "the crate `profiler_builtins` is not \ |
| a profiler runtime" |
| )); |
| } |
| } |
| } |
| |
| fn inject_allocator_crate(&mut self, krate: &ast::Crate) { |
| self.cstore.has_global_allocator = match &*global_allocator_spans(krate) { |
| [span1, span2, ..] => { |
| self.sess |
| .struct_span_err(*span2, "cannot define multiple global allocators") |
| .span_label(*span2, "cannot define a new global allocator") |
| .span_label(*span1, "previous global allocator is defined here") |
| .emit(); |
| true |
| } |
| spans => !spans.is_empty(), |
| }; |
| |
| // Check to see if we actually need an allocator. This desire comes |
| // about through the `#![needs_allocator]` attribute and is typically |
| // written down in liballoc. |
| let mut needs_allocator = attr::contains_name(&krate.attrs, sym::needs_allocator); |
| self.cstore.iter_crate_data(|_, data| { |
| needs_allocator = needs_allocator || data.needs_allocator(); |
| }); |
| if !needs_allocator { |
| return; |
| } |
| |
| // At this point we've determined that we need an allocator. Let's see |
| // if our compilation session actually needs an allocator based on what |
| // we're emitting. |
| let all_rlib = self.sess.crate_types.borrow().iter().all(|ct| match *ct { |
| config::CrateType::Rlib => true, |
| _ => false, |
| }); |
| if all_rlib { |
| return; |
| } |
| |
| // Ok, we need an allocator. Not only that but we're actually going to |
| // create an artifact that needs one linked in. Let's go find the one |
| // that we're going to link in. |
| // |
| // First up we check for global allocators. Look at the crate graph here |
| // and see what's a global allocator, including if we ourselves are a |
| // global allocator. |
| let mut global_allocator = |
| self.cstore.has_global_allocator.then(|| Symbol::intern("this crate")); |
| self.cstore.iter_crate_data(|_, data| { |
| if !data.has_global_allocator() { |
| return; |
| } |
| match global_allocator { |
| Some(other_crate) => { |
| self.sess.err(&format!( |
| "the `#[global_allocator]` in {} \ |
| conflicts with global \ |
| allocator in: {}", |
| other_crate, |
| data.name() |
| )); |
| } |
| None => global_allocator = Some(data.name()), |
| } |
| }); |
| if global_allocator.is_some() { |
| self.cstore.allocator_kind = Some(AllocatorKind::Global); |
| return; |
| } |
| |
| // Ok we haven't found a global allocator but we still need an |
| // allocator. At this point our allocator request is typically fulfilled |
| // by the standard library, denoted by the `#![default_lib_allocator]` |
| // attribute. |
| let mut has_default = attr::contains_name(&krate.attrs, sym::default_lib_allocator); |
| self.cstore.iter_crate_data(|_, data| { |
| if data.has_default_lib_allocator() { |
| has_default = true; |
| } |
| }); |
| |
| if !has_default { |
| self.sess.err( |
| "no global memory allocator found but one is \ |
| required; link to std or \ |
| add `#[global_allocator]` to a static item \ |
| that implements the GlobalAlloc trait.", |
| ); |
| } |
| self.cstore.allocator_kind = Some(AllocatorKind::Default); |
| } |
| |
| fn inject_dependency_if( |
| &self, |
| krate: CrateNum, |
| what: &str, |
| needs_dep: &dyn Fn(&CrateMetadata) -> bool, |
| ) { |
| // don't perform this validation if the session has errors, as one of |
| // those errors may indicate a circular dependency which could cause |
| // this to stack overflow. |
| if self.sess.has_errors() { |
| return; |
| } |
| |
| // Before we inject any dependencies, make sure we don't inject a |
| // circular dependency by validating that this crate doesn't |
| // transitively depend on any crates satisfying `needs_dep`. |
| for dep in self.cstore.crate_dependencies_in_reverse_postorder(krate) { |
| let data = self.cstore.get_crate_data(dep); |
| if needs_dep(&data) { |
| self.sess.err(&format!( |
| "the crate `{}` cannot depend \ |
| on a crate that needs {}, but \ |
| it depends on `{}`", |
| self.cstore.get_crate_data(krate).name(), |
| what, |
| data.name() |
| )); |
| } |
| } |
| |
| // All crates satisfying `needs_dep` do not explicitly depend on the |
| // crate provided for this compile, but in order for this compilation to |
| // be successfully linked we need to inject a dependency (to order the |
| // crates on the command line correctly). |
| self.cstore.iter_crate_data(|cnum, data| { |
| if !needs_dep(data) { |
| return; |
| } |
| |
| info!("injecting a dep from {} to {}", cnum, krate); |
| data.add_dependency(krate); |
| }); |
| } |
| |
| pub fn postprocess(&mut self, krate: &ast::Crate) { |
| self.inject_profiler_runtime(); |
| self.inject_allocator_crate(krate); |
| self.inject_panic_runtime(krate); |
| |
| if log_enabled!(log::Level::Info) { |
| dump_crates(&self.cstore); |
| } |
| } |
| |
| pub fn process_extern_crate( |
| &mut self, |
| item: &ast::Item, |
| definitions: &Definitions, |
| ) -> CrateNum { |
| match item.kind { |
| ast::ItemKind::ExternCrate(orig_name) => { |
| debug!( |
| "resolving extern crate stmt. ident: {} orig_name: {:?}", |
| item.ident, orig_name |
| ); |
| let name = match orig_name { |
| Some(orig_name) => { |
| crate::validate_crate_name( |
| Some(self.sess), |
| &orig_name.as_str(), |
| Some(item.span), |
| ); |
| orig_name |
| } |
| None => item.ident.name, |
| }; |
| let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { |
| DepKind::UnexportedMacrosOnly |
| } else { |
| DepKind::Explicit |
| }; |
| |
| let cnum = self.resolve_crate(name, item.span, dep_kind, None); |
| |
| let def_id = definitions.opt_local_def_id(item.id).unwrap(); |
| let path_len = definitions.def_path(def_id.index).data.len(); |
| self.update_extern_crate( |
| cnum, |
| ExternCrate { |
| src: ExternCrateSource::Extern(def_id), |
| span: item.span, |
| path_len, |
| dependency_of: LOCAL_CRATE, |
| }, |
| ); |
| cnum |
| } |
| _ => bug!(), |
| } |
| } |
| |
| pub fn process_path_extern(&mut self, name: Symbol, span: Span) -> CrateNum { |
| let cnum = self.resolve_crate(name, span, DepKind::Explicit, None); |
| |
| self.update_extern_crate( |
| cnum, |
| ExternCrate { |
| src: ExternCrateSource::Path, |
| span, |
| // to have the least priority in `update_extern_crate` |
| path_len: usize::max_value(), |
| dependency_of: LOCAL_CRATE, |
| }, |
| ); |
| |
| cnum |
| } |
| |
| pub fn maybe_process_path_extern(&mut self, name: Symbol, span: Span) -> Option<CrateNum> { |
| self.maybe_resolve_crate(name, span, DepKind::Explicit, None).ok() |
| } |
| } |