diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 9a0c379b4e4..ac2a8f969df 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -31,7 +31,9 @@ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{create_rmeta_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; -use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib}; +use crate::{ + errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, +}; use cc::windows_registry; use regex::Regex; @@ -93,7 +95,7 @@ pub fn link_binary<'a>( let tmpdir = TempFileBuilder::new() .prefix("rustc") .tempdir() - .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); + .unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let out_filename = out_filename( sess, @@ -208,7 +210,7 @@ pub fn link_binary<'a>( pub fn each_linked_rlib( info: &CrateInfo, f: &mut dyn FnMut(CrateNum, &Path), -) -> Result<(), String> { +) -> Result<(), errors::LinkRlibError> { let crates = info.used_crates.iter(); let mut fmts = None; for (ty, list) in info.dependency_formats.iter() { @@ -224,26 +226,23 @@ pub fn each_linked_rlib( } } let Some(fmts) = fmts else { - return Err("could not find formats for rlibs".to_string()); + return Err(errors::LinkRlibError::MissingFormat); }; for &cnum in crates { match fmts.get(cnum.as_usize() - 1) { Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue, Some(_) => {} - None => return Err("could not find formats for rlibs".to_string()), + None => return Err(errors::LinkRlibError::MissingFormat), } - let name = info.crate_name[&cnum]; + let crate_name = info.crate_name[&cnum]; let used_crate_source = &info.used_crate_source[&cnum]; if let Some((path, _)) = &used_crate_source.rlib { f(cnum, &path); } else { if used_crate_source.rmeta.is_some() { - return Err(format!( - "could not find rlib for: `{}`, found rmeta (metadata) file", - name - )); + return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name }); } else { - return Err(format!("could not find rlib for: `{}`", name)); + return Err(errors::LinkRlibError::NotFound { crate_name }); } } } @@ -340,10 +339,7 @@ fn link_rlib<'a>( // -whole-archive and it isn't clear how we can currently handle such a // situation correctly. // See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897 - sess.err( - "the linking modifiers `+bundle` and `+whole-archive` are not compatible \ - with each other when generating rlibs", - ); + sess.emit_err(errors::IncompatibleLinkingModifiers); } NativeLibKind::Static { bundle: None | Some(true), .. } => {} NativeLibKind::Static { bundle: Some(false), .. } @@ -365,12 +361,11 @@ fn link_rlib<'a>( )); continue; } - ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| { - sess.fatal(&format!( - "failed to add native library {}: {}", - location.to_string_lossy(), - e - )); + ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { + library_path: &location.to_string_lossy(), + error, + }); }); } } @@ -385,8 +380,11 @@ fn link_rlib<'a>( tmpdir.as_ref(), ); - ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|e| { - sess.fatal(&format!("failed to add native library {}: {}", output_path.display(), e)); + ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { + library_path: &output_path.display().to_string(), + error, + }); }); } @@ -451,14 +449,11 @@ fn collate_raw_dylibs( // FIXME: when we add support for ordinals, figure out if we need to do anything // if we have two DllImport values with the same name but different ordinals. if import.calling_convention != old_import.calling_convention { - sess.span_err( - import.span, - &format!( - "multiple declarations of external function `{}` from \ - library `{}` have different calling conventions", - import.name, name, - ), - ); + sess.emit_err(errors::MultipleExternalFuncDecl { + span: import.span, + function: import.name, + library_name: &name, + }); } } } @@ -560,7 +555,7 @@ fn link_staticlib<'a>( all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); }); if let Err(e) = res { - sess.fatal(&e); + sess.emit_fatal(e); } ab.build(out_filename); @@ -673,9 +668,8 @@ fn link_dwarf_object<'a>( }) { Ok(()) => {} Err(e) => { - sess.struct_err("linking dwarf objects with thorin failed") - .note(&format!("{:?}", e)) - .emit(); + let thorin_error = errors::ThorinErrorWrapper(e); + sess.emit_err(errors::ThorinDwarfLinking { thorin_error }); sess.abort_if_errors(); } } @@ -879,23 +873,14 @@ fn link_natively<'a>( let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); let escaped_output = escape_string(&output); - let mut err = sess.struct_err(&format!( - "linking with `{}` failed: {}", - linker_path.display(), - prog.status - )); - err.note(&format!("{:?}", &cmd)).note(&escaped_output); - if escaped_output.contains("undefined reference to") { - err.help( - "some `extern` functions couldn't be found; some native libraries may \ - need to be installed or have their path specified", - ); - err.note("use the `-l` flag to specify native libraries to link"); - err.note("use the `cargo:rustc-link-lib` directive to specify the native \ - libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)"); - } - err.emit(); - + // FIXME: Add UI tests for this error. + let err = errors::LinkingFailed { + linker_path: &linker_path, + exit_status: prog.status, + command: &cmd, + escaped_output: &escaped_output, + }; + sess.diagnostic().emit_err(err); // If MSVC's `link.exe` was expected but the return code // is not a Microsoft LNK error then suggest a way to fix or // install the Visual Studio build tools. diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cf98cb2468a..6bd1dc2e03f 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1,10 +1,16 @@ //! Errors emitted by codegen_ssa -use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; +use crate::back::command::Command; +use rustc_errors::{ + fluent, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, + IntoDiagnosticArg, +}; use rustc_macros::Diagnostic; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::io::Error; use std::path::{Path, PathBuf}; +use std::process::ExitStatus; #[derive(Diagnostic)] #[diag(codegen_ssa::lib_def_write_failure)] @@ -73,17 +79,15 @@ pub struct CopyPath<'a> { impl<'a> CopyPath<'a> { pub fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> { - CopyPath { from: DebugArgPath { path: from }, to: DebugArgPath { path: to }, error } + CopyPath { from: DebugArgPath(from), to: DebugArgPath(to), error } } } -struct DebugArgPath<'a> { - pub path: &'a Path, -} +struct DebugArgPath<'a>(pub &'a Path); impl IntoDiagnosticArg for DebugArgPath<'_> { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.path))) + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.0))) } } @@ -98,3 +102,96 @@ pub struct IgnoringEmitPath { pub struct IgnoringOutput { pub extension: String, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa::create_temp_dir)] +pub struct CreateTempDir { + pub error: Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa::incompatible_linking_modifiers)] +pub struct IncompatibleLinkingModifiers; + +#[derive(Diagnostic)] +#[diag(codegen_ssa::add_native_library)] +pub struct AddNativeLibrary<'a> { + pub library_path: &'a str, + pub error: Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa::multiple_external_func_decl)] +pub struct MultipleExternalFuncDecl<'a> { + #[primary_span] + pub span: Span, + pub function: Symbol, + pub library_name: &'a str, +} + +pub enum LinkRlibError { + MissingFormat, + OnlyRmetaFound { crate_name: Symbol }, + NotFound { crate_name: Symbol }, +} + +impl IntoDiagnostic<'_, !> for LinkRlibError { + fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { + match self { + LinkRlibError::MissingFormat => { + handler.struct_fatal(fluent::codegen_ssa::rlib_missing_format) + } + LinkRlibError::OnlyRmetaFound { crate_name } => { + let mut diag = handler.struct_fatal(fluent::codegen_ssa::rlib_only_rmeta_found); + diag.set_arg("crate_name", crate_name); + diag + } + LinkRlibError::NotFound { crate_name } => { + let mut diag = handler.struct_fatal(fluent::codegen_ssa::rlib_not_found); + diag.set_arg("crate_name", crate_name); + diag + } + } + } +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa::thorin_dwarf_linking)] +#[note] +pub struct ThorinDwarfLinking { + pub thorin_error: ThorinErrorWrapper, +} +pub struct ThorinErrorWrapper(pub thorin::Error); + +// FIXME: How should we support translations for external crate errors? +impl IntoDiagnosticArg for ThorinErrorWrapper { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.0))) + } +} + +pub struct LinkingFailed<'a> { + pub linker_path: &'a PathBuf, + pub exit_status: ExitStatus, + pub command: &'a Command, + pub escaped_output: &'a str, +} + +impl IntoDiagnostic<'_> for LinkingFailed<'_> { + fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_err(fluent::codegen_ssa::linking_failed); + diag.set_arg("linker_path", format!("{}", self.linker_path.display())); + diag.set_arg("exit_status", format!("{}", self.exit_status)); + + diag.note(format!("{:?}", self.command)).note(self.escaped_output); + + // Trying to match an error from OS linkers + // which by now we have no way to translate. + if self.escaped_output.contains("undefined reference to") { + diag.note(fluent::codegen_ssa::extern_funcs_not_found) + .note(fluent::codegen_ssa::specify_libraries_to_link) + .note(fluent::codegen_ssa::use_cargo_directive); + } + diag + } +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index e62b6f342b2..ceebe4d417f 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,6 +6,7 @@ #![feature(strict_provenance)] #![feature(int_roundings)] #![feature(if_let_guard)] +#![feature(never_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl index 4d43b2eb0b6..d996a096a89 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -25,3 +25,28 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced + +codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} + +codegen_ssa_incompatible_linking_modifiers = the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs + +codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error} + +codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions + +codegen_ssa_rlib_missing_format = could not find formats for rlibs + +codegen_ssa_rlib_only_rmeta_found = could not find rlib for: `{$crate_name}`, found rmeta (metadata) file + +codegen_ssa_rlib_not_found = could not find rlib for: `{$crate_name}` + +codegen_ssa_thorin_dwarf_linking = linking dwarf objects with thorin failed + .note = {$thorin_error} + +codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} + +codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified + +codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libraries to link + +codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)