diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 8d3c8fc435c8d..522466314d660 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -634,7 +634,28 @@ impl<'a> Builder<'a> { if compiler.is_snapshot(self) { self.rustc_snapshot_libdir() } else { - self.sysroot(compiler).join(libdir(&compiler.host)) + match self.config.libdir_relative() { + Some(relative_libdir) if compiler.stage >= 1 + => self.sysroot(compiler).join(relative_libdir), + _ => self.sysroot(compiler).join(libdir(&compiler.host)) + } + } + } + + /// Returns the compiler's relative libdir where it stores the dynamic libraries that + /// it itself links against. + /// + /// For example this returns `lib` on Unix and `bin` on + /// Windows. + pub fn libdir_relative(&self, compiler: Compiler) -> &Path { + if compiler.is_snapshot(self) { + libdir(&self.config.build).as_ref() + } else { + match self.config.libdir_relative() { + Some(relative_libdir) if compiler.stage >= 1 + => relative_libdir, + _ => libdir(&compiler.host).as_ref() + } } } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 237f5c0ea2f15..08316b71ea85b 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -20,7 +20,7 @@ use filetime::FileTime; use serde_json; use crate::dist; -use crate::util::{exe, libdir, is_dylib}; +use crate::util::{exe, is_dylib}; use crate::{Compiler, Mode, GitRepo}; use crate::native; @@ -1005,13 +1005,13 @@ impl Step for Assemble { // Link in all dylibs to the libdir let sysroot = builder.sysroot(target_compiler); - let sysroot_libdir = sysroot.join(libdir(&*host)); - t!(fs::create_dir_all(&sysroot_libdir)); + let rustc_libdir = builder.rustc_libdir(target_compiler); + t!(fs::create_dir_all(&rustc_libdir)); let src_libdir = builder.sysroot_libdir(build_compiler, host); for f in builder.read_dir(&src_libdir) { let filename = f.file_name().into_string().unwrap(); if is_dylib(&filename) { - builder.copy(&f.path(), &sysroot_libdir.join(&filename)); + builder.copy(&f.path(), &rustc_libdir.join(&filename)); } } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index d982330a61640..a4d924d64ee78 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -18,7 +18,7 @@ use build_helper::output; use crate::{Compiler, Mode, LLVM_TOOLS}; use crate::channel; -use crate::util::{libdir, is_dylib, exe}; +use crate::util::{is_dylib, exe}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::compile; use crate::tool::{self, Tool}; @@ -473,7 +473,7 @@ impl Step for Rustc { fn prepare_image(builder: &Builder<'_>, compiler: Compiler, image: &Path) { let host = compiler.host; let src = builder.sysroot(compiler); - let libdir = libdir(&host); + let libdir = builder.rustc_libdir(compiler); // Copy rustc/rustdoc binaries t!(fs::create_dir_all(image.join("bin"))); @@ -481,13 +481,15 @@ impl Step for Rustc { builder.install(&builder.rustdoc(compiler), &image.join("bin"), 0o755); + let libdir_relative = builder.libdir_relative(compiler); + // Copy runtime DLLs needed by the compiler - if libdir != "bin" { - for entry in builder.read_dir(&src.join(libdir)) { + if libdir_relative.to_str() != Some("bin") { + for entry in builder.read_dir(&libdir) { let name = entry.file_name(); if let Some(s) = name.to_str() { if is_dylib(s) { - builder.install(&entry.path(), &image.join(libdir), 0o644); + builder.install(&entry.path(), &image.join(&libdir_relative), 0o644); } } } @@ -516,7 +518,8 @@ impl Step for Rustc { .join("bin") .join(&exe); // for the rationale about this rename check `compile::copy_lld_to_sysroot` - let dst = image.join("lib/rustlib") + let dst = image.join(libdir_relative) + .join("rustlib") .join(&*host) .join("bin") .join(&exe); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 2394ae7fb7913..47ac04baf6d6d 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1275,6 +1275,7 @@ impl Build { fn install(&self, src: &Path, dstdir: &Path, perms: u32) { if self.config.dry_run { return; } let dst = dstdir.join(src.file_name().unwrap()); + self.verbose_than(1, &format!("Install {:?} to {:?}", src, dst)); t!(fs::create_dir_all(dstdir)); drop(fs::remove_file(&dst)); { diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index e549b120da979..94d520ec78c71 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -117,6 +117,32 @@ impl TypeMap<'ll, 'tcx> { } } + // Removes a Ty to metadata mapping + // This is useful when computing the metadata for a potentially + // recursive type (e.g. a function ptr of the form: + // + // fn foo() -> impl Copy { foo } + // + // This kind of type cannot be properly represented + // via LLVM debuginfo. As a workaround, + // we register a temporary Ty to metadata mapping + // for the function before we compute its actual metadata. + // If the metadata computation ends up recursing back to the + // original function, it will use the temporary mapping + // for the inner self-reference, preventing us from + // recursing forever. + // + // This function is used to remove the temporary metadata + // mapping after we've computed the actual metadata + fn remove_type( + &mut self, + type_: Ty<'tcx>, + ) { + if self.type_to_metadata.remove(type_).is_none() { + bug!("Type metadata Ty '{}' is not in the TypeMap!", type_); + } + } + // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will // fail if the mapping already exists. fn register_unique_id_with_metadata( @@ -608,10 +634,7 @@ pub fn type_metadata( } } ty::FnDef(..) | ty::FnPtr(_) => { - let fn_metadata = subroutine_type_metadata(cx, - unique_type_id, - t.fn_sig(cx.tcx), - usage_site_span).metadata; + if let Some(metadata) = debug_context(cx).type_map .borrow() .find_metadata_for_unique_id(unique_type_id) @@ -619,6 +642,41 @@ pub fn type_metadata( return metadata; } + // It's possible to create a self-referential + // type in Rust by using 'impl trait': + // + // fn foo() -> impl Copy { foo } + // + // See TypeMap::remove_type for more detals + // about the workaround + + let temp_type = { + unsafe { + // The choice of type here is pretty arbitrary - + // anything reading the debuginfo for a recursive + // type is going to see *somthing* weird - the only + // question is what exactly it will see + let (size, align) = cx.size_and_align_of(t); + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + SmallCStr::new("").as_ptr(), + size.bits(), + align.bits() as u32, + DW_ATE_unsigned) + } + }; + + let type_map = &debug_context(cx).type_map; + type_map.borrow_mut().register_type_with_metadata(t, temp_type); + + let fn_metadata = subroutine_type_metadata(cx, + unique_type_id, + t.fn_sig(cx.tcx), + usage_site_span).metadata; + + type_map.borrow_mut().remove_type(t); + + // This is actually a function pointer, so wrap it in pointer DI MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) diff --git a/src/librustc_codegen_llvm/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs index 8b218ab39d99b..eff7cd1bc8a48 100644 --- a/src/librustc_codegen_llvm/debuginfo/type_names.rs +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -5,6 +5,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::SubstsRef; use rustc::ty::{self, Ty}; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashSet; use rustc::hir; @@ -17,7 +18,8 @@ pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, qualified: bool) -> String { let mut result = String::with_capacity(64); - push_debuginfo_type_name(cx, t, qualified, &mut result); + let mut visited = FxHashSet::default(); + push_debuginfo_type_name(cx, t, qualified, &mut result, &mut visited); result } @@ -26,7 +28,9 @@ pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, t: Ty<'tcx>, qualified: bool, - output: &mut String) { + output: &mut String, + visited: &mut FxHashSet>) { + // When targeting MSVC, emit C++ style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_names = cx.sess().target.target.options.is_like_msvc; @@ -42,12 +46,12 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ty::Foreign(def_id) => push_item_name(cx, def_id, qualified, output), ty::Adt(def, substs) => { push_item_name(cx, def.did, qualified, output); - push_type_params(cx, substs, output); + push_type_params(cx, substs, output, visited); }, ty::Tuple(component_types) => { output.push('('); for &component_type in component_types { - push_debuginfo_type_name(cx, component_type, true, output); + push_debuginfo_type_name(cx, component_type, true, output, visited); output.push_str(", "); } if !component_types.is_empty() { @@ -65,7 +69,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, hir::MutMutable => output.push_str("mut "), } - push_debuginfo_type_name(cx, inner_type, true, output); + push_debuginfo_type_name(cx, inner_type, true, output, visited); if cpp_like_names { output.push('*'); @@ -79,7 +83,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, output.push_str("mut "); } - push_debuginfo_type_name(cx, inner_type, true, output); + push_debuginfo_type_name(cx, inner_type, true, output, visited); if cpp_like_names { output.push('*'); @@ -87,7 +91,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, }, ty::Array(inner_type, len) => { output.push('['); - push_debuginfo_type_name(cx, inner_type, true, output); + push_debuginfo_type_name(cx, inner_type, true, output, visited); output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx))); output.push(']'); }, @@ -98,7 +102,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, output.push('['); } - push_debuginfo_type_name(cx, inner_type, true, output); + push_debuginfo_type_name(cx, inner_type, true, output, visited); if cpp_like_names { output.push('>'); @@ -113,12 +117,31 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, &principal, ); push_item_name(cx, principal.def_id, false, output); - push_type_params(cx, principal.substs, output); + push_type_params(cx, principal.substs, output, visited); } else { output.push_str("dyn '_"); } }, ty::FnDef(..) | ty::FnPtr(_) => { + // We've encountered a weird 'recursive type' + // Currently, the only way to generate such a type + // is by using 'impl trait': + // + // fn foo() -> impl Copy { foo } + // + // There's not really a sensible name we can generate, + // since we don't include 'impl trait' types (e.g. ty::Opaque) + // in the output + // + // Since we need to generate *something*, we just + // use a dummy string that should make it clear + // that something unusual is going on + if !visited.insert(t) { + output.push_str(""); + return; + } + + let sig = t.fn_sig(cx.tcx); if sig.unsafety() == hir::Unsafety::Unsafe { output.push_str("unsafe "); @@ -136,7 +159,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { - push_debuginfo_type_name(cx, parameter_type, true, output); + push_debuginfo_type_name(cx, parameter_type, true, output, visited); output.push_str(", "); } output.pop(); @@ -155,8 +178,20 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, if !sig.output().is_unit() { output.push_str(" -> "); - push_debuginfo_type_name(cx, sig.output(), true, output); + push_debuginfo_type_name(cx, sig.output(), true, output, visited); } + + + // We only keep the type in 'visited' + // for the duration of the body of this method. + // It's fine for a particular function type + // to show up multiple times in one overall type + // (e.g. MyType u8, fn() -> u8> + // + // We only care about avoiding recursing + // directly back to the type we're currently + // processing + visited.remove(t); }, ty::Closure(..) => { output.push_str("closure"); @@ -200,7 +235,8 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, // common denominator - otherwise we would run into conflicts. fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, substs: SubstsRef<'tcx>, - output: &mut String) { + output: &mut String, + visited: &mut FxHashSet>) { if substs.types().next().is_none() { return; } @@ -208,7 +244,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, output.push('<'); for type_parameter in substs.types() { - push_debuginfo_type_name(cx, type_parameter, true, output); + push_debuginfo_type_name(cx, type_parameter, true, output, visited); output.push_str(", "); } diff --git a/src/test/codegen/fn-impl-trait-self.rs b/src/test/codegen/fn-impl-trait-self.rs new file mode 100644 index 0000000000000..f9113d50197c1 --- /dev/null +++ b/src/test/codegen/fn-impl-trait-self.rs @@ -0,0 +1,15 @@ +// compile-flags: -g +// +// CHECK-LABEL: @main +// CHECK: {{.*}}DIDerivedType(tag: DW_TAG_pointer_type, name: "fn() -> ",{{.*}} +// +// CHECK: {{.*}}DISubroutineType{{.*}} +// CHECK: {{.*}}DIBasicType(name: "", encoding: DW_ATE_unsigned) + +pub fn foo() -> impl Copy { + foo +} + +fn main() { + let my_res = foo(); +} diff --git a/src/test/run-pass/issues/issue-58463.rs b/src/test/run-pass/issues/issue-58463.rs new file mode 100644 index 0000000000000..8ab845366b7b4 --- /dev/null +++ b/src/test/run-pass/issues/issue-58463.rs @@ -0,0 +1,8 @@ +// run-pass +// compile-flags:-C debuginfo=2 +fn foo() -> impl Copy { + foo +} +fn main() { + foo(); +}