From 074c27f8246a2efecc1edadaa71398b9965022ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 13 Jan 2019 09:18:37 +0100 Subject: [PATCH] Allow linking to a proc macro on the target in metadata and still use a host proc macro to execute them --- src/librustc/session/config.rs | 2 + src/librustc/session/filesearch.rs | 1 + src/librustc_metadata/creader.rs | 166 +++++++++++++++++++---------- src/librustc_metadata/locator.rs | 16 ++- 4 files changed, 126 insertions(+), 59 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index e2f4049b1db28..ca75703c29541 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1216,6 +1216,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, Use with RUST_REGION_GRAPH=help for more info"), parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link"), + dual_proc_macros: bool = (false, parse_bool, [TRACKED], + "load proc macros for both target and host, but only link to the target"), no_codegen: bool = (false, parse_bool, [TRACKED], "run all passes except codegen; no output"), treat_err_as_bug: bool = (false, parse_bool, [TRACKED], diff --git a/src/librustc/session/filesearch.rs b/src/librustc/session/filesearch.rs index 19f1c7a18fad1..ef83967b8ece3 100644 --- a/src/librustc/session/filesearch.rs +++ b/src/librustc/session/filesearch.rs @@ -18,6 +18,7 @@ pub enum FileMatch { // A module for searching for libraries +#[derive(Clone)] pub struct FileSearch<'a> { sysroot: &'a Path, triple: &'a str, diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index e9785e7c88d0d..56f373ec901fd 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -187,13 +187,15 @@ impl<'a> CrateLoader<'a> { }); } - fn register_crate(&mut self, - root: &Option, - ident: Symbol, - span: Span, - lib: Library, - dep_kind: DepKind) - -> (CrateNum, Lrc) { + fn register_crate( + &mut self, + host_lib: Option, + root: &Option, + ident: Symbol, + span: Span, + lib: Library, + dep_kind: DepKind + ) -> (CrateNum, Lrc) { let crate_root = lib.metadata.get_root(); info!("register crate `extern crate {} as {}`", crate_root.name, ident); self.verify_no_symbol_conflicts(span, &crate_root); @@ -221,7 +223,16 @@ impl<'a> CrateLoader<'a> { let dependencies: Vec = cnum_map.iter().cloned().collect(); let proc_macros = crate_root.proc_macro_decls_static.map(|_| { - self.load_derive_macros(&crate_root, dylib.clone().map(|p| p.0), span) + if self.sess.opts.debugging_opts.dual_proc_macros { + let host_lib = host_lib.unwrap(); + self.load_derive_macros( + &host_lib.metadata.get_root(), + host_lib.dylib.clone().map(|p| p.0), + span + ) + } else { + self.load_derive_macros(&crate_root, dylib.clone().map(|p| p.0), span) + } }); let def_path_table = record_time(&self.sess.perf_stats.decode_def_path_tables_time, || { @@ -268,6 +279,58 @@ impl<'a> CrateLoader<'a> { (cnum, cmeta) } + fn load_proc_macro<'b> ( + &mut self, + locate_ctxt: &mut locator::Context<'b>, + path_kind: PathKind, + ) -> Option<(LoadResult, Option)> + where + 'a: 'b + { + // Use a new locator Context so trying to load a proc macro doesn't affect the error + // message we emit + let mut proc_macro_locator = locate_ctxt.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)) + }; + // Use the locate_ctxt when looking for the host proc macro crate, as that is required + // so we want it to affect the error message + (locate_ctxt, 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, root: &'b Option, @@ -280,53 +343,39 @@ impl<'a> CrateLoader<'a> { mut dep_kind: DepKind, ) -> Result<(CrateNum, Lrc), LoadError<'b>> { info!("resolving crate `extern crate {} as {}`", name, ident); + let mut locate_ctxt = locator::Context { + sess: self.sess, + span, + ident, + crate_name: name, + hash: hash.map(|a| &*a), + extra_filename: extra_filename, + filesearch: self.sess.target_filesearch(path_kind), + target: &self.sess.target.target, + triple: self.sess.opts.target_triple.clone(), + root, + rejected_via_hash: vec![], + rejected_via_triple: vec![], + rejected_via_kind: vec![], + rejected_via_version: vec![], + rejected_via_filename: vec![], + should_match_name: true, + is_proc_macro: Some(false), + metadata_loader: &*self.cstore.metadata_loader, + }; + let result = if let Some(cnum) = self.existing_match(name, hash, path_kind) { - LoadResult::Previous(cnum) + (LoadResult::Previous(cnum), None) } else { info!("falling back to a load"); - let mut locate_ctxt = locator::Context { - sess: self.sess, - span, - ident, - crate_name: name, - hash: hash.map(|a| &*a), - extra_filename: extra_filename, - filesearch: self.sess.target_filesearch(path_kind), - target: &self.sess.target.target, - triple: &self.sess.opts.target_triple, - root, - rejected_via_hash: vec![], - rejected_via_triple: vec![], - rejected_via_kind: vec![], - rejected_via_version: vec![], - rejected_via_filename: vec![], - should_match_name: true, - is_proc_macro: Some(false), - metadata_loader: &*self.cstore.metadata_loader, - }; - - self.load(&mut locate_ctxt).or_else(|| { + self.load(&mut locate_ctxt).map(|r| (r, None)).or_else(|| { dep_kind = DepKind::UnexportedMacrosOnly; - - let mut proc_macro_locator = locator::Context { - target: &self.sess.host, - triple: &TargetTriple::from_triple(config::host_triple()), - filesearch: self.sess.host_filesearch(path_kind), - rejected_via_hash: vec![], - rejected_via_triple: vec![], - rejected_via_kind: vec![], - rejected_via_version: vec![], - rejected_via_filename: vec![], - is_proc_macro: Some(true), - ..locate_ctxt - }; - - self.load(&mut proc_macro_locator) + self.load_proc_macro(&mut locate_ctxt, path_kind) }).ok_or_else(move || LoadError::LocatorError(locate_ctxt))? }; match result { - LoadResult::Previous(cnum) => { + (LoadResult::Previous(cnum), None) => { let data = self.cstore.get_crate_data(cnum); if data.root.proc_macro_decls_static.is_some() { dep_kind = DepKind::UnexportedMacrosOnly; @@ -336,9 +385,10 @@ impl<'a> CrateLoader<'a> { }); Ok((cnum, data)) } - LoadResult::Loaded(library) => { - Ok(self.register_crate(root, ident, span, library, dep_kind)) + (LoadResult::Loaded(library), host_library) => { + Ok(self.register_crate(host_library, root, ident, span, library, dep_kind)) } + _ => panic!() } } @@ -354,7 +404,7 @@ impl<'a> CrateLoader<'a> { // don't want to match a host crate against an equivalent target one // already loaded. let root = library.metadata.get_root(); - if locate_ctxt.triple == &self.sess.opts.target_triple { + if locate_ctxt.triple == self.sess.opts.target_triple { let mut result = LoadResult::Loaded(library); self.cstore.iter_crate_data(|cnum, data| { if data.root.name == root.name && root.hash == data.root.hash { @@ -450,9 +500,9 @@ impl<'a> CrateLoader<'a> { fn read_extension_crate(&mut self, span: Span, orig_name: Symbol, rename: Symbol) -> ExtensionCrate { info!("read extension crate `extern crate {} as {}`", orig_name, rename); - let target_triple = &self.sess.opts.target_triple; + let target_triple = self.sess.opts.target_triple.clone(); let host_triple = TargetTriple::from_triple(config::host_triple()); - let is_cross = target_triple != &host_triple; + let is_cross = target_triple != host_triple; let mut target_only = false; let mut locate_ctxt = locator::Context { sess: self.sess, @@ -463,7 +513,7 @@ impl<'a> CrateLoader<'a> { extra_filename: None, filesearch: self.sess.host_filesearch(PathKind::Crate), target: &self.sess.host, - triple: &host_triple, + triple: host_triple, root: &None, rejected_via_hash: vec![], rejected_via_triple: vec![], @@ -547,7 +597,7 @@ impl<'a> CrateLoader<'a> { *(sym as *const &[ProcMacro]) }; - let extensions = decls.iter().map(|&decl| { + let mut extensions: Vec<_> = decls.iter().map(|&decl| { match decl { ProcMacro::CustomDerive { trait_name, attributes, client } => { let attrs = attributes.iter().cloned().map(Symbol::intern).collect::>(); @@ -574,13 +624,17 @@ impl<'a> CrateLoader<'a> { }) } } - }).map(|(name, ext)| (Symbol::intern(name), Lrc::new(ext))).collect(); + }).map(|(name, ext)| (name, Lrc::new(ext))).collect(); // Intentionally leak the dynamic library. We can't ever unload it // since the library can make things that will live arbitrarily long. mem::forget(lib); - extensions + // Sort by macro name to ensure this is ordered the same way on + // both host and target proc macro crates + extensions.sort_by_key(|ext| ext.0); + + extensions.into_iter().map(|(name, ext)| (Symbol::intern(name), ext)).collect() } /// Look for a plugin registrar. Returns library path, crate diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index 6b49d6b9e52cb..20c89569efa53 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -241,11 +241,13 @@ use flate2::read::DeflateDecoder; use rustc_data_structures::owning_ref::OwningRef; +#[derive(Clone)] pub struct CrateMismatch { path: PathBuf, got: String, } +#[derive(Clone)] pub struct Context<'a> { pub sess: &'a Session, pub span: Span, @@ -255,7 +257,7 @@ pub struct Context<'a> { pub extra_filename: Option<&'a str>, // points to either self.sess.target.target or self.sess.host, must match triple pub target: &'a Target, - pub triple: &'a TargetTriple, + pub triple: TargetTriple, pub filesearch: FileSearch<'a>, pub root: &'a Option, pub rejected_via_hash: Vec, @@ -299,6 +301,14 @@ impl CratePaths { } impl<'a> Context<'a> { + pub fn reset(&mut self) { + self.rejected_via_hash.clear(); + self.rejected_via_triple.clear(); + self.rejected_via_kind.clear(); + self.rejected_via_version.clear(); + self.rejected_via_filename.clear(); + } + pub fn maybe_load_library_crate(&mut self) -> Option { let mut seen_paths = FxHashSet::default(); match self.extra_filename { @@ -396,7 +406,7 @@ impl<'a> Context<'a> { add); if (self.ident == "std" || self.ident == "core") - && self.triple != &TargetTriple::from_triple(config::host_triple()) { + && self.triple != TargetTriple::from_triple(config::host_triple()) { err.note(&format!("the `{}` target may not be installed", self.triple)); } err.span_label(self.span, "can't find crate"); @@ -715,7 +725,7 @@ impl<'a> Context<'a> { } } - if &root.triple != self.triple { + if root.triple != self.triple { info!("Rejecting via crate triple: expected {} got {}", self.triple, root.triple);