| //! Validates all used crates and extern libraries and loads their metadata | |
| use std::error::Error; | |
| use std::path::Path; | |
| use std::str::FromStr; | |
| use std::time::Duration; | |
| use std::{cmp, env, iter}; | |
| use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name}; | |
| use rustc_ast::{self as ast, *}; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_data_structures::owned_slice::OwnedSlice; | |
| use rustc_data_structures::svh::Svh; | |
| use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; | |
| use rustc_data_structures::unord::UnordMap; | |
| use rustc_expand::base::SyntaxExtension; | |
| use rustc_fs_util::try_canonicalize; | |
| use rustc_hir as hir; | |
| use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; | |
| use rustc_hir::definitions::Definitions; | |
| use rustc_index::IndexVec; | |
| use rustc_middle::bug; | |
| use rustc_middle::ty::data_structures::IndexSet; | |
| use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; | |
| use rustc_proc_macro::bridge::client::ProcMacro; | |
| use rustc_session::Session; | |
| use rustc_session::config::{ | |
| CrateType, ExtendedTargetModifierInfo, ExternLocation, Externs, OptionsTargetModifiers, | |
| TargetModifier, | |
| }; | |
| use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; | |
| use rustc_session::lint::{self, BuiltinLintDiag}; | |
| use rustc_session::output::validate_crate_name; | |
| use rustc_session::search_paths::PathKind; | |
| use rustc_span::def_id::DefId; | |
| use rustc_span::edition::Edition; | |
| use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; | |
| use rustc_target::spec::{PanicStrategy, Target}; | |
| use tracing::{debug, info, trace}; | |
| use crate::errors; | |
| use crate::locator::{CrateError, CrateLocator, CratePaths, CrateRejections}; | |
| use crate::rmeta::{ | |
| CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers, | |
| }; | |
| /// The backend's way to give the crate store access to the metadata in a library. | |
| /// Note that it returns the raw metadata bytes stored in the library file, whether | |
| /// it is compressed, uncompressed, some weird mix, etc. | |
| /// rmeta files are backend independent and not handled here. | |
| pub trait MetadataLoader { | |
| fn get_rlib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>; | |
| fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>; | |
| } | |
| pub type MetadataLoaderDyn = dyn MetadataLoader + Send + Sync + sync::DynSend + sync::DynSync; | |
| pub struct CStore { | |
| metadata_loader: Box<MetadataLoaderDyn>, | |
| metas: IndexVec<CrateNum, Option<Box<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 needs an allocation error handler 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. | |
| alloc_error_handler_kind: Option<AllocatorKind>, | |
| /// This crate has a `#[global_allocator]` item. | |
| has_global_allocator: bool, | |
| /// This crate has a `#[alloc_error_handler]` item. | |
| has_alloc_error_handler: bool, | |
| /// Names that were used to load the crates via `extern crate` or paths. | |
| resolved_externs: UnordMap<Symbol, CrateNum>, | |
| /// Unused externs of the crate | |
| unused_externs: Vec<Symbol>, | |
| used_extern_options: FxHashSet<Symbol>, | |
| } | |
| impl std::fmt::Debug for CStore { | |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| f.debug_struct("CStore").finish_non_exhaustive() | |
| } | |
| } | |
| pub enum LoadedMacro { | |
| MacroDef { | |
| def: MacroDef, | |
| ident: Ident, | |
| attrs: Vec<hir::Attribute>, | |
| span: Span, | |
| edition: Edition, | |
| }, | |
| ProcMacro(SyntaxExtension), | |
| } | |
| pub(crate) struct Library { | |
| pub source: CrateSource, | |
| pub metadata: MetadataBlob, | |
| } | |
| enum LoadResult { | |
| Previous(CrateNum), | |
| Loaded(Library), | |
| } | |
| /// A reference to `CrateMetadata` that can also give access to whole crate store when necessary. | |
| #[derive(Clone, Copy)] | |
| pub(crate) struct CrateMetadataRef<'a> { | |
| pub cdata: &'a CrateMetadata, | |
| pub cstore: &'a CStore, | |
| } | |
| impl std::ops::Deref for CrateMetadataRef<'_> { | |
| type Target = CrateMetadata; | |
| fn deref(&self) -> &Self::Target { | |
| self.cdata | |
| } | |
| } | |
| struct CrateDump<'a>(&'a CStore); | |
| impl<'a> std::fmt::Debug for CrateDump<'a> { | |
| fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| writeln!(fmt, "resolved crates:")?; | |
| for (cnum, data) in self.0.iter_crate_data() { | |
| writeln!(fmt, " name: {}", data.name())?; | |
| writeln!(fmt, " cnum: {cnum}")?; | |
| writeln!(fmt, " hash: {}", data.hash())?; | |
| writeln!(fmt, " reqd: {:?}", data.dep_kind())?; | |
| writeln!(fmt, " priv: {:?}", data.is_private_dep())?; | |
| let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source(); | |
| if let Some(dylib) = dylib { | |
| writeln!(fmt, " dylib: {}", dylib.display())?; | |
| } | |
| if let Some(rlib) = rlib { | |
| writeln!(fmt, " rlib: {}", rlib.display())?; | |
| } | |
| if let Some(rmeta) = rmeta { | |
| writeln!(fmt, " rmeta: {}", rmeta.display())?; | |
| } | |
| if let Some(sdylib_interface) = sdylib_interface { | |
| writeln!(fmt, " sdylib interface: {}", sdylib_interface.display())?; | |
| } | |
| } | |
| Ok(()) | |
| } | |
| } | |
| /// Reason that a crate is being sourced as a dependency. | |
| #[derive(Clone, Copy)] | |
| enum CrateOrigin<'a> { | |
| /// This crate was a dependency of another crate. | |
| IndirectDependency { | |
| /// Where this dependency was included from. Should only be used in error messages. | |
| dep_root_for_errors: &'a CratePaths, | |
| /// True if the parent is private, meaning the dependent should also be private. | |
| parent_private: bool, | |
| /// Dependency info about this crate. | |
| dep: &'a CrateDep, | |
| }, | |
| /// Injected by `rustc`. | |
| Injected, | |
| /// Provided by `extern crate foo` or as part of the extern prelude. | |
| Extern, | |
| } | |
| impl<'a> CrateOrigin<'a> { | |
| /// Return the dependency root, if any. | |
| fn dep_root_for_errors(&self) -> Option<&'a CratePaths> { | |
| match self { | |
| CrateOrigin::IndirectDependency { dep_root_for_errors, .. } => { | |
| Some(dep_root_for_errors) | |
| } | |
| _ => None, | |
| } | |
| } | |
| /// Return dependency information, if any. | |
| fn dep(&self) -> Option<&'a CrateDep> { | |
| match self { | |
| CrateOrigin::IndirectDependency { dep, .. } => Some(dep), | |
| _ => None, | |
| } | |
| } | |
| /// `Some(true)` if the dependency is private or its parent is private, `Some(false)` if the | |
| /// dependency is not private, `None` if it could not be determined. | |
| fn private_dep(&self) -> Option<bool> { | |
| match self { | |
| CrateOrigin::IndirectDependency { parent_private, dep, .. } => { | |
| Some(dep.is_private || *parent_private) | |
| } | |
| CrateOrigin::Injected => Some(true), | |
| _ => None, | |
| } | |
| } | |
| } | |
| impl CStore { | |
| pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> { | |
| FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| { | |
| cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`") | |
| }) | |
| } | |
| pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> FreezeWriteGuard<'_, CStore> { | |
| FreezeWriteGuard::map(tcx.untracked().cstore.write(), |cstore| { | |
| cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`") | |
| }) | |
| } | |
| fn intern_stable_crate_id<'tcx>( | |
| &mut self, | |
| tcx: TyCtxt<'tcx>, | |
| root: &CrateRoot, | |
| ) -> Result<TyCtxtFeed<'tcx, CrateNum>, CrateError> { | |
| assert_eq!(self.metas.len(), tcx.untracked().stable_crate_ids.read().len()); | |
| let num = tcx.create_crate_num(root.stable_crate_id()).map_err(|existing| { | |
| // Check for (potential) conflicts with the local crate | |
| if existing == LOCAL_CRATE { | |
| CrateError::SymbolConflictsCurrent(root.name()) | |
| } else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name()) | |
| { | |
| let crate_name0 = root.name(); | |
| CrateError::StableCrateIdCollision(crate_name0, crate_name1) | |
| } else { | |
| CrateError::NotFound(root.name()) | |
| } | |
| })?; | |
| self.metas.push(None); | |
| Ok(num) | |
| } | |
| pub fn has_crate_data(&self, cnum: CrateNum) -> bool { | |
| self.metas[cnum].is_some() | |
| } | |
| pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { | |
| let cdata = self.metas[cnum] | |
| .as_ref() | |
| .unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}")); | |
| CrateMetadataRef { cdata, cstore: self } | |
| } | |
| pub(crate) fn get_crate_data_mut(&mut self, cnum: CrateNum) -> &mut CrateMetadata { | |
| self.metas[cnum].as_mut().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(Box::new(data)); | |
| } | |
| /// Save the name used to resolve the extern crate in the local crate | |
| /// | |
| /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name. | |
| /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`. | |
| pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) { | |
| self.resolved_externs.insert(name, extern_crate); | |
| } | |
| /// Crate resolved and loaded via the given extern name | |
| /// (corresponds to names in `sess.opts.externs`) | |
| /// | |
| /// May be `None` if the crate wasn't used | |
| pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option<CrateNum> { | |
| self.resolved_externs.get(&externs_name).copied() | |
| } | |
| pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> { | |
| self.metas | |
| .iter_enumerated() | |
| .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data))) | |
| } | |
| pub fn all_proc_macro_def_ids(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = DefId> { | |
| self.iter_crate_data() | |
| .flat_map(move |(krate, data)| data.proc_macros_for_crate(tcx, krate, self)) | |
| } | |
| fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) { | |
| if !deps.contains(&cnum) { | |
| let data = self.get_crate_data(cnum); | |
| for dep in data.dependencies() { | |
| if dep != cnum { | |
| self.push_dependencies_in_postorder(deps, dep); | |
| } | |
| } | |
| deps.insert(cnum); | |
| } | |
| } | |
| pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> IndexSet<CrateNum> { | |
| let mut deps = IndexSet::default(); | |
| if cnum == LOCAL_CRATE { | |
| for (cnum, _) in self.iter_crate_data() { | |
| self.push_dependencies_in_postorder(&mut deps, cnum); | |
| } | |
| } else { | |
| self.push_dependencies_in_postorder(&mut deps, cnum); | |
| } | |
| deps | |
| } | |
| pub(crate) fn injected_panic_runtime(&self) -> Option<CrateNum> { | |
| self.injected_panic_runtime | |
| } | |
| pub(crate) fn allocator_kind(&self) -> Option<AllocatorKind> { | |
| self.allocator_kind | |
| } | |
| pub(crate) fn alloc_error_handler_kind(&self) -> Option<AllocatorKind> { | |
| self.alloc_error_handler_kind | |
| } | |
| pub(crate) fn has_global_allocator(&self) -> bool { | |
| self.has_global_allocator | |
| } | |
| pub(crate) fn has_alloc_error_handler(&self) -> bool { | |
| self.has_alloc_error_handler | |
| } | |
| pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) { | |
| let json_unused_externs = tcx.sess.opts.json_unused_externs; | |
| // We put the check for the option before the lint_level_at_node call | |
| // because the call mutates internal state and introducing it | |
| // leads to some ui tests failing. | |
| if !json_unused_externs.is_enabled() { | |
| return; | |
| } | |
| let level = tcx | |
| .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID) | |
| .level; | |
| if level != lint::Level::Allow { | |
| let unused_externs = | |
| self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>(); | |
| let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>(); | |
| tcx.dcx().emit_unused_externs(level, json_unused_externs.is_loud(), &unused_externs); | |
| } | |
| } | |
| fn report_target_modifiers_extended( | |
| tcx: TyCtxt<'_>, | |
| krate: &Crate, | |
| mods: &TargetModifiers, | |
| dep_mods: &TargetModifiers, | |
| data: &CrateMetadata, | |
| ) { | |
| let span = krate.spans.inner_span.shrink_to_lo(); | |
| let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; | |
| let local_crate = tcx.crate_name(LOCAL_CRATE); | |
| let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); | |
| let report_diff = |prefix: &String, | |
| opt_name: &String, | |
| flag_local_value: Option<&String>, | |
| flag_extern_value: Option<&String>| { | |
| if allowed_flag_mismatches.contains(&opt_name) { | |
| return; | |
| } | |
| let extern_crate = data.name(); | |
| let flag_name = opt_name.clone(); | |
| let flag_name_prefixed = format!("-{}{}", prefix, opt_name); | |
| match (flag_local_value, flag_extern_value) { | |
| (Some(local_value), Some(extern_value)) => { | |
| tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { | |
| span, | |
| extern_crate, | |
| local_crate, | |
| flag_name, | |
| flag_name_prefixed, | |
| local_value: local_value.to_string(), | |
| extern_value: extern_value.to_string(), | |
| }) | |
| } | |
| (None, Some(extern_value)) => { | |
| tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed { | |
| span, | |
| extern_crate, | |
| local_crate, | |
| flag_name, | |
| flag_name_prefixed, | |
| extern_value: extern_value.to_string(), | |
| }) | |
| } | |
| (Some(local_value), None) => { | |
| tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed { | |
| span, | |
| extern_crate, | |
| local_crate, | |
| flag_name, | |
| flag_name_prefixed, | |
| local_value: local_value.to_string(), | |
| }) | |
| } | |
| (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"), | |
| }; | |
| }; | |
| let mut it1 = mods.iter().map(tmod_extender); | |
| let mut it2 = dep_mods.iter().map(tmod_extender); | |
| let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; | |
| let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; | |
| loop { | |
| left_name_val = left_name_val.or_else(|| it1.next()); | |
| right_name_val = right_name_val.or_else(|| it2.next()); | |
| match (&left_name_val, &right_name_val) { | |
| (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { | |
| cmp::Ordering::Equal => { | |
| if !l.1.consistent(&tcx.sess, Some(&r.1)) { | |
| report_diff( | |
| &l.0.prefix, | |
| &l.0.name, | |
| Some(&l.1.value_name), | |
| Some(&r.1.value_name), | |
| ); | |
| } | |
| left_name_val = None; | |
| right_name_val = None; | |
| } | |
| cmp::Ordering::Greater => { | |
| if !r.1.consistent(&tcx.sess, None) { | |
| report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); | |
| } | |
| right_name_val = None; | |
| } | |
| cmp::Ordering::Less => { | |
| if !l.1.consistent(&tcx.sess, None) { | |
| report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); | |
| } | |
| left_name_val = None; | |
| } | |
| }, | |
| (Some(l), None) => { | |
| if !l.1.consistent(&tcx.sess, None) { | |
| report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); | |
| } | |
| left_name_val = None; | |
| } | |
| (None, Some(r)) => { | |
| if !r.1.consistent(&tcx.sess, None) { | |
| report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); | |
| } | |
| right_name_val = None; | |
| } | |
| (None, None) => break, | |
| } | |
| } | |
| } | |
| pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) { | |
| for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch { | |
| if !OptionsTargetModifiers::is_target_modifier(flag_name) { | |
| tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed { | |
| span: krate.spans.inner_span.shrink_to_lo(), | |
| flag_name: flag_name.clone(), | |
| }); | |
| } | |
| } | |
| let mods = tcx.sess.opts.gather_target_modifiers(); | |
| for (_cnum, data) in self.iter_crate_data() { | |
| if data.is_proc_macro_crate() { | |
| continue; | |
| } | |
| let dep_mods = data.target_modifiers(); | |
| if mods != dep_mods { | |
| Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data); | |
| } | |
| } | |
| } | |
| // Report about async drop types in dependency if async drop feature is disabled | |
| pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) { | |
| if tcx.features().async_drop() { | |
| return; | |
| } | |
| for (_cnum, data) in self.iter_crate_data() { | |
| if data.is_proc_macro_crate() { | |
| continue; | |
| } | |
| if data.has_async_drops() { | |
| let extern_crate = data.name(); | |
| let local_crate = tcx.crate_name(LOCAL_CRATE); | |
| tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency { | |
| span: krate.spans.inner_span.shrink_to_lo(), | |
| extern_crate, | |
| local_crate, | |
| }); | |
| } | |
| } | |
| } | |
| pub fn new(metadata_loader: Box<MetadataLoaderDyn>) -> CStore { | |
| CStore { | |
| metadata_loader, | |
| // 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_iter(iter::once(None)), | |
| injected_panic_runtime: None, | |
| allocator_kind: None, | |
| alloc_error_handler_kind: None, | |
| has_global_allocator: false, | |
| has_alloc_error_handler: false, | |
| resolved_externs: UnordMap::default(), | |
| unused_externs: Vec::new(), | |
| used_extern_options: Default::default(), | |
| } | |
| } | |
| fn existing_match(&self, name: Symbol, hash: Option<Svh>) -> Option<CrateNum> { | |
| let hash = hash?; | |
| for (cnum, data) in self.iter_crate_data() { | |
| if data.name() != name { | |
| trace!("{} did not match {}", data.name(), name); | |
| continue; | |
| } | |
| if hash == data.hash() { | |
| return Some(cnum); | |
| } else { | |
| debug!("actual hash {} did not match expected {}", hash, data.hash()); | |
| } | |
| } | |
| None | |
| } | |
| /// Determine whether a dependency should be considered private. | |
| /// | |
| /// Dependencies are private if they get extern option specified, e.g. `--extern priv:mycrate`. | |
| /// This is stored in metadata, so `private_dep` can be correctly set during load. A `Some` | |
| /// value for `private_dep` indicates that the crate is known to be private or public (note | |
| /// that any `None` or `Some(false)` use of the same crate will make it public). | |
| /// | |
| /// Sometimes the directly dependent crate is not specified by `--extern`, in this case, | |
| /// `private-dep` is none during loading. This is equivalent to the scenario where the | |
| /// command parameter is set to `public-dependency` | |
| fn is_private_dep(&self, externs: &Externs, name: Symbol, private_dep: Option<bool>) -> bool { | |
| let extern_private = externs.get(name.as_str()).map(|e| e.is_private_dep); | |
| match (extern_private, private_dep) { | |
| // Explicit non-private via `--extern`, explicit non-private from metadata, or | |
| // unspecified with default to public. | |
| (Some(false), _) | (_, Some(false)) | (None, None) => false, | |
| // Marked private via `--extern priv:mycrate` or in metadata. | |
| (Some(true) | None, Some(true) | None) => true, | |
| } | |
| } | |
| fn register_crate<'tcx>( | |
| &mut self, | |
| tcx: TyCtxt<'tcx>, | |
| host_lib: Option<Library>, | |
| origin: CrateOrigin<'_>, | |
| lib: Library, | |
| dep_kind: CrateDepKind, | |
| name: Symbol, | |
| private_dep: Option<bool>, | |
| ) -> Result<CrateNum, CrateError> { | |
| let _prof_timer = | |
| tcx.sess.prof.generic_activity_with_arg("metadata_register_crate", name.as_str()); | |
| 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()); | |
| let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); | |
| // Claim this crate number and cache it | |
| let feed = self.intern_stable_crate_id(tcx, &crate_root)?; | |
| let cnum = feed.key(); | |
| info!( | |
| "register crate `{}` (cnum = {}. private_dep = {})", | |
| crate_root.name(), | |
| cnum, | |
| private_dep | |
| ); | |
| // Maintain a reference to the top most crate. | |
| // Stash paths for top-most crate locally if necessary. | |
| let crate_paths; | |
| let dep_root_for_errors = if let Some(dep_root_for_errors) = origin.dep_root_for_errors() { | |
| dep_root_for_errors | |
| } else { | |
| crate_paths = CratePaths::new(crate_root.name(), source.clone()); | |
| &crate_paths | |
| }; | |
| let cnum_map = self.resolve_crate_deps( | |
| tcx, | |
| dep_root_for_errors, | |
| &crate_root, | |
| &metadata, | |
| cnum, | |
| dep_kind, | |
| private_dep, | |
| )?; | |
| 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(tcx.sess, dlsym_dylib, dlsym_root.stable_crate_id())?) | |
| } else { | |
| None | |
| }; | |
| let crate_metadata = CrateMetadata::new( | |
| tcx, | |
| self, | |
| metadata, | |
| crate_root, | |
| raw_proc_macros, | |
| cnum, | |
| cnum_map, | |
| dep_kind, | |
| source, | |
| private_dep, | |
| host_hash, | |
| ); | |
| self.set_crate_data(cnum, crate_metadata); | |
| Ok(cnum) | |
| } | |
| fn load_proc_macro<'a, 'b>( | |
| &self, | |
| sess: &'a Session, | |
| locator: &mut CrateLocator<'b>, | |
| crate_rejections: &mut CrateRejections, | |
| path_kind: PathKind, | |
| host_hash: Option<Svh>, | |
| ) -> Result<Option<(LoadResult, Option<Library>)>, CrateError> | |
| where | |
| 'a: 'b, | |
| { | |
| if sess.opts.unstable_opts.dual_proc_macros { | |
| // Use a new crate locator and crate rejections 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.for_target_proc_macro(sess, path_kind); | |
| // Load the proc macro crate for the target | |
| let target_result = | |
| match self.load(&mut proc_macro_locator, &mut CrateRejections::default())? { | |
| Some(LoadResult::Previous(cnum)) => { | |
| return Ok(Some((LoadResult::Previous(cnum), None))); | |
| } | |
| Some(LoadResult::Loaded(library)) => Some(LoadResult::Loaded(library)), | |
| None => return Ok(None), | |
| }; | |
| // Use the existing crate_rejections as we want the error message to be affected by | |
| // loading the host proc macro. | |
| *crate_rejections = CrateRejections::default(); | |
| // Load the proc macro crate for the host | |
| locator.for_proc_macro(sess, path_kind); | |
| locator.hash = host_hash; | |
| let Some(host_result) = self.load(locator, crate_rejections)? else { | |
| return Ok(None); | |
| }; | |
| let host_result = match host_result { | |
| LoadResult::Previous(..) => { | |
| panic!("host and target proc macros must be loaded in lock-step") | |
| } | |
| LoadResult::Loaded(library) => library, | |
| }; | |
| Ok(Some((target_result.unwrap(), Some(host_result)))) | |
| } else { | |
| // Use a new crate locator and crate rejections so trying to load a proc macro doesn't | |
| // affect the error message we emit | |
| let mut proc_macro_locator = locator.clone(); | |
| // Load the proc macro crate for the host | |
| proc_macro_locator.for_proc_macro(sess, path_kind); | |
| let Some(host_result) = | |
| self.load(&mut proc_macro_locator, &mut CrateRejections::default())? | |
| else { | |
| return Ok(None); | |
| }; | |
| Ok(Some((host_result, None))) | |
| } | |
| } | |
| fn resolve_crate<'tcx>( | |
| &mut self, | |
| tcx: TyCtxt<'tcx>, | |
| name: Symbol, | |
| span: Span, | |
| dep_kind: CrateDepKind, | |
| origin: CrateOrigin<'_>, | |
| ) -> Option<CrateNum> { | |
| self.used_extern_options.insert(name); | |
| match self.maybe_resolve_crate(tcx, name, dep_kind, origin) { | |
| Ok(cnum) => { | |
| self.set_used_recursively(cnum); | |
| Some(cnum) | |
| } | |
| Err(err) => { | |
| debug!("failed to resolve crate {} {:?}", name, dep_kind); | |
| let missing_core = self | |
| .maybe_resolve_crate( | |
| tcx, | |
| sym::core, | |
| CrateDepKind::Unconditional, | |
| CrateOrigin::Extern, | |
| ) | |
| .is_err(); | |
| err.report(tcx.sess, span, missing_core); | |
| None | |
| } | |
| } | |
| } | |
| fn maybe_resolve_crate<'b, 'tcx>( | |
| &'b mut self, | |
| tcx: TyCtxt<'tcx>, | |
| name: Symbol, | |
| mut dep_kind: CrateDepKind, | |
| origin: CrateOrigin<'b>, | |
| ) -> Result<CrateNum, CrateError> { | |
| info!("resolving crate `{}`", name); | |
| if !name.as_str().is_ascii() { | |
| return Err(CrateError::NonAsciiName(name)); | |
| } | |
| let dep_root_for_errors = origin.dep_root_for_errors(); | |
| let dep = origin.dep(); | |
| let hash = dep.map(|d| d.hash); | |
| let host_hash = dep.map(|d| d.host_hash).flatten(); | |
| let extra_filename = dep.map(|d| &d.extra_filename[..]); | |
| let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate }; | |
| let private_dep = origin.private_dep(); | |
| let result = if let Some(cnum) = self.existing_match(name, hash) { | |
| (LoadResult::Previous(cnum), None) | |
| } else { | |
| info!("falling back to a load"); | |
| let mut locator = CrateLocator::new( | |
| tcx.sess, | |
| &*self.metadata_loader, | |
| name, | |
| // The all loop is because `--crate-type=rlib --crate-type=rlib` is | |
| // legal and produces both inside this type. | |
| tcx.crate_types().iter().all(|c| *c == CrateType::Rlib), | |
| hash, | |
| extra_filename, | |
| path_kind, | |
| ); | |
| let mut crate_rejections = CrateRejections::default(); | |
| match self.load(&mut locator, &mut crate_rejections)? { | |
| Some(res) => (res, None), | |
| None => { | |
| info!("falling back to loading proc_macro"); | |
| dep_kind = CrateDepKind::MacrosOnly; | |
| match self.load_proc_macro( | |
| tcx.sess, | |
| &mut locator, | |
| &mut crate_rejections, | |
| path_kind, | |
| host_hash, | |
| )? { | |
| Some(res) => res, | |
| None => { | |
| return Err( | |
| locator.into_error(crate_rejections, dep_root_for_errors.cloned()) | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| match result { | |
| (LoadResult::Previous(cnum), None) => { | |
| info!("library for `{}` was loaded previously, cnum {cnum}", name); | |
| // When `private_dep` is none, it indicates the directly dependent crate. If it is | |
| // not specified by `--extern` on command line parameters, it may be | |
| // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to | |
| // `public-dependency` here. | |
| let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); | |
| let data = self.get_crate_data_mut(cnum); | |
| if data.is_proc_macro_crate() { | |
| dep_kind = CrateDepKind::MacrosOnly; | |
| } | |
| data.set_dep_kind(cmp::max(data.dep_kind(), dep_kind)); | |
| data.update_and_private_dep(private_dep); | |
| Ok(cnum) | |
| } | |
| (LoadResult::Loaded(library), host_library) => { | |
| info!("register newly loaded library for `{}`", name); | |
| self.register_crate(tcx, host_library, origin, library, dep_kind, name, private_dep) | |
| } | |
| _ => panic!(), | |
| } | |
| } | |
| fn load( | |
| &self, | |
| locator: &CrateLocator<'_>, | |
| crate_rejections: &mut CrateRejections, | |
| ) -> Result<Option<LoadResult>, CrateError> { | |
| let Some(library) = locator.maybe_load_library_crate(crate_rejections)? else { | |
| return Ok(None); | |
| }; | |
| // 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. | |
| let root = library.metadata.get_root(); | |
| let mut result = LoadResult::Loaded(library); | |
| for (cnum, data) in self.iter_crate_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); | |
| break; | |
| } | |
| } | |
| Ok(Some(result)) | |
| } | |
| /// Go through the crate metadata and load any crates that it references. | |
| fn resolve_crate_deps( | |
| &mut self, | |
| tcx: TyCtxt<'_>, | |
| dep_root_for_errors: &CratePaths, | |
| crate_root: &CrateRoot, | |
| metadata: &MetadataBlob, | |
| krate: CrateNum, | |
| dep_kind: CrateDepKind, | |
| parent_is_private: bool, | |
| ) -> Result<CrateNumMap, CrateError> { | |
| debug!( | |
| "resolving deps of external crate `{}` with dep root `{}`", | |
| crate_root.name(), | |
| dep_root_for_errors.name | |
| ); | |
| if crate_root.is_proc_macro_crate() { | |
| return Ok(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. | |
| let deps = crate_root.decode_crate_deps(metadata); | |
| let mut crate_num_map = CrateNumMap::with_capacity(1 + deps.len()); | |
| crate_num_map.push(krate); | |
| for dep in deps { | |
| info!( | |
| "resolving dep `{}`->`{}` hash: `{}` extra filename: `{}` private {}", | |
| crate_root.name(), | |
| dep.name, | |
| dep.hash, | |
| dep.extra_filename, | |
| dep.is_private, | |
| ); | |
| let dep_kind = match dep_kind { | |
| CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly, | |
| _ => dep.kind, | |
| }; | |
| let cnum = self.maybe_resolve_crate( | |
| tcx, | |
| dep.name, | |
| dep_kind, | |
| CrateOrigin::IndirectDependency { | |
| dep_root_for_errors, | |
| parent_private: parent_is_private, | |
| dep: &dep, | |
| }, | |
| )?; | |
| crate_num_map.push(cnum); | |
| } | |
| debug!("resolve_crate_deps: cnum_map for {:?} is {:?}", krate, crate_num_map); | |
| Ok(crate_num_map) | |
| } | |
| fn dlsym_proc_macros( | |
| &self, | |
| sess: &Session, | |
| path: &Path, | |
| stable_crate_id: StableCrateId, | |
| ) -> Result<&'static [ProcMacro], CrateError> { | |
| let sym_name = sess.generate_proc_macro_decls_symbol(stable_crate_id); | |
| debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name); | |
| unsafe { | |
| let result = load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name); | |
| match result { | |
| Ok(result) => { | |
| debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name); | |
| Ok(*result) | |
| } | |
| Err(err) => { | |
| debug!( | |
| "failed to dlsym proc_macros {} for symbol `{}`", | |
| path.display(), | |
| sym_name | |
| ); | |
| Err(err.into()) | |
| } | |
| } | |
| } | |
| } | |
| fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, 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 only_rlib = tcx.crate_types().iter().all(|ct| *ct == CrateType::Rlib); | |
| if only_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. | |
| let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); | |
| for (_cnum, data) in self.iter_crate_data() { | |
| needs_panic_runtime |= data.needs_panic_runtime(); | |
| } | |
| // If we just don't need a panic runtime at all, then we're done here | |
| // and there's nothing else to do. | |
| if !needs_panic_runtime { | |
| return; | |
| } | |
| // By this point we know that we need a panic runtime. Here we just load | |
| // an appropriate default runtime for our panic strategy. | |
| // | |
| // We may resolve to an already loaded crate (as the crate may not have | |
| // been explicitly linked prior to this), but this is 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 desired_strategy = tcx.sess.panic_strategy(); | |
| let name = match desired_strategy { | |
| PanicStrategy::Unwind => sym::panic_unwind, | |
| PanicStrategy::Abort => sym::panic_abort, | |
| PanicStrategy::ImmediateAbort => { | |
| // Immediate-aborting panics don't use a runtime. | |
| return; | |
| } | |
| }; | |
| info!("panic runtime not found -- loading {}", name); | |
| // This has to be conditional as both panic_unwind and panic_abort may be present in the | |
| // crate graph at the same time. One of them will later be activated in dependency_formats. | |
| let Some(cnum) = self.resolve_crate( | |
| tcx, | |
| name, | |
| DUMMY_SP, | |
| CrateDepKind::Conditional, | |
| CrateOrigin::Injected, | |
| ) else { | |
| return; | |
| }; | |
| let data = self.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() { | |
| tcx.dcx().emit_err(errors::CrateNotPanicRuntime { crate_name: name }); | |
| } | |
| if data.required_panic_strategy() != Some(desired_strategy) { | |
| tcx.dcx() | |
| .emit_err(errors::NoPanicStrategy { crate_name: name, strategy: desired_strategy }); | |
| } | |
| self.injected_panic_runtime = Some(cnum); | |
| } | |
| fn inject_profiler_runtime(&mut self, tcx: TyCtxt<'_>) { | |
| let needs_profiler_runtime = | |
| tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled(); | |
| if !needs_profiler_runtime || tcx.sess.opts.unstable_opts.no_profiler_runtime { | |
| return; | |
| } | |
| info!("loading profiler"); | |
| let name = Symbol::intern(&tcx.sess.opts.unstable_opts.profiler_runtime); | |
| let Some(cnum) = self.resolve_crate( | |
| tcx, | |
| name, | |
| DUMMY_SP, | |
| CrateDepKind::Unconditional, | |
| CrateOrigin::Injected, | |
| ) else { | |
| return; | |
| }; | |
| let data = self.get_crate_data(cnum); | |
| // Sanity check the loaded crate to ensure it is indeed a profiler runtime | |
| if !data.is_profiler_runtime() { | |
| tcx.dcx().emit_err(errors::NotProfilerRuntime { crate_name: name }); | |
| } | |
| } | |
| fn inject_allocator_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { | |
| self.has_global_allocator = | |
| match &*fn_spans(krate, Symbol::intern(&global_fn_name(sym::alloc))) { | |
| [span1, span2, ..] => { | |
| tcx.dcx() | |
| .emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 }); | |
| true | |
| } | |
| spans => !spans.is_empty(), | |
| }; | |
| let alloc_error_handler = Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER)); | |
| self.has_alloc_error_handler = match &*fn_spans(krate, alloc_error_handler) { | |
| [span1, span2, ..] => { | |
| tcx.dcx() | |
| .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); | |
| 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. | |
| if !attr::contains_name(&krate.attrs, sym::needs_allocator) | |
| && !self.iter_crate_data().any(|(_, data)| data.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 = tcx.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); | |
| 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. | |
| #[allow(rustc::symbol_intern_string_literal)] | |
| let this_crate = Symbol::intern("this crate"); | |
| let mut global_allocator = self.has_global_allocator.then_some(this_crate); | |
| for (_, data) in self.iter_crate_data() { | |
| if data.has_global_allocator() { | |
| match global_allocator { | |
| Some(other_crate) => { | |
| tcx.dcx().emit_err(errors::ConflictingGlobalAlloc { | |
| crate_name: data.name(), | |
| other_crate_name: other_crate, | |
| }); | |
| } | |
| None => global_allocator = Some(data.name()), | |
| } | |
| } | |
| } | |
| let mut alloc_error_handler = self.has_alloc_error_handler.then_some(this_crate); | |
| for (_, data) in self.iter_crate_data() { | |
| if data.has_alloc_error_handler() { | |
| match alloc_error_handler { | |
| Some(other_crate) => { | |
| tcx.dcx().emit_err(errors::ConflictingAllocErrorHandler { | |
| crate_name: data.name(), | |
| other_crate_name: other_crate, | |
| }); | |
| } | |
| None => alloc_error_handler = Some(data.name()), | |
| } | |
| } | |
| } | |
| if global_allocator.is_some() { | |
| self.allocator_kind = Some(AllocatorKind::Global); | |
| } else { | |
| // 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. | |
| if !attr::contains_name(&krate.attrs, sym::default_lib_allocator) | |
| && !self.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator()) | |
| { | |
| tcx.dcx().emit_err(errors::GlobalAllocRequired); | |
| } | |
| self.allocator_kind = Some(AllocatorKind::Default); | |
| } | |
| if alloc_error_handler.is_some() { | |
| self.alloc_error_handler_kind = Some(AllocatorKind::Global); | |
| } else { | |
| // The alloc crate provides a default allocation error handler if | |
| // one isn't specified. | |
| self.alloc_error_handler_kind = Some(AllocatorKind::Default); | |
| } | |
| } | |
| fn inject_forced_externs(&mut self, tcx: TyCtxt<'_>) { | |
| for (name, entry) in tcx.sess.opts.externs.iter() { | |
| if entry.force { | |
| let name_interned = Symbol::intern(name); | |
| if !self.used_extern_options.contains(&name_interned) { | |
| self.resolve_crate( | |
| tcx, | |
| name_interned, | |
| DUMMY_SP, | |
| CrateDepKind::Unconditional, | |
| CrateOrigin::Extern, | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| /// Inject the `compiler_builtins` crate if it is not already in the graph. | |
| fn inject_compiler_builtins(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { | |
| // `compiler_builtins` does not get extern builtins, nor do `#![no_core]` crates | |
| if attr::contains_name(&krate.attrs, sym::compiler_builtins) | |
| || attr::contains_name(&krate.attrs, sym::no_core) | |
| { | |
| info!("`compiler_builtins` unneeded"); | |
| return; | |
| } | |
| // If a `#![compiler_builtins]` crate already exists, avoid injecting it twice. This is | |
| // the common case since usually it appears as a dependency of `std` or `alloc`. | |
| for (cnum, cmeta) in self.iter_crate_data() { | |
| if cmeta.is_compiler_builtins() { | |
| info!("`compiler_builtins` already exists (cnum = {cnum}); skipping injection"); | |
| return; | |
| } | |
| } | |
| // `compiler_builtins` is not yet in the graph; inject it. Error on resolution failure. | |
| let Some(cnum) = self.resolve_crate( | |
| tcx, | |
| sym::compiler_builtins, | |
| krate.spans.inner_span.shrink_to_lo(), | |
| CrateDepKind::Unconditional, | |
| CrateOrigin::Injected, | |
| ) else { | |
| info!("`compiler_builtins` not resolved"); | |
| return; | |
| }; | |
| // Sanity check that the loaded crate is `#![compiler_builtins]` | |
| let cmeta = self.get_crate_data(cnum); | |
| if !cmeta.is_compiler_builtins() { | |
| tcx.dcx().emit_err(errors::CrateNotCompilerBuiltins { crate_name: cmeta.name() }); | |
| } | |
| } | |
| fn report_unused_deps_in_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { | |
| // Make a point span rather than covering the whole file | |
| let span = krate.spans.inner_span.shrink_to_lo(); | |
| // Complain about anything left over | |
| for (name, entry) in tcx.sess.opts.externs.iter() { | |
| if let ExternLocation::FoundInLibrarySearchDirectories = entry.location { | |
| // Don't worry about pathless `--extern foo` sysroot references | |
| continue; | |
| } | |
| if entry.nounused_dep || entry.force { | |
| // We're not worried about this one | |
| continue; | |
| } | |
| let name_interned = Symbol::intern(name); | |
| if self.used_extern_options.contains(&name_interned) { | |
| continue; | |
| } | |
| // Got a real unused --extern | |
| if tcx.sess.opts.json_unused_externs.is_enabled() { | |
| self.unused_externs.push(name_interned); | |
| continue; | |
| } | |
| tcx.sess.psess.buffer_lint( | |
| lint::builtin::UNUSED_CRATE_DEPENDENCIES, | |
| span, | |
| ast::CRATE_NODE_ID, | |
| BuiltinLintDiag::UnusedCrateDependency { | |
| extern_crate: name_interned, | |
| local_crate: tcx.crate_name(LOCAL_CRATE), | |
| }, | |
| ); | |
| } | |
| } | |
| fn report_future_incompatible_deps(&self, tcx: TyCtxt<'_>, krate: &ast::Crate) { | |
| let name = tcx.crate_name(LOCAL_CRATE); | |
| if name.as_str() == "wasm_bindgen" { | |
| let major = env::var("CARGO_PKG_VERSION_MAJOR") | |
| .ok() | |
| .and_then(|major| u64::from_str(&major).ok()); | |
| let minor = env::var("CARGO_PKG_VERSION_MINOR") | |
| .ok() | |
| .and_then(|minor| u64::from_str(&minor).ok()); | |
| let patch = env::var("CARGO_PKG_VERSION_PATCH") | |
| .ok() | |
| .and_then(|patch| u64::from_str(&patch).ok()); | |
| match (major, minor, patch) { | |
| // v1 or bigger is valid. | |
| (Some(1..), _, _) => return, | |
| // v0.3 or bigger is valid. | |
| (Some(0), Some(3..), _) => return, | |
| // v0.2.88 or bigger is valid. | |
| (Some(0), Some(2), Some(88..)) => return, | |
| // Not using Cargo. | |
| (None, None, None) => return, | |
| _ => (), | |
| } | |
| // Make a point span rather than covering the whole file | |
| let span = krate.spans.inner_span.shrink_to_lo(); | |
| tcx.sess.dcx().emit_err(errors::WasmCAbi { span }); | |
| } | |
| } | |
| pub fn postprocess(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { | |
| self.inject_compiler_builtins(tcx, krate); | |
| self.inject_forced_externs(tcx); | |
| self.inject_profiler_runtime(tcx); | |
| self.inject_allocator_crate(tcx, krate); | |
| self.inject_panic_runtime(tcx, krate); | |
| self.report_unused_deps_in_crate(tcx, krate); | |
| self.report_future_incompatible_deps(tcx, krate); | |
| info!("{:?}", CrateDump(self)); | |
| } | |
| /// Process an `extern crate foo` AST node. | |
| pub fn process_extern_crate( | |
| &mut self, | |
| tcx: TyCtxt<'_>, | |
| item: &ast::Item, | |
| def_id: LocalDefId, | |
| definitions: &Definitions, | |
| ) -> Option<CrateNum> { | |
| match item.kind { | |
| ast::ItemKind::ExternCrate(orig_name, ident) => { | |
| debug!("resolving extern crate stmt. ident: {} orig_name: {:?}", ident, orig_name); | |
| let name = match orig_name { | |
| Some(orig_name) => { | |
| validate_crate_name(tcx.sess, orig_name, Some(item.span)); | |
| orig_name | |
| } | |
| None => ident.name, | |
| }; | |
| let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { | |
| CrateDepKind::MacrosOnly | |
| } else { | |
| CrateDepKind::Unconditional | |
| }; | |
| let cnum = | |
| self.resolve_crate(tcx, name, item.span, dep_kind, CrateOrigin::Extern)?; | |
| let path_len = definitions.def_path(def_id).data.len(); | |
| self.update_extern_crate( | |
| cnum, | |
| name, | |
| ExternCrate { | |
| src: ExternCrateSource::Extern(def_id.to_def_id()), | |
| span: item.span, | |
| path_len, | |
| dependency_of: LOCAL_CRATE, | |
| }, | |
| ); | |
| Some(cnum) | |
| } | |
| _ => bug!(), | |
| } | |
| } | |
| pub fn process_path_extern( | |
| &mut self, | |
| tcx: TyCtxt<'_>, | |
| name: Symbol, | |
| span: Span, | |
| ) -> Option<CrateNum> { | |
| let cnum = | |
| self.resolve_crate(tcx, name, span, CrateDepKind::Unconditional, CrateOrigin::Extern)?; | |
| self.update_extern_crate( | |
| cnum, | |
| name, | |
| ExternCrate { | |
| src: ExternCrateSource::Path, | |
| span, | |
| // to have the least priority in `update_extern_crate` | |
| path_len: usize::MAX, | |
| dependency_of: LOCAL_CRATE, | |
| }, | |
| ); | |
| Some(cnum) | |
| } | |
| pub fn maybe_process_path_extern(&mut self, tcx: TyCtxt<'_>, name: Symbol) -> Option<CrateNum> { | |
| self.maybe_resolve_crate(tcx, name, CrateDepKind::Unconditional, CrateOrigin::Extern).ok() | |
| } | |
| } | |
| fn fn_spans(krate: &ast::Crate, name: Symbol) -> Vec<Span> { | |
| struct Finder { | |
| name: Symbol, | |
| spans: Vec<Span>, | |
| } | |
| impl<'ast> visit::Visitor<'ast> for Finder { | |
| fn visit_item(&mut self, item: &'ast ast::Item) { | |
| if let Some(ident) = item.kind.ident() | |
| && ident.name == self.name | |
| && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol) | |
| { | |
| self.spans.push(item.span); | |
| } | |
| visit::walk_item(self, item) | |
| } | |
| } | |
| let mut f = Finder { name, spans: Vec::new() }; | |
| visit::walk_crate(&mut f, krate); | |
| f.spans | |
| } | |
| fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String { | |
| e.sources().map(|e| format!(": {e}")).collect() | |
| } | |
| fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> { | |
| #[cfg(target_os = "aix")] | |
| if let Some(ext) = path.extension() | |
| && ext.eq("a") | |
| { | |
| // On AIX, we ship all libraries as .a big_af archive | |
| // the expected format is lib<name>.a(libname.so) for the actual | |
| // dynamic library | |
| let library_name = path.file_stem().expect("expect a library name"); | |
| let mut archive_member = std::ffi::OsString::from("a("); | |
| archive_member.push(library_name); | |
| archive_member.push(".so)"); | |
| let new_path = path.with_extension(archive_member); | |
| // On AIX, we need RTLD_MEMBER to dlopen an archived shared | |
| let flags = libc::RTLD_LAZY | libc::RTLD_LOCAL | libc::RTLD_MEMBER; | |
| return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) } | |
| .map(|lib| lib.into()); | |
| } | |
| unsafe { libloading::Library::new(&path) } | |
| } | |
| // On Windows the compiler would sometimes intermittently fail to open the | |
| // proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the | |
| // system still holds a lock on the file, so we retry a few times before calling it | |
| // an error. | |
| fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> { | |
| assert!(max_attempts > 0); | |
| let mut last_error = None; | |
| for attempt in 0..max_attempts { | |
| debug!("Attempt to load proc-macro `{}`.", path.display()); | |
| match attempt_load_dylib(path) { | |
| Ok(lib) => { | |
| if attempt > 0 { | |
| debug!( | |
| "Loaded proc-macro `{}` after {} attempts.", | |
| path.display(), | |
| attempt + 1 | |
| ); | |
| } | |
| return Ok(lib); | |
| } | |
| Err(err) => { | |
| // Only try to recover from this specific error. | |
| if !matches!(err, libloading::Error::LoadLibraryExW { .. }) { | |
| debug!("Failed to load proc-macro `{}`. Not retrying", path.display()); | |
| let err = format_dlopen_err(&err); | |
| // We include the path of the dylib in the error ourselves, so | |
| // if it's in the error, we strip it. | |
| if let Some(err) = err.strip_prefix(&format!(": {}", path.display())) { | |
| return Err(err.to_string()); | |
| } | |
| return Err(err); | |
| } | |
| last_error = Some(err); | |
| std::thread::sleep(Duration::from_millis(100)); | |
| debug!("Failed to load proc-macro `{}`. Retrying.", path.display()); | |
| } | |
| } | |
| } | |
| debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts); | |
| let last_error = last_error.unwrap(); | |
| let message = if let Some(src) = last_error.source() { | |
| format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error)) | |
| } else { | |
| format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error)) | |
| }; | |
| Err(message) | |
| } | |
| pub enum DylibError { | |
| DlOpen(String, String), | |
| DlSym(String, String), | |
| } | |
| impl From<DylibError> for CrateError { | |
| fn from(err: DylibError) -> CrateError { | |
| match err { | |
| DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err), | |
| DylibError::DlSym(path, err) => CrateError::DlSym(path, err), | |
| } | |
| } | |
| } | |
| pub unsafe fn load_symbol_from_dylib<T: Copy>( | |
| path: &Path, | |
| sym_name: &str, | |
| ) -> Result<T, DylibError> { | |
| // Make sure the path contains a / or the linker will search for it. | |
| let path = try_canonicalize(path).unwrap(); | |
| let lib = | |
| load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?; | |
| let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) } | |
| .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?; | |
| // Intentionally leak the dynamic library. We can't ever unload it | |
| // since the library can make things that will live arbitrarily long. | |
| let sym = unsafe { sym.into_raw() }; | |
| std::mem::forget(lib); | |
| Ok(*sym) | |
| } |