diff --git a/Cargo.lock b/Cargo.lock index 4bee0e0e2ad2f..a13bc0e74760a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,7 @@ dependencies = [ "cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0", ] @@ -2023,6 +2023,11 @@ dependencies = [ "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "punycode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quick-error" version = "1.2.2" @@ -2492,7 +2497,7 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.10" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "compiler_builtins 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2648,7 +2653,7 @@ dependencies = [ "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_llvm 0.0.0", ] @@ -2665,7 +2670,7 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_apfloat 0.0.0", "rustc_codegen_utils 0.0.0", @@ -2687,7 +2692,9 @@ version = "0.0.0" dependencies = [ "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "punycode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", + "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_data_structures 0.0.0", "rustc_metadata 0.0.0", "rustc_mir 0.0.0", @@ -4276,6 +4283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24f5844db2f839e97e3021980975f6ebf8691d9b9b2ca67ed3feb38dc3edb52c" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "051e60ace841b3bfecd402fe5051c06cb3bec4a6e6fdd060a37aa8eb829a1db3" +"checksum punycode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ddd112cca70a4d30883b2d21568a1d376ff8be4758649f64f973c6845128ad3" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" @@ -4313,7 +4321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-ap-serialize 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf09c60aaee892b0fd107544cfe607d8d463e7f33da34aa823566b8fd2b17f53" "checksum rustc-ap-syntax 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69f38cc120ff317678bbda8c4f58c1bbc1de64b615383ab01480482dde5e95a1" "checksum rustc-ap-syntax_pos 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20a0a201141c5c416b1924b079eeefc7b013e34ece0740ce4997f358b3684a7f" -"checksum rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "82ae957aa1b3055d8e086486723c0ccd3d7b8fa190ae8fa2e35543b6171c810e" +"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d98c51d9cbbe810c8b6693236d3412d8cd60513ff27a3e1b6af483dca0af544" "checksum rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2e07e19601f21c59aad953c2632172ba70cb27e685771514ea66e4062b3363" diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 81aa8d434d37f..f61677fa59499 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -624,6 +624,10 @@ rustc_queries! { fatal_cycle desc { "test whether a crate has #![no_builtins]" } } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + fatal_cycle + desc { "query a crate's symbol mangling version" } + } query extern_crate(_: DefId) -> Option<&'tcx ExternCrate> { eval_always diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 300d0cbfba55b..d8efa17defe3d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -131,6 +131,14 @@ impl SwitchWithOptPath { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +pub enum SymbolManglingVersion { + Legacy, + V0, +} + +impl_stable_hash_via_hash!(SymbolManglingVersion); + #[derive(Clone, Copy, PartialEq, Hash)] pub enum DebugInfo { None, @@ -838,11 +846,14 @@ macro_rules! options { Some("an optional path to the profiling data output directory"); pub const parse_merge_functions: Option<&str> = Some("one of: `disabled`, `trampolines`, or `aliases`"); + pub const parse_symbol_mangling_version: Option<&str> = + Some("either `legacy` or `v0` (RFC 2603)"); } #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath}; + use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, + SymbolManglingVersion}; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use std::path::PathBuf; use std::str::FromStr; @@ -1112,6 +1123,18 @@ macro_rules! options { } true } + + fn parse_symbol_mangling_version( + slot: &mut SymbolManglingVersion, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("legacy") => SymbolManglingVersion::Legacy, + Some("v0") => SymbolManglingVersion::V0, + _ => return false, + }; + true + } } ) } @@ -1457,6 +1480,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "only allow the listed language features to be enabled in code (space separated)"), emit_artifact_notifications: bool = (false, parse_bool, [UNTRACKED], "emit notifications after each artifact has been output (only in the JSON format)"), + symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, + parse_symbol_mangling_version, [TRACKED], + "which mangling version to use for symbol names"), } pub fn default_lib_output() -> CrateType { @@ -2551,7 +2577,8 @@ mod dep_tracking { use std::path::PathBuf; use std::collections::hash_map::DefaultHasher; use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes, - Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath}; + Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, + SymbolManglingVersion}; use syntax::feature_gate::UnstableFeatures; use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple}; use syntax::edition::Edition; @@ -2620,6 +2647,7 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); + impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); @@ -2693,7 +2721,7 @@ mod tests { use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; use std::path::PathBuf; - use super::{Externs, OutputType, OutputTypes}; + use super::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel}; use syntax::symbol::sym; use syntax::edition::{Edition, DEFAULT_EDITION}; @@ -3367,6 +3395,10 @@ mod tests { opts = reference.clone(); opts.debugging_opts.allow_features = Some(vec![String::from("lang_items")]); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); } #[test] diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index be52b7e645198..a2bced97102a6 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -20,7 +20,7 @@ use crate::mir::mono::CodegenUnit; use crate::mir; use crate::mir::interpret::GlobalId; use crate::session::CrateDisambiguator; -use crate::session::config::{EntryFnType, OutputFilenames, OptLevel}; +use crate::session::config::{EntryFnType, OutputFilenames, OptLevel, SymbolManglingVersion}; use crate::traits::{self, Vtable}; use crate::traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml index 841cf98164eb4..4ae8303c76d3c 100644 --- a/src/librustc_codegen_llvm/Cargo.toml +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -13,7 +13,7 @@ test = false [dependencies] cc = "1.0.1" # Used to locate MSVC num_cpus = "1.0" -rustc-demangle = "0.1.4" +rustc-demangle = "0.1.15" rustc_llvm = { path = "../librustc_llvm" } memmap = "0.6" diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index af99d39182c24..0b34ec8e8bc1c 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -14,7 +14,7 @@ test = false bitflags = "1.0.4" cc = "1.0.1" num_cpus = "1.0" -rustc-demangle = "0.1.4" +rustc-demangle = "0.1.15" memmap = "0.6" log = "0.4.5" libc = "0.2.44" diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml index c75208b9e06c1..268be2b109114 100644 --- a/src/librustc_codegen_utils/Cargo.toml +++ b/src/librustc_codegen_utils/Cargo.toml @@ -13,6 +13,8 @@ test = false [dependencies] flate2 = "1.0" log = "0.4" +punycode = "0.4.0" +rustc-demangle = "0.1.15" syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs index b4ba90c61f650..ea1d08354528b 100644 --- a/src/librustc_codegen_utils/lib.rs +++ b/src/librustc_codegen_utils/lib.rs @@ -7,7 +7,9 @@ #![feature(arbitrary_self_types)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(core_intrinsics)] #![feature(custom_attribute)] +#![feature(never_type)] #![feature(nll)] #![allow(unused_attributes)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 4a5d356096eec..37847b1b0988f 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -87,18 +87,12 @@ //! virtually impossible. Thus, symbol hash generation exclusively relies on //! DefPaths which are much more robust in the face of changes to the code base. -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc::hir::def_id::LOCAL_CRATE; use rustc::hir::Node; use rustc::hir::CodegenFnAttrFlags; -use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; -use rustc::ich::NodeIdHashingMode; -use rustc::ty::print::{PrettyPrinter, Printer, Print}; +use rustc::session::config::SymbolManglingVersion; use rustc::ty::query::Providers; -use rustc::ty::subst::{Kind, SubstsRef, UnpackedKind}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::mir::interpret::{ConstValue, Scalar}; -use rustc::util::common::record_time; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc::ty::{self, TyCtxt}; use rustc_mir::monomorphize::item::{InstantiationMode, MonoItem, MonoItemExt}; use rustc_mir::monomorphize::Instance; @@ -106,129 +100,20 @@ use syntax_pos::symbol::InternedString; use log::debug; -use std::fmt::{self, Write}; -use std::mem::{self, discriminant}; +mod legacy; +mod v0; pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { - symbol_name, + symbol_name: |tcx, instance| ty::SymbolName { + name: symbol_name(tcx, instance), + }, ..*providers }; } -fn get_symbol_hash<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - - // the DefId of the item this name is for - def_id: DefId, - - // instance this name will be for - instance: Instance<'tcx>, - - // type of the item, without any generic - // parameters substituted; this is - // included in the hash as a kind of - // safeguard. - item_type: Ty<'tcx>, - - // values for generic type parameters, - // if any. - substs: SubstsRef<'tcx>, -) -> u64 { - debug!( - "get_symbol_hash(def_id={:?}, parameters={:?})", - def_id, substs - ); - - let mut hasher = StableHasher::::new(); - let mut hcx = tcx.create_stable_hashing_context(); - - record_time(&tcx.sess.perf_stats.symbol_hash_time, || { - // the main symbol name is not necessarily unique; hash in the - // compiler's internal def-path, guaranteeing each symbol has a - // truly unique path - tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); - - // Include the main item-type. Note that, in this case, the - // assertions about `needs_subst` may not hold, but this item-type - // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions()); - hcx.while_hashing_spans(false, |hcx| { - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - item_type.hash_stable(hcx, &mut hasher); - }); - }); - - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.sty { - item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); - } - - // also include any type parameters (for generic items) - assert!(!substs.has_erasable_regions()); - assert!(!substs.needs_subst()); - substs.hash_stable(&mut hcx, &mut hasher); - - let is_generic = substs.non_erasable_generics().next().is_some(); - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic || - - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compliation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; - - if avoid_cross_crate_conflicts { - let instantiating_crate = if is_generic { - if !def_id.is_local() && tcx.sess.opts.share_generics() { - // If we are re-using a monomorphization from another crate, - // we have to compute the symbol hash accordingly. - let upstream_monomorphizations = tcx.upstream_monomorphizations_for(def_id); - - upstream_monomorphizations - .and_then(|monos| monos.get(&substs).cloned()) - .unwrap_or(LOCAL_CRATE) - } else { - LOCAL_CRATE - } - } else { - LOCAL_CRATE - }; - - (&tcx.original_crate_name(instantiating_crate).as_str()[..]) - .hash_stable(&mut hcx, &mut hasher); - (&tcx.crate_disambiguator(instantiating_crate)).hash_stable(&mut hcx, &mut hasher); - } - - // We want to avoid accidental collision between different types of instances. - // Especially, VtableShim may overlap with its original instance without this. - discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher); - }); - - // 64 bits should be enough to avoid collisions. - hasher.finish() -} - -fn symbol_name(tcx: TyCtxt<'_, 'tcx, 'tcx>, instance: Instance<'tcx>) -> ty::SymbolName { - ty::SymbolName { - name: compute_symbol_name(tcx, instance), - } -} - -fn compute_symbol_name(tcx: TyCtxt<'_, 'tcx, 'tcx>, instance: Instance<'tcx>) -> InternedString { +fn symbol_name(tcx: TyCtxt<'_, 'tcx, 'tcx>, instance: Instance<'tcx>) -> InternedString { let def_id = instance.def_id(); let substs = instance.substs; @@ -278,376 +163,65 @@ fn compute_symbol_name(tcx: TyCtxt<'_, 'tcx, 'tcx>, instance: Instance<'tcx>) -> return tcx.item_name(def_id).as_interned_str(); } - // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. - let mut ty_def_id = def_id; - let instance_ty; - loop { - let key = tcx.def_key(ty_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => { - instance_ty = tcx.type_of(ty_def_id); - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!( - "finding type for {:?}, encountered def-id {:?} with no \ - parent", - def_id, - ty_def_id - ); - }); - } - } - } - // Erase regions because they may not be deterministic when hashed - // and should not matter anyhow. - let instance_ty = tcx.erase_regions(&instance_ty); + let is_generic = substs.non_erasable_generics().next().is_some(); + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic || - let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); - - let mut printer = SymbolPrinter { - tcx, - path: SymbolPath::new(), - keep_within_component: false, - }.print_def_path(def_id, &[]).unwrap(); - - if instance.is_vtable_shim() { - let _ = printer.write_str("{{vtable-shim}}"); - } - - InternedString::intern(&printer.path.finish(hash)) -} - -// Follow C++ namespace-mangling style, see -// http://en.wikipedia.org/wiki/Name_mangling for more info. -// -// It turns out that on macOS you can actually have arbitrary symbols in -// function names (at least when given to LLVM), but this is not possible -// when using unix's linker. Perhaps one day when we just use a linker from LLVM -// we won't need to do this name mangling. The problem with name mangling is -// that it seriously limits the available characters. For example we can't -// have things like &T in symbol names when one would theoretically -// want them for things like impls of traits on that type. -// -// To be able to work on all platforms and get *some* reasonable output, we -// use C++ name-mangling. -#[derive(Debug)] -struct SymbolPath { - result: String, - temp_buf: String, -} - -impl SymbolPath { - fn new() -> Self { - let mut result = SymbolPath { - result: String::with_capacity(64), - temp_buf: String::with_capacity(16), + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, }; - result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested - result - } - - fn finalize_pending_component(&mut self) { - if !self.temp_buf.is_empty() { - let _ = write!(self.result, "{}{}", self.temp_buf.len(), self.temp_buf); - self.temp_buf.clear(); - } - } - - fn finish(mut self, hash: u64) -> String { - self.finalize_pending_component(); - // E = end name-sequence - let _ = write!(self.result, "17h{:016x}E", hash); - self.result - } -} - -struct SymbolPrinter<'a, 'tcx> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - path: SymbolPath, - - // When `true`, `finalize_pending_component` isn't used. - // This is needed when recursing into `path_qualified`, - // or `path_generic_args`, as any nested paths are - // logically within one component. - keep_within_component: bool, -} - -// HACK(eddyb) this relies on using the `fmt` interface to get -// `PrettyPrinter` aka pretty printing of e.g. types in paths, -// symbol names should have their own printing machinery. - -impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> { - type Error = fmt::Error; - - type Path = Self; - type Region = Self; - type Type = Self; - type DynExistential = Self; - type Const = Self; - - fn tcx(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { - self.tcx - } - - fn print_region( - self, - _region: ty::Region<'_>, - ) -> Result { - Ok(self) - } - - fn print_type( - self, - ty: Ty<'tcx>, - ) -> Result { - match ty.sty { - // Print all nominal types as paths (unlike `pretty_print_type`). - ty::FnDef(def_id, substs) | - ty::Opaque(def_id, substs) | - ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) | - ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | - ty::Closure(def_id, ty::ClosureSubsts { substs }) | - ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => { - self.print_def_path(def_id, substs) - } - _ => self.pretty_print_type(ty), - } - } - - fn print_dyn_existential( - mut self, - predicates: &'tcx ty::List>, - ) -> Result { - let mut first = true; - for p in predicates { - if !first { - write!(self, "+")?; - } - first = false; - self = p.print(self)?; - } - Ok(self) - } - fn print_const( - mut self, - ct: &'tcx ty::Const<'tcx>, - ) -> Result { - // only print integers - if let ConstValue::Scalar(Scalar::Raw { .. }) = ct.val { - if ct.ty.is_integral() { - return self.pretty_print_const(ct); - } - } - self.write_str("_")?; - Ok(self) - } - - fn path_crate( - mut self, - cnum: CrateNum, - ) -> Result { - self.write_str(&self.tcx.original_crate_name(cnum).as_str())?; - Ok(self) - } - fn path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - // Similar to `pretty_path_qualified`, but for the other - // types that are printed as paths (see `print_type` above). - match self_ty.sty { - ty::FnDef(..) | - ty::Opaque(..) | - ty::Projection(_) | - ty::UnnormalizedProjection(_) | - ty::Closure(..) | - ty::Generator(..) - if trait_ref.is_none() => - { - self.print_type(self_ty) - } - - _ => self.pretty_path_qualified(self_ty, trait_ref) - } - } + let instantiating_crate = if avoid_cross_crate_conflicts { + Some(if is_generic { + if !def_id.is_local() && tcx.sess.opts.share_generics() { + // If we are re-using a monomorphization from another crate, + // we have to compute the symbol hash accordingly. + let upstream_monomorphizations = tcx.upstream_monomorphizations_for(def_id); - fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result, - _disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; - - if cx.keep_within_component { - // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. - cx.write_str("::")?; - } else { - cx.path.finalize_pending_component(); - } - - Ok(cx) - }, - self_ty, - trait_ref, - ) - } - fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result { - self = print_prefix(self)?; - - // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(self), - _ => {} - } - - if self.keep_within_component { - // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. - self.write_str("::")?; - } else { - self.path.finalize_pending_component(); - } - - self.write_str(&disambiguated_data.data.as_interned_str().as_str())?; - Ok(self) - } - fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - args: &[Kind<'tcx>], - ) -> Result { - self = print_prefix(self)?; - - let args = args.iter().cloned().filter(|arg| { - match arg.unpack() { - UnpackedKind::Lifetime(_) => false, - _ => true, + upstream_monomorphizations + .and_then(|monos| monos.get(&substs).cloned()) + .unwrap_or(LOCAL_CRATE) + } else { + LOCAL_CRATE } - }); - - if args.clone().next().is_some() { - self.generic_delimiters(|cx| cx.comma_sep(args)) } else { - Ok(self) - } - } -} - -impl PrettyPrinter<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> { - fn region_should_not_be_omitted( - &self, - _region: ty::Region<'_>, - ) -> bool { - false - } - fn comma_sep( - mut self, - mut elems: impl Iterator, - ) -> Result - where T: Print<'tcx, 'tcx, Self, Output = Self, Error = Self::Error> - { - if let Some(first) = elems.next() { - self = first.print(self)?; - for elem in elems { - self.write_str(",")?; - self = elem.print(self)?; - } - } - Ok(self) - } - - fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result, - ) -> Result { - write!(self, "<")?; - - let kept_within_component = - mem::replace(&mut self.keep_within_component, true); - self = f(self)?; - self.keep_within_component = kept_within_component; - - write!(self, ">")?; - - Ok(self) - } -} - -impl fmt::Write for SymbolPrinter<'_, '_> { - fn write_str(&mut self, s: &str) -> fmt::Result { - // Name sanitation. LLVM will happily accept identifiers with weird names, but - // gas doesn't! - // gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ - // NVPTX assembly has more strict naming rules than gas, so additionally, dots - // are replaced with '$' there. - - for c in s.chars() { - if self.path.temp_buf.is_empty() { - match c { - 'a'..='z' | 'A'..='Z' | '_' => {} - _ => { - // Underscore-qualify anything that didn't start as an ident. - self.path.temp_buf.push('_'); - } - } - } - match c { - // Escape these with $ sequences - '@' => self.path.temp_buf.push_str("$SP$"), - '*' => self.path.temp_buf.push_str("$BP$"), - '&' => self.path.temp_buf.push_str("$RF$"), - '<' => self.path.temp_buf.push_str("$LT$"), - '>' => self.path.temp_buf.push_str("$GT$"), - '(' => self.path.temp_buf.push_str("$LP$"), - ')' => self.path.temp_buf.push_str("$RP$"), - ',' => self.path.temp_buf.push_str("$C$"), - - '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => { - // NVPTX doesn't support these characters in symbol names. - self.path.temp_buf.push('$') - } - - // '.' doesn't occur in types and functions, so reuse it - // for ':' and '-' - '-' | ':' => self.path.temp_buf.push('.'), - - // Avoid segmentation fault on some platforms, see #60925. - 'm' if self.path.temp_buf.ends_with(".llv") => self.path.temp_buf.push_str("$6d$"), + LOCAL_CRATE + }) + } else { + None + }; - // These are legal symbols - 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c), + // Pick the crate responsible for the symbol mangling version, which has to: + // 1. be stable for each instance, whether it's being defined or imported + // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible + // We solve these as follows: + // 1. because symbol names depend on both `def_id` and `instantiating_crate`, + // both their `CrateNum`s are stable for any given instance, so we can pick + // either and have a stable choice of symbol mangling version + // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) + let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); + let mangling_version = if mangling_version_crate == LOCAL_CRATE { + tcx.sess.opts.debugging_opts.symbol_mangling_version + } else { + tcx.symbol_mangling_version(mangling_version_crate) + }; - _ => { - self.path.temp_buf.push('$'); - for c in c.escape_unicode().skip(1) { - match c { - '{' => {} - '}' => self.path.temp_buf.push('$'), - c => self.path.temp_buf.push(c), - } - } - } - } - } + let mangled = match mangling_version { + SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), + SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), + }; - Ok(()) - } + InternedString::intern(&mangled) } diff --git a/src/librustc_codegen_utils/symbol_names/legacy.rs b/src/librustc_codegen_utils/symbol_names/legacy.rs new file mode 100644 index 0000000000000..53682b9bdc2c8 --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names/legacy.rs @@ -0,0 +1,463 @@ +use rustc::hir::def_id::CrateNum; +use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; +use rustc::ich::NodeIdHashingMode; +use rustc::mir::interpret::{ConstValue, Scalar}; +use rustc::ty::print::{PrettyPrinter, Printer, Print}; +use rustc::ty::subst::{Kind, UnpackedKind}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::util::common::record_time; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_mir::monomorphize::Instance; + +use log::debug; + +use std::fmt::{self, Write}; +use std::mem::{self, discriminant}; + +pub(super) fn mangle( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + instance: Instance<'tcx>, + instantiating_crate: Option, +) -> String { + let def_id = instance.def_id(); + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = tcx.def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => { + instance_ty = tcx.type_of(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!( + "finding type for {:?}, encountered def-id {:?} with no \ + parent", + def_id, + ty_def_id + ); + }); + } + } + } + + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = tcx.erase_regions(&instance_ty); + + let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); + + let mut printer = SymbolPrinter { + tcx, + path: SymbolPath::new(), + keep_within_component: false, + }.print_def_path(def_id, &[]).unwrap(); + + if instance.is_vtable_shim() { + let _ = printer.write_str("{{vtable-shim}}"); + } + + printer.path.finish(hash) +} + +fn get_symbol_hash<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + + // instance this name will be for + instance: Instance<'tcx>, + + // type of the item, without any generic + // parameters substituted; this is + // included in the hash as a kind of + // safeguard. + item_type: Ty<'tcx>, + + instantiating_crate: Option, +) -> u64 { + let def_id = instance.def_id(); + let substs = instance.substs; + debug!( + "get_symbol_hash(def_id={:?}, parameters={:?})", + def_id, substs + ); + + let mut hasher = StableHasher::::new(); + let mut hcx = tcx.create_stable_hashing_context(); + + record_time(&tcx.sess.perf_stats.symbol_hash_time, || { + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); + + // Include the main item-type. Note that, in this case, the + // assertions about `needs_subst` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hcx.while_hashing_spans(false, |hcx| { + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + item_type.hash_stable(hcx, &mut hasher); + }); + }); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::FnDef(..) = item_type.sty { + item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); + } + + // also include any type parameters (for generic items) + assert!(!substs.has_erasable_regions()); + assert!(!substs.needs_subst()); + substs.hash_stable(&mut hcx, &mut hasher); + + if let Some(instantiating_crate) = instantiating_crate { + (&tcx.original_crate_name(instantiating_crate).as_str()[..]) + .hash_stable(&mut hcx, &mut hasher); + (&tcx.crate_disambiguator(instantiating_crate)).hash_stable(&mut hcx, &mut hasher); + } + + // We want to avoid accidental collision between different types of instances. + // Especially, VtableShim may overlap with its original instance without this. + discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher); + }); + + // 64 bits should be enough to avoid collisions. + hasher.finish() +} + +// Follow C++ namespace-mangling style, see +// http://en.wikipedia.org/wiki/Name_mangling for more info. +// +// It turns out that on macOS you can actually have arbitrary symbols in +// function names (at least when given to LLVM), but this is not possible +// when using unix's linker. Perhaps one day when we just use a linker from LLVM +// we won't need to do this name mangling. The problem with name mangling is +// that it seriously limits the available characters. For example we can't +// have things like &T in symbol names when one would theoretically +// want them for things like impls of traits on that type. +// +// To be able to work on all platforms and get *some* reasonable output, we +// use C++ name-mangling. +#[derive(Debug)] +struct SymbolPath { + result: String, + temp_buf: String, +} + +impl SymbolPath { + fn new() -> Self { + let mut result = SymbolPath { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16), + }; + result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested + result + } + + fn finalize_pending_component(&mut self) { + if !self.temp_buf.is_empty() { + let _ = write!(self.result, "{}{}", self.temp_buf.len(), self.temp_buf); + self.temp_buf.clear(); + } + } + + fn finish(mut self, hash: u64) -> String { + self.finalize_pending_component(); + // E = end name-sequence + let _ = write!(self.result, "17h{:016x}E", hash); + self.result + } +} + +struct SymbolPrinter<'a, 'tcx> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + path: SymbolPath, + + // When `true`, `finalize_pending_component` isn't used. + // This is needed when recursing into `path_qualified`, + // or `path_generic_args`, as any nested paths are + // logically within one component. + keep_within_component: bool, +} + +// HACK(eddyb) this relies on using the `fmt` interface to get +// `PrettyPrinter` aka pretty printing of e.g. types in paths, +// symbol names should have their own printing machinery. + +impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> { + type Error = fmt::Error; + + type Path = Self; + type Region = Self; + type Type = Self; + type DynExistential = Self; + type Const = Self; + + fn tcx(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } + + fn print_region( + self, + _region: ty::Region<'_>, + ) -> Result { + Ok(self) + } + + fn print_type( + self, + ty: Ty<'tcx>, + ) -> Result { + match ty.sty { + // Print all nominal types as paths (unlike `pretty_print_type`). + ty::FnDef(def_id, substs) | + ty::Opaque(def_id, substs) | + ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) | + ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | + ty::Closure(def_id, ty::ClosureSubsts { substs }) | + ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => { + self.print_def_path(def_id, substs) + } + _ => self.pretty_print_type(ty), + } + } + + fn print_dyn_existential( + mut self, + predicates: &'tcx ty::List>, + ) -> Result { + let mut first = true; + for p in predicates { + if !first { + write!(self, "+")?; + } + first = false; + self = p.print(self)?; + } + Ok(self) + } + + fn print_const( + mut self, + ct: &'tcx ty::Const<'tcx>, + ) -> Result { + // only print integers + if let ConstValue::Scalar(Scalar::Raw { .. }) = ct.val { + if ct.ty.is_integral() { + return self.pretty_print_const(ct); + } + } + self.write_str("_")?; + Ok(self) + } + + fn path_crate( + mut self, + cnum: CrateNum, + ) -> Result { + self.write_str(&self.tcx.original_crate_name(cnum).as_str())?; + Ok(self) + } + fn path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + // Similar to `pretty_path_qualified`, but for the other + // types that are printed as paths (see `print_type` above). + match self_ty.sty { + ty::FnDef(..) | + ty::Opaque(..) | + ty::Projection(_) | + ty::UnnormalizedProjection(_) | + ty::Closure(..) | + ty::Generator(..) + if trait_ref.is_none() => + { + self.print_type(self_ty) + } + + _ => self.pretty_path_qualified(self_ty, trait_ref) + } + } + + fn path_append_impl( + self, + print_prefix: impl FnOnce(Self) -> Result, + _disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self.pretty_path_append_impl( + |mut cx| { + cx = print_prefix(cx)?; + + if cx.keep_within_component { + // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. + cx.write_str("::")?; + } else { + cx.path.finalize_pending_component(); + } + + Ok(cx) + }, + self_ty, + trait_ref, + ) + } + fn path_append( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + self = print_prefix(self)?; + + // Skip `::{{constructor}}` on tuple/unit structs. + match disambiguated_data.data { + DefPathData::Ctor => return Ok(self), + _ => {} + } + + if self.keep_within_component { + // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. + self.write_str("::")?; + } else { + self.path.finalize_pending_component(); + } + + self.write_str(&disambiguated_data.data.as_interned_str().as_str())?; + Ok(self) + } + fn path_generic_args( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[Kind<'tcx>], + ) -> Result { + self = print_prefix(self)?; + + let args = args.iter().cloned().filter(|arg| { + match arg.unpack() { + UnpackedKind::Lifetime(_) => false, + _ => true, + } + }); + + if args.clone().next().is_some() { + self.generic_delimiters(|cx| cx.comma_sep(args)) + } else { + Ok(self) + } + } +} + +impl PrettyPrinter<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> { + fn region_should_not_be_omitted( + &self, + _region: ty::Region<'_>, + ) -> bool { + false + } + fn comma_sep( + mut self, + mut elems: impl Iterator, + ) -> Result + where T: Print<'tcx, 'tcx, Self, Output = Self, Error = Self::Error> + { + if let Some(first) = elems.next() { + self = first.print(self)?; + for elem in elems { + self.write_str(",")?; + self = elem.print(self)?; + } + } + Ok(self) + } + + fn generic_delimiters( + mut self, + f: impl FnOnce(Self) -> Result, + ) -> Result { + write!(self, "<")?; + + let kept_within_component = + mem::replace(&mut self.keep_within_component, true); + self = f(self)?; + self.keep_within_component = kept_within_component; + + write!(self, ">")?; + + Ok(self) + } +} + +impl fmt::Write for SymbolPrinter<'_, '_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + // Name sanitation. LLVM will happily accept identifiers with weird names, but + // gas doesn't! + // gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ + // NVPTX assembly has more strict naming rules than gas, so additionally, dots + // are replaced with '$' there. + + for c in s.chars() { + if self.path.temp_buf.is_empty() { + match c { + 'a'..='z' | 'A'..='Z' | '_' => {} + _ => { + // Underscore-qualify anything that didn't start as an ident. + self.path.temp_buf.push('_'); + } + } + } + match c { + // Escape these with $ sequences + '@' => self.path.temp_buf.push_str("$SP$"), + '*' => self.path.temp_buf.push_str("$BP$"), + '&' => self.path.temp_buf.push_str("$RF$"), + '<' => self.path.temp_buf.push_str("$LT$"), + '>' => self.path.temp_buf.push_str("$GT$"), + '(' => self.path.temp_buf.push_str("$LP$"), + ')' => self.path.temp_buf.push_str("$RP$"), + ',' => self.path.temp_buf.push_str("$C$"), + + '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => { + // NVPTX doesn't support these characters in symbol names. + self.path.temp_buf.push('$') + } + + // '.' doesn't occur in types and functions, so reuse it + // for ':' and '-' + '-' | ':' => self.path.temp_buf.push('.'), + + // Avoid crashing LLVM in certain (LTO-related) situations, see #60925. + 'm' if self.path.temp_buf.ends_with(".llv") => self.path.temp_buf.push_str("$6d$"), + + // These are legal symbols + 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c), + + _ => { + self.path.temp_buf.push('$'); + for c in c.escape_unicode().skip(1) { + match c { + '{' => {} + '}' => self.path.temp_buf.push('$'), + c => self.path.temp_buf.push(c), + } + } + } + } + } + + Ok(()) + } +} diff --git a/src/librustc_codegen_utils/symbol_names/v0.rs b/src/librustc_codegen_utils/symbol_names/v0.rs new file mode 100644 index 0000000000000..1615a097b3bf9 --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names/v0.rs @@ -0,0 +1,657 @@ +use rustc::hir; +use rustc::hir::def_id::{CrateNum, DefId}; +use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::print::{Printer, Print}; +use rustc::ty::subst::{Kind, Subst, UnpackedKind}; +use rustc_data_structures::base_n; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_mir::monomorphize::Instance; +use rustc_target::spec::abi::Abi; +use syntax::ast::{IntTy, UintTy, FloatTy}; + +use std::fmt::Write; +use std::ops::Range; + +pub(super) fn mangle( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + instance: Instance<'tcx>, + instantiating_crate: Option, +) -> String { + let def_id = instance.def_id(); + // FIXME(eddyb) this should ideally not be needed. + let substs = + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), instance.substs); + + let prefix = "_R"; + let mut cx = SymbolMangler { + tcx, + compress: Some(Box::new(CompressionCaches { + start_offset: prefix.len(), + + paths: FxHashMap::default(), + types: FxHashMap::default(), + consts: FxHashMap::default(), + })), + binders: vec![], + out: String::from(prefix), + }; + cx = if instance.is_vtable_shim() { + cx.path_append_ns( + |cx| cx.print_def_path(def_id, substs), + 'S', + 0, + "", + ).unwrap() + } else { + cx.print_def_path(def_id, substs).unwrap() + }; + if let Some(instantiating_crate) = instantiating_crate { + cx = cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap(); + } + cx.out +} + +struct CompressionCaches<'tcx> { + // The length of the prefix in `out` (e.g. 2 for `_R`). + start_offset: usize, + + // The values are start positions in `out`, in bytes. + paths: FxHashMap<(DefId, &'tcx [Kind<'tcx>]), usize>, + types: FxHashMap, usize>, + consts: FxHashMap<&'tcx ty::Const<'tcx>, usize>, +} + +struct BinderLevel { + /// The range of distances from the root of what's + /// being printed, to the lifetimes in a binder. + /// Specifically, a `BrAnon(i)` lifetime has depth + /// `lifetime_depths.start + i`, going away from the + /// the root and towards its use site, as `i` increases. + /// This is used to flatten rustc's pairing of `BrAnon` + /// (intra-binder disambiguation) with a `DebruijnIndex` + /// (binder addressing), to "true" de Bruijn indices, + /// by subtracting the depth of a certain lifetime, from + /// the innermost depth at its use site. + lifetime_depths: Range, +} + +struct SymbolMangler<'a, 'tcx> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + compress: Option>>, + binders: Vec, + out: String, +} + +impl SymbolMangler<'_, 'tcx> { + fn push(&mut self, s: &str) { + self.out.push_str(s); + } + + /// Push a `_`-terminated base 62 integer, using the format + /// specified in the RFC as ``, that is: + /// * `x = 0` is encoded as just the `"_"` terminator + /// * `x > 0` is encoded as `x - 1` in base 62, followed by `"_"`, + /// e.g. `1` becomes `"0_"`, `62` becomes `"Z_"`, etc. + fn push_integer_62(&mut self, x: u64) { + if let Some(x) = x.checked_sub(1) { + base_n::push_str(x as u128, 62, &mut self.out); + } + self.push("_"); + } + + /// Push a `tag`-prefixed base 62 integer, when larger than `0`, that is: + /// * `x = 0` is encoded as `""` (nothing) + /// * `x > 0` is encoded as the `tag` followed by `push_integer_62(x - 1)` + /// e.g. `1` becomes `tag + "_"`, `2` becomes `tag + "0_"`, etc. + fn push_opt_integer_62(&mut self, tag: &str, x: u64) { + if let Some(x) = x.checked_sub(1) { + self.push(tag); + self.push_integer_62(x); + } + } + + fn push_disambiguator(&mut self, dis: u64) { + self.push_opt_integer_62("s", dis); + } + + fn push_ident(&mut self, ident: &str) { + let mut use_punycode = false; + for b in ident.bytes() { + match b { + b'_' | b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' => {} + 0x80..=0xff => use_punycode = true, + _ => bug!("symbol_names: bad byte {} in ident {:?}", b, ident), + } + } + + let punycode_string; + let ident = if use_punycode { + self.push("u"); + + // FIXME(eddyb) we should probably roll our own punycode implementation. + let mut punycode_bytes = match ::punycode::encode(ident) { + Ok(s) => s.into_bytes(), + Err(()) => bug!("symbol_names: punycode encoding failed for ident {:?}", ident), + }; + + // Replace `-` with `_`. + if let Some(c) = punycode_bytes.iter_mut().rfind(|&&mut c| c == b'-') { + *c = b'_'; + } + + // FIXME(eddyb) avoid rechecking UTF-8 validity. + punycode_string = String::from_utf8(punycode_bytes).unwrap(); + &punycode_string + } else { + ident + }; + + let _ = write!(self.out, "{}", ident.len()); + + // Write a separating `_` if necessary (leading digit or `_`). + match ident.chars().next() { + Some('_') | Some('0'..='9') => { + self.push("_"); + } + _ => {} + } + + self.push(ident); + } + + fn path_append_ns( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + ns: char, + disambiguator: u64, + name: &str, + ) -> Result { + self.push("N"); + self.out.push(ns); + self = print_prefix(self)?; + self.push_disambiguator(disambiguator as u64); + self.push_ident(name); + Ok(self) + } + + fn print_backref(mut self, i: usize) -> Result { + self.push("B"); + self.push_integer_62((i - self.compress.as_ref().unwrap().start_offset) as u64); + Ok(self) + } + + fn in_binder( + mut self, + value: &ty::Binder, + print_value: impl FnOnce(Self, &T) -> Result + ) -> Result + where T: TypeFoldable<'tcx> + { + let regions = if value.has_late_bound_regions() { + self.tcx.collect_referenced_late_bound_regions(value) + } else { + FxHashSet::default() + }; + + let mut lifetime_depths = + self.binders.last().map(|b| b.lifetime_depths.end).map_or(0..0, |i| i..i); + + let lifetimes = regions.into_iter().map(|br| { + match br { + ty::BrAnon(i) => i + 1, + _ => bug!("symbol_names: non-anonymized region `{:?}` in `{:?}`", br, value), + } + }).max().unwrap_or(0); + + self.push_opt_integer_62("G", lifetimes as u64); + lifetime_depths.end += lifetimes; + + self.binders.push(BinderLevel { lifetime_depths }); + self = print_value(self, value.skip_binder())?; + self.binders.pop(); + + Ok(self) + } +} + +impl Printer<'tcx, 'tcx> for SymbolMangler<'_, 'tcx> { + type Error = !; + + type Path = Self; + type Region = Self; + type Type = Self; + type DynExistential = Self; + type Const = Self; + + fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } + + fn print_def_path( + mut self, + def_id: DefId, + substs: &'tcx [Kind<'tcx>], + ) -> Result { + if let Some(&i) = self.compress.as_ref().and_then(|c| c.paths.get(&(def_id, substs))) { + return self.print_backref(i); + } + let start = self.out.len(); + + self = self.default_print_def_path(def_id, substs)?; + + // Only cache paths that do not refer to an enclosing + // binder (which would change depending on context). + if !substs.iter().any(|k| k.has_escaping_bound_vars()) { + if let Some(c) = &mut self.compress { + c.paths.insert((def_id, substs), start); + } + } + Ok(self) + } + + fn print_impl_path( + self, + impl_def_id: DefId, + substs: &'tcx [Kind<'tcx>], + mut self_ty: Ty<'tcx>, + mut impl_trait_ref: Option>, + ) -> Result { + let key = self.tcx.def_key(impl_def_id); + let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; + + let mut param_env = self.tcx.param_env(impl_def_id) + .with_reveal_all(); + if !substs.is_empty() { + param_env = param_env.subst(self.tcx, substs); + } + + match &mut impl_trait_ref { + Some(impl_trait_ref) => { + assert_eq!(impl_trait_ref.self_ty(), self_ty); + *impl_trait_ref = + self.tcx.normalize_erasing_regions(param_env, *impl_trait_ref); + self_ty = impl_trait_ref.self_ty(); + } + None => { + self_ty = self.tcx.normalize_erasing_regions(param_env, self_ty); + } + } + + self.path_append_impl( + |cx| cx.print_def_path(parent_def_id, &[]), + &key.disambiguated_data, + self_ty, + impl_trait_ref, + ) + } + + fn print_region( + mut self, + region: ty::Region<'_>, + ) -> Result { + let i = match *region { + // Erased lifetimes use the index 0, for a + // shorter mangling of `L_`. + ty::ReErased => 0, + + // Late-bound lifetimes use indices starting at 1, + // see `BinderLevel` for more details. + ty::ReLateBound(debruijn, ty::BrAnon(i)) => { + let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; + let depth = binder.lifetime_depths.start + i; + + 1 + (self.binders.last().unwrap().lifetime_depths.end - 1 - depth) + } + + _ => bug!("symbol_names: non-erased region `{:?}`", region), + }; + self.push("L"); + self.push_integer_62(i as u64); + Ok(self) + } + + fn print_type( + mut self, + ty: Ty<'tcx>, + ) -> Result { + // Basic types, never cached (single-character). + let basic_type = match ty.sty { + ty::Bool => "b", + ty::Char => "c", + ty::Str => "e", + ty::Tuple(_) if ty.is_unit() => "u", + ty::Int(IntTy::I8) => "a", + ty::Int(IntTy::I16) => "s", + ty::Int(IntTy::I32) => "l", + ty::Int(IntTy::I64) => "x", + ty::Int(IntTy::I128) => "n", + ty::Int(IntTy::Isize) => "i", + ty::Uint(UintTy::U8) => "h", + ty::Uint(UintTy::U16) => "t", + ty::Uint(UintTy::U32) => "m", + ty::Uint(UintTy::U64) => "y", + ty::Uint(UintTy::U128) => "o", + ty::Uint(UintTy::Usize) => "j", + ty::Float(FloatTy::F32) => "f", + ty::Float(FloatTy::F64) => "d", + ty::Never => "z", + + // Placeholders (should be demangled as `_`). + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | + ty::Infer(_) | ty::Error => "p", + + _ => "", + }; + if !basic_type.is_empty() { + self.push(basic_type); + return Ok(self); + } + + if let Some(&i) = self.compress.as_ref().and_then(|c| c.types.get(&ty)) { + return self.print_backref(i); + } + let start = self.out.len(); + + match ty.sty { + // Basic types, handled above. + ty::Bool | ty::Char | ty::Str | + ty::Int(_) | ty::Uint(_) | ty::Float(_) | + ty::Never => unreachable!(), + ty::Tuple(_) if ty.is_unit() => unreachable!(), + + // Placeholders, also handled as part of basic types. + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | + ty::Infer(_) | ty::Error => unreachable!(), + + ty::Ref(r, ty, mutbl) => { + self.push(match mutbl { + hir::MutImmutable => "R", + hir::MutMutable => "Q", + }); + if *r != ty::ReErased { + self = r.print(self)?; + } + self = ty.print(self)?; + } + + ty::RawPtr(mt) => { + self.push(match mt.mutbl { + hir::MutImmutable => "P", + hir::MutMutable => "O", + }); + self = mt.ty.print(self)?; + } + + ty::Array(ty, len) => { + self.push("A"); + self = ty.print(self)?; + self = self.print_const(len)?; + } + ty::Slice(ty) => { + self.push("S"); + self = ty.print(self)?; + } + + ty::Tuple(tys) => { + self.push("T"); + for ty in tys.iter().map(|k| k.expect_ty()) { + self = ty.print(self)?; + } + self.push("E"); + } + + // Mangle all nominal types as paths. + ty::Adt(&ty::AdtDef { did: def_id, .. }, substs) | + ty::FnDef(def_id, substs) | + ty::Opaque(def_id, substs) | + ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) | + ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | + ty::Closure(def_id, ty::ClosureSubsts { substs }) | + ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => { + self = self.print_def_path(def_id, substs)?; + } + ty::Foreign(def_id) => { + self = self.print_def_path(def_id, &[])?; + } + + ty::FnPtr(sig) => { + self.push("F"); + self = self.in_binder(&sig, |mut cx, sig| { + if sig.unsafety == hir::Unsafety::Unsafe { + cx.push("U"); + } + match sig.abi { + Abi::Rust => {} + Abi::C => cx.push("KC"), + abi => { + cx.push("K"); + let name = abi.name(); + if name.contains('-') { + cx.push_ident(&name.replace('-', "_")); + } else { + cx.push_ident(name); + } + } + } + for &ty in sig.inputs() { + cx = ty.print(cx)?; + } + if sig.c_variadic { + cx.push("v"); + } + cx.push("E"); + sig.output().print(cx) + })?; + } + + ty::Dynamic(predicates, r) => { + self.push("D"); + self = self.in_binder(&predicates, |cx, predicates| { + cx.print_dyn_existential(predicates) + })?; + self = r.print(self)?; + } + + ty::GeneratorWitness(_) => { + bug!("symbol_names: unexpected `GeneratorWitness`") + } + } + + // Only cache types that do not refer to an enclosing + // binder (which would change depending on context). + if !ty.has_escaping_bound_vars() { + if let Some(c) = &mut self.compress { + c.types.insert(ty, start); + } + } + Ok(self) + } + + fn print_dyn_existential( + mut self, + predicates: &'tcx ty::List>, + ) -> Result { + for predicate in predicates { + match *predicate { + ty::ExistentialPredicate::Trait(trait_ref) => { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = self.tcx.mk_ty_infer(ty::FreshTy(0)); + let trait_ref = trait_ref.with_self_ty(self.tcx, dummy_self); + self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?; + } + ty::ExistentialPredicate::Projection(projection) => { + let name = self.tcx.associated_item(projection.item_def_id).ident; + self.push("p"); + self.push_ident(&name.as_str()); + self = projection.ty.print(self)?; + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + self = self.print_def_path(def_id, &[])?; + } + } + } + self.push("E"); + Ok(self) + } + + fn print_const( + mut self, + ct: &'tcx ty::Const<'tcx>, + ) -> Result { + if let Some(&i) = self.compress.as_ref().and_then(|c| c.consts.get(&ct)) { + return self.print_backref(i); + } + let start = self.out.len(); + + match ct.ty.sty { + ty::Uint(_) => {} + _ => { + bug!("symbol_names: unsupported constant of type `{}` ({:?})", + ct.ty, ct); + } + } + self = ct.ty.print(self)?; + + if let Some(bits) = ct.assert_bits(self.tcx, ty::ParamEnv::empty().and(ct.ty)) { + let _ = write!(self.out, "{:x}_", bits); + } else { + // NOTE(eddyb) despite having the path, we need to + // encode a placeholder, as the path could refer + // back to e.g. an `impl` using the constant. + self.push("p"); + } + + // Only cache consts that do not refer to an enclosing + // binder (which would change depending on context). + if !ct.has_escaping_bound_vars() { + if let Some(c) = &mut self.compress { + c.consts.insert(ct, start); + } + } + Ok(self) + } + + fn path_crate( + mut self, + cnum: CrateNum, + ) -> Result { + self.push("C"); + let fingerprint = self.tcx.crate_disambiguator(cnum).to_fingerprint(); + self.push_disambiguator(fingerprint.to_smaller_hash()); + let name = self.tcx.original_crate_name(cnum).as_str(); + self.push_ident(&name); + Ok(self) + } + fn path_qualified( + mut self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + assert!(trait_ref.is_some()); + let trait_ref = trait_ref.unwrap(); + + self.push("Y"); + self = self_ty.print(self)?; + self.print_def_path(trait_ref.def_id, trait_ref.substs) + } + + fn path_append_impl( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self.push(match trait_ref { + Some(_) => "X", + None => "M", + }); + self.push_disambiguator(disambiguated_data.disambiguator as u64); + self = print_prefix(self)?; + self = self_ty.print(self)?; + if let Some(trait_ref) = trait_ref { + self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?; + } + Ok(self) + } + fn path_append( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + let ns = match disambiguated_data.data { + // Uppercase categories are more stable than lowercase ones. + DefPathData::TypeNs(_) => 't', + DefPathData::ValueNs(_) => 'v', + DefPathData::ClosureExpr => 'C', + DefPathData::Ctor => 'c', + DefPathData::AnonConst => 'k', + DefPathData::ImplTrait => 'i', + + // These should never show up as `path_append` arguments. + DefPathData::CrateRoot + | DefPathData::Misc + | DefPathData::Impl + | DefPathData::MacroNs(_) + | DefPathData::LifetimeNs(_) + | DefPathData::GlobalMetaData(_) => { + bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data) + } + }; + + let name = disambiguated_data.data.get_opt_name().map(|s| s.as_str()); + + self.path_append_ns( + print_prefix, + ns, + disambiguated_data.disambiguator as u64, + name.as_ref().map_or("", |s| &s[..]) + ) + } + fn path_generic_args( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[Kind<'tcx>], + ) -> Result { + // Don't print any regions if they're all erased. + let print_regions = args.iter().any(|arg| { + match arg.unpack() { + UnpackedKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + } + }); + let args = args.iter().cloned().filter(|arg| { + match arg.unpack() { + UnpackedKind::Lifetime(_) => print_regions, + _ => true, + } + }); + + if args.clone().next().is_none() { + return print_prefix(self); + } + + self.push("I"); + self = print_prefix(self)?; + for arg in args { + match arg.unpack() { + UnpackedKind::Lifetime(lt) => { + self = lt.print(self)?; + } + UnpackedKind::Type(ty) => { + self = ty.print(self)?; + } + UnpackedKind::Const(c) => { + self.push("K"); + // FIXME(const_generics) implement `ty::print::Print` on `ty::Const`. + // self = c.print(self)?; + self = self.print_const(c)?; + } + } + } + self.push("E"); + + Ok(self) + } +} diff --git a/src/librustc_codegen_utils/symbol_names_test.rs b/src/librustc_codegen_utils/symbol_names_test.rs index 27ae0b97e5945..b935ccb7398e2 100644 --- a/src/librustc_codegen_utils/symbol_names_test.rs +++ b/src/librustc_codegen_utils/symbol_names_test.rs @@ -39,8 +39,12 @@ impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { if attr.check_name(SYMBOL_NAME) { // for now, can only use on monomorphic names let instance = Instance::mono(tcx, def_id); - let name = self.tcx.symbol_name(instance); - tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); + let mangled = self.tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); + if let Ok(demangling) = rustc_demangle::try_demangle(&mangled.as_str()) { + tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); + tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); + } } else if attr.check_name(DEF_PATH) { let path = tcx.def_path_str(def_id); tcx.sess.span_err(attr.span, &format!("def-path({})", path)); diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index cc79f7b077eb2..e04372ea280b5 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -176,6 +176,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, r.map(|c| &*tcx.arena.alloc(c)) } is_no_builtins => { cdata.root.no_builtins } + symbol_mangling_version => { cdata.root.symbol_mangling_version } impl_defaultness => { cdata.get_impl_defaultness(def_id.index) } reachable_non_generics => { let reachable_non_generics = tcx diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 588682a2420a9..586fc507dd3eb 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -498,6 +498,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { panic_runtime: attr::contains_name(&attrs, sym::panic_runtime), profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime), sanitizer_runtime: attr::contains_name(&attrs, sym::sanitizer_runtime), + symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version, crate_deps, dylib_dependency_formats, diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 2c3291a41d32b..8d1de4fd6c392 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -8,6 +8,7 @@ use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary, ForeignMo use rustc::middle::lang_items; use rustc::mir; use rustc::session::CrateDisambiguator; +use rustc::session::config::SymbolManglingVersion; use rustc::ty::{self, Ty, ReprOptions}; use rustc_target::spec::{PanicStrategy, TargetTriple}; use rustc_data_structures::svh::Svh; @@ -189,6 +190,7 @@ pub struct CrateRoot<'tcx> { pub panic_runtime: bool, pub profiler_runtime: bool, pub sanitizer_runtime: bool, + pub symbol_mangling_version: SymbolManglingVersion, } #[derive(RustcEncodable, RustcDecodable)] diff --git a/src/test/codegen/drop.rs b/src/test/codegen/drop.rs index 7e6f8eaaa30d2..307c4e2c1e273 100644 --- a/src/test/codegen/drop.rs +++ b/src/test/codegen/drop.rs @@ -19,15 +19,18 @@ pub fn droppy() { // that's one new drop call per call to possibly_unwinding(), and finally 3 drop calls for the // regular function exit. We used to have problems with quadratic growths of drop calls in such // functions. -// CHECK-NOT: invoke{{.*}}drop{{.*}}SomeUniqueName -// CHECK: call{{.*}}drop{{.*}}SomeUniqueName -// CHECK: call{{.*}}drop{{.*}}SomeUniqueName -// CHECK-NOT: call{{.*}}drop{{.*}}SomeUniqueName -// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName -// CHECK: call{{.*}}drop{{.*}}SomeUniqueName -// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName -// CHECK: call{{.*}}drop{{.*}}SomeUniqueName -// CHECK-NOT: {{(call|invoke).*}}drop{{.*}}SomeUniqueName +// FIXME(eddyb) the `void @` forces a match on the instruction, instead of the +// comment, that's `; call core::ptr::real_drop_in_place::` +// for the `v0` mangling, should switch to matching on that once `legacy` is gone. +// CHECK-NOT: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName +// CHECK-NOT: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName // The next line checks for the } that ends the function definition // CHECK-LABEL: {{^[}]}} let _s = SomeUniqueName; diff --git a/src/test/codegen/external-no-mangle-fns.rs b/src/test/codegen/external-no-mangle-fns.rs index 79d5dc2400c40..902882144996f 100644 --- a/src/test/codegen/external-no-mangle-fns.rs +++ b/src/test/codegen/external-no-mangle-fns.rs @@ -33,7 +33,9 @@ const HIDDEN: () = { }; // The surrounding item should not accidentally become external -// CHECK: define internal{{.*}} void @_ZN22external_no_mangle_fns1x +// CHECK-LABEL: ; external_no_mangle_fns::x +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal #[inline(never)] fn x() { // CHECK: define void @g() diff --git a/src/test/codegen/external-no-mangle-statics.rs b/src/test/codegen/external-no-mangle-statics.rs index 2998000180edb..e44373926b76a 100644 --- a/src/test/codegen/external-no-mangle-statics.rs +++ b/src/test/codegen/external-no-mangle-statics.rs @@ -75,4 +75,6 @@ fn x() { #[no_mangle] pub static mut P: u8 = 0; } -// CHECK: define internal void @_ZN26external_no_mangle_statics1x{{.*$}} +// CHECK-LABEL: ; external_no_mangle_statics::x +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal diff --git a/src/test/codegen/internalize-closures.rs b/src/test/codegen/internalize-closures.rs index 3434820aa8a18..8d9192c6fa0c1 100644 --- a/src/test/codegen/internalize-closures.rs +++ b/src/test/codegen/internalize-closures.rs @@ -4,7 +4,11 @@ pub fn main() { // We want to make sure that closures get 'internal' linkage instead of // 'weak_odr' when they are not shared between codegen units - // CHECK: define internal {{.*}}_ZN20internalize_closures4main{{.*}}$u7b$$u7b$closure$u7d$$u7d$ + // FIXME(eddyb) `legacy` mangling uses `{{closure}}`, while `v0` + // uses `{closure#0}`, switch to the latter once `legacy` is gone. + // CHECK-LABEL: ; internalize_closures::main::{{.*}}closure + // CHECK-NEXT: ; Function Attrs: + // CHECK-NEXT: define internal let c = |x:i32| { x + 1 }; let _ = c(1); } diff --git a/src/test/codegen/link-dead-code.rs b/src/test/codegen/link-dead-code.rs index cb3dd07a2a798..de5a237c5f8a3 100644 --- a/src/test/codegen/link-dead-code.rs +++ b/src/test/codegen/link-dead-code.rs @@ -5,12 +5,18 @@ // This test makes sure that, when -Clink-dead-code is specified, we generate // code for functions that would otherwise be skipped. -// CHECK-LABEL: define hidden i32 @_ZN14link_dead_code8const_fn +// CHECK-LABEL: ; link_dead_code::const_fn +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define hidden const fn const_fn() -> i32 { 1 } -// CHECK-LABEL: define hidden i32 @_ZN14link_dead_code9inline_fn +// CHECK-LABEL: ; link_dead_code::inline_fn +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define hidden #[inline] fn inline_fn() -> i32 { 2 } -// CHECK-LABEL: define hidden i32 @_ZN14link_dead_code10private_fn +// CHECK-LABEL: ; link_dead_code::private_fn +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define hidden fn private_fn() -> i32 { 3 } diff --git a/src/test/codegen/local-generics-in-exe-internalized.rs b/src/test/codegen/local-generics-in-exe-internalized.rs index 6bdbf92919389..e5430fbf17a1d 100644 --- a/src/test/codegen/local-generics-in-exe-internalized.rs +++ b/src/test/codegen/local-generics-in-exe-internalized.rs @@ -2,7 +2,9 @@ // Check that local generics are internalized if they are in the same CGU -// CHECK: define internal {{.*}} @_ZN34local_generics_in_exe_internalized3foo{{.*}} +// CHECK-LABEL: ; local_generics_in_exe_internalized::foo +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal pub fn foo(x: T, y: T) -> (T, T) { (x, y) } diff --git a/src/test/codegen/target-cpu-on-functions.rs b/src/test/codegen/target-cpu-on-functions.rs index 3fdf6ab6d002f..523216deb8400 100644 --- a/src/test/codegen/target-cpu-on-functions.rs +++ b/src/test/codegen/target-cpu-on-functions.rs @@ -13,7 +13,9 @@ pub extern fn exported() { not_exported(); } -// CHECK-LABEL: define {{.*}} @_ZN23target_cpu_on_functions12not_exported{{.*}}() {{.*}} #0 +// CHECK-LABEL: ; target_cpu_on_functions::not_exported +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define {{.*}}() {{.*}} #0 fn not_exported() {} // CHECK: attributes #0 = {{.*}} "target-cpu"="{{.*}}" diff --git a/src/test/run-make-fulldeps/a-b-a-linker-guard/Makefile b/src/test/run-make-fulldeps/a-b-a-linker-guard/Makefile index 0962ebfbff546..54526e8ef23b3 100644 --- a/src/test/run-make-fulldeps/a-b-a-linker-guard/Makefile +++ b/src/test/run-make-fulldeps/a-b-a-linker-guard/Makefile @@ -4,9 +4,12 @@ # of types, it will not run with a dylib that has a different set of # types. +# NOTE(eddyb) this test only works with the `legacy` mangling, +# and will probably get removed once `legacy` is gone. + all: - $(RUSTC) a.rs --cfg x -C prefer-dynamic - $(RUSTC) b.rs -C prefer-dynamic + $(RUSTC) a.rs --cfg x -C prefer-dynamic -Z symbol-mangling-version=legacy + $(RUSTC) b.rs -C prefer-dynamic -Z symbol-mangling-version=legacy $(call RUN,b) - $(RUSTC) a.rs --cfg y -C prefer-dynamic + $(RUSTC) a.rs --cfg y -C prefer-dynamic -Z symbol-mangling-version=legacy $(call FAIL,b) diff --git a/src/test/run-make-fulldeps/stable-symbol-names/Makefile b/src/test/run-make-fulldeps/stable-symbol-names/Makefile index 3cbc5593ac0a2..451af809b2223 100644 --- a/src/test/run-make-fulldeps/stable-symbol-names/Makefile +++ b/src/test/run-make-fulldeps/stable-symbol-names/Makefile @@ -3,14 +3,15 @@ # The following command will: # 1. dump the symbols of a library using `nm` # 2. extract only those lines that we are interested in via `grep` -# 3. from those lines, extract just the symbol name via `sed` -# (symbol names always start with "_ZN" and end with "E") +# 3. from those lines, extract just the symbol name via `sed`, which: +# * always starts with "_ZN" and ends with "E" (`legacy` mangling) +# * always starts with "_R" (`v0` mangling) # 4. sort those symbol names for deterministic comparison # 5. write the result into a file dump-symbols = nm "$(TMPDIR)/lib$(1).rlib" \ | grep -E "$(2)" \ - | sed "s/.*\(_ZN.*E\).*/\1/" \ + | sed -E "s/.*(_ZN.*E|_R[a-zA-Z0-9_]*).*/\1/" \ | sort \ > "$(TMPDIR)/$(1)$(3).nm" diff --git a/src/test/run-make-fulldeps/symbol-visibility/Makefile b/src/test/run-make-fulldeps/symbol-visibility/Makefile index d99470e30d7ee..7901866015bf2 100644 --- a/src/test/run-make-fulldeps/symbol-visibility/Makefile +++ b/src/test/run-make-fulldeps/symbol-visibility/Makefile @@ -19,6 +19,9 @@ EXE_NAME=an_executable COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.dylib endif +# `grep` regex for symbols produced by either `legacy` or `v0` mangling +RE_ANY_RUST_SYMBOL="_ZN.*h.*E\|_R[a-zA-Z0-9_]+" + all: $(RUSTC) -Zshare-generics=no an_rlib.rs $(RUSTC) -Zshare-generics=no a_cdylib.rs @@ -31,20 +34,20 @@ all: # Check that a cdylib exports the public #[no_mangle] functions of dependencies [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] # Check that a cdylib DOES NOT export any public Rust functions - [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ] + [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ] # Check that a Rust dylib exports its monomorphic functions [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ] - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rust_dylib)" -eq "1" ] # Check that a Rust dylib does not export generics if -Zshare-generics=no - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rust_dylib.*E)" -eq "0" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rust_dylib)" -eq "0" ] # Check that a Rust dylib exports the monomorphic functions from its dependencies [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ] # Check that a Rust dylib does not export generics if -Zshare-generics=no - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rlib.*E)" -eq "0" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rlib)" -eq "0" ] # Check that an executable does not export any dynamic symbols [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ] @@ -58,7 +61,7 @@ all: # Check that a cdylib exports the public #[no_mangle] functions of dependencies [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] # Check that a cdylib DOES NOT export any public Rust functions - [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ] + [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ] $(RUSTC) -Zshare-generics=yes an_rlib.rs @@ -71,17 +74,17 @@ all: # Check that a cdylib exports the public #[no_mangle] functions of dependencies [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] # Check that a cdylib DOES NOT export any public Rust functions - [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ] + [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ] # Check that a Rust dylib exports its monomorphic functions, including generics this time [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ] - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ] - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rust_dylib.*E)" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rust_dylib)" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rust_dylib)" -eq "1" ] # Check that a Rust dylib exports the monomorphic functions from its dependencies [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ] - [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_generic_function_from_rlib.*E)" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rlib)" -eq "1" ] # Check that an executable does not export any dynamic symbols [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ] diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 5f6198aff5009..c73ba293ee25b 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -42,6 +42,21 @@ fn expected(fn_name: &str) -> String { format!(" backtrace::{}", fn_name) } +fn contains_verbose_expected(s: &str, fn_name: &str) -> bool { + // HACK(eddyb) work around the fact that verbosely demangled stack traces + // (from `RUST_BACKTRACE=full`, or, as is the case here, panic-in-panic) + // may contain symbols with hashes in them, i.e. `backtrace[...]::`. + let prefix = " backtrace"; + let suffix = &format!("::{}", fn_name); + s.match_indices(prefix).any(|(i, _)| { + s[i + prefix.len()..] + .trim_start_matches('[') + .trim_start_matches(char::is_alphanumeric) + .trim_start_matches(']') + .starts_with(suffix) + }) +} + fn runtest(me: &str) { // Make sure that the stack trace is printed let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap(); @@ -79,7 +94,7 @@ fn runtest(me: &str) { let s = str::from_utf8(&out.stderr).unwrap(); // loosened the following from double::h to double:: due to // spurious failures on mac, 32bit, optimized - assert!(s.contains("stack backtrace") && s.contains(&expected("double")), + assert!(s.contains("stack backtrace") && contains_verbose_expected(s, "double"), "bad output3: {}", s); // Make sure a stack trace isn't printed too many times diff --git a/src/test/run-pass/struct-ctor-mangling.rs b/src/test/run-pass/struct-ctor-mangling.rs new file mode 100644 index 0000000000000..5f5ee7cfe4410 --- /dev/null +++ b/src/test/run-pass/struct-ctor-mangling.rs @@ -0,0 +1,12 @@ +fn size_of_val(_: &T) -> usize { + std::mem::size_of::() +} + +struct Foo(i64); + +// Test that the (symbol) mangling of `Foo` (the `struct` type) and that of +// `typeof Foo` (the function type of the `struct` constructor) don't collide. +fn main() { + size_of_val(&Foo(0)); + size_of_val(&Foo); +} diff --git a/src/test/ui/symbol-names/basic.legacy.stderr b/src/test/ui/symbol-names/basic.legacy.stderr new file mode 100644 index 0000000000000..e26168dcfc488 --- /dev/null +++ b/src/test/ui/symbol-names/basic.legacy.stderr @@ -0,0 +1,26 @@ +error: symbol-name(_ZN5basic4main17hd72940ef9669d526E) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(basic::main::hd72940ef9669d526) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(basic::main) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(main) + --> $DIR/basic.rs:14:1 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/symbol-names/basic.rs b/src/test/ui/symbol-names/basic.rs index 086b903b973b0..aa88184eddfd4 100644 --- a/src/test/ui/symbol-names/basic.rs +++ b/src/test/ui/symbol-names/basic.rs @@ -1,6 +1,18 @@ +// revisions: legacy v0 +//[legacy]compile-flags: -Z symbol-mangling-version=legacy + //[v0]compile-flags: -Z symbol-mangling-version=v0 + #![feature(rustc_attrs)] -#[rustc_symbol_name] //~ ERROR _ZN5basic4main -#[rustc_def_path] //~ ERROR def-path(main) +#[rustc_symbol_name] +//[legacy]~^ ERROR symbol-name(_ZN5basic4main +//[legacy]~| ERROR demangling(basic::main +//[legacy]~| ERROR demangling-alt(basic::main) + //[v0]~^^^^ ERROR symbol-name(_RNvCs4fqI2P2rA04_5basic4main) + //[v0]~| ERROR demangling(basic[317d481089b8c8fe]::main) + //[v0]~| ERROR demangling-alt(basic::main) +#[rustc_def_path] +//[legacy]~^ ERROR def-path(main) + //[v0]~^^ ERROR def-path(main) fn main() { } diff --git a/src/test/ui/symbol-names/basic.stderr b/src/test/ui/symbol-names/basic.stderr deleted file mode 100644 index 7539cbada8b7b..0000000000000 --- a/src/test/ui/symbol-names/basic.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: symbol-name(_ZN5basic4main17hd72940ef9669d526E) - --> $DIR/basic.rs:3:1 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: def-path(main) - --> $DIR/basic.rs:4:1 - | -LL | #[rustc_def_path] - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/symbol-names/basic.v0.stderr b/src/test/ui/symbol-names/basic.v0.stderr new file mode 100644 index 0000000000000..40a39daaedce1 --- /dev/null +++ b/src/test/ui/symbol-names/basic.v0.stderr @@ -0,0 +1,26 @@ +error: symbol-name(_RNvCs4fqI2P2rA04_5basic4main) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(basic[317d481089b8c8fe]::main) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(basic::main) + --> $DIR/basic.rs:7:1 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(main) + --> $DIR/basic.rs:14:1 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/symbol-names/impl1.legacy.stderr b/src/test/ui/symbol-names/impl1.legacy.stderr new file mode 100644 index 0000000000000..c1d22a919d900 --- /dev/null +++ b/src/test/ui/symbol-names/impl1.legacy.stderr @@ -0,0 +1,74 @@ +error: symbol-name(_ZN5impl13foo3Foo3bar17he53b9bee7600ed8dE) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(impl1::foo::Foo::bar::he53b9bee7600ed8d) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(impl1::foo::Foo::bar) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(foo::Foo::bar) + --> $DIR/impl1.rs:20:9 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h86c41f0462d901d4E) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(impl1::bar::::baz::h86c41f0462d901d4) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(impl1::bar::::baz) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(bar::::baz) + --> $DIR/impl1.rs:38:9 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: symbol-name(_ZN198_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17h6f205aef6a8ccc7bE) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method::h6f205aef6a8ccc7b) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(<[&dyn Foo extern "C" fn(&'r u8)> + AutoTrait; _] as main::{{closure}}#1::Bar>::method) + --> $DIR/impl1.rs:70:13 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index c712137e828f9..9ed93bb98185a 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -1,12 +1,25 @@ -#![feature(rustc_attrs)] +// ignore-tidy-linelength +// revisions: legacy v0 +//[legacy]compile-flags: -Z symbol-mangling-version=legacy + //[v0]compile-flags: -Z symbol-mangling-version=v0 + +#![feature(optin_builtin_traits, rustc_attrs)] #![allow(dead_code)] mod foo { pub struct Foo { x: u32 } impl Foo { - #[rustc_symbol_name] //~ ERROR _ZN5impl13foo3Foo3bar - #[rustc_def_path] //~ ERROR def-path(foo::Foo::bar) + #[rustc_symbol_name] + //[legacy]~^ ERROR symbol-name(_ZN5impl13foo3Foo3bar + //[legacy]~| ERROR demangling(impl1::foo::Foo::bar + //[legacy]~| ERROR demangling-alt(impl1::foo::Foo::bar) + //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13fooNtB2_3Foo3bar) + //[v0]~| ERROR demangling(::bar) + //[v0]~| ERROR demangling-alt(::bar) + #[rustc_def_path] + //[legacy]~^ ERROR def-path(foo::Foo::bar) + //[v0]~^^ ERROR def-path(foo::Foo::bar) fn bar() { } } } @@ -15,11 +28,49 @@ mod bar { use foo::Foo; impl Foo { - #[rustc_symbol_name] //~ ERROR _ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz - #[rustc_def_path] //~ ERROR def-path(bar::::baz) + #[rustc_symbol_name] + //[legacy]~^ ERROR symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz + //[legacy]~| ERROR demangling(impl1::bar::::baz + //[legacy]~| ERROR demangling-alt(impl1::bar::::baz) + //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13barNtNtB4_3foo3Foo3baz) + //[v0]~| ERROR demangling(::baz) + //[v0]~| ERROR demangling-alt(::baz) + #[rustc_def_path] + //[legacy]~^ ERROR def-path(bar::::baz) + //[v0]~^^ ERROR def-path(bar::::baz) fn baz() { } } } +trait Foo { + type Assoc; +} + +auto trait AutoTrait {} + fn main() { + // Test closure mangling, and disambiguators. + || {}; + || { + trait Bar { + fn method(&self) {} + } + + // Test type mangling, by putting them in an `impl` header. + // FIXME(eddyb) test C varargs when `core::ffi::VaList` stops leaking into the signature + // (which is a problem because `core` has an unpredictable hash) - see also #44930. + impl Bar for [&'_ (dyn Foo + AutoTrait); 3] { + #[rustc_symbol_name] + //[legacy]~^ ERROR symbol-name(_ZN198_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method + //[legacy]~| ERROR demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method + //[legacy]~| ERROR demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method) + //[v0]~^^^^ ERROR symbol-name(_RNvXNCNvCs4fqI2P2rA04_5impl14mains_0ARDNtB6_3Foop5AssocFG0_KCRL0_hEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) + //[v0]~| ERROR demangling(<[&dyn impl1[317d481089b8c8fe]::Foo extern "C" fn(&'b u8)> + impl1[317d481089b8c8fe]::AutoTrait; 3: usize] as impl1[317d481089b8c8fe]::main::{closure#1}::Bar>::method) + //[v0]~| ERROR demangling-alt(<[&dyn impl1::Foo extern "C" fn(&'b u8)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method) + #[rustc_def_path] + //[legacy]~^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8)> + AutoTrait; _] as main::{{closure}}#1::Bar>::method) + //[v0]~^^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8)> + AutoTrait; _] as main::{{closure}}#1::Bar>::method) + fn method(&self) {} + } + }; } diff --git a/src/test/ui/symbol-names/impl1.stderr b/src/test/ui/symbol-names/impl1.stderr deleted file mode 100644 index 20e48782a3a9e..0000000000000 --- a/src/test/ui/symbol-names/impl1.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: symbol-name(_ZN5impl13foo3Foo3bar17he53b9bee7600ed8dE) - --> $DIR/impl1.rs:8:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: def-path(foo::Foo::bar) - --> $DIR/impl1.rs:9:9 - | -LL | #[rustc_def_path] - | ^^^^^^^^^^^^^^^^^ - -error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h86c41f0462d901d4E) - --> $DIR/impl1.rs:18:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: def-path(bar::::baz) - --> $DIR/impl1.rs:19:9 - | -LL | #[rustc_def_path] - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/symbol-names/impl1.v0.stderr b/src/test/ui/symbol-names/impl1.v0.stderr new file mode 100644 index 0000000000000..1c4b256c9e933 --- /dev/null +++ b/src/test/ui/symbol-names/impl1.v0.stderr @@ -0,0 +1,74 @@ +error: symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13fooNtB2_3Foo3bar) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(::bar) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(::bar) + --> $DIR/impl1.rs:13:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(foo::Foo::bar) + --> $DIR/impl1.rs:20:9 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13barNtNtB4_3foo3Foo3baz) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(::baz) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(::baz) + --> $DIR/impl1.rs:31:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(bar::::baz) + --> $DIR/impl1.rs:38:9 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: symbol-name(_RNvXNCNvCs4fqI2P2rA04_5impl14mains_0ARDNtB6_3Foop5AssocFG0_KCRL0_hEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(<[&dyn impl1[317d481089b8c8fe]::Foo extern "C" fn(&'b u8)> + impl1[317d481089b8c8fe]::AutoTrait; 3: usize] as impl1[317d481089b8c8fe]::main::{closure#1}::Bar>::method) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(<[&dyn impl1::Foo extern "C" fn(&'b u8)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method) + --> $DIR/impl1.rs:63:13 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: def-path(<[&dyn Foo extern "C" fn(&'r u8)> + AutoTrait; _] as main::{{closure}}#1::Bar>::method) + --> $DIR/impl1.rs:70:13 + | +LL | #[rustc_def_path] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/test/ui/symbol-names/issue-60925.legacy.stderr b/src/test/ui/symbol-names/issue-60925.legacy.stderr new file mode 100644 index 0000000000000..0bbe424aa025b --- /dev/null +++ b/src/test/ui/symbol-names/issue-60925.legacy.stderr @@ -0,0 +1,20 @@ +error: symbol-name(_ZN11issue_609253foo36Foo$LT$issue_60925..llv$6d$..Foo$GT$3foo17h059a991a004536adE) + --> $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(issue_60925::foo::Foo $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(issue_60925::foo::Foo $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/symbol-names/issue-60925.rs b/src/test/ui/symbol-names/issue-60925.rs index e9f763ad7cf67..09d68eebb950e 100644 --- a/src/test/ui/symbol-names/issue-60925.rs +++ b/src/test/ui/symbol-names/issue-60925.rs @@ -1,8 +1,13 @@ +// ignore-tidy-linelength +// revisions: legacy v0 +//[legacy]compile-flags: -Z symbol-mangling-version=legacy + //[v0]compile-flags: -Z symbol-mangling-version=v0 + #![feature(rustc_attrs)] // This test is the same code as in ui/issue-53912.rs but this test checks that the symbol mangling // fix produces the correct result, whereas that test just checks that the reproduction compiles -// successfully and doesn't segfault +// successfully and doesn't crash LLVM fn dummy() {} @@ -14,7 +19,12 @@ mod foo { impl Foo<::llvm::Foo> { #[rustc_symbol_name] -//~^ ERROR _ZN11issue_609253foo36Foo$LT$issue_60925..llv$6d$..Foo$GT$3foo17h059a991a004536adE + //[legacy]~^ ERROR symbol-name(_ZN11issue_609253foo36Foo$LT$issue_60925..llv$6d$..Foo$GT$3foo + //[legacy]~| ERROR demangling(issue_60925::foo::Foo>::foo) + //[v0]~| ERROR demangling-alt(>::foo) pub(crate) fn foo() { for _ in 0..0 { for _ in &[::dummy()] { diff --git a/src/test/ui/symbol-names/issue-60925.stderr b/src/test/ui/symbol-names/issue-60925.stderr index 84a18745e2ea7..ae753f0cebbcd 100644 --- a/src/test/ui/symbol-names/issue-60925.stderr +++ b/src/test/ui/symbol-names/issue-60925.stderr @@ -4,5 +4,17 @@ error: symbol-name(_ZN11issue_609253foo36Foo$LT$issue_60925..llv$6d$..Foo$GT$3fo LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: demangling(issue_60925::foo::Foo $DIR/issue-60925.rs:16:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(issue_60925::foo::Foo $DIR/issue-60925.rs:16:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/test/ui/symbol-names/issue-60925.v0.stderr b/src/test/ui/symbol-names/issue-60925.v0.stderr new file mode 100644 index 0000000000000..5ead40211d20d --- /dev/null +++ b/src/test/ui/symbol-names/issue-60925.v0.stderr @@ -0,0 +1,20 @@ +error: symbol-name(_RNvMNtCs4fqI2P2rA04_11issue_609253fooINtB2_3FooNtNtB4_4llvm3FooE3foo) + --> $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling(>::foo) + --> $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: demangling-alt(>::foo) + --> $DIR/issue-60925.rs:21:9 + | +LL | #[rustc_symbol_name] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors +