diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 9233287cf3a75..2440f20502ab1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -187,6 +187,12 @@ impl Display for RegionName { } } +impl rustc_errors::IntoDiagnosticArg for RegionName { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { pub(crate) fn mir_def_id(&self) -> hir::def_id::LocalDefId { self.body.source.def_id().expect_local() diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index b2d92d0dba7a4..c71413e8e7c04 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -369,6 +369,8 @@ pub(super) fn dump_mir_results<'tcx>( }; } +#[allow(rustc::diagnostic_outside_of_impl)] +#[allow(rustc::untranslatable_diagnostic)] pub(super) fn dump_annotation<'tcx>( infcx: &InferCtxt<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 23acf159240fa..13199d0385255 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -1,4 +1,4 @@ -use rustc_errors::{IntoDiagnosticArg, MultiSpan}; +use rustc_errors::MultiSpan; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; @@ -128,18 +128,6 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> { }, } -impl IntoDiagnosticArg for &RegionName { - fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - format!("{}", self).into_diagnostic_arg() - } -} - -impl IntoDiagnosticArg for RegionName { - fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - format!("{}", self).into_diagnostic_arg() - } -} - #[derive(Subdiagnostic)] pub(crate) enum RequireStaticErr { #[note(borrowck_used_impl_require_static)] diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index bf1da38312f7d..5ab87feb98b11 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -200,6 +200,7 @@ unsafe impl Sync for GccContext {} impl WriteBackendMethods for GccCodegenBackend { type Module = GccContext; type TargetMachine = (); + type TargetMachineError = (); type ModuleBuffer = ModuleBuffer; type ThinData = (); type ThinBuffer = ThinBuffer; diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 6c0faf37a63ce..d2e01708a37bc 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -1,5 +1,7 @@ use crate::back::write::{self, save_temp_bitcode, DiagnosticHandlers}; -use crate::errors::DynamicLinkingWithLTO; +use crate::errors::{ + DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, +}; use crate::llvm::{self, build_string}; use crate::{LlvmCodegenBackend, ModuleLlvm}; use object::read::archive::ArchiveFile; @@ -77,15 +79,12 @@ fn prepare_lto( // Make sure we actually can run LTO for crate_type in cgcx.crate_types.iter() { if !crate_type_allows_lto(*crate_type) { - let e = diag_handler.fatal( - "lto can only be run for executables, cdylibs and \ - static library outputs", - ); - return Err(e); + diag_handler.emit_err(LtoDisallowed); + return Err(FatalError); } else if *crate_type == CrateType::Dylib { if !cgcx.opts.unstable_opts.dylib_lto { - return Err(diag_handler - .fatal("lto cannot be used for `dylib` crate type without `-Zdylib-lto`")); + diag_handler.emit_err(LtoDylib); + return Err(FatalError); } } } @@ -127,7 +126,10 @@ fn prepare_lto( let module = SerializedModule::FromRlib(data.to_vec()); upstream_modules.push((module, CString::new(name).unwrap())); } - Err(msg) => return Err(diag_handler.fatal(&msg)), + Err(e) => { + diag_handler.emit_err(e); + return Err(FatalError); + } } } } @@ -140,7 +142,7 @@ fn prepare_lto( Ok((symbols_below_threshold, upstream_modules)) } -fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], String> { +fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], LtoBitcodeFromRlib> { let mut len = 0; let data = unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) }; @@ -155,8 +157,9 @@ fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], String> { Ok(bc) } else { assert!(len == 0); - let msg = llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()); - Err(format!("failed to get bitcode from object file for LTO ({})", msg)) + Err(LtoBitcodeFromRlib { + llvm_err: llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()), + }) } } @@ -328,10 +331,9 @@ fn fat_lto( }); info!("linking {:?}", name); let data = bc_decoded.data(); - linker.add(data).map_err(|()| { - let msg = format!("failed to load bitcode of module {:?}", name); - write::llvm_err(diag_handler, &msg) - })?; + linker + .add(data) + .map_err(|()| write::llvm_err(diag_handler, LlvmError::LoadBitcode { name }))?; serialized_bitcode.push(bc_decoded); } drop(linker); @@ -489,7 +491,7 @@ fn thin_lto( symbols_below_threshold.as_ptr(), symbols_below_threshold.len() as u32, ) - .ok_or_else(|| write::llvm_err(diag_handler, "failed to prepare thin LTO context"))?; + .ok_or_else(|| write::llvm_err(diag_handler, LlvmError::PrepareThinLtoContext))?; let data = ThinData(data); @@ -562,8 +564,7 @@ fn thin_lto( // session, overwriting the previous serialized data (if any). if let Some(path) = key_map_path { if let Err(err) = curr_key_map.save_to_file(&path) { - let msg = format!("Error while writing ThinLTO key data: {}", err); - return Err(write::llvm_err(diag_handler, &msg)); + return Err(write::llvm_err(diag_handler, LlvmError::WriteThinLtoKey { err })); } } @@ -689,8 +690,7 @@ pub unsafe fn optimize_thin_module( let module_name = &thin_module.shared.module_names[thin_module.idx]; let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); - let tm = - (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?; + let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, e))?; // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module @@ -717,8 +717,7 @@ pub unsafe fn optimize_thin_module( let mut cu2 = ptr::null_mut(); llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); if !cu2.is_null() { - let msg = "multiple source DICompileUnits found"; - return Err(write::llvm_err(&diag_handler, msg)); + return Err(write::llvm_err(&diag_handler, LlvmError::MultipleSourceDiCompileUnit)); } // Up next comes the per-module local analyses that we do for Thin LTO. @@ -733,8 +732,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)); + return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } @@ -744,8 +742,7 @@ pub unsafe fn optimize_thin_module( .prof .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)); + return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve"); } @@ -755,8 +752,7 @@ pub unsafe fn optimize_thin_module( .prof .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)); + return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize"); } @@ -765,8 +761,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)); + return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); } @@ -886,11 +881,7 @@ pub fn parse_module<'a>( diag_handler: &Handler, ) -> Result<&'a llvm::Module, FatalError> { unsafe { - llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr()).ok_or_else( - || { - let msg = "failed to parse bitcode for LTO module"; - write::llvm_err(diag_handler, msg) - }, - ) + llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr()) + .ok_or_else(|| write::llvm_err(diag_handler, LlvmError::ParseBitcode)) } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b2af9f31e4494..38f8733763dfa 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -5,6 +5,9 @@ use crate::back::profiling::{ use crate::base; use crate::common; use crate::consts; +use crate::errors::{ + CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode, +}; use crate::llvm::{self, DiagnosticInfo, PassManager}; use crate::llvm_util; use crate::type_::Type; @@ -37,10 +40,10 @@ use std::slice; use std::str; use std::sync::Arc; -pub fn llvm_err(handler: &rustc_errors::Handler, msg: &str) -> FatalError { +pub fn llvm_err<'a>(handler: &rustc_errors::Handler, err: LlvmError<'a>) -> FatalError { match llvm::last_error() { - Some(err) => handler.fatal(&format!("{}: {}", msg, err)), - None => handler.fatal(msg), + Some(llvm_err) => handler.emit_almost_fatal(WithLlvmError(err, llvm_err)), + None => handler.emit_almost_fatal(err), } } @@ -85,10 +88,9 @@ pub fn write_output_file<'ll>( } } - result.into_result().map_err(|()| { - let msg = format!("could not write output to {}", output.display()); - llvm_err(handler, &msg) - }) + result + .into_result() + .map_err(|()| llvm_err(handler, LlvmError::WriteOutput { path: output })) } } @@ -98,7 +100,7 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm: // system/tcx is set up. let features = llvm_util::global_llvm_features(sess, false); target_machine_factory(sess, config::OptLevel::No, &features)(config) - .unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise()) + .unwrap_or_else(|err| llvm_err(sess.diagnostic(), err).raise()) } pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { @@ -117,7 +119,7 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut ll tcx.backend_optimization_level(()), tcx.global_backend_features(()), )(config) - .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise()) + .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), err).raise()) } pub fn to_llvm_opt_settings( @@ -240,9 +242,7 @@ pub fn target_machine_factory( ) }; - tm.ok_or_else(|| { - format!("Could not create LLVM TargetMachine for triple: {}", triple.to_str().unwrap()) - }) + tm.ok_or_else(|| LlvmError::CreateTargetMachine { triple: triple.clone() }) }) } @@ -355,25 +355,28 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void }; if enabled { - diag_handler.note_without_error(&format!( - "{}:{}:{}: {}: {}", - opt.filename, opt.line, opt.column, opt.pass_name, opt.message, - )); + diag_handler.emit_note(FromLlvmOptimizationDiag { + filename: &opt.filename, + line: opt.line, + column: opt.column, + pass_name: &opt.pass_name, + message: &opt.message, + }); } } llvm::diagnostic::PGO(diagnostic_ref) | llvm::diagnostic::Linker(diagnostic_ref) => { - let msg = llvm::build_string(|s| { + let message = llvm::build_string(|s| { llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) }) .expect("non-UTF8 diagnostic"); - diag_handler.warn(&msg); + diag_handler.emit_warning(FromLlvmDiag { message }); } llvm::diagnostic::Unsupported(diagnostic_ref) => { - let msg = llvm::build_string(|s| { + let message = llvm::build_string(|s| { llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) }) .expect("non-UTF8 diagnostic"); - diag_handler.err(&msg); + diag_handler.emit_err(FromLlvmDiag { message }); } llvm::diagnostic::UnknownDiagnostic(..) => {} } @@ -494,7 +497,7 @@ pub(crate) unsafe fn llvm_optimize( llvm_plugins.as_ptr().cast(), llvm_plugins.len(), ); - result.into_result().map_err(|()| llvm_err(diag_handler, "failed to run LLVM passes")) + result.into_result().map_err(|()| llvm_err(diag_handler, LlvmError::RunLlvmPasses)) } // Unsafe due to LLVM calls. @@ -547,8 +550,7 @@ pub(crate) fn link( let _timer = cgcx.prof.generic_activity_with_arg("LLVM_link_module", &*module.name); let buffer = ModuleBuffer::new(module.module_llvm.llmod()); linker.add(buffer.data()).map_err(|()| { - let msg = format!("failed to serialize module {:?}", module.name); - llvm_err(diag_handler, &msg) + llvm_err(diag_handler, LlvmError::SerializeModule { name: &module.name }) })?; } drop(linker); @@ -626,9 +628,8 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_bitcode", &*module.name); - if let Err(e) = fs::write(&bc_out, data) { - let msg = format!("failed to write bytecode to {}: {}", bc_out.display(), e); - diag_handler.err(&msg); + if let Err(err) = fs::write(&bc_out, data) { + diag_handler.emit_err(WriteBytecode { path: &bc_out, err }); } } @@ -678,10 +679,9 @@ pub(crate) unsafe fn codegen( record_artifact_size(&cgcx.prof, "llvm_ir", &out); } - result.into_result().map_err(|()| { - let msg = format!("failed to write LLVM IR to {}", out.display()); - llvm_err(diag_handler, &msg) - })?; + result + .into_result() + .map_err(|()| llvm_err(diag_handler, LlvmError::WriteIr { path: &out }))?; } if config.emit_asm { @@ -749,8 +749,8 @@ pub(crate) unsafe fn codegen( EmitObj::Bitcode => { debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); - if let Err(e) = link_or_copy(&bc_out, &obj_out) { - diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); + if let Err(err) = link_or_copy(&bc_out, &obj_out) { + diag_handler.emit_err(CopyBitcode { err }); } if !config.emit_bc { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 001d1ce93d8b4..81072edc475c4 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -1,10 +1,11 @@ use std::borrow::Cow; +use std::ffi::CString; +use std::path::Path; -use rustc_errors::fluent; -use rustc_errors::DiagnosticBuilder; -use rustc_errors::ErrorGuaranteed; -use rustc_errors::Handler; -use rustc_errors::IntoDiagnostic; +use rustc_data_structures::small_c_str::SmallCStr; +use rustc_errors::{ + fluent, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, Handler, IntoDiagnostic, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -81,10 +82,18 @@ pub(crate) struct DlltoolFailImportLibrary<'a> { #[note] pub(crate) struct DynamicLinkingWithLTO; -#[derive(Diagnostic)] -#[diag(codegen_llvm_fail_parsing_target_machine_config_to_target_machine)] -pub(crate) struct FailParsingTargetMachineConfigToTargetMachine { - pub error: String, +pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); + +impl<EM: EmissionGuarantee> IntoDiagnostic<'_, EM> for ParseTargetMachineConfig<'_> { + fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, EM> { + let diag: DiagnosticBuilder<'_, EM> = self.0.into_diagnostic(sess); + let (message, _) = diag.styled_message().first().expect("`LlvmError` with no message"); + let message = sess.eagerly_translate_to_string(message.clone(), diag.args()); + + let mut diag = sess.struct_diagnostic(fluent::codegen_llvm_parse_target_machine_config); + diag.set_arg("error", message); + diag + } } pub(crate) struct TargetFeatureDisableOrEnable<'a> { @@ -110,3 +119,99 @@ impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> { diag } } + +#[derive(Diagnostic)] +#[diag(codegen_llvm_lto_disallowed)] +pub(crate) struct LtoDisallowed; + +#[derive(Diagnostic)] +#[diag(codegen_llvm_lto_dylib)] +pub(crate) struct LtoDylib; + +#[derive(Diagnostic)] +#[diag(codegen_llvm_lto_bitcode_from_rlib)] +pub(crate) struct LtoBitcodeFromRlib { + pub llvm_err: String, +} + +#[derive(Diagnostic)] +pub enum LlvmError<'a> { + #[diag(codegen_llvm_write_output)] + WriteOutput { path: &'a Path }, + #[diag(codegen_llvm_target_machine)] + CreateTargetMachine { triple: SmallCStr }, + #[diag(codegen_llvm_run_passes)] + RunLlvmPasses, + #[diag(codegen_llvm_serialize_module)] + SerializeModule { name: &'a str }, + #[diag(codegen_llvm_write_ir)] + WriteIr { path: &'a Path }, + #[diag(codegen_llvm_prepare_thin_lto_context)] + PrepareThinLtoContext, + #[diag(codegen_llvm_load_bitcode)] + LoadBitcode { name: CString }, + #[diag(codegen_llvm_write_thinlto_key)] + WriteThinLtoKey { err: std::io::Error }, + #[diag(codegen_llvm_multiple_source_dicompileunit)] + MultipleSourceDiCompileUnit, + #[diag(codegen_llvm_prepare_thin_lto_module)] + PrepareThinLtoModule, + #[diag(codegen_llvm_parse_bitcode)] + ParseBitcode, +} + +pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String); + +impl<EM: EmissionGuarantee> IntoDiagnostic<'_, EM> for WithLlvmError<'_> { + fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, EM> { + use LlvmError::*; + let msg_with_llvm_err = match &self.0 { + WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err, + CreateTargetMachine { .. } => fluent::codegen_llvm_target_machine_with_llvm_err, + RunLlvmPasses => fluent::codegen_llvm_run_passes_with_llvm_err, + SerializeModule { .. } => fluent::codegen_llvm_serialize_module_with_llvm_err, + WriteIr { .. } => fluent::codegen_llvm_write_ir_with_llvm_err, + PrepareThinLtoContext => fluent::codegen_llvm_prepare_thin_lto_context_with_llvm_err, + LoadBitcode { .. } => fluent::codegen_llvm_load_bitcode_with_llvm_err, + WriteThinLtoKey { .. } => fluent::codegen_llvm_write_thinlto_key_with_llvm_err, + MultipleSourceDiCompileUnit => { + fluent::codegen_llvm_multiple_source_dicompileunit_with_llvm_err + } + PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, + ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, + }; + let mut diag = self.0.into_diagnostic(sess); + diag.set_primary_message(msg_with_llvm_err); + diag.set_arg("llvm_err", self.1); + diag + } +} + +#[derive(Diagnostic)] +#[diag(codegen_llvm_from_llvm_optimization_diag)] +pub(crate) struct FromLlvmOptimizationDiag<'a> { + pub filename: &'a str, + pub line: std::ffi::c_uint, + pub column: std::ffi::c_uint, + pub pass_name: &'a str, + pub message: &'a str, +} + +#[derive(Diagnostic)] +#[diag(codegen_llvm_from_llvm_diag)] +pub(crate) struct FromLlvmDiag { + pub message: String, +} + +#[derive(Diagnostic)] +#[diag(codegen_llvm_write_bytecode)] +pub(crate) struct WriteBytecode<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_llvm_copy_bitcode)] +pub(crate) struct CopyBitcode { + pub err: std::io::Error, +} diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 246e82545c874..111d14b265cde 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,11 +5,12 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(extern_types)] #![feature(hash_raw_entry)] +#![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(extern_types)] +#![feature(never_type)] #![feature(once_cell)] -#![feature(iter_intersperse)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] @@ -22,7 +23,7 @@ extern crate tracing; use back::write::{create_informational_target_machine, create_target_machine}; -use errors::FailParsingTargetMachineConfigToTargetMachine; +use errors::ParseTargetMachineConfig; pub use llvm_util::target_features; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; @@ -169,6 +170,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { type Module = ModuleLlvm; type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = &'static mut llvm::TargetMachine; + type TargetMachineError = crate::errors::LlvmError<'static>; type ThinData = back::lto::ThinData; type ThinBuffer = back::lto::ThinBuffer; fn print_pass_timings(&self) { @@ -416,8 +418,7 @@ impl ModuleLlvm { let tm = match (cgcx.tm_factory)(tm_factory_config) { Ok(m) => m, Err(e) => { - handler.emit_err(FailParsingTargetMachineConfigToTargetMachine { error: e }); - return Err(FatalError); + return Err(handler.emit_almost_fatal(ParseTargetMachineConfig(e))); } }; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 9f1614af7b16c..8508ab87532c2 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -305,8 +305,12 @@ impl TargetMachineFactoryConfig { } pub type TargetMachineFactoryFn<B> = Arc< - dyn Fn(TargetMachineFactoryConfig) -> Result<<B as WriteBackendMethods>::TargetMachine, String> - + Send + dyn Fn( + TargetMachineFactoryConfig, + ) -> Result< + <B as WriteBackendMethods>::TargetMachine, + <B as WriteBackendMethods>::TargetMachineError, + > + Send + Sync, >; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index e0e8ffa89ed15..9826256a4c5d5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -8,6 +8,7 @@ use rustc_middle::dep_graph::WorkProduct; pub trait WriteBackendMethods: 'static + Sized + Clone { type Module: Send + Sync; type TargetMachine; + type TargetMachineError; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index ccefd6adaf14b..02e0b042ad263 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -43,7 +43,6 @@ use rustc_span::source_map::{FileLoader, FileName}; use rustc_span::symbol::sym; use rustc_target::json::ToJson; -use std::borrow::Cow; use std::cmp::max; use std::env; use std::ffi::OsString; @@ -1205,29 +1204,20 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { handler.emit_diagnostic(&mut d); } - let mut xs: Vec<Cow<'static, str>> = vec![ - "the compiler unexpectedly panicked. this is a bug.".into(), - format!("we would appreciate a bug report: {bug_report_url}").into(), - format!( - "rustc {} running on {}", - util::version_str!().unwrap_or("unknown_version"), - config::host_triple() - ) - .into(), - ]; + handler.emit_note(session_diagnostics::Ice); + handler.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + handler.emit_note(session_diagnostics::IceVersion { + version: util::version_str!().unwrap_or("unknown_version"), + triple: config::host_triple(), + }); if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() { - xs.push(format!("compiler flags: {}", flags.join(" ")).into()); - + handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") }); if excluded_cargo_defaults { - xs.push("some of the compiler flags provided by cargo are hidden".into()); + handler.emit_note(session_diagnostics::IceExcludeCargoDefaults); } } - for note in &xs { - handler.note_without_error(note.as_ref()); - } - // If backtraces are enabled, also print the query stack let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); diff --git a/compiler/rustc_driver/src/session_diagnostics.rs b/compiler/rustc_driver/src/session_diagnostics.rs index c1bc10891144c..a7aef9cbc2c8a 100644 --- a/compiler/rustc_driver/src/session_diagnostics.rs +++ b/compiler/rustc_driver/src/session_diagnostics.rs @@ -38,3 +38,30 @@ pub(crate) struct UnprettyDumpFail { pub path: String, pub err: String, } + +#[derive(Diagnostic)] +#[diag(driver_ice)] +pub(crate) struct Ice; + +#[derive(Diagnostic)] +#[diag(driver_ice_bug_report)] +pub(crate) struct IceBugReport<'a> { + pub bug_report_url: &'a str, +} + +#[derive(Diagnostic)] +#[diag(driver_ice_version)] +pub(crate) struct IceVersion<'a> { + pub version: &'a str, + pub triple: &'a str, +} + +#[derive(Diagnostic)] +#[diag(driver_ice_flags)] +pub(crate) struct IceFlags { + pub flags: String, +} + +#[derive(Diagnostic)] +#[diag(driver_ice_exclude_cargo_defaults)] +pub(crate) struct IceExcludeCargoDefaults; diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl index 08ce5172574ac..6101b28ab0cdd 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl @@ -23,7 +23,7 @@ codegen_gcc_invalid_monomorphization_unsupported_element = invalid monomorphization of `{$name}` intrinsic: unsupported {$name} from `{$in_ty}` with element `{$elem_ty}` to `{$ret_ty}` codegen_gcc_invalid_monomorphization_invalid_bitmask = - invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` + invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` codegen_gcc_invalid_monomorphization_simd_shuffle = invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}` diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl index b82c903290b9a..e5df417370bb9 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl @@ -39,5 +39,51 @@ codegen_llvm_dynamic_linking_with_lto = cannot prefer dynamic linking when performing LTO .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO -codegen_llvm_fail_parsing_target_machine_config_to_target_machine = +codegen_llvm_parse_target_machine_config = failed to parse target machine config to target machine: {$error} + +codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs + +codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` + +codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err}) + +codegen_llvm_write_output = could not write output to {$path} +codegen_llvm_write_output_with_llvm_err = could not write output to {$path}: {$llvm_err} + +codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple} +codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err} + +codegen_llvm_run_passes = failed to run LLVM passes +codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err} + +codegen_llvm_serialize_module = failed to serialize module {$name} +codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}: {$llvm_err} + +codegen_llvm_write_ir = failed to write LLVM IR to {$path} +codegen_llvm_write_ir_with_llvm_err = failed to write LLVM IR to {$path}: {$llvm_err} + +codegen_llvm_prepare_thin_lto_context = failed to prepare thin LTO context +codegen_llvm_prepare_thin_lto_context_with_llvm_err = failed to prepare thin LTO context: {$llvm_err} + +codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" +codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} + +codegen_llvm_write_thinlto_key = error while writing ThinLTO key data: {$err} +codegen_llvm_write_thinlto_key_with_llvm_err = error while writing ThinLTO key data: {$err}: {$llvm_err} + +codegen_llvm_multiple_source_dicompileunit = multiple source DICompileUnits found +codegen_llvm_multiple_source_dicompileunit_with_llvm_err = multiple source DICompileUnits found: {$llvm_err} + +codegen_llvm_prepare_thin_lto_module = failed to prepare thin LTO module +codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO module: {$llvm_err} + +codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module +codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err} + +codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name}: {$message} +codegen_llvm_from_llvm_diag = {$message} + +codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} + +codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} 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 c8c7afb5f9196..4924105128db6 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -179,9 +179,9 @@ codegen_ssa_extract_bundled_libs_write_file = failed to write file '{$rlib}': {$ codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}` -codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {error} +codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error} -codegen_ssa_read_file = failed to read file: {message} +codegen_ssa_read_file = failed to read file: {$message} codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_error_messages/locales/en-US/driver.ftl index 8ad198c86c933..79ffc82c6c67d 100644 --- a/compiler/rustc_error_messages/locales/en-US/driver.ftl +++ b/compiler/rustc_error_messages/locales/en-US/driver.ftl @@ -11,3 +11,9 @@ driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version driver_rlink_no_a_file = rlink must be a file driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` + +driver_ice = the compiler unexpectedly panicked. this is a bug. +driver_ice_bug_report = we would appreciate a bug report: {$bug_report_url} +driver_ice_version = rustc {$version} running on {$triple} +driver_ice_flags = compiler flags: {$flags} +driver_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden diff --git a/compiler/rustc_error_messages/locales/en-US/expand.ftl b/compiler/rustc_error_messages/locales/en-US/expand.ftl index df0e8ae5dd8f5..dbd80954382db 100644 --- a/compiler/rustc_error_messages/locales/en-US/expand.ftl +++ b/compiler/rustc_error_messages/locales/en-US/expand.ftl @@ -127,3 +127,5 @@ expand_module_file_not_found = expand_module_multiple_candidates = file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}" .help = delete or rename one of them to remove the ambiguity + +expand_trace_macro = trace_macro diff --git a/compiler/rustc_error_messages/locales/en-US/incremental.ftl b/compiler/rustc_error_messages/locales/en-US/incremental.ftl new file mode 100644 index 0000000000000..4852ee0d9595c --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/incremental.ftl @@ -0,0 +1,118 @@ +incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} + +incremental_missing_depnode = missing `DepNode` variant + +incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected + +incremental_no_path = no path from `{$source}` to `{$target}` + +incremental_ok = OK + +incremental_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified + +incremental_missing_query_depgraph = + found CGU-reuse attribute but `-Zquery-dep-graph` was not specified + +incremental_malformed_cgu_name = + found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case). + +incremental_no_module_named = + no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names} + +incremental_field_associated_value_expected = associated value expected for `{$name}` + +incremental_no_field = no field `{$name}` + +incremental_assertion_auto = + `except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\" + +incremental_undefined_clean_dirty_assertions_item = + clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} + +incremental_undefined_clean_dirty_assertions = + clean/dirty auto-assertions not yet defined for {$kind} + +incremental_repeated_depnode_label = dep-node label `{$label}` is repeated + +incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized + +incremental_not_dirty = `{$dep_node_str}` should be dirty but is not + +incremental_not_clean = `{$dep_node_str}` should be clean but is not + +incremental_not_loaded = `{$dep_node_str}` should have been loaded from disk but it was not + +incremental_unknown_item = unknown item `{$name}` + +incremental_no_cfg = no cfg attribute + +incremental_associated_value_expected_for = associated value expected for `{$ident}` + +incremental_associated_value_expected = expected an associated value + +incremental_unchecked_clean = found unchecked `#[rustc_clean]` attribute + +incremental_delete_old = unable to delete old {$name} at `{$path}`: {$err} + +incremental_create_new = failed to create {$name} at `{$path}`: {$err} + +incremental_write_new = failed to write {$name} to `{$path}`: {$err} + +incremental_canonicalize_path = incremental compilation: error canonicalizing path `{$path}`: {$err} + +incremental_create_incr_comp_dir = + could not create incremental compilation {$tag} directory `{$path}`: {$err} + +incremental_create_lock = + incremental compilation: could not create session directory lock file: {$lock_err} +incremental_lock_unsupported = + the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation +incremental_cargo_help_1 = + incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental) +incremental_cargo_help_2 = + the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir) + +incremental_delete_lock = + error deleting lock file for incremental compilation session directory `{$path}`: {$err} + +incremental_hard_link_failed = + hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}` + +incremental_delete_partial = failed to delete partly initialized session dir `{$path}`: {$err} + +incremental_delete_full = error deleting incremental compilation session directory `{$path}`: {$err} + +incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err} + +incremental_invalid_gc_failed = + failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err} + +incremental_finalized_gc_failed = + failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err} + +incremental_session_gc_failed = + failed to garbage collect incremental compilation session directory `{$path}`: {$err} + +incremental_assert_not_loaded = + we asserted that the incremental cache should not be loaded, but it was loaded + +incremental_assert_loaded = + we asserted that an existing incremental cache directory should be successfully loaded, but it was not + +incremental_delete_incompatible = + failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err} + +incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err} + +incremental_decode_incr_cache = could not decode incremental cache: {$err} + +incremental_write_dep_graph = failed to write dependency graph to `{$path}`: {$err} + +incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err} + +incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} + +incremental_copy_workproduct_to_cache = + error copying object file `{$from}` to incremental directory as `{$to}`: {$err} + +incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err} diff --git a/compiler/rustc_error_messages/locales/en-US/interface.ftl b/compiler/rustc_error_messages/locales/en-US/interface.ftl index 688b044722260..a7bc0e7af1fe9 100644 --- a/compiler/rustc_error_messages/locales/en-US/interface.ftl +++ b/compiler/rustc_error_messages/locales/en-US/interface.ftl @@ -44,3 +44,13 @@ interface_failed_writing_file = interface_proc_macro_crate_panic_abort = building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic + +interface_unsupported_crate_type_for_target = + dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` + +interface_multiple_output_types_adaption = + due to multiple output types requested, the explicitly specified output file name will be adapted for each output type + +interface_ignoring_extra_filename = ignoring -C extra-filename flag due to -o flag + +interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag diff --git a/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl b/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl index 243d10bfa0621..6cea6a603f336 100644 --- a/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl +++ b/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl @@ -24,3 +24,9 @@ monomorphize_large_assignments = monomorphize_couldnt_dump_mono_stats = unexpected error occurred while dumping monomorphization stats: {$error} + +monomorphize_encountered_error_while_instantiating = + the above error was encountered while instantiating `{$formatted_item}` + +monomorphize_unknown_cgu_collection_mode = + unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 0c2ab3d08f9de..6ebb188288f00 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -731,3 +731,5 @@ passes_proc_macro_missing_args = mismatched {$kind} signature passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"` passes_proc_macro_unsafe = proc macro functions may not be `unsafe` + +passes_skipping_const_checks = skipping const checks diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index bc37d91a7c6af..5984c201af0d0 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -89,3 +89,5 @@ session_int_literal_too_large = integer literal is too large session_invalid_int_literal_width = invalid width `{$width}` for integer literal .help = valid widths are 8, 16, 32, 64 and 128 + +session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index f053bdc3809be..1882d4b698e61 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -52,6 +52,7 @@ fluent_messages! { expand => "../locales/en-US/expand.ftl", hir_analysis => "../locales/en-US/hir_analysis.ftl", hir_typeck => "../locales/en-US/hir_typeck.ftl", + incremental => "../locales/en-US/incremental.ftl", infer => "../locales/en-US/infer.ftl", interface => "../locales/en-US/interface.ftl", lint => "../locales/en-US/lint.ftl", diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index cbfee582d871f..c9d662ad43fe5 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -408,6 +408,59 @@ impl EmissionGuarantee for ! { } } +impl<'a> DiagnosticBuilder<'a, rustc_span::fatal_error::FatalError> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + #[track_caller] + pub(crate) fn new_almost_fatal( + handler: &'a Handler, + message: impl Into<DiagnosticMessage>, + ) -> Self { + let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); + Self::new_diagnostic_almost_fatal(handler, diagnostic) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + pub(crate) fn new_diagnostic_almost_fatal( + handler: &'a Handler, + diagnostic: Diagnostic, + ) -> Self { + debug!("Created new diagnostic"); + Self { + inner: DiagnosticBuilderInner { + state: DiagnosticBuilderState::Emittable(handler), + diagnostic: Box::new(diagnostic), + }, + _marker: PhantomData, + } + } +} + +impl EmissionGuarantee for rustc_span::fatal_error::FatalError { + fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { + match db.inner.state { + // First `.emit()` call, the `&Handler` is still available. + DiagnosticBuilderState::Emittable(handler) => { + db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + + handler.emit_diagnostic(&mut db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + // Then fatally error.. + rustc_span::fatal_error::FatalError + } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new_almost_fatal(handler, msg) + } +} + /// In general, the `DiagnosticBuilder` uses deref to allow access to /// the fields and methods of the embedded `diagnostic` in a /// transparent way. *However,* many of the methods are intended to diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index dad5e98aac021..7a94ce3777a29 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -36,6 +36,12 @@ impl<'a, T: fmt::Display> From<&'a T> for DiagnosticArgFromDisplay<'a> { } } +impl<'a, T: Clone + IntoDiagnosticArg> IntoDiagnosticArg for &'a T { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.clone().into_diagnostic_arg() + } +} + macro_rules! into_diagnostic_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( @@ -153,12 +159,6 @@ impl IntoDiagnosticArg for ast::Path { } } -impl IntoDiagnosticArg for &ast::Path { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(self))) - } -} - impl IntoDiagnosticArg for ast::token::Token { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(pprust::token_to_string(&self)) @@ -177,6 +177,18 @@ impl IntoDiagnosticArg for type_ir::FloatTy { } } +impl IntoDiagnosticArg for std::ffi::CString { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + +impl IntoDiagnosticArg for rustc_data_structures::small_c_str::SmallCStr { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + impl IntoDiagnosticArg for Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 535812fb0e228..2903bdb7aad0d 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -616,22 +616,24 @@ impl Handler { } } - /// Translate `message` eagerly with `args`. + /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`. pub fn eagerly_translate<'a>( &self, message: DiagnosticMessage, args: impl Iterator<Item = DiagnosticArg<'a, 'static>>, ) -> SubdiagnosticMessage { + SubdiagnosticMessage::Eager(self.eagerly_translate_to_string(message, args)) + } + + /// Translate `message` eagerly with `args` to `String`. + pub fn eagerly_translate_to_string<'a>( + &self, + message: DiagnosticMessage, + args: impl Iterator<Item = DiagnosticArg<'a, 'static>>, + ) -> String { let inner = self.inner.borrow(); let args = crate::translation::to_fluent_args(args); - SubdiagnosticMessage::Eager( - inner - .emitter - .translate_message(&message, &args) - .map_err(Report::new) - .unwrap() - .to_string(), - ) + inner.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string() } // This is here to not allow mutation of flags; @@ -1009,6 +1011,7 @@ impl Handler { } #[track_caller] + #[rustc_lint_diagnostics] pub fn span_note_without_error( &self, span: impl Into<MultiSpan>, @@ -1018,6 +1021,7 @@ impl Handler { } #[track_caller] + #[rustc_lint_diagnostics] pub fn span_note_diag( &self, span: Span, @@ -1029,19 +1033,23 @@ impl Handler { } // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread + #[rustc_lint_diagnostics] pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError { self.inner.borrow_mut().fatal(msg) } + #[rustc_lint_diagnostics] pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.inner.borrow_mut().err(msg) } + #[rustc_lint_diagnostics] pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { let mut db = DiagnosticBuilder::new(self, Warning(None), msg); db.emit(); } + #[rustc_lint_diagnostics] pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { DiagnosticBuilder::new(self, Note, msg).emit(); } @@ -1058,6 +1066,7 @@ impl Handler { pub fn has_errors(&self) -> Option<ErrorGuaranteed> { if self.inner.borrow().has_errors() { Some(ErrorGuaranteed(())) } else { None } } + pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> { if self.inner.borrow().has_errors_or_lint_errors() { Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) @@ -1131,6 +1140,20 @@ impl Handler { self.create_warning(warning).emit() } + pub fn create_almost_fatal<'a>( + &'a self, + fatal: impl IntoDiagnostic<'a, FatalError>, + ) -> DiagnosticBuilder<'a, FatalError> { + fatal.into_diagnostic(self) + } + + pub fn emit_almost_fatal<'a>( + &'a self, + fatal: impl IntoDiagnostic<'a, FatalError>, + ) -> FatalError { + self.create_almost_fatal(fatal).emit() + } + pub fn create_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, !>, @@ -1156,6 +1179,17 @@ impl Handler { self.create_bug(bug).emit() } + pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted { + self.create_note(note).emit() + } + + pub fn create_note<'a>( + &'a self, + note: impl IntoDiagnostic<'a, Noted>, + ) -> DiagnosticBuilder<'a, Noted> { + note.into_diagnostic(self) + } + fn emit_diag_at_span( &self, mut diag: Diagnostic, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 951d59246785d..5a48473d5b07c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -4,7 +4,7 @@ use crate::errors::{ ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord, AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid, MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord, - ResolveRelativePath, TakesNoArguments, + ResolveRelativePath, TakesNoArguments, TraceMacro, }; use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirOwnership; @@ -1142,8 +1142,8 @@ impl<'a> ExtCtxt<'a> { self.sess.parse_sess.span_diagnostic.span_bug(sp, msg); } pub fn trace_macros_diag(&mut self) { - for (sp, notes) in self.expansions.iter() { - let mut db = self.sess.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro"); + for (span, notes) in self.expansions.iter() { + let mut db = self.sess.parse_sess.create_note(TraceMacro { span: *span }); for note in notes { db.note(note); } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index afe5169d3f5c0..9b9697ab13d26 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -368,3 +368,10 @@ pub(crate) struct ModuleMultipleCandidates { pub default_path: String, pub secondary_path: String, } + +#[derive(Diagnostic)] +#[diag(expand_trace_macro)] +pub struct TraceMacro { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 27284f8b983b6..caf26a75d3cc4 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -27,7 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; @@ -37,7 +37,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT use rustc_span::edition::Edition; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::astconv_object_safety_violations; @@ -54,7 +54,7 @@ use std::slice; pub struct PathSeg(pub DefId, pub usize); pub trait AstConv<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + fn tcx(&self) -> TyCtxt<'tcx>; fn item_def_id(&self) -> DefId; @@ -131,6 +131,8 @@ pub trait AstConv<'tcx> { { self } + + fn infcx(&self) -> Option<&InferCtxt<'tcx>>; } #[derive(Debug)] @@ -2132,48 +2134,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) .emit() // Already reported in an earlier stage. } else { - // Find all the `impl`s that `qself_ty` has for any trait that has the - // associated type, so that we suggest the right one. - let infcx = tcx.infer_ctxt().build(); - // We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()` - // to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`. - let param_env = ty::ParamEnv::empty(); - let traits: Vec<_> = self - .tcx() - .all_traits() - .filter(|trait_def_id| { - // Consider only traits with the associated type - tcx.associated_items(*trait_def_id) - .in_definition_order() - .any(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - && matches!(i.kind, ty::AssocKind::Type) - }) - // Consider only accessible traits - && tcx.visibility(*trait_def_id) - .is_accessible_from(self.item_def_id(), tcx) - && tcx.all_impls(*trait_def_id) - .any(|impl_def_id| { - let trait_ref = tcx.impl_trait_ref(impl_def_id); - trait_ref.map_or(false, |trait_ref| { - let impl_ = trait_ref.subst( - tcx, - infcx.fresh_substs_for_item(span, impl_def_id), - ); - infcx - .can_eq( - param_env, - tcx.erase_regions(impl_.self_ty()), - tcx.erase_regions(qself_ty), - ) - .is_ok() - }) - && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative - }) - }) - .map(|trait_def_id| tcx.def_path_str(trait_def_id)) - .collect(); + let traits: Vec<_> = + self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); // Don't print `TyErr` to the user. self.report_ambiguous_associated_type( @@ -2232,6 +2194,60 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok((ty, DefKind::AssocTy, assoc_ty_did)) } + fn probe_traits_that_match_assoc_ty( + &self, + qself_ty: Ty<'tcx>, + assoc_ident: Ident, + ) -> Vec<String> { + let tcx = self.tcx(); + + // In contexts that have no inference context, just make a new one. + // We do need a local variable to store it, though. + let infcx_; + let infcx = if let Some(infcx) = self.infcx() { + infcx + } else { + assert!(!qself_ty.needs_infer()); + infcx_ = tcx.infer_ctxt().build(); + &infcx_ + }; + + tcx.all_traits() + .filter(|trait_def_id| { + // Consider only traits with the associated type + tcx.associated_items(*trait_def_id) + .in_definition_order() + .any(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident + && matches!(i.kind, ty::AssocKind::Type) + }) + // Consider only accessible traits + && tcx.visibility(*trait_def_id) + .is_accessible_from(self.item_def_id(), tcx) + && tcx.all_impls(*trait_def_id) + .any(|impl_def_id| { + let trait_ref = tcx.impl_trait_ref(impl_def_id); + trait_ref.map_or(false, |trait_ref| { + let impl_ = trait_ref.subst( + tcx, + infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id), + ); + infcx + .can_eq( + ty::ParamEnv::empty(), + tcx.erase_regions(impl_.self_ty()), + tcx.erase_regions(qself_ty), + ) + .is_ok() + }) + && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative + }) + }) + .map(|trait_def_id| tcx.def_path_str(trait_def_id)) + .collect() + } + fn lookup_assoc_ty( &self, ident: Ident, diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b73a05ff39846..f5a1e51c07b2f 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -25,7 +25,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericParamKind, Node}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; @@ -517,6 +517,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { // There's no place to record types from signatures? } + + fn infcx(&self) -> Option<&InferCtxt<'tcx>> { + None + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 8724e69cc5134..4940015ddd571 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -324,6 +324,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty }; self.write_ty(hir_id, ty) } + + fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { + Some(&self.infcx) + } } /// Represents a user-provided type in the raw form (never normalized). diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 33a9a0cabb9d5..8e9be8a63122f 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -33,6 +33,7 @@ //! fn baz() { foo(); } //! ``` +use crate::errors; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING}; @@ -133,12 +134,10 @@ impl<'tcx> IfThisChanged<'tcx> { Some(n) => { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); - } + Err(()) => self.tcx.sess.emit_fatal(errors::UnrecognizedDepNode { + span: attr.span, + name: n, + }), } } }; @@ -149,16 +148,14 @@ impl<'tcx> IfThisChanged<'tcx> { Some(n) => { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); - } + Err(()) => self.tcx.sess.emit_fatal(errors::UnrecognizedDepNode { + span: attr.span, + name: n, + }), } } None => { - self.tcx.sess.span_fatal(attr.span, "missing DepNode variant"); + self.tcx.sess.emit_fatal(errors::MissingDepNode { span: attr.span }); } }; self.then_this_would_need.push(( @@ -204,7 +201,7 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou // Return early here so as not to construct the query, which is not cheap. if if_this_changed.is_empty() { for &(target_span, _, _, _) in then_this_would_need { - tcx.sess.span_err(target_span, "no `#[rustc_if_this_changed]` annotation detected"); + tcx.sess.emit_err(errors::MissingIfThisChanged { span: target_span }); } return; } @@ -213,16 +210,13 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou let dependents = query.transitive_predecessors(source_dep_node); for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { if !dependents.contains(&target_dep_node) { - tcx.sess.span_err( - target_span, - &format!( - "no path from `{}` to `{}`", - tcx.def_path_str(source_def_id), - target_pass - ), - ); + tcx.sess.emit_err(errors::NoPath { + span: target_span, + source: tcx.def_path_str(source_def_id), + target: *target_pass, + }); } else { - tcx.sess.span_err(target_span, "OK"); + tcx.sess.emit_err(errors::Ok { span: target_span }); } } } diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 89d419bc8e90f..2968a0e1203a9 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -22,6 +22,7 @@ //! allows for doing a more fine-grained check to see if pre- or post-lto data //! was re-used. +use crate::errors; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LOCAL_CRATE; @@ -66,10 +67,9 @@ impl<'tcx> AssertModuleSource<'tcx> { sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact), sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast), other => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unknown cgu-reuse-kind `{}` specified", other), - ); + self.tcx + .sess + .emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other }); } } } else { @@ -77,10 +77,7 @@ impl<'tcx> AssertModuleSource<'tcx> { }; if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.tcx.sess.span_fatal( - attr.span, - "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified", - ); + self.tcx.sess.emit_fatal(errors::MissingQueryDepGraph { span: attr.span }); } if !self.check_config(attr) { @@ -92,13 +89,11 @@ impl<'tcx> AssertModuleSource<'tcx> { let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string(); if !user_path.starts_with(&crate_name) { - let msg = format!( - "Found malformed codegen unit name `{}`. \ - Codegen units names must always start with the name of the \ - crate (`{}` in this case).", - user_path, crate_name - ); - self.tcx.sess.span_fatal(attr.span, &msg); + self.tcx.sess.emit_fatal(errors::MalformedCguName { + span: attr.span, + user_path, + crate_name, + }); } // Split of the "special suffix" if there is one. @@ -125,15 +120,12 @@ impl<'tcx> AssertModuleSource<'tcx> { let mut cgu_names: Vec<&str> = self.available_cgus.iter().map(|cgu| cgu.as_str()).collect(); cgu_names.sort(); - self.tcx.sess.span_err( - attr.span, - &format!( - "no module named `{}` (mangled: {}). Available modules: {}", - user_path, - cgu_name, - cgu_names.join(", ") - ), - ); + self.tcx.sess.emit_err(errors::NoModuleNamed { + span: attr.span, + user_path, + cgu_name, + cgu_names: cgu_names.join(", "), + }); } self.tcx.sess.cgu_reuse_tracker.set_expectation( @@ -151,15 +143,15 @@ impl<'tcx> AssertModuleSource<'tcx> { if let Some(value) = item.value_str() { return value; } else { - self.tcx.sess.span_fatal( - item.span(), - &format!("associated value expected for `{}`", name), - ); + self.tcx.sess.emit_fatal(errors::FieldAssociatedValueExpected { + span: item.span(), + name, + }); } } } - self.tcx.sess.span_fatal(attr.span, &format!("no field `{}`", name)); + self.tcx.sess.emit_fatal(errors::NoField { span: attr.span, name }); } /// Scan for a `cfg="foo"` attribute and check whether we have a diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs new file mode 100644 index 0000000000000..deb8767836543 --- /dev/null +++ b/compiler/rustc_incremental/src/errors.rs @@ -0,0 +1,364 @@ +use rustc_macros::Diagnostic; +use rustc_span::{symbol::Ident, Span, Symbol}; +use std::path::{Path, PathBuf}; + +#[derive(Diagnostic)] +#[diag(incremental_unrecognized_depnode)] +pub struct UnrecognizedDepNode { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(incremental_missing_depnode)] +pub struct MissingDepNode { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_missing_if_this_changed)] +pub struct MissingIfThisChanged { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_ok)] +pub struct Ok { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_no_path)] +pub struct NoPath { + #[primary_span] + pub span: Span, + pub target: Symbol, + pub source: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_unknown_reuse_kind)] +pub struct UnknownReuseKind { + #[primary_span] + pub span: Span, + pub kind: Symbol, +} + +#[derive(Diagnostic)] +#[diag(incremental_missing_query_depgraph)] +pub struct MissingQueryDepGraph { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_malformed_cgu_name)] +pub struct MalformedCguName { + #[primary_span] + pub span: Span, + pub user_path: String, + pub crate_name: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_no_module_named)] +pub struct NoModuleNamed<'a> { + #[primary_span] + pub span: Span, + pub user_path: &'a str, + pub cgu_name: Symbol, + pub cgu_names: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_field_associated_value_expected)] +pub struct FieldAssociatedValueExpected { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(incremental_no_field)] +pub struct NoField { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(incremental_assertion_auto)] +pub struct AssertionAuto<'a> { + #[primary_span] + pub span: Span, + pub name: &'a str, + pub e: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_undefined_clean_dirty_assertions_item)] +pub struct UndefinedCleanDirtyItem { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_undefined_clean_dirty_assertions)] +pub struct UndefinedCleanDirty { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_repeated_depnode_label)] +pub struct RepeatedDepNodeLabel<'a> { + #[primary_span] + pub span: Span, + pub label: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_unrecognized_depnode_label)] +pub struct UnrecognizedDepNodeLabel<'a> { + #[primary_span] + pub span: Span, + pub label: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_not_dirty)] +pub struct NotDirty<'a> { + #[primary_span] + pub span: Span, + pub dep_node_str: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_not_clean)] +pub struct NotClean<'a> { + #[primary_span] + pub span: Span, + pub dep_node_str: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_not_loaded)] +pub struct NotLoaded<'a> { + #[primary_span] + pub span: Span, + pub dep_node_str: &'a str, +} + +#[derive(Diagnostic)] +#[diag(incremental_unknown_item)] +pub struct UnknownItem { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(incremental_no_cfg)] +pub struct NoCfg { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_associated_value_expected_for)] +pub struct AssociatedValueExpectedFor { + #[primary_span] + pub span: Span, + pub ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(incremental_associated_value_expected)] +pub struct AssociatedValueExpected { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_unchecked_clean)] +pub struct UncheckedClean { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(incremental_delete_old)] +pub struct DeleteOld<'a> { + pub name: &'a str, + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_create_new)] +pub struct CreateNew<'a> { + pub name: &'a str, + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_write_new)] +pub struct WriteNew<'a> { + pub name: &'a str, + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_canonicalize_path)] +pub struct CanonicalizePath { + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_create_incr_comp_dir)] +pub struct CreateIncrCompDir<'a> { + pub tag: &'a str, + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_create_lock)] +pub struct CreateLock<'a> { + pub lock_err: std::io::Error, + pub session_dir: &'a Path, + #[note(incremental_lock_unsupported)] + pub is_unsupported_lock: Option<()>, + #[help(incremental_cargo_help_1)] + #[help(incremental_cargo_help_2)] + pub is_cargo: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(incremental_delete_lock)] +pub struct DeleteLock<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_hard_link_failed)] +pub struct HardLinkFailed<'a> { + pub path: &'a Path, +} + +#[derive(Diagnostic)] +#[diag(incremental_delete_partial)] +pub struct DeletePartial<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_delete_full)] +pub struct DeleteFull<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_finalize)] +pub struct Finalize<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_invalid_gc_failed)] +pub struct InvalidGcFailed<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_finalized_gc_failed)] +pub struct FinalizedGcFailed<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_session_gc_failed)] +pub struct SessionGcFailed<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_assert_not_loaded)] +pub struct AssertNotLoaded; + +#[derive(Diagnostic)] +#[diag(incremental_assert_loaded)] +pub struct AssertLoaded; + +#[derive(Diagnostic)] +#[diag(incremental_delete_incompatible)] +pub struct DeleteIncompatible { + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_load_dep_graph)] +pub struct LoadDepGraph { + pub path: PathBuf, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_decode_incr_cache)] +pub struct DecodeIncrCache { + pub err: String, +} + +#[derive(Diagnostic)] +#[diag(incremental_write_dep_graph)] +pub struct WriteDepGraph<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_move_dep_graph)] +pub struct MoveDepGraph<'a> { + pub from: &'a Path, + pub to: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_create_dep_graph)] +pub struct CreateDepGraph<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_copy_workproduct_to_cache)] +pub struct CopyWorkProductToCache<'a> { + pub from: &'a Path, + pub to: &'a Path, + pub err: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(incremental_delete_workproduct)] +pub struct DeleteWorkProduct<'a> { + pub path: &'a Path, + pub err: std::io::Error, +} diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 83dd9a67e61bc..3c58cfa38f280 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -2,8 +2,11 @@ #![deny(missing_docs)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(never_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_middle; @@ -12,6 +15,7 @@ extern crate tracing; mod assert_dep_graph; pub mod assert_module_sources; +mod errors; mod persist; use assert_dep_graph::assert_dep_graph; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index ed7b272b13d17..c6e63998c7935 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -19,6 +19,7 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. +use crate::errors; use rustc_ast::{self as ast, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; @@ -196,11 +197,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let loaded_from_disk = self.loaded_from_disk(attr); for e in except.iter() { if !auto.remove(e) { - let msg = format!( - "`except` specified DepNodes that can not be affected for \"{}\": \"{}\"", - name, e - ); - self.tcx.sess.span_fatal(attr.span, &msg); + self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } } Assertion { clean: auto, dirty: except, loaded_from_disk } @@ -282,14 +279,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { // An implementation, eg `impl<A> Trait for Foo { .. }` HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), - _ => self.tcx.sess.span_fatal( - attr.span, - &format!( - "clean/dirty auto-assertions not yet defined \ - for Node::Item.node={:?}", - item.kind - ), - ), + _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirtyItem { + span: attr.span, + kind: format!("{:?}", item.kind), + }), } } HirNode::TraitItem(item) => match item.kind { @@ -302,10 +295,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), }, - _ => self.tcx.sess.span_fatal( - attr.span, - &format!("clean/dirty auto-assertions not yet defined for {:?}", node), - ), + _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirty { + span: attr.span, + kind: format!("{:?}", node), + }), }; let labels = Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); @@ -318,16 +311,15 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let label = label.trim(); if DepNode::has_label_string(label) { if out.contains(label) { - self.tcx.sess.span_fatal( - item.span(), - &format!("dep-node label `{}` is repeated", label), - ); + self.tcx + .sess + .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label }); } out.insert(label.to_string()); } else { self.tcx .sess - .span_fatal(item.span(), &format!("dep-node label `{}` not recognized", label)); + .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label }); } } out @@ -348,7 +340,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .sess - .span_err(item_span, &format!("`{}` should be dirty but is not", dep_node_str)); + .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str }); } } @@ -359,7 +351,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .sess - .span_err(item_span, &format!("`{}` should be clean but is not", dep_node_str)); + .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str }); } } @@ -368,10 +360,9 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) { let dep_node_str = self.dep_node_str(&dep_node); - self.tcx.sess.span_err( - item_span, - &format!("`{}` should have been loaded from disk but it was not", dep_node_str), - ); + self.tcx + .sess + .emit_err(errors::NotLoaded { span: item_span, dep_node_str: &dep_node_str }); } } @@ -412,12 +403,12 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.sess.span_err(attr.span, &format!("unknown item `{}`", item.name_or_empty())); + tcx.sess.emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() }); } } match cfg { - None => tcx.sess.span_fatal(attr.span, "no cfg attribute"), + None => tcx.sess.emit_fatal(errors::NoCfg { span: attr.span }), Some(c) => c, } } @@ -426,13 +417,11 @@ fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol { if let Some(value) = item.value_str() { value } else { - let msg = if let Some(ident) = item.ident() { - format!("associated value expected for `{}`", ident) + if let Some(ident) = item.ident() { + tcx.sess.emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident }); } else { - "expected an associated value".to_string() - }; - - tcx.sess.span_fatal(item.span(), &msg); + tcx.sess.emit_fatal(errors::AssociatedValueExpected { span: item.span() }); + } } } @@ -456,7 +445,7 @@ impl<'tcx> FindAllAttrs<'tcx> { fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) { for attr in &self.found_attrs { if !checked_attrs.contains(&attr.id) { - self.tcx.sess.span_err(attr.span, "found unchecked `#[rustc_clean]` attribute"); + self.tcx.sess.emit_err(errors::UncheckedClean { span: attr.span }); checked_attrs.insert(attr.id); } } diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 2dbd4b6bce85a..dc981c6179eeb 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -9,15 +9,15 @@ //! compiler versions don't change frequently for the typical user, being //! conservative here practically has no downside. -use std::env; -use std::fs; -use std::io::{self, Read}; -use std::path::{Path, PathBuf}; - +use crate::errors; use rustc_data_structures::memmap::Mmap; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::Encoder; use rustc_session::Session; +use std::env; +use std::fs; +use std::io::{self, Read}; +use std::path::{Path, PathBuf}; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; @@ -60,12 +60,7 @@ where } Err(err) if err.kind() == io::ErrorKind::NotFound => (), Err(err) => { - sess.err(&format!( - "unable to delete old {} at `{}`: {}", - name, - path_buf.display(), - err - )); + sess.emit_err(errors::DeleteOld { name, path: path_buf, err }); return; } } @@ -73,7 +68,7 @@ where let mut encoder = match FileEncoder::new(&path_buf) { Ok(encoder) => encoder, Err(err) => { - sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err)); + sess.emit_err(errors::CreateNew { name, path: path_buf, err }); return; } }; @@ -90,7 +85,7 @@ where debug!("save: data written to disk successfully"); } Err(err) => { - sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err)); + sess.emit_err(errors::WriteNew { name, path: path_buf, err }); } } } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 1fd2b9b0d7b7b..73d7e3becab48 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -103,6 +103,7 @@ //! unsupported file system and emit a warning in that case. This is not yet //! implemented. +use crate::errors; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::{base_n, flock}; @@ -225,12 +226,7 @@ pub fn prepare_session_directory( let crate_dir = match crate_dir.canonicalize() { Ok(v) => v, Err(err) => { - let reported = sess.err(&format!( - "incremental compilation: error canonicalizing path `{}`: {}", - crate_dir.display(), - err - )); - return Err(reported); + return Err(sess.emit_err(errors::CanonicalizePath { path: crate_dir, err })); } }; @@ -273,14 +269,7 @@ pub fn prepare_session_directory( debug!("successfully copied data from: {}", source_directory.display()); if !allows_links { - sess.warn(&format!( - "Hard linking files in the incremental \ - compilation cache failed. Copying files \ - instead. Consider moving the cache \ - directory to a file system which supports \ - hard linking in session dir `{}`", - session_dir.display() - )); + sess.emit_warning(errors::HardLinkFailed { path: &session_dir }); } sess.init_incr_comp_session(session_dir, directory_lock, true); @@ -295,12 +284,7 @@ pub fn prepare_session_directory( // Try to remove the session directory we just allocated. We don't // know if there's any garbage in it from the failed copy action. if let Err(err) = safe_remove_dir_all(&session_dir) { - sess.warn(&format!( - "Failed to delete partly initialized \ - session dir `{}`: {}", - session_dir.display(), - err - )); + sess.emit_warning(errors::DeletePartial { path: &session_dir, err }); } delete_session_dir_lock_file(sess, &lock_file_path); @@ -332,12 +316,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Svh) { ); if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) { - sess.warn(&format!( - "Error deleting incremental compilation \ - session directory `{}`: {}", - incr_comp_session_dir.display(), - err - )); + sess.emit_warning(errors::DeleteFull { path: &incr_comp_session_dir, err }); } let lock_file_path = lock_file_path(&*incr_comp_session_dir); @@ -380,12 +359,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Svh) { } Err(e) => { // Warn about the error. However, no need to abort compilation now. - sess.warn(&format!( - "Error finalizing incremental compilation \ - session directory `{}`: {}", - incr_comp_session_dir.display(), - e - )); + sess.emit_warning(errors::Finalize { path: &incr_comp_session_dir, err: e }); debug!("finalize_session_directory() - error, marking as invalid"); // Drop the file lock, so we can garage collect @@ -488,16 +462,7 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGua debug!("{} directory created successfully", dir_tag); Ok(()) } - Err(err) => { - let reported = sess.err(&format!( - "Could not create incremental compilation {} \ - directory `{}`: {}", - dir_tag, - path.display(), - err - )); - Err(reported) - } + Err(err) => Err(sess.emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })), } } @@ -518,46 +483,20 @@ fn lock_directory( // the lock should be exclusive Ok(lock) => Ok((lock, lock_file_path)), Err(lock_err) => { - let mut err = sess.struct_err(&format!( - "incremental compilation: could not create \ - session directory lock file: {}", - lock_err - )); - if flock::Lock::error_unsupported(&lock_err) { - err.note(&format!( - "the filesystem for the incremental path at {} \ - does not appear to support locking, consider changing the \ - incremental path to a filesystem that supports locking \ - or disable incremental compilation", - session_dir.display() - )); - if std::env::var_os("CARGO").is_some() { - err.help( - "incremental compilation can be disabled by setting the \ - environment variable CARGO_INCREMENTAL=0 (see \ - https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)", - ); - err.help( - "the entire build directory can be changed to a different \ - filesystem by setting the environment variable CARGO_TARGET_DIR \ - to a different path (see \ - https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)", - ); - } - } - Err(err.emit()) + let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err).then_some(()); + Err(sess.emit_err(errors::CreateLock { + lock_err, + session_dir, + is_unsupported_lock, + is_cargo: std::env::var_os("CARGO").map(|_| ()), + })) } } } fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) { if let Err(err) = safe_remove_file(&lock_file_path) { - sess.warn(&format!( - "Error deleting lock file for incremental \ - compilation session directory `{}`: {}", - lock_file_path.display(), - err - )); + sess.emit_warning(errors::DeleteLock { path: lock_file_path, err }); } } @@ -774,12 +713,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { if !lock_file_to_session_dir.values().any(|dir| *dir == directory_name) { let path = crate_directory.join(directory_name); if let Err(err) = safe_remove_dir_all(&path) { - sess.warn(&format!( - "Failed to garbage collect invalid incremental \ - compilation session directory `{}`: {}", - path.display(), - err - )); + sess.emit_warning(errors::InvalidGcFailed { path: &path, err }); } } } @@ -885,12 +819,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = safe_remove_dir_all(&path) { - sess.warn(&format!( - "Failed to garbage collect finalized incremental \ - compilation session directory `{}`: {}", - path.display(), - err - )); + sess.emit_warning(errors::FinalizedGcFailed { path: &path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(&path)); } @@ -907,11 +836,7 @@ fn delete_old(sess: &Session, path: &Path) { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = safe_remove_dir_all(&path) { - sess.warn(&format!( - "Failed to garbage collect incremental compilation session directory `{}`: {}", - path.display(), - err - )); + sess.emit_warning(errors::SessionGcFailed { path: &path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(&path)); } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 1c5fd91690230..d5097065dda2e 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -1,5 +1,6 @@ //! Code to save/load the dep-graph from files. +use crate::errors; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId}; @@ -8,7 +9,7 @@ use rustc_serialize::opaque::MemDecoder; use rustc_serialize::Decodable; use rustc_session::config::IncrementalStateAssertion; use rustc_session::Session; -use std::path::Path; +use std::path::{Path, PathBuf}; use super::data::*; use super::file_format; @@ -27,11 +28,10 @@ pub enum LoadResult<T> { }, /// The file either didn't exist or was produced by an incompatible compiler version. DataOutOfDate, - /// An error occurred. - Error { - #[allow(missing_docs)] - message: String, - }, + /// Loading the dep graph failed. + LoadDepGraph(PathBuf, std::io::Error), + /// Decoding loaded incremental cache failed. + DecodeIncrCache(Box<dyn std::any::Any + Send>), } impl<T: Default> LoadResult<T> { @@ -40,36 +40,31 @@ impl<T: Default> LoadResult<T> { // Check for errors when using `-Zassert-incremental-state` match (sess.opts.assert_incr_state, &self) { (Some(IncrementalStateAssertion::NotLoaded), LoadResult::Ok { .. }) => { - sess.fatal( - "We asserted that the incremental cache should not be loaded, \ - but it was loaded.", - ); + sess.emit_fatal(errors::AssertNotLoaded); } ( Some(IncrementalStateAssertion::Loaded), - LoadResult::Error { .. } | LoadResult::DataOutOfDate, + LoadResult::LoadDepGraph(..) + | LoadResult::DecodeIncrCache(..) + | LoadResult::DataOutOfDate, ) => { - sess.fatal( - "We asserted that an existing incremental cache directory should \ - be successfully loaded, but it was not.", - ); + sess.emit_fatal(errors::AssertLoaded); } _ => {} }; match self { - LoadResult::Error { message } => { - sess.warn(&message); + LoadResult::LoadDepGraph(path, err) => { + sess.emit_warning(errors::LoadDepGraph { path, err }); + Default::default() + } + LoadResult::DecodeIncrCache(err) => { + sess.emit_warning(errors::DecodeIncrCache { err: format!("{err:?}") }); Default::default() } LoadResult::DataOutOfDate => { if let Err(err) = delete_all_session_dir_contents(sess) { - sess.err(&format!( - "Failed to delete invalidated or incompatible \ - incremental compilation session directory contents `{}`: {}.", - dep_graph_path(sess).display(), - err - )); + sess.emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err }); } Default::default() } @@ -90,9 +85,7 @@ fn load_data( // compiler version. Neither is an error. LoadResult::DataOutOfDate } - Err(err) => LoadResult::Error { - message: format!("could not load dep-graph from `{}`: {}", path.display(), err), - }, + Err(err) => LoadResult::LoadDepGraph(path.to_path_buf(), err), } } @@ -114,9 +107,9 @@ impl<T> MaybeAsync<LoadResult<T>> { pub fn open(self) -> LoadResult<T> { match self { MaybeAsync::Sync(result) => result, - MaybeAsync::Async(handle) => handle.join().unwrap_or_else(|e| LoadResult::Error { - message: format!("could not decode incremental cache: {:?}", e), - }), + MaybeAsync::Async(handle) => { + handle.join().unwrap_or_else(|e| LoadResult::DecodeIncrCache(e)) + } } } } @@ -185,7 +178,8 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { match load_data(report_incremental_info, &path, nightly_build) { LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, - LoadResult::Error { message } => LoadResult::Error { message }, + LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), + LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err), LoadResult::Ok { data: (bytes, start_pos) } => { let mut decoder = MemDecoder::new(&bytes, start_pos); let prev_commandline_args_hash = u64::decode(&mut decoder); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 6e9dcdd981ec4..27be56eac6f99 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,3 +1,4 @@ +use crate::errors; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::join; use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; @@ -59,19 +60,14 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { move || { sess.time("incr_comp_persist_dep_graph", || { if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) { - sess.err(&format!( - "failed to write dependency graph to `{}`: {}", - staging_dep_graph_path.display(), - err - )); + sess.emit_err(errors::WriteDepGraph { path: &staging_dep_graph_path, err }); } if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { - sess.err(&format!( - "failed to move dependency graph from `{}` to `{}`: {}", - staging_dep_graph_path.display(), - dep_graph_path.display(), - err - )); + sess.emit_err(errors::MoveDepGraph { + from: &staging_dep_graph_path, + to: &dep_graph_path, + err, + }); } }); }, @@ -163,11 +159,7 @@ pub fn build_dep_graph( let mut encoder = match FileEncoder::new(&path_buf) { Ok(encoder) => encoder, Err(err) => { - sess.err(&format!( - "failed to create dependency graph at `{}`: {}", - path_buf.display(), - err - )); + sess.emit_err(errors::CreateDepGraph { path: &path_buf, err }); return None; } }; diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 2f1853c441eee..dc98fbeb0d166 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -2,6 +2,7 @@ //! //! [work products]: WorkProduct +use crate::errors; use crate::persist::fs::*; use rustc_data_structures::fx::FxHashMap; use rustc_fs_util::link_or_copy; @@ -28,12 +29,11 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( let _ = saved_files.insert(ext.to_string(), file_name); } Err(err) => { - sess.warn(&format!( - "error copying object file `{}` to incremental directory as `{}`: {}", - path.display(), - path_in_incr_dir.display(), - err - )); + sess.emit_warning(errors::CopyWorkProductToCache { + from: &path, + to: &path_in_incr_dir, + err, + }); } } } @@ -49,11 +49,7 @@ pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { for (_, path) in &work_product.saved_files { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { - sess.warn(&format!( - "file-system error deleting outdated file `{}`: {}", - path.display(), - err - )); + sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); } } } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index f817c5bc1cd73..1199ff287c430 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -49,9 +49,6 @@ rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } -[dev-dependencies] -rustc_target = { path = "../rustc_target" } - [features] llvm = ['rustc_codegen_llvm'] rustc_use_parallel_compiler = ['rayon', 'rustc-rayon-core', 'rustc_query_impl/rustc_use_parallel_compiler', 'rustc_errors/rustc_use_parallel_compiler'] diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 15d7e977bbe88..29543fe2f932c 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,5 +1,7 @@ use rustc_macros::Diagnostic; +use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; +use rustc_target::spec::TargetTriple; use std::io; use std::path::Path; @@ -91,3 +93,22 @@ pub struct FailedWritingFile<'a> { #[derive(Diagnostic)] #[diag(interface_proc_macro_crate_panic_abort)] pub struct ProcMacroCratePanicAbort; + +#[derive(Diagnostic)] +#[diag(interface_unsupported_crate_type_for_target)] +pub struct UnsupportedCrateTypeForTarget<'a> { + pub crate_type: CrateType, + pub target_triple: &'a TargetTriple, +} + +#[derive(Diagnostic)] +#[diag(interface_multiple_output_types_adaption)] +pub struct MultipleOutputTypesAdaption; + +#[derive(Diagnostic)] +#[diag(interface_ignoring_extra_filename)] +pub struct IgnoringExtraFilename; + +#[derive(Diagnostic)] +#[diag(interface_ignoring_out_dir)] +pub struct IgnoringOutDir; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 54363e07b971a..e4b4d5375e64a 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,3 +1,4 @@ +use crate::errors; use info; use libloading::Library; use rustc_ast as ast; @@ -472,16 +473,15 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C } base.retain(|crate_type| { - let res = !output::invalid_output_for_target(session, *crate_type); - - if !res { - session.warn(&format!( - "dropping unsupported crate type `{}` for target `{}`", - *crate_type, session.opts.target_triple - )); + if output::invalid_output_for_target(session, *crate_type) { + session.emit_warning(errors::UnsupportedCrateTypeForTarget { + crate_type: *crate_type, + target_triple: &session.opts.target_triple, + }); + false + } else { + true } - - res }); base @@ -517,19 +517,16 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu let unnamed_output_types = sess.opts.output_types.values().filter(|a| a.is_none()).count(); let ofile = if unnamed_output_types > 1 { - sess.warn( - "due to multiple output types requested, the explicitly specified \ - output file name will be adapted for each output type", - ); + sess.emit_warning(errors::MultipleOutputTypesAdaption); None } else { if !sess.opts.cg.extra_filename.is_empty() { - sess.warn("ignoring -C extra-filename flag due to -o flag"); + sess.emit_warning(errors::IgnoringExtraFilename); } Some(out_file.clone()) }; if sess.io.output_dir != None { - sess.warn("ignoring --out-dir flag due to -o flag"); + sess.emit_warning(errors::IgnoringOutDir); } OutputFilenames::new( diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 32338f9dfc5e3..08098c9bb2a85 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -4,7 +4,10 @@ use annotate_snippets::{ }; use fluent_bundle::{FluentBundle, FluentError, FluentResource}; use fluent_syntax::{ - ast::{Attribute, Entry, Identifier, Message}, + ast::{ + Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, + PatternElement, + }, parser::ParserError, }; use proc_macro::{Diagnostic, Level, Span}; @@ -185,9 +188,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok }; let mut constants = TokenStream::new(); + let mut messagerefs = Vec::new(); for entry in resource.entries() { let span = res.krate.span(); - if let Entry::Message(Message { id: Identifier { name }, attributes, .. }) = entry { + if let Entry::Message(Message { id: Identifier { name }, attributes, value, .. }) = + entry + { let _ = previous_defns.entry(name.to_string()).or_insert(path_span); if name.contains('-') { @@ -200,6 +206,18 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok .emit(); } + if let Some(Pattern { elements }) = value { + for elt in elements { + if let PatternElement::Placeable { + expression: + Expression::Inline(InlineExpression::MessageReference { id, .. }), + } = elt + { + messagerefs.push((id.name, *name)); + } + } + } + // Require that the message name starts with the crate name // `hir_typeck_foo_bar` (in `hir_typeck.ftl`) // `const_eval_baz` (in `const_eval.ftl`) @@ -258,6 +276,18 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok } } + for (mref, name) in messagerefs.into_iter() { + if !previous_defns.contains_key(mref) { + Diagnostic::spanned( + path_span, + Level::Error, + format!("referenced message `{mref}` does not exist (in message `{name}`)"), + ) + .help(&format!("you may have meant to use a variable reference (`{{${mref}}}`)")) + .emit(); + } + } + if let Err(errs) = bundle.add_resource(resource) { for e in errs { match e { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4af29fcbfb586..7001f81aa7750 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -31,7 +31,6 @@ pub use generics::*; use rustc_ast as ast; use rustc_ast::node_id::NodeMap; use rustc_attr as attr; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -453,18 +452,6 @@ pub struct CReaderCacheKey { #[rustc_pass_by_value] pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>); -impl<'tcx> TyCtxt<'tcx> { - /// A "bool" type used in rustc_mir_transform unit tests when we - /// have not spun up a TyCtxt. - pub const BOOL_TY_FOR_UNIT_TESTING: Ty<'tcx> = - Ty(Interned::new_unchecked(&WithCachedTypeInfo { - internee: ty::Bool, - stable_hash: Fingerprint::ZERO, - flags: TypeFlags::empty(), - outer_exclusive_binder: DebruijnIndex::from_usize(0), - })); -} - impl ty::EarlyBoundRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 305f0427e501b..31a3ffbb1d891 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -201,7 +201,9 @@ use rustc_target::abi::Size; use std::ops::Range; use std::path::PathBuf; -use crate::errors::{LargeAssignmentsLint, RecursionLimit, TypeLengthLimit}; +use crate::errors::{ + EncounteredErrorWhileInstantiating, LargeAssignmentsLint, RecursionLimit, TypeLengthLimit, +}; #[derive(PartialEq)] pub enum MonoItemCollectionMode { @@ -524,10 +526,10 @@ fn collect_items_rec<'tcx>( && starting_point.node.is_user_defined() { let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string()); - tcx.sess.span_note_without_error( - starting_point.span, - &format!("the above error was encountered while instantiating `{formatted_item}`"), - ); + tcx.sess.emit_note(EncounteredErrorWhileInstantiating { + span: starting_point.span, + formatted_item, + }); } inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors.items); diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 5233cfb21203b..a53bd7e1fef5e 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -83,3 +83,17 @@ pub struct SymbolAlreadyDefined { pub struct CouldntDumpMonoStats { pub error: String, } + +#[derive(Diagnostic)] +#[diag(monomorphize_encountered_error_while_instantiating)] +pub struct EncounteredErrorWhileInstantiating { + #[primary_span] + pub span: Span, + pub formatted_item: String, +} + +#[derive(Diagnostic)] +#[diag(monomorphize_unknown_cgu_collection_mode)] +pub struct UnknownCguCollectionMode<'a> { + pub mode: &'a str, +} diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index fd6bcad18983a..af3f60bb9326d 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -114,7 +114,9 @@ use rustc_span::symbol::Symbol; use crate::collector::InliningMap; use crate::collector::{self, MonoItemCollectionMode}; -use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownPartitionStrategy}; +use crate::errors::{ + CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode, UnknownPartitionStrategy, +}; pub struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -348,17 +350,13 @@ where fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { - let mode_string = s.to_lowercase(); - let mode_string = mode_string.trim(); - if mode_string == "eager" { + let mode = s.to_lowercase(); + let mode = mode.trim(); + if mode == "eager" { MonoItemCollectionMode::Eager } else { - if mode_string != "lazy" { - let message = format!( - "Unknown codegen-item collection mode '{mode_string}'. \ - Falling back to 'lazy' mode." - ); - tcx.sess.warn(&message); + if mode != "lazy" { + tcx.sess.emit_warning(UnknownCguCollectionMode { mode }); } MonoItemCollectionMode::Lazy diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs new file mode 100644 index 0000000000000..386bf026bb4af --- /dev/null +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -0,0 +1,119 @@ +use super::UnmatchedBrace; +use rustc_ast::token::Delimiter; +use rustc_errors::Diagnostic; +use rustc_span::source_map::SourceMap; +use rustc_span::Span; + +#[derive(Default)] +pub struct TokenTreeDiagInfo { + /// Stack of open delimiters and their spans. Used for error message. + pub open_braces: Vec<(Delimiter, Span)>, + pub unmatched_braces: Vec<UnmatchedBrace>, + + /// Used only for error recovery when arriving to EOF with mismatched braces. + pub last_unclosed_found_span: Option<Span>, + + /// Collect empty block spans that might have been auto-inserted by editors. + pub empty_block_spans: Vec<Span>, + + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + pub matching_block_spans: Vec<(Span, Span)>, +} + +pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool { + match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) { + (Some(open_padding), Some(close_padding)) => open_padding == close_padding, + _ => false, + } +} + +// When we get a `)` or `]` for `{`, we should emit help message here +// it's more friendly compared to report `unmatched error` in later phase +pub fn report_missing_open_delim( + err: &mut Diagnostic, + unmatched_braces: &[UnmatchedBrace], +) -> bool { + let mut reported_missing_open = false; + for unmatch_brace in unmatched_braces.iter() { + if let Some(delim) = unmatch_brace.found_delim + && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket) + { + let missed_open = match delim { + Delimiter::Parenthesis => "(", + Delimiter::Bracket => "[", + _ => unreachable!(), + }; + err.span_label( + unmatch_brace.found_span.shrink_to_lo(), + format!("missing open `{}` for this delimiter", missed_open), + ); + reported_missing_open = true; + } + } + reported_missing_open +} + +pub fn report_suspicious_mismatch_block( + err: &mut Diagnostic, + diag_info: &TokenTreeDiagInfo, + sm: &SourceMap, + delim: Delimiter, +) { + if report_missing_open_delim(err, &diag_info.unmatched_braces) { + return; + } + + let mut matched_spans: Vec<(Span, bool)> = diag_info + .matching_block_spans + .iter() + .map(|&(open, close)| (open.with_hi(close.lo()), same_identation_level(sm, open, close))) + .collect(); + + // sort by `lo`, so the large block spans in the front + matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo())); + + // We use larger block whose identation is well to cover those inner mismatched blocks + // O(N^2) here, but we are on error reporting path, so it is fine + for i in 0..matched_spans.len() { + let (block_span, same_ident) = matched_spans[i]; + if same_ident { + for j in i + 1..matched_spans.len() { + let (inner_block, inner_same_ident) = matched_spans[j]; + if block_span.contains(inner_block) && !inner_same_ident { + matched_spans[j] = (inner_block, true); + } + } + } + } + + // Find the inner-most span candidate for final report + let candidate_span = + matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span); + + if let Some(block_span) = candidate_span { + err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed..."); + err.span_label( + block_span.shrink_to_hi(), + "...as it matches this but it has different indentation", + ); + + // If there is a empty block in the mismatched span, note it + if delim == Delimiter::Brace { + for span in diag_info.empty_block_spans.iter() { + if block_span.contains(*span) { + err.span_label(*span, "block is empty, you might have not meant to close it"); + break; + } + } + } + } else { + // If there is no suspicious span, give the last properly closed block may help + if let Some(parent) = diag_info.matching_block_spans.last() + && diag_info.open_braces.last().is_none() + && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { + err.span_label(parent.0, "this opening brace..."); + err.span_label(parent.1, "...matches this closing brace"); + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 9fe8d9836ba60..e957224a03377 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -17,6 +17,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{edition::Edition, BytePos, Pos, Span}; +mod diagnostics; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b2701817d489b..0de8f79112c65 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,29 +1,18 @@ +use super::diagnostics::report_suspicious_mismatch_block; +use super::diagnostics::same_identation_level; +use super::diagnostics::TokenTreeDiagInfo; use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::{PErr, PResult}; -use rustc_span::Span; pub(super) struct TokenTreesReader<'a> { string_reader: StringReader<'a>, /// The "next" token, which has been obtained from the `StringReader` but /// not yet handled by the `TokenTreesReader`. token: Token, - /// Stack of open delimiters and their spans. Used for error message. - open_braces: Vec<(Delimiter, Span)>, - unmatched_braces: Vec<UnmatchedBrace>, - /// The type and spans for all braces - /// - /// Used only for error recovery when arriving to EOF with mismatched braces. - matching_delim_spans: Vec<(Delimiter, Span, Span)>, - last_unclosed_found_span: Option<Span>, - /// Collect empty block spans that might have been auto-inserted by editors. - last_delim_empty_block_spans: FxHashMap<Delimiter, Span>, - /// Collect the spans of braces (Open, Close). Used only - /// for detecting if blocks are empty and only braces. - matching_block_spans: Vec<(Span, Span)>, + diag_info: TokenTreeDiagInfo, } impl<'a> TokenTreesReader<'a> { @@ -33,15 +22,10 @@ impl<'a> TokenTreesReader<'a> { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), - open_braces: Vec::new(), - unmatched_braces: Vec::new(), - matching_delim_spans: Vec::new(), - last_unclosed_found_span: None, - last_delim_empty_block_spans: FxHashMap::default(), - matching_block_spans: Vec::new(), + diag_info: TokenTreeDiagInfo::default(), }; let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.unmatched_braces) + (res, tt_reader.diag_info.unmatched_braces) } // Parse a stream of tokens into a list of `TokenTree`s. @@ -92,9 +76,9 @@ impl<'a> TokenTreesReader<'a> { fn eof_err(&mut self) -> PErr<'a> { let msg = "this file contains an unclosed delimiter"; let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); - for &(_, sp) in &self.open_braces { + for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); - self.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, @@ -103,23 +87,13 @@ impl<'a> TokenTreesReader<'a> { }); } - if let Some((delim, _)) = self.open_braces.last() { - if let Some((_, open_sp, close_sp)) = - self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| { - let sm = self.string_reader.sess.source_map(); - if let Some(close_padding) = sm.span_to_margin(*close_sp) { - if let Some(open_padding) = sm.span_to_margin(*open_sp) { - return delim == d && close_padding != open_padding; - } - } - false - }) - // these are in reverse order as they get inserted on close, but - { - // we want the last open/first close - err.span_label(*open_sp, "this delimiter might not be properly closed..."); - err.span_label(*close_sp, "...as it matches this but it has different indentation"); - } + if let Some((delim, _)) = self.diag_info.open_braces.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + *delim, + ) } err } @@ -128,7 +102,7 @@ impl<'a> TokenTreesReader<'a> { // The span for beginning of the delimited section let pre_span = self.token.span; - self.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_braces.push((open_delim, self.token.span)); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -137,35 +111,29 @@ impl<'a> TokenTreesReader<'a> { // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); + let sm = self.string_reader.sess.source_map(); match self.token.kind { // Correct delimiter. token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.open_braces.pop().unwrap(); + let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); let close_brace_span = self.token.span; - if tts.is_empty() { + if tts.is_empty() && close_delim == Delimiter::Brace { let empty_block_span = open_brace_span.to(close_brace_span); - let sm = self.string_reader.sess.source_map(); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. - self.last_delim_empty_block_spans.insert(open_delim, empty_block_span); + self.diag_info.empty_block_spans.push(empty_block_span); } } - //only add braces + // only add braces if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { - self.matching_block_spans.push((open_brace_span, close_brace_span)); + // Add all the matching spans, we will sort by span later + self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span)); } - if self.open_braces.is_empty() { - // Clear up these spans to avoid suggesting them as we've found - // properly matched delimiters so far for an entire block. - self.matching_delim_spans.clear(); - } else { - self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span)); - } // Move past the closing delimiter. self.token = self.string_reader.next_token().0; } @@ -174,28 +142,25 @@ impl<'a> TokenTreesReader<'a> { let mut unclosed_delimiter = None; let mut candidate = None; - if self.last_unclosed_found_span != Some(self.token.span) { + if self.diag_info.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times - self.last_unclosed_found_span = Some(self.token.span); + self.diag_info.last_unclosed_found_span = Some(self.token.span); // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be // closed! The parser just hasn't gotten to them yet. - if let Some(&(_, sp)) = self.open_braces.last() { + if let Some(&(_, sp)) = self.diag_info.open_braces.last() { unclosed_delimiter = Some(sp); }; - let sm = self.string_reader.sess.source_map(); - if let Some(current_padding) = sm.span_to_margin(self.token.span) { - for (brace, brace_span) in &self.open_braces { - if let Some(padding) = sm.span_to_margin(*brace_span) { - // high likelihood of these two corresponding - if current_padding == padding && brace == &close_delim { - candidate = Some(*brace_span); - } - } + for (brace, brace_span) in &self.diag_info.open_braces { + if same_identation_level(&sm, self.token.span, *brace_span) + && brace == &close_delim + { + // high likelihood of these two corresponding + candidate = Some(*brace_span); } } - let (tok, _) = self.open_braces.pop().unwrap(); - self.unmatched_braces.push(UnmatchedBrace { + let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, @@ -203,7 +168,7 @@ impl<'a> TokenTreesReader<'a> { candidate_span: candidate, }); } else { - self.open_braces.pop(); + self.diag_info.open_braces.pop(); } // If the incorrect delimiter matches an earlier opening @@ -213,7 +178,7 @@ impl<'a> TokenTreesReader<'a> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { self.token = self.string_reader.next_token().0; } } @@ -236,22 +201,12 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - // Braces are added at the end, so the last element is the biggest block - if let Some(parent) = self.matching_block_spans.last() { - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - // Check if the (empty block) is in the last properly closed block - if (parent.0.to(parent.1)).contains(span) { - err.span_label(span, "block is empty, you might have not meant to close it"); - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } - + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + delim, + ); err.span_label(self.token.span, "unexpected closing delimiter"); err } diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index dd8c646a43c82..526b829bf676a 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; -use crate::errors::ExprNotAllowedInContext; +use crate::errors::{ExprNotAllowedInContext, SkippingConstChecks}; /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] @@ -124,7 +124,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { // corresponding feature gate. This encourages nightly users to use feature gates when // possible. None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => { - tcx.sess.span_warn(span, "skipping const checks"); + tcx.sess.emit_warning(SkippingConstChecks { span }); return; } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9e05ad22e6241..97169a6367c3d 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1565,3 +1565,10 @@ pub(crate) struct ProcMacroUnsafe { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(passes_skipping_const_checks)] +pub struct SkippingConstChecks { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index db95b8bca2f8c..3cb41c322f12e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -24,7 +24,7 @@ use rustc_span::RealFileName; use rustc_span::SourceFileHashAlgorithm; use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, HandlerFlags}; +use rustc_errors::{ColorConfig, DiagnosticArgValue, HandlerFlags, IntoDiagnosticArg}; use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, @@ -2692,6 +2692,12 @@ impl fmt::Display for CrateType { } } +impl IntoDiagnosticArg for CrateType { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + #[derive(Copy, Clone, PartialEq, Debug)] pub enum PpSourceMode { /// `-Zunpretty=normal` diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index f5a72573d58cd..8e8fba5e236f5 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -375,3 +375,9 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: } } } + +#[derive(Diagnostic)] +#[diag(session_optimization_fuel_exhausted)] +pub struct OptimisationFuelExhausted { + pub msg: String, +} diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 95f199de6ff6f..91d23f1041fe9 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -5,9 +5,10 @@ use crate::config::Input; use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::errors::{ BranchProtectionRequiresAArch64, CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers, - LinkerPluginToWindowsNotSupported, NotCircumventFeature, ProfileSampleUseFileDoesNotExist, - ProfileUseFileDoesNotExist, SanitizerCfiEnabled, SanitizerNotSupported, SanitizersNotSupported, - SkippingConstChecks, SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget, + LinkerPluginToWindowsNotSupported, NotCircumventFeature, OptimisationFuelExhausted, + ProfileSampleUseFileDoesNotExist, ProfileUseFileDoesNotExist, SanitizerCfiEnabled, + SanitizerNotSupported, SanitizersNotSupported, SkippingConstChecks, + SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget, TargetRequiresUnwindTables, UnleashedFeatureHelp, UnstableVirtualFunctionElimination, UnsupportedDwarfVersion, }; @@ -483,6 +484,8 @@ impl Session { self.diagnostic().span_err_with_code(sp, msg, code) } #[rustc_lint_diagnostics] + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } @@ -583,12 +586,16 @@ impl Session { )) } } + + #[rustc_lint_diagnostics] #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] #[track_caller] pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) { self.diagnostic().span_warn(sp, msg) } + + #[rustc_lint_diagnostics] #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn span_warn_with_code<S: Into<MultiSpan>>( @@ -599,6 +606,10 @@ impl Session { ) { self.diagnostic().span_warn_with_code(sp, msg, code) } + + #[rustc_lint_diagnostics] + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().warn(msg) } @@ -641,11 +652,17 @@ impl Session { self.diagnostic().delay_good_path_bug(msg) } + #[rustc_lint_diagnostics] + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().note_without_error(msg) } #[track_caller] + #[rustc_lint_diagnostics] + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] pub fn span_note_without_error<S: Into<MultiSpan>>( &self, sp: S, @@ -653,6 +670,8 @@ impl Session { ) { self.diagnostic().span_note_without_error(sp, msg) } + + #[rustc_lint_diagnostics] #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn struct_note_without_error( @@ -882,7 +901,7 @@ impl Session { // We only call `msg` in case we can actually emit warnings. // Otherwise, this could cause a `delay_good_path_bug` to // trigger (issue #79546). - self.warn(&format!("optimization-fuel-exhausted: {}", msg())); + self.emit_warning(OptimisationFuelExhausted { msg: msg() }); } fuel.out_of_fuel = true; } else if fuel.remaining > 0 { diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index d23b550621e17..61319a3ed7c19 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,6 +1,8 @@ //! Code shared by trait and projection goals for candidate assembly. use super::infcx_ext::InferCtxtExt; +#[cfg(doc)] +use super::trait_goals::structural_traits::*; use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; @@ -98,52 +100,75 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx>; + // A type implements an `auto trait` if its components do as well. These components + // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A type is `Copy` or `Clone` if its components are `Sized`. These components + // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These + // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A type is `PointerSized` if we can compute its layout, and that layout + // matches the layout of `usize`. fn consider_builtin_pointer_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>` + // family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, ) -> QueryResult<'tcx>; + // `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // `Pointee` is always implemented. + // + // See the projection implementation for the `Metadata` types for all of + // the built-in types. For structs, the metadata type is given by the struct + // tail. fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A generator (that comes from an `async` desugaring) is known to implement + // `Future<Output = O>`, where `O` is given by the generator's return type + // that was computed during type-checking. fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A generator (that doesn't come from an `async` desugaring) is known to + // implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield, + // and return types of the generator computed during type-checking. fn consider_builtin_generator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 7880cbad5fe03..e240dd5a2257a 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -335,15 +335,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // That won't actually reflect in the query response, so it seems moot. self.make_canonical_response(Certainty::AMBIGUOUS) } else { - self.infcx.probe(|_| { - let InferOk { value: (), obligations } = self - .infcx - .at(&ObligationCause::dummy(), goal.param_env) - .sub(goal.predicate.a, goal.predicate.b)?; - self.evaluate_all_and_make_canonical_response( - obligations.into_iter().map(|pred| pred.into()).collect(), - ) - }) + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .sub(goal.predicate.a, goal.predicate.b)?; + self.evaluate_all_and_make_canonical_response( + obligations.into_iter().map(|pred| pred.into()).collect(), + ) } } @@ -376,22 +374,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { &mut self, goal: Goal<'tcx, ty::GenericArg<'tcx>>, ) -> QueryResult<'tcx> { - self.infcx.probe(|_| { - match crate::traits::wf::unnormalized_obligations( - self.infcx, - goal.param_env, - goal.predicate, - ) { - Some(obligations) => self.evaluate_all_and_make_canonical_response( - obligations.into_iter().map(|o| o.into()).collect(), - ), - None => self.make_canonical_response(Certainty::AMBIGUOUS), - } - }) + match crate::traits::wf::unnormalized_obligations( + self.infcx, + goal.param_env, + goal.predicate, + ) { + Some(obligations) => self.evaluate_all_and_make_canonical_response( + obligations.into_iter().map(|o| o.into()).collect(), + ), + None => self.make_canonical_response(Certainty::AMBIGUOUS), + } } } impl<'tcx> EvalCtxt<'_, 'tcx> { + // Recursively evaluates a list of goals to completion, returning the certainty + // of all of the goals. fn evaluate_all( &mut self, mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, @@ -428,6 +426,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + // Recursively evaluates a list of goals to completion, making a query response. + // + // This is just a convenient way of calling [`EvalCtxt::evaluate_all`], + // then [`EvalCtxt::make_canonical_response`]. fn evaluate_all_and_make_canonical_response( &mut self, goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 30902c2bc4506..8ba267b4f48a6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -296,7 +296,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx> { - if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() { + if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() + && poly_projection_pred.projection_def_id() == goal.predicate.def_id() + { ecx.infcx.probe(|_| { let assumption_projection_pred = ecx.infcx.instantiate_bound_vars_with_infer(poly_projection_pred); diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index d74857dc4b480..45b6a5f4ec578 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -65,7 +65,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx> { - if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() { + if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() + && poly_trait_pred.def_id() == goal.predicate.def_id() + { // FIXME: Constness and polarity ecx.infcx.probe(|_| { let assumption_trait_pred = diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index bf5e77e6ce12f..7b742a6a03695 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3759,13 +3759,13 @@ fn hint_missing_borrow<'tcx>( err: &mut Diagnostic, ) { let found_args = match found.kind() { - ty::FnPtr(f) => f.inputs().skip_binder().iter(), + ty::FnPtr(f) => infcx.replace_bound_vars_with_placeholders(*f).inputs().iter(), kind => { span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind) } }; let expected_args = match expected.kind() { - ty::FnPtr(f) => f.inputs().skip_binder().iter(), + ty::FnPtr(f) => infcx.replace_bound_vars_with_placeholders(*f).inputs().iter(), kind => { span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind) } @@ -3776,12 +3776,12 @@ fn hint_missing_borrow<'tcx>( let args = fn_decl.inputs.iter().map(|ty| ty); - fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { - let mut refs = 0; + fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { + let mut refs = vec![]; - while let ty::Ref(_, new_ty, _) = ty.kind() { + while let ty::Ref(_, new_ty, mutbl) = ty.kind() { ty = *new_ty; - refs += 1; + refs.push(*mutbl); } (ty, refs) @@ -3795,11 +3795,21 @@ fn hint_missing_borrow<'tcx>( let (expected_ty, expected_refs) = get_deref_type_and_refs(*expected_arg); if infcx.can_eq(param_env, found_ty, expected_ty).is_ok() { - if found_refs < expected_refs { - to_borrow.push((arg.span.shrink_to_lo(), "&".repeat(expected_refs - found_refs))); - } else if found_refs > expected_refs { + // FIXME: This could handle more exotic cases like mutability mismatches too! + if found_refs.len() < expected_refs.len() + && found_refs[..] == expected_refs[expected_refs.len() - found_refs.len()..] + { + to_borrow.push(( + arg.span.shrink_to_lo(), + expected_refs[..expected_refs.len() - found_refs.len()] + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + )); + } else if found_refs.len() > expected_refs.len() { let mut span = arg.span.shrink_to_lo(); - let mut left = found_refs - expected_refs; + let mut left = found_refs.len() - expected_refs.len(); let mut ty = arg; while let hir::TyKind::Ref(_, mut_ty) = &ty.kind && left > 0 { span = span.with_hi(mut_ty.ty.span.lo()); diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 09b58894d3040..f183248f2d08b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,7 +1,9 @@ use rustc_middle::ty; +use rustc_session::config::TraitSolver; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; +use crate::solve::{Certainty, Goal, InferCtxtEvalExt, MaybeCause}; use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; pub trait InferCtxtExt<'tcx> { @@ -77,12 +79,38 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - let c_pred = self - .canonicalize_query_keep_static(param_env.and(obligation.predicate), &mut _orig_values); - // Run canonical query. If overflow occurs, rerun from scratch but this time - // in standard trait query mode so that overflow is handled appropriately - // within `SelectionContext`. - self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) + if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + let c_pred = self.canonicalize_query_keep_static( + param_env.and(obligation.predicate), + &mut _orig_values, + ); + self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) + } else { + self.probe(|snapshot| { + if let Ok((_, certainty)) = + self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate)) + { + match certainty { + Certainty::Yes => { + if self.opaque_types_added_in_snapshot(snapshot) { + Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) + } else if self.region_constraints_added_in_snapshot(snapshot).is_some() + { + Ok(EvaluationResult::EvaluatedToOkModuloRegions) + } else { + Ok(EvaluationResult::EvaluatedToOk) + } + } + Certainty::Maybe(MaybeCause::Ambiguity) => { + Ok(EvaluationResult::EvaluatedToAmbig) + } + Certainty::Maybe(MaybeCause::Overflow) => Err(OverflowError::Canonical), + } + } else { + Ok(EvaluationResult::EvaluatedToErr) + } + }) + } } // Helper function that canonicalizes and runs the query. If an @@ -92,6 +120,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, ) -> EvaluationResult { + // Run canonical query. If overflow occurs, rerun from scratch but this time + // in standard trait query mode so that overflow is handled appropriately + // within `SelectionContext`. match self.evaluate_obligation(obligation) { Ok(result) => result, Err(OverflowError::Canonical) => { diff --git a/tests/run-make/incremental-session-fail/Makefile b/tests/run-make/incremental-session-fail/Makefile index 0461bb926e76e..6ce1370927bc1 100644 --- a/tests/run-make/incremental-session-fail/Makefile +++ b/tests/run-make/incremental-session-fail/Makefile @@ -9,6 +9,6 @@ all: touch $(SESSION_DIR) # Check exit code is 1 for an error, and not 101 for ICE. $(RUSTC) foo.rs --crate-type=rlib -C incremental=$(SESSION_DIR) > $(OUTPUT_FILE) 2>&1; [ $$? -eq 1 ] - $(CGREP) "Could not create incremental compilation crate directory" < $(OUTPUT_FILE) + $(CGREP) "could not create incremental compilation crate directory" < $(OUTPUT_FILE) # -v tests are fragile, hopefully this text won't change $(CGREP) -v "internal compiler error" < $(OUTPUT_FILE) diff --git a/tests/ui-fulldeps/fluent-messages/missing-message-ref.ftl b/tests/ui-fulldeps/fluent-messages/missing-message-ref.ftl new file mode 100644 index 0000000000000..0cd8229b23010 --- /dev/null +++ b/tests/ui-fulldeps/fluent-messages/missing-message-ref.ftl @@ -0,0 +1 @@ +missing_message_ref = {message} diff --git a/tests/ui-fulldeps/fluent-messages/test.rs b/tests/ui-fulldeps/fluent-messages/test.rs index 4e8147e2b76dc..74303e97dba94 100644 --- a/tests/ui-fulldeps/fluent-messages/test.rs +++ b/tests/ui-fulldeps/fluent-messages/test.rs @@ -96,3 +96,12 @@ mod missing_crate_name { use self::fluent_generated::{DEFAULT_LOCALE_RESOURCES, test_crate_foo, with_hyphens}; } + +mod missing_message_ref { + use super::fluent_messages; + + fluent_messages! { + missing => "./missing-message-ref.ftl" +//~^ ERROR referenced message `message` does not exist + } +} diff --git a/tests/ui-fulldeps/fluent-messages/test.stderr b/tests/ui-fulldeps/fluent-messages/test.stderr index d1cd4fe26da27..2631b0a623275 100644 --- a/tests/ui-fulldeps/fluent-messages/test.stderr +++ b/tests/ui-fulldeps/fluent-messages/test.stderr @@ -93,6 +93,14 @@ LL | test_crate => "./missing-crate-name.ftl", | = help: replace any '-'s with '_'s -error: aborting due to 10 previous errors +error: referenced message `message` does not exist (in message `missing_message_ref`) + --> $DIR/test.rs:104:20 + | +LL | missing => "./missing-message-ref.ftl" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you may have meant to use a variable reference (`{$message}`) + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs new file mode 100644 index 0000000000000..54485262a0cc8 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -0,0 +1,24 @@ +#![feature(let_chains)] +trait Demo {} + +impl dyn Demo { + pub fn report(&self) -> u32 { + let sum = |a: u32, + b: u32, + c: u32| { + a + b + c + }; + sum(1, 2, 3) + } + + fn check(&self, val: Option<u32>, num: Option<u32>) { + if let Some(b) = val + && let Some(c) = num { + && b == c { + //~^ ERROR expected struct + //~| ERROR mismatched types + } + } +} + +fn main() { } //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr new file mode 100644 index 0000000000000..1119edb199f05 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -0,0 +1,37 @@ +error: this file contains an unclosed delimiter + --> $DIR/deli-ident-issue-1.rs:24:66 + | +LL | impl dyn Demo { + | - unclosed delimiter +... +LL | && let Some(c) = num { + | - this delimiter might not be properly closed... +... +LL | } + | - ...as it matches this but it has different indentation +... +LL | fn main() { } + | ^ + +error[E0574]: expected struct, variant or union type, found local variable `c` + --> $DIR/deli-ident-issue-1.rs:17:17 + | +LL | && b == c { + | ^ not a struct, variant or union type + +error[E0308]: mismatched types + --> $DIR/deli-ident-issue-1.rs:17:9 + | +LL | fn check(&self, val: Option<u32>, num: Option<u32>) { + | - expected `()` because of default return type +... +LL | / && b == c { +LL | | +LL | | +LL | | } + | |_________^ expected `()`, found `bool` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0574. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/deli-ident-issue-2.rs b/tests/ui/parser/deli-ident-issue-2.rs new file mode 100644 index 0000000000000..5394760df7026 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-2.rs @@ -0,0 +1,7 @@ +fn main() { + if 1 < 2 { + let _a = vec!]; //~ ERROR mismatched closing delimiter + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr new file mode 100644 index 0000000000000..c8f59c9d32b5f --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-2.stderr @@ -0,0 +1,19 @@ +error: unexpected closing delimiter: `}` + --> $DIR/deli-ident-issue-2.rs:5:1 + | +LL | let _a = vec!]; + | - missing open `[` for this delimiter +LL | } +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `]` + --> $DIR/deli-ident-issue-2.rs:2:14 + | +LL | if 1 < 2 { + | ^ unclosed delimiter +LL | let _a = vec!]; + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.rs b/tests/ui/parser/issue-68987-unmatch-issue-1.rs new file mode 100644 index 0000000000000..30e7ef467368a --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.rs @@ -0,0 +1,12 @@ +// This file has unexpected closing delimiter, + +fn func(o: Option<u32>) { + match o { + Some(_x) => {} // Extra '}' + let _ = if true {}; + } + None => {} + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr new file mode 100644 index 0000000000000..2d873b46193ce --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr @@ -0,0 +1,16 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-1.rs:10:1 + | +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => {} // Extra '}' + | -- block is empty, you might have not meant to close it +LL | let _ = if true {}; +LL | } + | - ...as it matches this but it has different indentation +... +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.rs b/tests/ui/parser/issue-68987-unmatch-issue-2.rs new file mode 100644 index 0000000000000..89aaa68ba4095 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.rs @@ -0,0 +1,14 @@ +// FIXME: this case need more work to fix +// currently the TokenTree matching ')' with '{', which is not user friendly for diagnostics +async fn obstest() -> Result<> { + let obs_connect = || -> Result<(), MyError) { //~ ERROR mismatched closing delimiter + async { + } + } + + if let Ok(version, scene_list) = obs_connect() { + + } else { + + } +} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr new file mode 100644 index 0000000000000..2c08d41a15f1b --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr @@ -0,0 +1,19 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-2.rs:14:1 + | +LL | let obs_connect = || -> Result<(), MyError) { + | - missing open `(` for this delimiter +... +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-2.rs:3:32 + | +LL | async fn obstest() -> Result<> { + | ^ unclosed delimiter +LL | let obs_connect = || -> Result<(), MyError) { + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.rs b/tests/ui/parser/issue-68987-unmatch-issue-3.rs new file mode 100644 index 0000000000000..e98df8d7c3c4e --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.rs @@ -0,0 +1,8 @@ +// the `{` is closed with `)`, there is a missing `(` +fn f(i: u32, j: u32) { + let res = String::new(); + let mut cnt = i; + while cnt < j { + write!&mut res, " "); //~ ERROR mismatched closing delimiter + } +} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr new file mode 100644 index 0000000000000..a3fc46a1e883c --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr @@ -0,0 +1,19 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-3.rs:8:1 + | +LL | write!&mut res, " "); + | - missing open `(` for this delimiter +LL | } +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-3.rs:5:19 + | +LL | while cnt < j { + | ^ unclosed delimiter +LL | write!&mut res, " "); + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue.rs b/tests/ui/parser/issue-68987-unmatch-issue.rs new file mode 100644 index 0000000000000..5a3620bf24bd4 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue.rs @@ -0,0 +1,12 @@ +// This file has unexpected closing delimiter, + +fn func(o: Option<u32>) { + match o { + Some(_x) => // Missing '{' + let _ = if true {}; + } + None => {} + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/issue-68987-unmatch-issue.stderr b/tests/ui/parser/issue-68987-unmatch-issue.stderr new file mode 100644 index 0000000000000..cabd133242f60 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue.stderr @@ -0,0 +1,16 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue.rs:10:1 + | +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => // Missing '{' +LL | let _ = if true {}; + | -- block is empty, you might have not meant to close it +LL | } + | - ...as it matches this but it has different indentation +... +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issue-81827.stderr b/tests/ui/parser/issue-81827.stderr index 069de33919494..867244b72e849 100644 --- a/tests/ui/parser/issue-81827.stderr +++ b/tests/ui/parser/issue-81827.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr index 4737bc71860c2..3cb6d75a6754b 100644 --- a/tests/ui/parser/issues/issue-62973.stderr +++ b/tests/ui/parser/issues/issue-62973.stderr @@ -2,8 +2,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | @@ -13,8 +15,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | diff --git a/tests/ui/parser/issues/issue-63116.stderr b/tests/ui/parser/issues/issue-63116.stderr index cfdd99d1434ae..a1f8a77ffa7c6 100644 --- a/tests/ui/parser/issues/issue-63116.stderr +++ b/tests/ui/parser/issues/issue-63116.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-63116.rs:3:18 | LL | impl W <s(f;Y(;] - | - ^ - | | + | - - ^ + | | | + | | missing open `[` for this delimiter | unclosed delimiter error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` diff --git a/tests/ui/parser/issues/issue-69259.rs b/tests/ui/parser/issues/issue-69259.rs new file mode 100644 index 0000000000000..01fc2c0854647 --- /dev/null +++ b/tests/ui/parser/issues/issue-69259.rs @@ -0,0 +1,3 @@ +fn main() {} + +fn f) {} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-69259.stderr b/tests/ui/parser/issues/issue-69259.stderr new file mode 100644 index 0000000000000..604b7de331946 --- /dev/null +++ b/tests/ui/parser/issues/issue-69259.stderr @@ -0,0 +1,8 @@ +error: unexpected closing delimiter: `)` + --> $DIR/issue-69259.rs:3:5 + | +LL | fn f) {} + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr index 39bf113ef83de..46cbb056d1d88 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr +++ b/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-1.rs:20:1 | LL | fn struct_generic(x: Vec<i32>) { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr index 5d37b216427f6..9ae94c701869b 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr +++ b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr @@ -1,8 +1,12 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-2.rs:14:1 | +LL | match self { + | - this delimiter might not be properly closed... LL | ErrorHandled::Reported => {}} - | -- block is empty, you might have not meant to close it + | --- ...as it matches this but it has different indentation + | | + | block is empty, you might have not meant to close it ... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr index 967a3e6fdc11b..689ce1eb6b704 100644 --- a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | LL | fn main() { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.rs b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.rs new file mode 100644 index 0000000000000..3bf6b7bb9b19e --- /dev/null +++ b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.rs @@ -0,0 +1,28 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct Trader<'a> { + closure: Box<dyn Fn(&mut Trader) + 'a>, +} + +impl<'a> Trader<'a> { + pub fn new() -> Self { + Trader { + closure: Box::new(|_| {}), + } + } + pub fn set_closure(&mut self, function: impl Fn(&mut Trader) + 'a) { + //foo + } +} + +fn main() { + let closure = |trader : Trader| { + println!("Woooosh!"); + }; + + let mut trader = Trader::new(); + trader.set_closure(closure); + //~^ ERROR type mismatch in closure arguments +} diff --git a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr new file mode 100644 index 0000000000000..6820af1fd45c3 --- /dev/null +++ b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr @@ -0,0 +1,26 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/late-bound-in-borrow-closure-sugg.rs:26:24 + | +LL | let closure = |trader : Trader| { + | ----------------- found signature defined here +... +LL | trader.set_closure(closure); + | ----------- ^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected closure signature `for<'a, 'b> fn(&'a mut Trader<'b>) -> _` + found closure signature `for<'a> fn(Trader<'a>) -> _` +note: required by a bound in `Trader::<'a>::set_closure` + --> $DIR/late-bound-in-borrow-closure-sugg.rs:15:50 + | +LL | pub fn set_closure(&mut self, function: impl Fn(&mut Trader) + 'a) { + | ^^^^^^^^^^^^^^^ required by this bound in `Trader::<'a>::set_closure` +help: consider borrowing the argument + | +LL | let closure = |trader : &mut Trader| { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/traits/alias/issue-60755.rs b/tests/ui/traits/alias/issue-60755.rs new file mode 100644 index 0000000000000..6b955a752479f --- /dev/null +++ b/tests/ui/traits/alias/issue-60755.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(trait_alias)] + +struct MyStruct {} +trait MyFn = Fn(&MyStruct); + +fn foo(_: impl MyFn) {} + +fn main() { + foo(|_| {}); +} diff --git a/tests/ui/typeck/issue-107087.rs b/tests/ui/typeck/issue-107087.rs new file mode 100644 index 0000000000000..135cdf19e3e3e --- /dev/null +++ b/tests/ui/typeck/issue-107087.rs @@ -0,0 +1,18 @@ +struct A<T>(T); + +trait Foo { + type B; +} + +impl Foo for A<u32> { + type B = i32; +} + +impl Foo for A<i32> { + type B = i32; +} + +fn main() { + A::B::<>::C + //~^ ERROR ambiguous associated type +} diff --git a/tests/ui/typeck/issue-107087.stderr b/tests/ui/typeck/issue-107087.stderr new file mode 100644 index 0000000000000..70f19320802b9 --- /dev/null +++ b/tests/ui/typeck/issue-107087.stderr @@ -0,0 +1,9 @@ +error[E0223]: ambiguous associated type + --> $DIR/issue-107087.rs:16:5 + | +LL | A::B::<>::C + | ^^^^^^^^ help: use the fully-qualified path: `<A<_> as Foo>::B` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/typeck/issue-91334.stderr b/tests/ui/typeck/issue-91334.stderr index 8508f7a38e239..78f392c9a8acc 100644 --- a/tests/ui/typeck/issue-91334.stderr +++ b/tests/ui/typeck/issue-91334.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter